Merge "Wrap ViewGroup content in save/restore to protect composited children"
diff --git a/Android.mk b/Android.mk
index 3a3756b..8f7779e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -94,8 +94,6 @@
core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
core/java/android/app/backup/IRestoreObserver.aidl \
core/java/android/app/backup/IRestoreSession.aidl \
- core/java/android/app/maintenance/IIdleCallback.aidl \
- core/java/android/app/maintenance/IIdleService.aidl \
core/java/android/bluetooth/IBluetooth.aidl \
core/java/android/bluetooth/IBluetoothA2dp.aidl \
core/java/android/bluetooth/IBluetoothA2dpSink.aidl \
@@ -177,6 +175,7 @@
core/java/android/nfc/INfcAdapterExtras.aidl \
core/java/android/nfc/INfcTag.aidl \
core/java/android/nfc/INfcCardEmulation.aidl \
+ core/java/android/nfc/INfcLockscreenDispatch.aidl \
core/java/android/os/IBatteryPropertiesListener.aidl \
core/java/android/os/IBatteryPropertiesRegistrar.aidl \
core/java/android/os/ICancellationSignal.aidl \
@@ -346,6 +345,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 \
@@ -363,7 +363,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
@@ -591,9 +591,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 \
@@ -610,8 +610,12 @@
framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
# The since flag (-since N.xml API_LEVEL) is used to add API Level information
# to the reference documentation. Must be in order of oldest to newest.
+#
+# Conscrypt (com.android.org.conscrypt) is an implementation detail and should
+# not be referenced in the documentation.
framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-knowntags ./frameworks/base/docs/knowntags.txt \
+ -hidePackage com.android.org.conscrypt \
-since $(SRC_API_DIR)/1.xml 1 \
-since $(SRC_API_DIR)/2.xml 2 \
-since $(SRC_API_DIR)/3.xml 3 \
@@ -631,7 +635,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:= \
@@ -900,7 +904,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/CleanSpec.mk b/CleanSpec.mk
index 5d92792..1968a78 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -34,9 +34,9 @@
# made today requires touching the same file, just copy the old
# touch step and add it to the end of the list.
#
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
+# *****************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
+# *****************************************************************
# For example:
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
@@ -194,10 +194,15 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/ITv*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates)
-
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/task)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/task)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/TaskManager)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/maintenance)
+
+# ******************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
+# ******************************************************************
diff --git a/api/current.txt b/api/current.txt
index 2f813ac..f788098 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -91,6 +91,7 @@
field public static final java.lang.String NFC = "android.permission.NFC";
field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
+ field public static final java.lang.String READ_ALL_VOICEMAIL = "com.android.voicemail.permission.READ_ALL_VOICEMAIL";
field public static final java.lang.String READ_CALENDAR = "android.permission.READ_CALENDAR";
field public static final java.lang.String READ_CALL_LOG = "android.permission.READ_CALL_LOG";
field public static final java.lang.String READ_CONTACTS = "android.permission.READ_CONTACTS";
@@ -320,6 +321,8 @@
field public static final int backgroundDimEnabled = 16843295; // 0x101021f
field public static final int backgroundSplit = 16843659; // 0x101038b
field public static final int backgroundStacked = 16843658; // 0x101038a
+ field public static final int backgroundTint = 16843885; // 0x101046d
+ field public static final int backgroundTintMode = 16843886; // 0x101046e
field public static final int backupAgent = 16843391; // 0x101027f
field public static final int banner = 16843762; // 0x10103f2
field public static final int baseline = 16843548; // 0x101031c
@@ -344,6 +347,8 @@
field public static final int buttonStyleInset = 16842826; // 0x101004a
field public static final int buttonStyleSmall = 16842825; // 0x1010049
field public static final int buttonStyleToggle = 16842827; // 0x101004b
+ field public static final int buttonTint = 16843889; // 0x1010471
+ field public static final int buttonTintMode = 16843890; // 0x1010472
field public static final int cacheColorHint = 16843009; // 0x1010101
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
@@ -554,6 +559,8 @@
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
+ field public static final int foregroundTint = 16843887; // 0x101046f
+ field public static final int foregroundTintMode = 16843888; // 0x1010470
field public static final int format = 16843013; // 0x1010105
field public static final int format12Hour = 16843722; // 0x10103ca
field public static final int format24Hour = 16843723; // 0x10103cb
@@ -573,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
@@ -636,6 +644,8 @@
field public static final int indeterminateDuration = 16843069; // 0x101013d
field public static final int indeterminateOnly = 16843066; // 0x101013a
field public static final int indeterminateProgressStyle = 16843544; // 0x1010318
+ field public static final int indeterminateTint = 16843883; // 0x101046b
+ field public static final int indeterminateTintMode = 16843884; // 0x101046c
field public static final int indicatorEnd = 16843730; // 0x10103d2
field public static final int indicatorLeft = 16843021; // 0x101010d
field public static final int indicatorRight = 16843022; // 0x101010e
@@ -885,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
@@ -911,6 +921,8 @@
field public static final int privateImeOptions = 16843299; // 0x1010223
field public static final int process = 16842769; // 0x1010011
field public static final int progress = 16843063; // 0x1010137
+ field public static final int progressBackgroundTint = 16843879; // 0x1010467
+ field public static final int progressBackgroundTintMode = 16843880; // 0x1010468
field public static final int progressBarPadding = 16843545; // 0x1010319
field public static final int progressBarStyle = 16842871; // 0x1010077
field public static final int progressBarStyleHorizontal = 16842872; // 0x1010078
@@ -921,8 +933,12 @@
field public static final int progressBarStyleSmallInverse = 16843400; // 0x1010288
field public static final int progressBarStyleSmallTitle = 16843279; // 0x101020f
field public static final int progressDrawable = 16843068; // 0x101013c
+ field public static final int progressTint = 16843877; // 0x1010465
+ 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
@@ -941,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
@@ -1013,6 +1030,8 @@
field public static final int searchSuggestSelection = 16843224; // 0x10101d8
field public static final int searchSuggestThreshold = 16843373; // 0x101026d
field public static final int secondaryProgress = 16843064; // 0x1010138
+ field public static final int secondaryProgressTint = 16843881; // 0x1010469
+ field public static final int secondaryProgressTintMode = 16843882; // 0x101046a
field public static final int seekBarStyle = 16842875; // 0x101007b
field public static final int segmentedButtonStyle = 16843568; // 0x1010330
field public static final int selectAllOnFocus = 16843102; // 0x101015e
@@ -1216,6 +1235,8 @@
field public static final int thumb = 16843074; // 0x1010142
field public static final int thumbOffset = 16843075; // 0x1010143
field public static final int thumbTextPadding = 16843634; // 0x1010372
+ field public static final int thumbTint = 16843891; // 0x1010473
+ field public static final int thumbTintMode = 16843892; // 0x1010474
field public static final int thumbnail = 16843429; // 0x10102a5
field public static final int tileMode = 16843265; // 0x1010201
field public static final int timeZone = 16843724; // 0x10103cc
@@ -2504,7 +2525,14 @@
public static final class R.transition {
ctor public R.transition();
+ field public static final int explode = 17760259; // 0x10f0003
+ field public static final int fade = 17760258; // 0x10f0002
+ field public static final int move = 17760257; // 0x10f0001
field public static final int no_transition = 17760256; // 0x10f0000
+ field public static final int slide_bottom = 17760260; // 0x10f0004
+ field public static final int slide_left = 17760263; // 0x10f0007
+ field public static final int slide_right = 17760262; // 0x10f0006
+ field public static final int slide_top = 17760261; // 0x10f0005
}
public static final class R.xml {
@@ -4203,6 +4231,9 @@
method public abstract android.app.FragmentTransaction setBreadCrumbTitle(java.lang.CharSequence);
method public abstract android.app.FragmentTransaction setCustomAnimations(int, int);
method public abstract android.app.FragmentTransaction setCustomAnimations(int, int, int, int);
+ method public abstract android.app.FragmentTransaction setCustomTransition(int, int);
+ method public abstract android.app.FragmentTransaction setSharedElement(android.view.View, java.lang.String);
+ method public abstract android.app.FragmentTransaction setSharedElements(android.util.Pair<android.view.View, java.lang.String>...);
method public abstract android.app.FragmentTransaction setTransition(int);
method public abstract android.app.FragmentTransaction setTransitionStyle(int);
method public abstract android.app.FragmentTransaction show(android.app.Fragment);
@@ -5174,6 +5205,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);
@@ -5183,6 +5215,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";
@@ -5190,6 +5223,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 {
@@ -5197,7 +5232,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);
@@ -5224,12 +5259,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();
@@ -5243,7 +5281,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);
@@ -5262,6 +5300,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";
@@ -8042,6 +8081,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
@@ -8053,13 +8093,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
@@ -8084,6 +8126,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;
@@ -8105,6 +8148,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
@@ -8141,6 +8185,8 @@
field public int requiresSmallestWidthDp;
field public java.lang.String[] sharedLibraryFiles;
field public java.lang.String sourceDir;
+ field public java.lang.String[] splitPublicSourceDirs;
+ field public java.lang.String[] splitSourceDirs;
field public int targetSdkVersion;
field public java.lang.String taskAffinity;
field public int theme;
@@ -8209,6 +8255,8 @@
field public boolean handleProfiling;
field public java.lang.String publicSourceDir;
field public java.lang.String sourceDir;
+ field public java.lang.String[] splitPublicSourceDirs;
+ field public java.lang.String[] splitSourceDirs;
field public java.lang.String targetPackage;
}
@@ -8238,15 +8286,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);
@@ -10208,6 +10267,7 @@
method public void concat(android.graphics.Matrix);
method public void drawARGB(int, int, int, int);
method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
+ method public void drawArc(float, float, float, float, float, float, boolean, android.graphics.Paint);
method public void drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint);
method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint);
method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint);
@@ -10222,6 +10282,7 @@
method public void drawLines(float[], int, int, android.graphics.Paint);
method public void drawLines(float[], android.graphics.Paint);
method public void drawOval(android.graphics.RectF, android.graphics.Paint);
+ method public void drawOval(float, float, float, float, android.graphics.Paint);
method public void drawPaint(android.graphics.Paint);
method public void drawPath(android.graphics.Path, android.graphics.Paint);
method public void drawPicture(android.graphics.Picture);
@@ -10731,6 +10792,7 @@
ctor public Path();
ctor public Path(android.graphics.Path);
method public void addArc(android.graphics.RectF, float, float);
+ method public void addArc(float, float, float, float, float, float);
method public void addCircle(float, float, float, android.graphics.Path.Direction);
method public void addOval(android.graphics.RectF, android.graphics.Path.Direction);
method public void addOval(float, float, float, float, android.graphics.Path.Direction);
@@ -10740,9 +10802,12 @@
method public void addRect(android.graphics.RectF, android.graphics.Path.Direction);
method public void addRect(float, float, float, float, android.graphics.Path.Direction);
method public void addRoundRect(android.graphics.RectF, float, float, android.graphics.Path.Direction);
+ method public void addRoundRect(float, float, float, float, float, float, android.graphics.Path.Direction);
method public void addRoundRect(android.graphics.RectF, float[], android.graphics.Path.Direction);
+ method public void addRoundRect(float, float, float, float, float[], android.graphics.Path.Direction);
method public void arcTo(android.graphics.RectF, float, float, boolean);
method public void arcTo(android.graphics.RectF, float, float);
+ method public void arcTo(float, float, float, float, float, float, boolean);
method public void close();
method public void computeBounds(android.graphics.RectF, boolean);
method public void cubicTo(float, float, float, float, float, float);
@@ -12778,6 +12843,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 {
@@ -13685,7 +13751,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
@@ -13737,6 +13805,7 @@
method public deprecated void setWiredHeadsetOn(boolean);
method public deprecated boolean shouldVibrate(int);
method public void startBluetoothSco();
+ method public void startBluetoothScoVirtualCall();
method public void stopBluetoothSco();
method public void unloadSoundEffects();
method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
@@ -13759,6 +13828,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";
@@ -14420,6 +14490,11 @@
method public java.lang.String getDefaultUrl();
}
+ public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
+ ctor public MediaDrm.MediaDrmStateException(int, java.lang.String);
+ method public int getErrorCode();
+ }
+
public static abstract interface MediaDrm.OnEventListener {
method public abstract void onEvent(android.media.MediaDrm, byte[], int, int, byte[]);
}
@@ -15288,6 +15363,19 @@
ctor public UnsupportedSchemeException(java.lang.String);
}
+ public abstract class VolumeProvider {
+ ctor public VolumeProvider(int, int);
+ method public final int getMaxVolume();
+ method public final int getVolumeControl();
+ method public final void notifyVolumeChanged();
+ method public void onAdjustVolumeBy(int);
+ method public abstract int onGetCurrentVolume();
+ method public void onSetVolumeTo(int);
+ field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+ field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+ field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+ }
+
}
package android.media.audiofx {
@@ -15654,6 +15742,7 @@
method public android.media.session.PlaybackState getPlaybackState();
method public int getRatingType();
method public android.media.session.MediaController.TransportControls getTransportControls();
+ method public android.media.session.MediaController.VolumeInfo getVolumeInfo();
method public void removeCallback(android.media.session.MediaController.Callback);
method public void sendControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
}
@@ -15677,6 +15766,14 @@
method public void stop();
}
+ public static final class MediaController.VolumeInfo {
+ method public int getAudioStream();
+ method public int getCurrentVolume();
+ method public int getMaxVolume();
+ method public int getVolumeControl();
+ method public int getVolumeType();
+ }
+
public final class MediaSession {
method public void addCallback(android.media.session.MediaSession.Callback);
method public void addCallback(android.media.session.MediaSession.Callback, android.os.Handler);
@@ -15694,9 +15791,11 @@
method public void setMetadata(android.media.MediaMetadata);
method public void setPlaybackState(android.media.session.PlaybackState);
method public void setPlaybackToLocal(int);
- method public void setPlaybackToRemote(android.media.session.RemoteVolumeProvider);
+ method public void setPlaybackToRemote(android.media.VolumeProvider);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+ field public static final int VOLUME_TYPE_LOCAL = 1; // 0x1
+ field public static final int VOLUME_TYPE_REMOTE = 2; // 0x2
}
public static abstract class MediaSession.Callback {
@@ -15767,19 +15866,6 @@
field public static final int STATE_STOPPED = 1; // 0x1
}
- public abstract class RemoteVolumeProvider {
- ctor public RemoteVolumeProvider(int, int);
- method public final int getMaxVolume();
- method public final int getVolumeControl();
- method public final void notifyVolumeChanged();
- method public void onAdjustVolumeBy(int);
- method public abstract int onGetCurrentVolume();
- method public void onSetVolumeTo(int);
- field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
- field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
- field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
- }
-
}
package android.media.tv {
@@ -15791,7 +15877,9 @@
method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName);
method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName, boolean);
method public static final android.net.Uri buildProgramUri(long);
+ method public static final android.net.Uri buildProgramsUriForChannel(long);
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+ method public static final android.net.Uri buildProgramsUriForChannel(long, long, long);
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
field public static final java.lang.String AUTHORITY = "android.media.tv";
}
@@ -15801,6 +15889,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";
@@ -15814,6 +15903,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;
@@ -15845,6 +15935,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 {
@@ -15865,6 +15971,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;
@@ -15873,17 +15981,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 {
@@ -16370,6 +16478,18 @@
method public android.net.NetworkRequest.Builder removeTransportType(int);
}
+ public abstract interface PSKKeyManager {
+ method public abstract java.lang.String chooseClientKeyIdentity(java.lang.String, java.net.Socket);
+ method public abstract java.lang.String chooseClientKeyIdentity(java.lang.String, javax.net.ssl.SSLEngine);
+ method public abstract java.lang.String chooseServerKeyIdentityHint(java.net.Socket);
+ method public abstract java.lang.String chooseServerKeyIdentityHint(javax.net.ssl.SSLEngine);
+ method public abstract javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, java.net.Socket);
+ method public abstract javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, javax.net.ssl.SSLEngine);
+ field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80
+ field public static final int MAX_IDENTITY_LENGTH_BYTES = 128; // 0x80
+ field public static final int MAX_KEY_LENGTH_BYTES = 256; // 0x100
+ }
+
public class ParseException extends java.lang.RuntimeException {
field public java.lang.String response;
}
@@ -17544,6 +17664,7 @@
method public boolean invokeBeam(android.app.Activity);
method public boolean isEnabled();
method public boolean isNdefPushEnabled();
+ method public boolean registerLockscreenDispatch(android.nfc.NfcAdapter.NfcLockscreenDispatch, java.lang.String[]);
method public void setBeamPushUris(android.net.Uri[], android.app.Activity);
method public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
@@ -17579,6 +17700,10 @@
method public abstract android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
}
+ public static abstract interface NfcAdapter.NfcLockscreenDispatch {
+ method public abstract boolean onTagDetected(android.nfc.Tag);
+ }
+
public static abstract interface NfcAdapter.OnNdefPushCompleteCallback {
method public abstract void onNdefPushComplete(android.nfc.NfcEvent);
}
@@ -21502,6 +21627,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);
@@ -21519,7 +21645,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";
@@ -23366,7 +23492,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";
@@ -25993,29 +26119,44 @@
package android.service.fingerprint {
public class FingerprintManager {
- ctor public FingerprintManager(android.content.Context);
method public void enroll(long);
+ method public void enrollCancel();
method public boolean enrolledAndEnabled();
method public void remove(int);
method public void startListening(android.service.fingerprint.FingerprintManagerReceiver);
method public void stopListening();
+ field public static final int FINGERPRINT_ACQUIRED = 1; // 0x1
+ field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
+ field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4; // 0x4
+ field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
+ field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
+ field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16; // 0x10
+ field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8; // 0x8
field public static final int FINGERPRINT_ERROR = -1; // 0xffffffff
- field public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; // 0x2
field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; // 0xfffffff6
field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
- field public static final int FINGERPRINT_SCANNED = 1; // 0x1
- field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; // 0x2
+ field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
+ field public static final int FINGERPRINT_PROCESSED = 2; // 0x2
+ field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3; // 0x3
field public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; // 0x4
}
public class FingerprintManagerReceiver {
ctor public FingerprintManagerReceiver();
+ method public void onAcquired(int);
method public void onEnrollResult(int, int);
method public void onError(int);
+ method public void onProcessed(int);
method public void onRemoved(int);
- method public void onScanned(int, int);
+ }
+
+ public class FingerprintUtils {
+ ctor public FingerprintUtils();
+ method public static void addFingerprintIdForUser(int, android.content.ContentResolver, int);
+ method public static int[] getFingerprintIdsForUser(android.content.ContentResolver, int);
+ method public static boolean removeFingerprintIdForUser(int, android.content.ContentResolver, int);
}
}
@@ -27665,8 +27806,17 @@
}
public class Subscription implements android.os.Parcelable {
- ctor public Subscription();
+ ctor public Subscription(android.content.ComponentName, java.lang.String, android.net.Uri, int, int, int, boolean, boolean);
method public int describeContents();
+ method public android.content.ComponentName getComponentName();
+ method public android.net.Uri getHandle();
+ method public android.graphics.drawable.Drawable getIcon(android.content.Context);
+ method public android.graphics.drawable.Drawable getIcon(android.content.Context, int);
+ method public java.lang.String getId();
+ method public java.lang.String getLabel(android.content.Context);
+ method public java.lang.String getShortDescription(android.content.Context);
+ method public boolean isEnabled();
+ method public boolean isSystemDefault();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -27676,7 +27826,6 @@
field public static final java.lang.String ACTION_CALL_SERVICE;
field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
field public static final java.lang.String ACTION_CALL_SERVICE_SELECTOR;
- field public static final java.lang.String ACTION_CHANGE_DEFAULT_PHONE = "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
@@ -27684,7 +27833,7 @@
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
- field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
+ field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
}
public class TelecommManager {
@@ -28129,6 +28278,7 @@
method public java.lang.String getSimSerialNumber();
method public int getSimState();
method public java.lang.String getSubscriberId();
+ method public java.util.List<android.telecomm.Subscription> getSubscriptions();
method public java.lang.String getVoiceMailAlphaTag();
method public java.lang.String getVoiceMailNumber();
method public boolean hasIccCard();
@@ -28153,6 +28303,7 @@
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
field public static final java.lang.String EXTRA_STATE_RINGING;
+ field public static final java.lang.String EXTRA_SUBSCRIPTION = "subscription";
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -31233,6 +31384,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();
@@ -31241,6 +31393,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);
@@ -32473,6 +32626,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);
@@ -32488,6 +32642,8 @@
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
method public android.graphics.drawable.Drawable getBackground();
+ method public android.content.res.ColorStateList getBackgroundTint();
+ method public android.graphics.PorterDuff.Mode getBackgroundTintMode();
method public int getBaseline();
method public final int getBottom();
method protected float getBottomFadingEdgeStrength();
@@ -32765,6 +32921,8 @@
method public void setBackgroundColor(int);
method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
method public void setBackgroundResource(int);
+ method public void setBackgroundTint(android.content.res.ColorStateList);
+ method public void setBackgroundTintMode(android.graphics.PorterDuff.Mode);
method public final void setBottom(int);
method public void setCameraDistance(float);
method public void setClickable(boolean);
@@ -35047,12 +35205,15 @@
method public java.lang.String getCookie(java.lang.String);
method public static synchronized android.webkit.CookieManager getInstance();
method public synchronized boolean hasCookies();
- method public void removeAllCookie();
- method public void removeExpiredCookie();
- method public void removeSessionCookie();
+ method public deprecated void removeAllCookie();
+ method public void removeAllCookies(android.webkit.ValueCallback<java.lang.Boolean>);
+ method public deprecated void removeExpiredCookie();
+ method public deprecated void removeSessionCookie();
+ method public void removeSessionCookies(android.webkit.ValueCallback<java.lang.Boolean>);
method public synchronized void setAcceptCookie(boolean);
method public static void setAcceptFileSchemeCookies(boolean);
method public void setCookie(java.lang.String, java.lang.String);
+ method public void setCookie(java.lang.String, java.lang.String, android.webkit.ValueCallback<java.lang.Boolean>);
}
public final class CookieSyncManager extends android.webkit.WebSyncManager {
@@ -35728,10 +35889,14 @@
method public boolean getSplitTrack();
method public android.graphics.drawable.Drawable getThumb();
method public int getThumbOffset();
+ method public android.content.res.ColorStateList getThumbTint();
+ method public android.graphics.PorterDuff.Mode getThumbTintMode();
method public void setKeyProgressIncrement(int);
method public void setSplitTrack(boolean);
method public void setThumb(android.graphics.drawable.Drawable);
method public void setThumbOffset(int);
+ method public void setThumbTint(android.content.res.ColorStateList);
+ method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
}
public abstract class AbsSpinner extends android.widget.AdapterView {
@@ -36146,9 +36311,13 @@
ctor public CompoundButton(android.content.Context, android.util.AttributeSet);
ctor public CompoundButton(android.content.Context, android.util.AttributeSet, int);
ctor public CompoundButton(android.content.Context, android.util.AttributeSet, int, int);
+ method public android.content.res.ColorStateList getButtonTint();
+ method public android.graphics.PorterDuff.Mode getButtonTintMode();
method public boolean isChecked();
method public void setButtonDrawable(int);
method public void setButtonDrawable(android.graphics.drawable.Drawable);
+ method public void setButtonTint(android.content.res.ColorStateList);
+ method public void setButtonTintMode(android.graphics.PorterDuff.Mode);
method public void setChecked(boolean);
method public void setOnCheckedChangeListener(android.widget.CompoundButton.OnCheckedChangeListener);
method public void toggle();
@@ -36409,10 +36578,14 @@
method public deprecated boolean getConsiderGoneChildrenWhenMeasuring();
method public android.graphics.drawable.Drawable getForeground();
method public int getForegroundGravity();
+ method public android.content.res.ColorStateList getForegroundTint();
+ method public android.graphics.PorterDuff.Mode getForegroundTintMode();
method public boolean getMeasureAllChildren();
method protected void onLayout(boolean, int, int, int, int);
method public void setForeground(android.graphics.drawable.Drawable);
method public void setForegroundGravity(int);
+ method public void setForegroundTint(android.content.res.ColorStateList);
+ method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
method public void setMeasureAllChildren(boolean);
}
@@ -36621,6 +36794,8 @@
method public int getMaxHeight();
method public int getMaxWidth();
method public android.widget.ImageView.ScaleType getScaleType();
+ method public android.content.res.ColorStateList getTint();
+ method public android.graphics.PorterDuff.Mode getTintMode();
method public int[] onCreateDrawableState(int);
method public void setAdjustViewBounds(boolean);
method public deprecated void setAlpha(int);
@@ -36642,6 +36817,8 @@
method public void setMaxHeight(int);
method public void setMaxWidth(int);
method public void setScaleType(android.widget.ImageView.ScaleType);
+ method public void setTint(android.content.res.ColorStateList);
+ method public void setTintMode(android.graphics.PorterDuff.Mode);
}
public static final class ImageView.ScaleType extends java.lang.Enum {
@@ -37014,11 +37191,19 @@
ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int);
ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int, int);
method public android.graphics.drawable.Drawable getIndeterminateDrawable();
+ method public android.content.res.ColorStateList getIndeterminateTint();
+ method public android.graphics.PorterDuff.Mode getIndeterminateTintMode();
method public android.view.animation.Interpolator getInterpolator();
method public synchronized int getMax();
method public synchronized int getProgress();
+ method public android.content.res.ColorStateList getProgressBackgroundTint();
+ method public android.graphics.PorterDuff.Mode getProgressBackgroundTintMode();
method public android.graphics.drawable.Drawable getProgressDrawable();
+ method public android.content.res.ColorStateList getProgressTint();
+ method public android.graphics.PorterDuff.Mode getProgressTintMode();
method public synchronized int getSecondaryProgress();
+ method public android.content.res.ColorStateList getSecondaryProgressTint();
+ method public android.graphics.PorterDuff.Mode getSecondaryProgressTintMode();
method public final synchronized void incrementProgressBy(int);
method public final synchronized void incrementSecondaryProgressBy(int);
method public synchronized boolean isIndeterminate();
@@ -37027,13 +37212,21 @@
method public synchronized void setIndeterminate(boolean);
method public void setIndeterminateDrawable(android.graphics.drawable.Drawable);
method public void setIndeterminateDrawableTiled(android.graphics.drawable.Drawable);
+ method public void setIndeterminateTint(android.content.res.ColorStateList);
+ method public void setIndeterminateTintMode(android.graphics.PorterDuff.Mode);
method public void setInterpolator(android.content.Context, int);
method public void setInterpolator(android.view.animation.Interpolator);
method public synchronized void setMax(int);
method public synchronized void setProgress(int);
+ method public void setProgressBackgroundTint(android.content.res.ColorStateList);
+ method public void setProgressBackgroundTintMode(android.graphics.PorterDuff.Mode);
method public void setProgressDrawable(android.graphics.drawable.Drawable);
method public void setProgressDrawableTiled(android.graphics.drawable.Drawable);
+ method public void setProgressTint(android.content.res.ColorStateList);
+ method public void setProgressTintMode(android.graphics.PorterDuff.Mode);
method public synchronized void setSecondaryProgress(int);
+ method public void setSecondaryProgressTint(android.content.res.ColorStateList);
+ method public void setSecondaryProgressTintMode(android.graphics.PorterDuff.Mode);
}
public class QuickContactBadge extends android.widget.ImageView implements android.view.View.OnClickListener {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9c1f1d1..127b0fc 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -44,6 +44,7 @@
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.AndroidException;
import android.view.IWindowManager;
import android.view.View;
@@ -1717,11 +1718,7 @@
}
System.out.println("config: " + Configuration.resourceQualifierString(config));
- System.out.print("abi: " + Build.CPU_ABI);
- if (!Build.CPU_ABI2.isEmpty()) {
- System.out.print("," + Build.CPU_ABI2);
- }
- System.out.println();
+ System.out.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
} catch (RemoteException e) {
}
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@[email protected]@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 a59f5d31..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/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 47047b8..f85a7dc 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -58,11 +58,13 @@
import java.util.Comparator;
import java.util.List;
import java.util.WeakHashMap;
+
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.android.internal.content.PackageHelper;
+import com.android.internal.util.ArrayUtils;
public final class Pm {
IPackageManager mPm;
@@ -1548,6 +1550,12 @@
if (info != null && info.applicationInfo != null) {
System.out.print("package:");
System.out.println(info.applicationInfo.sourceDir);
+ if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) {
+ for (String splitSourceDir : info.applicationInfo.splitSourceDirs) {
+ System.out.print("package:");
+ System.out.println(splitSourceDir);
+ }
+ }
}
} catch (RemoteException e) {
System.err.println(e.toString());
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 6296dd1..6b2a0e2 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -52,13 +52,13 @@
);
}
-static SkBitmap::Config flinger2skia(PixelFormat f)
+static SkColorType flinger2skia(PixelFormat f)
{
switch (f) {
case PIXEL_FORMAT_RGB_565:
- return SkBitmap::kRGB_565_Config;
+ return kRGB_565_SkColorType;
default:
- return SkBitmap::kARGB_8888_Config;
+ return kN32_SkColorType;
}
}
@@ -174,9 +174,10 @@
if (base) {
if (png) {
+ const SkImageInfo info = SkImageInfo::Make(w, h, flinger2skia(f),
+ kPremul_SkAlphaType);
SkBitmap b;
- b.setConfig(flinger2skia(f), w, h, s*bytesPerPixel(f));
- b.setPixels((void*)base);
+ b.installPixels(info, const_cast<void*>(base), s*bytesPerPixel(f));
SkDynamicMemoryWStream stream;
SkImageEncoder::EncodeStream(&stream, b,
SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 06f5aca..0f5e954 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -17,14 +17,18 @@
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.Log;
+import android.util.PathParser;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;
+import android.view.InflateException;
import android.view.animation.AnimationUtils;
import com.android.internal.R;
@@ -45,7 +49,7 @@
* <em>something</em> file.)
*/
public class AnimatorInflater {
-
+ private static final String TAG = "AnimatorInflater";
/**
* These flags are used when parsing AnimatorSet objects
*/
@@ -57,9 +61,12 @@
*/
private static final int VALUE_TYPE_FLOAT = 0;
private static final int VALUE_TYPE_INT = 1;
+ private static final int VALUE_TYPE_PATH = 2;
private static final int VALUE_TYPE_COLOR = 4;
private static final int VALUE_TYPE_CUSTOM = 5;
+ private static final boolean DBG_ANIMATOR_INFLATER = false;
+
/**
* Loads an {@link Animator} object from a resource
*
@@ -158,7 +165,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,55 +193,242 @@
}
}
+ /**
+ * PathDataEvaluator is used to interpolate between two paths which are
+ * represented in the same format but different control points' values.
+ * The path is represented as an array of PathDataNode here, which is
+ * fundamentally an array of floating point numbers.
+ */
+ private static class PathDataEvaluator implements TypeEvaluator<PathParser.PathDataNode[]> {
+ private PathParser.PathDataNode[] mNodeArray;
- private static void parseAnimatorFromTypeArray(ValueAnimator anim, TypedArray a) {
- long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 300);
+ /**
+ * Create a PathParser.PathDataNode[] that does not reuse the animated value.
+ * Care must be taken when using this option because on every evaluation
+ * a new <code>PathParser.PathDataNode[]</code> will be allocated.
+ */
+ private PathDataEvaluator() {}
- long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
+ /**
+ * Create a PathDataEvaluator that reuses <code>nodeArray</code> for every evaluate() call.
+ * Caution must be taken to ensure that the value returned from
+ * {@link android.animation.ValueAnimator#getAnimatedValue()} is not cached, modified, or
+ * used across threads. The value will be modified on each <code>evaluate()</code> call.
+ *
+ * @param nodeArray The array to modify and return from <code>evaluate</code>.
+ */
+ public PathDataEvaluator(PathParser.PathDataNode[] nodeArray) {
+ mNodeArray = nodeArray;
+ }
- int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
+ @Override
+ public PathParser.PathDataNode[] evaluate(float fraction,
+ PathParser.PathDataNode[] startPathData,
+ PathParser.PathDataNode[] endPathData) {
+ if (!PathParser.canMorph(startPathData, endPathData)) {
+ throw new IllegalArgumentException("Can't interpolate between"
+ + " two incompatible pathData");
+ }
+
+ if (mNodeArray == null || !PathParser.canMorph(mNodeArray, startPathData)) {
+ mNodeArray = PathParser.deepCopyNodes(startPathData);
+ }
+
+ for (int i = 0; i < startPathData.length; i++) {
+ mNodeArray[i].interpolatePathDataNode(startPathData[i],
+ endPathData[i], fraction);
+ }
+
+ return mNodeArray;
+ }
+ }
+
+ /**
+ * @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);
+
+ long startDelay = arrayAnimator.getInt(R.styleable.Animator_startOffset, 0);
+
+ 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;
boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
- TypedValue tvFrom = a.peekValue(valueFromIndex);
+ TypedValue tvFrom = arrayAnimator.peekValue(R.styleable.Animator_valueFrom);
boolean hasFrom = (tvFrom != null);
int fromType = hasFrom ? tvFrom.type : 0;
- TypedValue tvTo = a.peekValue(valueToIndex);
+ TypedValue tvTo = arrayAnimator.peekValue(R.styleable.Animator_valueTo);
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))) {
- // special case for colors: ignore valueType and get ints
- getFloats = false;
- evaluator = ArgbEvaluator.getInstance();
+ // TODO: Further clean up this part of code into 4 types : path, color,
+ // integer and float.
+ if (valueType == VALUE_TYPE_PATH) {
+ evaluator = setupAnimatorForPath(anim, arrayAnimator);
+ } else {
+ // Integer and float value types are handled here.
+ 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))) {
+ // special case for colors: ignore valueType and get ints
+ getFloats = false;
+ evaluator = ArgbEvaluator.getInstance();
+ }
+ setupValues(anim, arrayAnimator, getFloats, hasFrom, fromType, hasTo, toType);
}
+ anim.setDuration(duration);
+ anim.setStartDelay(startDelay);
+
+ if (arrayAnimator.hasValue(R.styleable.Animator_repeatCount)) {
+ anim.setRepeatCount(
+ arrayAnimator.getInt(R.styleable.Animator_repeatCount, 0));
+ }
+ if (arrayAnimator.hasValue(R.styleable.Animator_repeatMode)) {
+ anim.setRepeatMode(
+ arrayAnimator.getInt(R.styleable.Animator_repeatMode,
+ ValueAnimator.RESTART));
+ }
+ if (evaluator != null) {
+ anim.setEvaluator(evaluator);
+ }
+
+ if (arrayObjectAnimator != null) {
+ setupObjectAnimator(anim, arrayObjectAnimator, getFloats);
+ }
+ }
+
+ /**
+ * Setup the Animator to achieve path morphing.
+ *
+ * @param anim The target Animator which will be updated.
+ * @param arrayAnimator TypedArray for the ValueAnimator.
+ * @return the PathDataEvaluator.
+ */
+ private static TypeEvaluator setupAnimatorForPath(ValueAnimator anim,
+ TypedArray arrayAnimator) {
+ TypeEvaluator evaluator = null;
+ String fromString = arrayAnimator.getString(R.styleable.Animator_valueFrom);
+ String toString = arrayAnimator.getString(R.styleable.Animator_valueTo);
+ PathParser.PathDataNode[] nodesFrom = PathParser.createNodesFromPathData(fromString);
+ PathParser.PathDataNode[] nodesTo = PathParser.createNodesFromPathData(toString);
+
+ if (nodesFrom != null) {
+ if (nodesTo != null) {
+ anim.setObjectValues(nodesFrom, nodesTo);
+ if (!PathParser.canMorph(nodesFrom, nodesTo)) {
+ throw new InflateException(arrayAnimator.getPositionDescription()
+ + " Can't morph from " + fromString + " to " + toString);
+ }
+ } else {
+ anim.setObjectValues((Object)nodesFrom);
+ }
+ evaluator = new PathDataEvaluator(PathParser.deepCopyNodes(nodesFrom));
+ } else if (nodesTo != null) {
+ anim.setObjectValues((Object)nodesTo);
+ evaluator = new PathDataEvaluator(PathParser.deepCopyNodes(nodesTo));
+ }
+
+ if (DBG_ANIMATOR_INFLATER && evaluator != null) {
+ Log.v(TAG, "create a new PathDataEvaluator here");
+ }
+
+ return evaluator;
+ }
+
+ /**
+ * Setup ObjectAnimator's property or values from pathData.
+ *
+ * @param anim The target Animator which will be updated.
+ * @param arrayObjectAnimator TypedArray for the ObjectAnimator.
+ * @param getFloats True if the value type is float.
+ */
+ private static void setupObjectAnimator(ValueAnimator anim, TypedArray arrayObjectAnimator,
+ boolean getFloats) {
+ 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 ignored.
+ 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 InflateException(arrayObjectAnimator.getPositionDescription()
+ + " propertyXName or propertyYName is needed for PathData");
+ } 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);
+ }
+ }
+
+ /**
+ * Setup ValueAnimator's values.
+ * This will handle all of the integer, float and color types.
+ *
+ * @param anim The target Animator which will be updated.
+ * @param arrayAnimator TypedArray for the ValueAnimator.
+ * @param getFloats True if the value type is float.
+ * @param hasFrom True if "valueFrom" exists.
+ * @param fromType The type of "valueFrom".
+ * @param hasTo True if "valueTo" exists.
+ * @param toType The type of "valueTo".
+ */
+ private static void setupValues(ValueAnimator anim, TypedArray arrayAnimator,
+ boolean getFloats, boolean hasFrom, int fromType, boolean hasTo, int toType) {
+ int valueFromIndex = R.styleable.Animator_valueFrom;
+ int valueToIndex = R.styleable.Animator_valueTo;
if (getFloats) {
float valueFrom;
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 +436,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 +447,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,33 +470,17 @@
} 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);
}
}
}
-
- anim.setDuration(duration);
- anim.setStartDelay(startDelay);
-
- if (a.hasValue(com.android.internal.R.styleable.Animator_repeatCount)) {
- anim.setRepeatCount(
- a.getInt(com.android.internal.R.styleable.Animator_repeatCount, 0));
- }
- if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) {
- anim.setRepeatMode(
- a.getInt(com.android.internal.R.styleable.Animator_repeatMode,
- ValueAnimator.RESTART));
- }
- if (evaluator != null) {
- anim.setEvaluator(evaluator);
- }
}
private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser)
@@ -338,11 +516,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 +558,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 +567,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..d0d0289 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -30,6 +30,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -929,7 +930,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 +1014,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 +1114,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 +1356,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 +5324,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 +5349,7 @@
// Window is already translucent.
mTranslucentCallback.onTranslucentConversionComplete(drawComplete);
}
+ return mChangeCanvasToTranslucent;
}
/** @hide */
@@ -5929,14 +5936,21 @@
}
/**
- * Put this Activity in a mode where the user is locked to the
+ * Request to put this Activity in a mode where the user is locked to the
* current task.
*
* This will prevent the user from launching other apps, going to settings,
* or reaching the home screen.
*
- * Lock task mode will only start if the activity has been whitelisted by the
- * Device Owner through DevicePolicyManager#setLockTaskComponents.
+ * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns true
+ * for this component then the app will go directly into Lock Task mode. The user
+ * will not be able to exit this mode until {@link Activity#stopLockTask()} is called.
+ *
+ * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns false
+ * then the system will prompt the user with a dialog requesting permission to enter
+ * this mode. When entered through this method the user can exit at any time by
+ * swiping down twice from the top of the screen. Calling stopLockTask will also
+ * exit the mode.
*/
public void startLockTask() {
try {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 56462ae..572d389 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2145,6 +2145,13 @@
return true;
}
+ case START_LOCK_TASK_BY_CURRENT: {
+ data.enforceInterface(IActivityManager.descriptor);
+ startLockTaskModeOnCurrent();
+ reply.writeNoException();
+ return true;
+ }
+
case STOP_LOCK_TASK_MODE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
stopLockTaskMode();
@@ -2152,6 +2159,13 @@
return true;
}
+ case STOP_LOCK_TASK_BY_CURRENT: {
+ data.enforceInterface(IActivityManager.descriptor);
+ stopLockTaskModeOnCurrent();
+ reply.writeNoException();
+ return true;
+ }
+
case IS_IN_LOCK_TASK_MODE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final boolean isInLockTaskMode = isInLockTaskMode();
@@ -4947,6 +4961,17 @@
}
@Override
+ public void startLockTaskModeOnCurrent() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(START_LOCK_TASK_BY_CURRENT, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
public void stopLockTaskMode() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -4958,6 +4983,17 @@
}
@Override
+ public void stopLockTaskModeOnCurrent() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(STOP_LOCK_TASK_BY_CURRENT, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
public boolean isInLockTaskMode() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ea46044..f5514f8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -191,11 +191,13 @@
/** Reference to singleton {@link ActivityThread} */
private static ActivityThread sCurrentActivityThread;
Instrumentation mInstrumentation;
+ String mInstrumentationPackageName = null;
String mInstrumentationAppDir = null;
- String mInstrumentationAppLibraryDir = null;
- String mInstrumentationAppPackage = null;
+ String[] mInstrumentationSplitAppDirs = null;
+ String mInstrumentationLibDir = null;
String mInstrumentedAppDir = null;
- String mInstrumentedAppLibraryDir = null;
+ String[] mInstrumentedSplitAppDirs = null;
+ String mInstrumentedLibDir = null;
boolean mSystemThread = false;
boolean mJitEnabled = false;
@@ -317,7 +319,7 @@
}
public boolean isPersistable() {
- return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+ return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
}
public String toString() {
@@ -1585,11 +1587,11 @@
/**
* Creates the top level resources for the given package.
*/
- Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs,
- int displayId, Configuration overrideConfiguration,
+ Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
+ String[] libDirs, int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
- return mResourcesManager.getTopLevelResources(resDir, overlayDirs, libDirs, displayId,
- overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
+ return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
+ displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
}
final Handler getHandler() {
@@ -4315,16 +4317,20 @@
+ data.instrumentationName);
}
+ mInstrumentationPackageName = ii.packageName;
mInstrumentationAppDir = ii.sourceDir;
- mInstrumentationAppLibraryDir = ii.nativeLibraryDir;
- mInstrumentationAppPackage = ii.packageName;
+ mInstrumentationSplitAppDirs = ii.splitSourceDirs;
+ mInstrumentationLibDir = ii.nativeLibraryDir;
mInstrumentedAppDir = data.info.getAppDir();
- mInstrumentedAppLibraryDir = data.info.getLibDir();
+ mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
+ mInstrumentedLibDir = data.info.getLibDir();
ApplicationInfo instrApp = new ApplicationInfo();
instrApp.packageName = ii.packageName;
instrApp.sourceDir = ii.sourceDir;
instrApp.publicSourceDir = ii.publicSourceDir;
+ instrApp.splitSourceDirs = ii.splitSourceDirs;
+ instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs;
instrApp.dataDir = ii.dataDir;
instrApp.nativeLibraryDir = ii.nativeLibraryDir;
LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 0cccedcd..fa9b1e3 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -206,6 +206,9 @@
protected ResultReceiver mResultReceiver;
final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
final protected boolean mIsReturning;
+ private Runnable mPendingTransition;
+ private boolean mIsStartingTransition;
+
public ActivityTransitionCoordinator(Window window,
ArrayList<String> allSharedElementNames,
@@ -230,6 +233,13 @@
if (getViewsTransition() != null) {
getDecor().captureTransitioningViews(mTransitioningViews);
mTransitioningViews.removeAll(mSharedElements);
+ Rect r = new Rect();
+ for (int i = mTransitioningViews.size() - 1; i >= 0; i--) {
+ View view = mTransitioningViews.get(i);
+ if (!view.getGlobalVisibleRect(r)) {
+ mTransitioningViews.remove(i);
+ }
+ }
}
setEpicenter();
}
@@ -290,13 +300,17 @@
if (transition == null || views == null || views.isEmpty()) {
return null;
}
+ // Add the targets to a set containing transition so that transition
+ // remains unaffected. We don't want to modify the targets of transition itself.
TransitionSet set = new TransitionSet();
- set.addTransition(transition);
if (views != null) {
- for (View view: views) {
+ for (View view : views) {
set.addTarget(view);
}
}
+ // By adding the transition after addTarget, we prevent addTarget from
+ // affecting transition.
+ set.addTransition(transition);
return set;
}
@@ -523,7 +537,7 @@
* @param transitionArgs Bundle to store shared element placement information.
* @param tempBounds A temporary Rect for capturing the current location of views.
*/
- private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
+ protected static void captureSharedElementState(View view, String name, Bundle transitionArgs,
Rect tempBounds) {
Bundle sharedElementBundle = new Bundle();
tempBounds.set(0, 0, view.getWidth(), view.getHeight());
@@ -559,6 +573,32 @@
transitionArgs.putBundle(name, sharedElementBundle);
}
+
+ protected void startTransition(Runnable runnable) {
+ if (mIsStartingTransition) {
+ mPendingTransition = runnable;
+ } else {
+ mIsStartingTransition = true;
+ runnable.run();
+ }
+ }
+
+ protected void transitionStarted() {
+ mIsStartingTransition = false;
+ }
+
+ protected class ContinueTransitionListener extends Transition.TransitionListenerAdapter {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ mIsStartingTransition = false;
+ Runnable pending = mPendingTransition;
+ mPendingTransition = null;
+ if (pending != null) {
+ startTransition(pending);
+ }
+ }
+ }
+
private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
if (scaleType == SCALE_TYPE_VALUES[i]) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 84673d9..5e17e1a 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -823,8 +823,10 @@
if (app.packageName.equals("system")) {
return mContext.mMainThread.getSystemContext().getResources();
}
+ final boolean sameUid = (app.uid == Process.myUid());
Resources r = mContext.mMainThread.getTopLevelResources(
- app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
+ sameUid ? app.sourceDir : app.publicSourceDir,
+ sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
app.resourceDirs, null, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
if (r != null) {
return r;
@@ -1455,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!
}
@@ -1468,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);
@@ -1485,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/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 89ee145..01a388f 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -16,36 +16,53 @@
package android.app;
+import com.android.internal.util.FastPrintWriter;
+
+import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.transition.Transition;
+import android.transition.TransitionInflater;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.LogWriter;
-import com.android.internal.util.FastPrintWriter;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewGroup;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
final class BackStackState implements Parcelable {
final int[] mOps;
final int mTransition;
final int mTransitionStyle;
+ final int mCustomTransition;
+ final int mSceneRoot;
final String mName;
final int mIndex;
final int mBreadCrumbTitleRes;
final CharSequence mBreadCrumbTitleText;
final int mBreadCrumbShortTitleRes;
final CharSequence mBreadCrumbShortTitleText;
+ final ArrayList<String> mSharedElementSourceNames;
+ final ArrayList<String> mSharedElementTargetNames;
public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
int numRemoved = 0;
BackStackRecord.Op op = bse.mHead;
while (op != null) {
- if (op.removed != null) numRemoved += op.removed.size();
+ if (op.removed != null) {
+ numRemoved += op.removed.size();
+ }
op = op.next;
}
- mOps = new int[bse.mNumOp*7 + numRemoved];
+ mOps = new int[bse.mNumOp * 7 + numRemoved];
if (!bse.mAddToBackStack) {
throw new IllegalStateException("Not on back stack");
@@ -63,7 +80,7 @@
if (op.removed != null) {
final int N = op.removed.size();
mOps[pos++] = N;
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
mOps[pos++] = op.removed.get(i).mIndex;
}
} else {
@@ -79,6 +96,10 @@
mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
+ mCustomTransition = bse.mCustomTransition;
+ mSceneRoot = bse.mSceneRoot;
+ mSharedElementSourceNames = bse.mSharedElementSourceNames;
+ mSharedElementTargetNames = bse.mSharedElementTargetNames;
}
public BackStackState(Parcel in) {
@@ -91,6 +112,10 @@
mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mBreadCrumbShortTitleRes = in.readInt();
mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mCustomTransition = in.readInt();
+ mSceneRoot = in.readInt();
+ mSharedElementSourceNames = in.createStringArrayList();
+ mSharedElementTargetNames = in.createStringArrayList();
}
public BackStackRecord instantiate(FragmentManagerImpl fm) {
@@ -100,8 +125,10 @@
while (pos < mOps.length) {
BackStackRecord.Op op = new BackStackRecord.Op();
op.cmd = mOps[pos++];
- if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
- "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(FragmentManagerImpl.TAG,
+ "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
+ }
int findex = mOps[pos++];
if (findex >= 0) {
Fragment f = fm.mActive.get(findex);
@@ -116,9 +143,11 @@
final int N = mOps[pos++];
if (N > 0) {
op.removed = new ArrayList<Fragment>(N);
- for (int i=0; i<N; i++) {
- if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
- "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
+ for (int i = 0; i < N; i++) {
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(FragmentManagerImpl.TAG,
+ "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
+ }
Fragment r = fm.mActive.get(mOps[pos++]);
op.removed.add(r);
}
@@ -135,6 +164,10 @@
bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
+ bse.mCustomTransition = mCustomTransition;
+ bse.mSceneRoot = mSceneRoot;
+ bse.mSharedElementSourceNames = mSharedElementSourceNames;
+ bse.mSharedElementTargetNames = mSharedElementTargetNames;
bse.bumpBackStackNesting(1);
return bse;
}
@@ -153,6 +186,10 @@
TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
dest.writeInt(mBreadCrumbShortTitleRes);
TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
+ dest.writeInt(mCustomTransition);
+ dest.writeInt(mSceneRoot);
+ dest.writeStringList(mSharedElementSourceNames);
+ dest.writeStringList(mSharedElementTargetNames);
}
public static final Parcelable.Creator<BackStackState> CREATOR
@@ -217,6 +254,11 @@
int mBreadCrumbShortTitleRes;
CharSequence mBreadCrumbShortTitleText;
+ int mCustomTransition;
+ int mSceneRoot;
+ ArrayList<String> mSharedElementSourceNames;
+ ArrayList<String> mSharedElementTargetNames;
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
@@ -240,78 +282,112 @@
void dump(String prefix, PrintWriter writer, boolean full) {
if (full) {
- writer.print(prefix); writer.print("mName="); writer.print(mName);
- writer.print(" mIndex="); writer.print(mIndex);
- writer.print(" mCommitted="); writer.println(mCommitted);
+ writer.print(prefix);
+ writer.print("mName=");
+ writer.print(mName);
+ writer.print(" mIndex=");
+ writer.print(mIndex);
+ writer.print(" mCommitted=");
+ writer.println(mCommitted);
if (mTransition != FragmentTransaction.TRANSIT_NONE) {
- writer.print(prefix); writer.print("mTransition=#");
- writer.print(Integer.toHexString(mTransition));
- writer.print(" mTransitionStyle=#");
- writer.println(Integer.toHexString(mTransitionStyle));
+ writer.print(prefix);
+ writer.print("mTransition=#");
+ writer.print(Integer.toHexString(mTransition));
+ writer.print(" mTransitionStyle=#");
+ writer.println(Integer.toHexString(mTransitionStyle));
}
- if (mEnterAnim != 0 || mExitAnim !=0) {
- writer.print(prefix); writer.print("mEnterAnim=#");
- writer.print(Integer.toHexString(mEnterAnim));
- writer.print(" mExitAnim=#");
- writer.println(Integer.toHexString(mExitAnim));
+ if (mEnterAnim != 0 || mExitAnim != 0) {
+ writer.print(prefix);
+ writer.print("mEnterAnim=#");
+ writer.print(Integer.toHexString(mEnterAnim));
+ writer.print(" mExitAnim=#");
+ writer.println(Integer.toHexString(mExitAnim));
}
- if (mPopEnterAnim != 0 || mPopExitAnim !=0) {
- writer.print(prefix); writer.print("mPopEnterAnim=#");
- writer.print(Integer.toHexString(mPopEnterAnim));
- writer.print(" mPopExitAnim=#");
- writer.println(Integer.toHexString(mPopExitAnim));
+ if (mPopEnterAnim != 0 || mPopExitAnim != 0) {
+ writer.print(prefix);
+ writer.print("mPopEnterAnim=#");
+ writer.print(Integer.toHexString(mPopEnterAnim));
+ writer.print(" mPopExitAnim=#");
+ writer.println(Integer.toHexString(mPopExitAnim));
}
if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) {
- writer.print(prefix); writer.print("mBreadCrumbTitleRes=#");
- writer.print(Integer.toHexString(mBreadCrumbTitleRes));
- writer.print(" mBreadCrumbTitleText=");
- writer.println(mBreadCrumbTitleText);
+ writer.print(prefix);
+ writer.print("mBreadCrumbTitleRes=#");
+ writer.print(Integer.toHexString(mBreadCrumbTitleRes));
+ writer.print(" mBreadCrumbTitleText=");
+ writer.println(mBreadCrumbTitleText);
}
if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) {
- writer.print(prefix); writer.print("mBreadCrumbShortTitleRes=#");
- writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
- writer.print(" mBreadCrumbShortTitleText=");
- writer.println(mBreadCrumbShortTitleText);
+ writer.print(prefix);
+ writer.print("mBreadCrumbShortTitleRes=#");
+ writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
+ writer.print(" mBreadCrumbShortTitleText=");
+ writer.println(mBreadCrumbShortTitleText);
}
}
if (mHead != null) {
- writer.print(prefix); writer.println("Operations:");
+ writer.print(prefix);
+ writer.println("Operations:");
String innerPrefix = prefix + " ";
Op op = mHead;
int num = 0;
while (op != null) {
String cmdStr;
switch (op.cmd) {
- case OP_NULL: cmdStr="NULL"; break;
- case OP_ADD: cmdStr="ADD"; break;
- case OP_REPLACE: cmdStr="REPLACE"; break;
- case OP_REMOVE: cmdStr="REMOVE"; break;
- case OP_HIDE: cmdStr="HIDE"; break;
- case OP_SHOW: cmdStr="SHOW"; break;
- case OP_DETACH: cmdStr="DETACH"; break;
- case OP_ATTACH: cmdStr="ATTACH"; break;
- default: cmdStr="cmd=" + op.cmd; break;
+ case OP_NULL:
+ cmdStr = "NULL";
+ break;
+ case OP_ADD:
+ cmdStr = "ADD";
+ break;
+ case OP_REPLACE:
+ cmdStr = "REPLACE";
+ break;
+ case OP_REMOVE:
+ cmdStr = "REMOVE";
+ break;
+ case OP_HIDE:
+ cmdStr = "HIDE";
+ break;
+ case OP_SHOW:
+ cmdStr = "SHOW";
+ break;
+ case OP_DETACH:
+ cmdStr = "DETACH";
+ break;
+ case OP_ATTACH:
+ cmdStr = "ATTACH";
+ break;
+ default:
+ cmdStr = "cmd=" + op.cmd;
+ break;
}
- writer.print(prefix); writer.print(" Op #"); writer.print(num);
- writer.print(": "); writer.print(cmdStr);
- writer.print(" "); writer.println(op.fragment);
+ writer.print(prefix);
+ writer.print(" Op #");
+ writer.print(num);
+ writer.print(": ");
+ writer.print(cmdStr);
+ writer.print(" ");
+ writer.println(op.fragment);
if (full) {
if (op.enterAnim != 0 || op.exitAnim != 0) {
- writer.print(innerPrefix); writer.print("enterAnim=#");
- writer.print(Integer.toHexString(op.enterAnim));
- writer.print(" exitAnim=#");
- writer.println(Integer.toHexString(op.exitAnim));
+ writer.print(innerPrefix);
+ writer.print("enterAnim=#");
+ writer.print(Integer.toHexString(op.enterAnim));
+ writer.print(" exitAnim=#");
+ writer.println(Integer.toHexString(op.exitAnim));
}
if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
- writer.print(innerPrefix); writer.print("popEnterAnim=#");
- writer.print(Integer.toHexString(op.popEnterAnim));
- writer.print(" popExitAnim=#");
- writer.println(Integer.toHexString(op.popExitAnim));
+ writer.print(innerPrefix);
+ writer.print("popEnterAnim=#");
+ writer.print(Integer.toHexString(op.popEnterAnim));
+ writer.print(" popExitAnim=#");
+ writer.println(Integer.toHexString(op.popExitAnim));
}
}
if (op.removed != null && op.removed.size() > 0) {
- for (int i=0; i<op.removed.size(); i++) {
+ for (int i = 0; i < op.removed.size(); i++) {
writer.print(innerPrefix);
if (op.removed.size() == 1) {
writer.print("Removed: ");
@@ -319,8 +395,10 @@
if (i == 0) {
writer.println("Removed:");
}
- writer.print(innerPrefix); writer.print(" #"); writer.print(i);
- writer.print(": ");
+ writer.print(innerPrefix);
+ writer.print(" #");
+ writer.print(i);
+ writer.print(": ");
}
writer.println(op.removed.get(i));
}
@@ -494,6 +572,51 @@
return this;
}
+ @Override
+ public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) {
+ mSceneRoot = sceneRootId;
+ mCustomTransition = transitionId;
+ return this;
+ }
+
+ @Override
+ public FragmentTransaction setSharedElement(View sharedElement, String name) {
+ String viewName = sharedElement.getViewName();
+ if (viewName == null) {
+ throw new IllegalArgumentException("Unique viewNames are required for all" +
+ " sharedElements");
+ }
+ mSharedElementSourceNames = new ArrayList<String>(1);
+ mSharedElementSourceNames.add(viewName);
+
+ mSharedElementTargetNames = new ArrayList<String>(1);
+ mSharedElementTargetNames.add(name);
+ return this;
+ }
+
+ @Override
+ public FragmentTransaction setSharedElements(Pair<View, String>... sharedElements) {
+ if (sharedElements == null || sharedElements.length == 0) {
+ mSharedElementSourceNames = null;
+ mSharedElementTargetNames = null;
+ } else {
+ ArrayList<String> sourceNames = new ArrayList<String>(sharedElements.length);
+ ArrayList<String> targetNames = new ArrayList<String>(sharedElements.length);
+ for (int i = 0; i < sharedElements.length; i++) {
+ String viewName = sharedElements[i].first.getViewName();
+ if (viewName == null) {
+ throw new IllegalArgumentException("Unique viewNames are required for all" +
+ " sharedElements");
+ }
+ sourceNames.add(viewName);
+ targetNames.add(sharedElements[i].second);
+ }
+ mSharedElementSourceNames = sourceNames;
+ mSharedElementTargetNames = targetNames;
+ }
+ return this;
+ }
+
public FragmentTransaction setTransitionStyle(int styleRes) {
mTransitionStyle = styleRes;
return this;
@@ -550,21 +673,27 @@
if (!mAddToBackStack) {
return;
}
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this
- + " by " + amt);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Bump nesting in " + this
+ + " by " + amt);
+ }
Op op = mHead;
while (op != null) {
if (op.fragment != null) {
op.fragment.mBackStackNesting += amt;
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
- + op.fragment + " to " + op.fragment.mBackStackNesting);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Bump nesting of "
+ + op.fragment + " to " + op.fragment.mBackStackNesting);
+ }
}
if (op.removed != null) {
- for (int i=op.removed.size()-1; i>=0; i--) {
+ for (int i = op.removed.size() - 1; i >= 0; i--) {
Fragment r = op.removed.get(i);
r.mBackStackNesting += amt;
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
- + r + " to " + r.mBackStackNesting);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Bump nesting of "
+ + r + " to " + r.mBackStackNesting);
+ }
}
}
op = op.next;
@@ -578,9 +707,11 @@
public int commitAllowingStateLoss() {
return commitInternal(true);
}
-
+
int commitInternal(boolean allowStateLoss) {
- if (mCommitted) throw new IllegalStateException("commit already called");
+ if (mCommitted) {
+ throw new IllegalStateException("commit already called");
+ }
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
@@ -597,9 +728,11 @@
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
-
+
public void run() {
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Run: " + this);
+ }
if (mAddToBackStack) {
if (mIndex < 0) {
@@ -609,6 +742,9 @@
bumpBackStackNesting(1);
+ TransitionState state = beginTransition(mSharedElementSourceNames,
+ mSharedElementTargetNames);
+
Op op = mHead;
while (op != null) {
switch (op.cmd) {
@@ -616,14 +752,17 @@
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
- } break;
+ }
+ break;
case OP_REPLACE: {
Fragment f = op.fragment;
if (mManager.mAdded != null) {
- for (int i=0; i<mManager.mAdded.size(); i++) {
+ for (int i = 0; i < mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
- if (FragmentManagerImpl.DEBUG) Log.v(TAG,
- "OP_REPLACE: adding=" + f + " old=" + old);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG,
+ "OP_REPLACE: adding=" + f + " old=" + old);
+ }
if (f == null || old.mContainerId == f.mContainerId) {
if (old == f) {
op.fragment = f = null;
@@ -635,8 +774,10 @@
old.mNextAnim = op.exitAnim;
if (mAddToBackStack) {
old.mBackStackNesting += 1;
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
- + old + " to " + old.mBackStackNesting);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Bump nesting of "
+ + old + " to " + old.mBackStackNesting);
+ }
}
mManager.removeFragment(old, mTransition, mTransitionStyle);
}
@@ -647,32 +788,38 @@
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
}
- } break;
+ }
+ break;
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.removeFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
case OP_HIDE: {
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.hideFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
case OP_SHOW: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.showFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
case OP_DETACH: {
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.detachFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
case OP_ATTACH: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.attachFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
default: {
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
@@ -687,9 +834,174 @@
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
+
+ if (state != null) {
+ updateTransitionEndState(state, mSharedElementTargetNames);
+ }
}
- public void popFromBackStack(boolean doStateMove) {
+ private TransitionState beginTransition(ArrayList<String> sourceNames,
+ ArrayList<String> targetNames) {
+ if (mCustomTransition <= 0 || mSceneRoot <= 0) {
+ return null;
+ }
+ View rootView = mManager.mContainer.findViewById(mSceneRoot);
+ if (!(rootView instanceof ViewGroup)) {
+ throw new IllegalArgumentException("SceneRoot is not a ViewGroup");
+ }
+ TransitionState state = new TransitionState();
+ // get Transition scene root and create Transitions
+ state.sceneRoot = (ViewGroup) rootView;
+ state.sceneRoot.captureTransitioningViews(state.transitioningViews);
+
+ state.exitTransition = TransitionInflater.from(mManager.mActivity)
+ .inflateTransition(mCustomTransition);
+ state.sharedElementTransition = TransitionInflater.from(mManager.mActivity)
+ .inflateTransition(mCustomTransition);
+ state.enterTransition = TransitionInflater.from(mManager.mActivity)
+ .inflateTransition(mCustomTransition);
+ // Adding a non-existent target view makes sure that the transitions don't target
+ // any views by default. They'll only target the views we tell add. If we don't
+ // add any, then no views will be targeted.
+ View nonExistentView = new View(mManager.mActivity);
+ state.enterTransition.addTarget(nonExistentView);
+ state.exitTransition.addTarget(nonExistentView);
+ state.sharedElementTransition.addTarget(nonExistentView);
+
+ setSharedElementEpicenter(state.enterTransition, state);
+
+ state.excludingTransition = new TransitionSet()
+ .addTransition(state.exitTransition)
+ .addTransition(state.enterTransition);
+
+ if (sourceNames != null) {
+ // Map shared elements.
+ state.sceneRoot.findNamedViews(state.namedViews);
+ state.namedViews.retainAll(sourceNames);
+ View epicenterView = state.namedViews.get(sourceNames.get(0));
+ if (epicenterView != null) {
+ // The epicenter is only the first shared element.
+ setEpicenter(state.exitTransition, epicenterView);
+ setEpicenter(state.sharedElementTransition, epicenterView);
+ }
+ state.transitioningViews.removeAll(state.namedViews.values());
+ state.excludingTransition.addTransition(state.sharedElementTransition);
+ addTransitioningViews(state.sharedElementTransition, state.namedViews.values());
+ }
+
+ // Adds the (maybe) exiting views, not including the shared element.
+ // If some stay, that's ok.
+ addTransitioningViews(state.exitTransition, state.transitioningViews);
+
+ // Prepare for shared element name mapping. This could be chained in the case
+ // of popping several back stack states.
+ state.excludingTransition.setNameOverrides(new ArrayMap<String, String>());
+ setNameOverrides(state, sourceNames, targetNames);
+
+ // Don't include any subtree in the views that are hidden when capturing the
+ // view hierarchy transitions. They should be as if not there.
+ excludeHiddenFragments(state, true);
+
+ TransitionManager.beginDelayedTransition(state.sceneRoot, state.excludingTransition);
+ return state;
+ }
+
+ private void updateTransitionEndState(TransitionState state, ArrayList<String> names) {
+ // Find all views that are entering.
+ ArrayList<View> enteringViews = new ArrayList<View>();
+ state.sceneRoot.captureTransitioningViews(enteringViews);
+ enteringViews.removeAll(state.transitioningViews);
+
+ if (names != null) {
+ // find all shared elements.
+ state.namedViews.clear();
+ state.sceneRoot.findNamedViews(state.namedViews);
+ state.namedViews.retainAll(names);
+ if (!state.namedViews.isEmpty()) {
+ enteringViews.removeAll(state.namedViews.values());
+ addTransitioningViews(state.sharedElementTransition, state.namedViews.values());
+ // now we know the epicenter of the entering transition.
+ state.mEnteringEpicenterView = state.namedViews.get(names.get(0));
+ }
+ }
+
+ // Add all entering views to the enter transition.
+ addTransitioningViews(state.enterTransition, enteringViews);
+
+ // Don't allow capturing state for the newly-hidden fragments.
+ excludeHiddenFragments(state, false);
+
+ // Allow capturing state for the newly-shown fragments
+ includeVisibleFragments(state.excludingTransition);
+ }
+
+ private void addTransitioningViews(Transition transition, Collection<View> views) {
+ if (views.isEmpty()) {
+ // Add a view so that we can modify the valid views at the end of the
+ // fragment transaction.
+ transition.addTarget(new View(mManager.mActivity));
+ } else {
+ for (View view : views) {
+ transition.addTarget(view);
+ }
+ }
+ }
+
+ private void excludeHiddenFragments(TransitionState state, boolean forceExclude) {
+ if (mManager.mAdded != null) {
+ for (int i = 0; i < mManager.mAdded.size(); i++) {
+ Fragment fragment = mManager.mAdded.get(i);
+ if (fragment.mView != null && fragment.mHidden
+ && (forceExclude || !state.hiddenViews.contains(fragment.mView))) {
+ state.excludingTransition.excludeTarget(fragment.mView, true);
+ state.hiddenViews.add(fragment.mView);
+ }
+ }
+ }
+ if (forceExclude && state.hiddenViews.isEmpty()) {
+ state.excludingTransition.excludeTarget(new View(mManager.mActivity), true);
+ }
+ }
+
+ private void includeVisibleFragments(Transition transition) {
+ if (mManager.mAdded != null) {
+ for (int i = 0; i < mManager.mAdded.size(); i++) {
+ Fragment fragment = mManager.mAdded.get(i);
+ if (fragment.mView != null && !fragment.mHidden) {
+ transition.excludeTarget(fragment.mView, false);
+ }
+ }
+ }
+ }
+
+ private static void setEpicenter(Transition transition, View view) {
+ final Rect epicenter = new Rect();
+ view.getBoundsOnScreen(epicenter);
+
+ transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return epicenter;
+ }
+ });
+ }
+
+ private void setSharedElementEpicenter(Transition transition, final TransitionState state) {
+ transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ private Rect mEpicenter;
+
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ if (mEpicenter == null && state.mEnteringEpicenterView != null) {
+ mEpicenter = new Rect();
+ state.mEnteringEpicenterView.getBoundsOnScreen(mEpicenter);
+ }
+ return mEpicenter;
+ }
+ });
+ }
+
+ public TransitionState popFromBackStack(boolean doStateMove, TransitionState state) {
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "popFromBackStack: " + this);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
@@ -698,6 +1010,12 @@
pw.flush();
}
+ if (state == null) {
+ state = beginTransition(mSharedElementTargetNames, mSharedElementSourceNames);
+ } else {
+ setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
+ }
+
bumpBackStackNesting(-1);
Op op = mTail;
@@ -709,7 +1027,8 @@
mManager.removeFragment(f,
FragmentManagerImpl.reverseTransit(mTransition),
mTransitionStyle);
- } break;
+ }
+ break;
case OP_REPLACE: {
Fragment f = op.fragment;
if (f != null) {
@@ -719,42 +1038,48 @@
mTransitionStyle);
}
if (op.removed != null) {
- for (int i=0; i<op.removed.size(); i++) {
+ for (int i = 0; i < op.removed.size(); i++) {
Fragment old = op.removed.get(i);
old.mNextAnim = op.popEnterAnim;
mManager.addFragment(old, false);
}
}
- } break;
+ }
+ break;
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = op.popEnterAnim;
mManager.addFragment(f, false);
- } break;
+ }
+ break;
case OP_HIDE: {
Fragment f = op.fragment;
f.mNextAnim = op.popEnterAnim;
mManager.showFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- } break;
+ }
+ break;
case OP_SHOW: {
Fragment f = op.fragment;
f.mNextAnim = op.popExitAnim;
mManager.hideFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- } break;
+ }
+ break;
case OP_DETACH: {
Fragment f = op.fragment;
f.mNextAnim = op.popEnterAnim;
mManager.attachFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- } break;
+ }
+ break;
case OP_ATTACH: {
Fragment f = op.fragment;
f.mNextAnim = op.popExitAnim;
mManager.detachFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- } break;
+ }
+ break;
default: {
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
@@ -766,12 +1091,39 @@
if (doStateMove) {
mManager.moveToState(mManager.mCurState,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
+ if (state != null) {
+ updateTransitionEndState(state, mSharedElementSourceNames);
+ state = null;
+ }
}
if (mIndex >= 0) {
mManager.freeBackStackIndex(mIndex);
mIndex = -1;
}
+ return state;
+ }
+
+ private static void setNameOverride(Transition transition, String source, String target) {
+ ArrayMap<String, String> overrides = transition.getNameOverrides();
+ for (int index = 0; index < overrides.size(); index++) {
+ if (source.equals(overrides.valueAt(index))) {
+ overrides.setValueAt(index, target);
+ return;
+ }
+ }
+ overrides.put(source, target);
+ }
+
+ private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
+ ArrayList<String> targetNames) {
+ if (sourceNames != null) {
+ for (int i = 0; i < sourceNames.size(); i++) {
+ String source = sourceNames.get(i);
+ String target = targetNames.get(i);
+ setNameOverride(state.excludingTransition, source, target);
+ }
+ }
}
public String getName() {
@@ -789,4 +1141,16 @@
public boolean isEmpty() {
return mNumOp == 0;
}
+
+ public class TransitionState {
+ public ArrayList<View> hiddenViews = new ArrayList<View>();
+ public ArrayList<View> transitioningViews = new ArrayList<View>();
+ public ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
+ public Transition exitTransition;
+ public Transition sharedElementTransition;
+ public Transition enterTransition;
+ public TransitionSet excludingTransition;
+ public ViewGroup sceneRoot;
+ public View mEnteringEpicenterView;
+ }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ab3bb49..425a140 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -115,9 +115,8 @@
import android.os.storage.StorageManager;
import android.print.IPrintManager;
import android.print.PrintManager;
+import android.service.fingerprint.IFingerprintService;
import android.service.fingerprint.FingerprintManager;
-import android.service.fingerprint.FingerprintManagerReceiver;
-import android.service.fingerprint.FingerprintService;
import android.telecomm.TelecommManager;
import android.telephony.TelephonyManager;
import android.content.ClipboardManager;
@@ -466,11 +465,6 @@
return new KeyguardManager();
}});
- registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
- public Object createService(ContextImpl ctx) {
- return new FingerprintManager(ctx);
- }});
-
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
@@ -519,6 +513,9 @@
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(POWER_SERVICE);
IPowerManager service = IPowerManager.Stub.asInterface(b);
+ if (service == null) {
+ Log.wtf(TAG, "Failed to get power manager service.");
+ }
return new PowerManager(ctx.getOuterContext(),
service, ctx.mMainThread.getHandler());
}});
@@ -690,6 +687,7 @@
return new MediaSessionManager(ctx);
}
});
+
registerService(TRUST_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(TRUST_SERVICE);
@@ -697,6 +695,14 @@
}
});
+ registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder binder = ServiceManager.getService(FINGERPRINT_SERVICE);
+ IFingerprintService service = IFingerprintService.Stub.asInterface(binder);
+ return new FingerprintManager(ctx.getOuterContext(), service);
+ }
+ });
+
registerService(TV_INPUT_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder iBinder = ServiceManager.getService(TV_INPUT_SERVICE);
@@ -2187,10 +2193,10 @@
|| overrideConfiguration != null
|| (compatInfo != null && compatInfo.applicationScale
!= resources.getCompatibilityInfo().applicationScale)) {
- resources = mResourcesManager.getTopLevelResources(
- packageInfo.getResDir(), packageInfo.getOverlayDirs(),
- packageInfo.getApplicationInfo().sharedLibraryFiles,
- displayId, overrideConfiguration, compatInfo, activityToken);
+ resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
+ packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
+ packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
+ overrideConfiguration, compatInfo, activityToken);
}
}
mResources = resources;
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index f54cb87..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) {
@@ -106,7 +107,16 @@
private void sendSharedElementDestination() {
ViewGroup decor = getDecor();
- if (!decor.isLayoutRequested()) {
+ boolean allReady = !decor.isLayoutRequested();
+ if (allReady) {
+ for (int i = 0; i < mSharedElements.size(); i++) {
+ if (mSharedElements.get(i).isLayoutRequested()) {
+ allReady = false;
+ break;
+ }
+ }
+ }
+ if (allReady) {
Bundle state = captureSharedElementState();
mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
} else {
@@ -115,10 +125,15 @@
@Override
public boolean onPreDraw() {
getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ Bundle state = captureSharedElementState();
+ mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
return true;
}
});
}
+ if (allowOverlappingTransitions()) {
+ startEnterTransitionOnly();
+ }
}
private static SharedElementListener getListener(Activity activity, boolean isReturning) {
@@ -177,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);
@@ -207,24 +222,6 @@
}
}
- protected void onTakeSharedElements() {
- if (!mIsReadyForTransition || mSharedElementsBundle == null) {
- return;
- }
- final Bundle sharedElementState = mSharedElementsBundle;
- mSharedElementsBundle = null;
- getDecor().getViewTreeObserver()
- .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
- startSharedElementTransition(sharedElementState);
- return false;
- }
- });
- getDecor().invalidate();
- }
-
private void startSharedElementTransition(Bundle sharedElementState) {
setEpicenter();
// Remove rejected shared elements
@@ -242,7 +239,7 @@
setSharedElementState(sharedElementState, sharedElementSnapshots);
requestLayoutForSharedElements();
- boolean startEnterTransition = allowOverlappingTransitions();
+ boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
boolean startSharedElementTransition = true;
Transition transition = beginTransition(startEnterTransition, startSharedElementTransition);
@@ -258,6 +255,29 @@
mResultReceiver = null; // all done sending messages.
}
+ private void onTakeSharedElements() {
+ if (!mIsReadyForTransition || mSharedElementsBundle == null) {
+ return;
+ }
+ final Bundle sharedElementState = mSharedElementsBundle;
+ mSharedElementsBundle = null;
+ getDecor().getViewTreeObserver()
+ .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ startSharedElementTransition(sharedElementState);
+ }
+ });
+ return false;
+ }
+ });
+ getDecor().invalidate();
+ }
+
private void requestLayoutForSharedElements() {
int numSharedElements = mSharedElements.size();
for (int i = 0; i < numSharedElements; i++) {
@@ -282,9 +302,10 @@
if (transition == null) {
sharedElementTransitionStarted();
} else {
- transition.addListener(new Transition.TransitionListenerAdapter() {
+ transition.addListener(new ContinueTransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
+ super.onTransitionStart(transition);
transition.removeListener(this);
sharedElementTransitionStarted();
}
@@ -292,12 +313,17 @@
}
}
if (transition != null) {
+ if (sharedElementTransition == null) {
+ transition.addListener(new ContinueTransitionListener());
+ }
TransitionManager.beginDelayedTransition(getDecor(), transition);
if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
mSharedElements.get(0).invalidate();
} else if (startEnterTransition && !mTransitioningViews.isEmpty()) {
mTransitioningViews.get(0).invalidate();
}
+ } else {
+ transitionStarted();
}
return transition;
}
@@ -351,7 +377,9 @@
private void makeOpaque() {
if (!mHasStopped && mActivity != null) {
- mActivity.convertFromTranslucent();
+ if (mWasOpaque) {
+ mActivity.convertFromTranslucent();
+ }
mActivity = null;
}
}
@@ -388,11 +416,21 @@
protected void onRemoteExitTransitionComplete() {
if (!allowOverlappingTransitions()) {
- boolean startEnterTransition = true;
- boolean startSharedElementTransition = false;
- Transition transition = beginTransition(startEnterTransition,
- startSharedElementTransition);
- startEnterTransition(transition);
+ startEnterTransitionOnly();
}
}
+
+ private void startEnterTransitionOnly() {
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ setEpicenter();
+ boolean startEnterTransition = true;
+ boolean startSharedElementTransition = false;
+ Transition transition = beginTransition(startEnterTransition,
+ startSharedElementTransition);
+ startEnterTransition(transition);
+ }
+ });
+ }
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 8d5b8317..9f3dbdc 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -19,6 +19,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Intent;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -27,6 +28,7 @@
import android.transition.Transition;
import android.transition.TransitionManager;
import android.view.View;
+import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
import java.util.ArrayList;
@@ -60,10 +62,10 @@
private boolean mIsHidden;
- private boolean mExitTransitionStarted;
-
private Bundle mExitSharedElementBundle;
+ private ArrayList<View> mSharedElementSnapshots;
+
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
@@ -106,30 +108,83 @@
break;
case MSG_SHARED_ELEMENT_DESTINATION:
mExitSharedElementBundle = resultData;
- if (mExitTransitionStarted) {
- startSharedElementExit();
- }
+ sharedElementExitBack();
break;
}
}
- private void startSharedElementExit() {
+ private void sharedElementExitBack() {
if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
- Transition transition = getSharedElementExitTransition();
- TransitionManager.beginDelayedTransition(getDecor(), transition);
- ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
- mSharedElementNames);
- setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
+ startTransition(new Runnable() {
+ public void run() {
+ startSharedElementExit();
+ }
+ });
}
}
+ private void startSharedElementExit() {
+ Transition transition = getSharedElementExitTransition();
+ final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
+ mSharedElementNames);
+ mSharedElementSnapshots = createSnapshots(mExitSharedElementBundle, mSharedElementNames);
+ transition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ setViewVisibility(mSharedElements, View.INVISIBLE);
+ ViewGroupOverlay overlay = getDecor().getOverlay();
+ if (mSharedElementSnapshots != null) {
+ for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
+ overlay.add(mSharedElementSnapshots.get(i));
+ }
+ }
+ }
+ });
+ getDecor().getViewTreeObserver()
+ .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
+ return true;
+ }
+ });
+ TransitionManager.beginDelayedTransition(getDecor(), transition);
+ getDecor().invalidate();
+ }
+
+ private static ArrayList<View> copySnapshots(ArrayList<View> snapshots) {
+ ArrayList<View> copy = new ArrayList<View>(snapshots.size());
+ for (int i = 0; i < snapshots.size(); i++) {
+ View view = snapshots.get(i);
+ View viewCopy = new View(view.getContext());
+ viewCopy.setBackground(view.getBackground());
+ copy.add(viewCopy);
+ }
+ return copy;
+ }
+
private void hideSharedElements() {
- setViewVisibility(mSharedElements, View.INVISIBLE);
+ if (!mIsHidden) {
+ setViewVisibility(mSharedElements, View.INVISIBLE);
+ }
+ if (mSharedElementSnapshots != null) {
+ ViewGroupOverlay overlay = getDecor().getOverlay();
+ for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
+ overlay.remove(mSharedElementSnapshots.get(i));
+ }
+ mSharedElementSnapshots = null;
+ }
finishIfNecessary();
}
public void startExit() {
- beginTransitions();
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ beginTransitions();
+ }
+ });
setViewVisibility(mTransitioningViews, View.INVISIBLE);
}
@@ -159,29 +214,25 @@
}
}
}, options);
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ startExitTransition();
+ }
+ });
+ }
+
+ private void startExitTransition() {
Transition sharedElementTransition = mSharedElements.isEmpty()
? null : getSharedElementTransition();
if (sharedElementTransition == null) {
sharedElementTransitionComplete();
}
- Transition transition = mergeTransitions(sharedElementTransition, getExitTransition());
- if (transition == null) {
- mExitTransitionStarted = true;
- } else {
+ Transition transition = mergeTransitions(sharedElementTransition,
+ getExitTransition());
+ if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
setViewVisibility(mTransitioningViews, View.INVISIBLE);
- getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
- mExitTransitionStarted = true;
- if (mExitSharedElementBundle != null) {
- startSharedElementExit();
- }
- notifyComplete();
- return true;
- }
- });
}
}
@@ -212,7 +263,7 @@
if (viewsTransition == null) {
exitTransitionComplete();
} else {
- viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
+ viewsTransition.addListener(new ContinueTransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
exitTransitionComplete();
@@ -238,7 +289,7 @@
if (sharedElementTransition == null) {
sharedElementTransitionComplete();
} else {
- sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
+ sharedElementTransition.addListener(new ContinueTransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
sharedElementTransitionComplete();
@@ -257,7 +308,6 @@
Transition viewsTransition = getExitTransition();
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
- mExitTransitionStarted = true;
if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
}
@@ -269,15 +319,31 @@
}
protected boolean isReadyToNotify() {
- return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady
- && mExitTransitionStarted;
+ return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
}
private void sharedElementTransitionComplete() {
- mSharedElementBundle = captureSharedElementState();
+ mSharedElementBundle = mExitSharedElementBundle == null
+ ? captureSharedElementState() : captureExitSharedElementsState();
notifyComplete();
}
+ private Bundle captureExitSharedElementsState() {
+ Bundle bundle = new Bundle();
+ Rect bounds = new Rect();
+ for (int i = 0; i < mSharedElementNames.size(); i++) {
+ String name = mSharedElementNames.get(i);
+ Bundle sharedElementState = mExitSharedElementBundle.getBundle(name);
+ if (sharedElementState != null) {
+ bundle.putBundle(name, sharedElementState);
+ } else {
+ View view = mSharedElements.get(i);
+ captureSharedElementState(view, name, bundle, bounds);
+ }
+ }
+ return bundle;
+ }
+
protected void notifyComplete() {
if (isReadyToNotify()) {
if (!mSharedElementNotified) {
@@ -294,8 +360,7 @@
}
private void finishIfNecessary() {
- if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty()
- || mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
+ if (mIsReturning && mExitNotified && mActivity != null && mSharedElementSnapshots == null) {
mActivity.finish();
mActivity.overridePendingTransition(0, 0);
mActivity = null;
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 76f9d97..b8f1962 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1494,7 +1494,7 @@
return false;
}
final BackStackRecord bss = mBackStack.remove(last);
- bss.popFromBackStack(true);
+ bss.popFromBackStack(true, null);
reportBackStackChanged();
} else {
int index = -1;
@@ -1538,9 +1538,10 @@
states.add(mBackStack.remove(i));
}
final int LAST = states.size()-1;
+ BackStackRecord.TransitionState state = null;
for (int i=0; i<=LAST; i++) {
if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
- states.get(i).popFromBackStack(i == LAST);
+ state = states.get(i).popFromBackStack(i == LAST, state);
}
reportBackStackChanged();
}
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 6e99899..7479ecd 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -1,5 +1,8 @@
package android.app;
+import android.util.Pair;
+import android.view.View;
+
/**
* API for performing a set of Fragment operations.
*
@@ -169,6 +172,36 @@
public abstract FragmentTransaction setTransition(int transit);
/**
+ * Set a {@link android.transition.Transition} resource id to use with this transaction.
+ * <var>transitionId</var> will be played for fragments when going forward and when popping
+ * the back stack.
+ * @param sceneRootId The ID of the element acting as the scene root for the transition.
+ * This should be a ViewGroup containing all Fragments in the transaction.
+ * @param transitionId The resource ID for the Transition used during the Fragment transaction.
+ */
+ public abstract FragmentTransaction setCustomTransition(int sceneRootId, int transitionId);
+
+ /**
+ * Used with {@link #setCustomTransition(int, int)} to map a View from a removed or hidden
+ * Fragment to a View from a shown or added Fragment.
+ * <var>sharedElement</var> must have a unique viewName in the View hierarchy.
+ * @param sharedElement A View in a disappearing Fragment to match with a View in an
+ * appearing Fragment.
+ * @param name The viewName for a View in an appearing Fragment to match to the shared
+ * element.
+ */
+ public abstract FragmentTransaction setSharedElement(View sharedElement, String name);
+
+ /**
+ * Used with {@link #setCustomTransition(int, int)} to map multiple Views from removed or hidden
+ * Fragments to a Views from a shown or added Fragments. Views in
+ * <var>sharedElements</var> must have unique viewNames in the View hierarchy.
+ * @param sharedElements Pairs of Views in disappearing Fragments to viewNames in
+ * appearing Fragments.
+ */
+ public abstract FragmentTransaction setSharedElements(Pair<View, String>... sharedElements);
+
+ /**
* Set a custom style resource that will be used for resolving transit
* animations.
*/
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index bf2d7e5..b630278 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -429,6 +429,9 @@
public IBinder getHomeActivityToken() throws RemoteException;
/** @hide */
+ public void startLockTaskModeOnCurrent() throws RemoteException;
+
+ /** @hide */
public void startLockTaskMode(int taskId) throws RemoteException;
/** @hide */
@@ -438,6 +441,9 @@
public void stopLockTaskMode() throws RemoteException;
/** @hide */
+ public void stopLockTaskModeOnCurrent() throws RemoteException;
+
+ /** @hide */
public boolean isInLockTaskMode() throws RemoteException;
/** @hide */
@@ -744,4 +750,6 @@
int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
int GET_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+219;
int GET_APP_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+220;
+ int START_LOCK_TASK_BY_CURRENT = IBinder.FIRST_CALL_TRANSACTION+221;
+ int STOP_LOCK_TASK_BY_CURRENT = IBinder.FIRST_CALL_TRANSACTION+222;
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b78b9c9..c583998 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -188,6 +188,9 @@
endPerformanceSnapshot();
}
if (mPerfMetrics != null) {
+ if (results == null) {
+ results = new Bundle();
+ }
results.putAll(mPerfMetrics);
}
if (mUiAutomation != null) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 3ae8bfc..065e88d 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -16,8 +16,8 @@
package android.app;
+import android.text.TextUtils;
import android.util.ArrayMap;
-import com.android.internal.util.ArrayUtils;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -52,6 +52,8 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
final class IntentReceiverLeaked extends AndroidRuntimeException {
@@ -79,6 +81,8 @@
final String mPackageName;
private final String mAppDir;
private final String mResDir;
+ private final String[] mSplitAppDirs;
+ private final String[] mSplitResDirs;
private final String[] mOverlayDirs;
private final String[] mSharedLibraries;
private final String mDataDir;
@@ -116,13 +120,14 @@
public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
CompatibilityInfo compatInfo, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode) {
+ final int myUid = Process.myUid();
mActivityThread = activityThread;
mApplicationInfo = aInfo;
mPackageName = aInfo.packageName;
mAppDir = aInfo.sourceDir;
- final int myUid = Process.myUid();
- mResDir = aInfo.uid == myUid ? aInfo.sourceDir
- : aInfo.publicSourceDir;
+ mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
+ mSplitAppDirs = aInfo.splitSourceDirs;
+ mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
mOverlayDirs = aInfo.resourceDirs;
if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid),
@@ -149,6 +154,8 @@
mPackageName = "android";
mAppDir = null;
mResDir = null;
+ mSplitAppDirs = null;
+ mSplitResDirs = null;
mOverlayDirs = null;
mSharedLibraries = null;
mDataDir = null;
@@ -214,53 +221,6 @@
return ai.sharedLibraryFiles;
}
- /**
- * Combines two arrays (of library names) such that they are
- * concatenated in order but are devoid of duplicates. The
- * result is a single string with the names of the libraries
- * separated by colons, or <code>null</code> if both lists
- * were <code>null</code> or empty.
- *
- * @param list1 null-ok; the first list
- * @param list2 null-ok; the second list
- * @return null-ok; the combination
- */
- private static String combineLibs(String[] list1, String[] list2) {
- StringBuilder result = new StringBuilder(300);
- boolean first = true;
-
- if (list1 != null) {
- for (String s : list1) {
- if (first) {
- first = false;
- } else {
- result.append(':');
- }
- result.append(s);
- }
- }
-
- // Only need to check for duplicates if list1 was non-empty.
- boolean dupCheck = !first;
-
- if (list2 != null) {
- for (String s : list2) {
- if (dupCheck && ArrayUtils.contains(list1, s)) {
- continue;
- }
-
- if (first) {
- first = false;
- } else {
- result.append(':');
- }
- result.append(s);
- }
- }
-
- return result.toString();
- }
-
public ClassLoader getClassLoader() {
synchronized (this) {
if (mClassLoader != null) {
@@ -268,8 +228,15 @@
}
if (mIncludeCode && !mPackageName.equals("android")) {
- String zip = mAppDir;
- String libraryPath = mLibDir;
+ final ArrayList<String> zipPaths = new ArrayList<>();
+ final ArrayList<String> libPaths = new ArrayList<>();
+
+ zipPaths.add(mAppDir);
+ if (mSplitAppDirs != null) {
+ Collections.addAll(zipPaths, mSplitAppDirs);
+ }
+
+ libPaths.add(mLibDir);
/*
* The following is a bit of a hack to inject
@@ -280,50 +247,70 @@
* concatenation of both apps' shared library lists.
*/
- String instrumentationAppDir =
- mActivityThread.mInstrumentationAppDir;
- String instrumentationAppLibraryDir =
- mActivityThread.mInstrumentationAppLibraryDir;
- String instrumentationAppPackage =
- mActivityThread.mInstrumentationAppPackage;
- String instrumentedAppDir =
- mActivityThread.mInstrumentedAppDir;
- String instrumentedAppLibraryDir =
- mActivityThread.mInstrumentedAppLibraryDir;
+ String instrumentationPackageName = mActivityThread.mInstrumentationPackageName;
+ String instrumentationAppDir = mActivityThread.mInstrumentationAppDir;
+ String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs;
+ String instrumentationLibDir = mActivityThread.mInstrumentationLibDir;
+
+ String instrumentedAppDir = mActivityThread.mInstrumentedAppDir;
+ String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs;
+ String instrumentedLibDir = mActivityThread.mInstrumentedLibDir;
String[] instrumentationLibs = null;
if (mAppDir.equals(instrumentationAppDir)
|| mAppDir.equals(instrumentedAppDir)) {
- zip = instrumentationAppDir + ":" + instrumentedAppDir;
- libraryPath = instrumentationAppLibraryDir + ":" + instrumentedAppLibraryDir;
- if (! instrumentedAppDir.equals(instrumentationAppDir)) {
- instrumentationLibs =
- getLibrariesFor(instrumentationAppPackage);
+ zipPaths.clear();
+ zipPaths.add(instrumentationAppDir);
+ if (instrumentationSplitAppDirs != null) {
+ Collections.addAll(zipPaths, instrumentationSplitAppDirs);
+ }
+ zipPaths.add(instrumentedAppDir);
+ if (instrumentedSplitAppDirs != null) {
+ Collections.addAll(zipPaths, instrumentedSplitAppDirs);
+ }
+
+ libPaths.clear();
+ libPaths.add(instrumentationLibDir);
+ libPaths.add(instrumentedLibDir);
+
+ if (!instrumentedAppDir.equals(instrumentationAppDir)) {
+ instrumentationLibs = getLibrariesFor(instrumentationPackageName);
}
}
- if ((mSharedLibraries != null) ||
- (instrumentationLibs != null)) {
- zip =
- combineLibs(mSharedLibraries, instrumentationLibs)
- + ':' + zip;
+ if (mSharedLibraries != null) {
+ for (String lib : mSharedLibraries) {
+ if (!zipPaths.contains(lib)) {
+ zipPaths.add(0, lib);
+ }
+ }
}
+ if (instrumentationLibs != null) {
+ for (String lib : instrumentationLibs) {
+ if (!zipPaths.contains(lib)) {
+ zipPaths.add(0, lib);
+ }
+ }
+ }
+
+ final String zip = TextUtils.join(File.pathSeparator, zipPaths);
+ final String lib = TextUtils.join(File.pathSeparator, libPaths);
+
/*
* With all the combination done (if necessary, actually
* create the class loader.
*/
if (ActivityThread.localLOGV)
- Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + libraryPath);
+ Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + lib);
// Temporarily disable logging of disk reads on the Looper thread
// as this is early and necessary.
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- mClassLoader =
- ApplicationLoaders.getDefault().getClassLoader(
- zip, libraryPath, mBaseClassLoader);
+ mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib,
+ mBaseClassLoader);
initializeJavaContextClassLoader();
StrictMode.setThreadPolicy(oldPolicy);
@@ -469,6 +456,14 @@
return mResDir;
}
+ public String[] getSplitAppDirs() {
+ return mSplitAppDirs;
+ }
+
+ public String[] getSplitResDirs() {
+ return mSplitResDirs;
+ }
+
public String[] getOverlayDirs() {
return mOverlayDirs;
}
@@ -487,7 +482,7 @@
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
- mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs,
+ mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,
mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
}
return mResources;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 276f936..b94fd41 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2867,16 +2867,16 @@
/**
* Helper class for generating large-format notifications that include a large image attachment.
*
- * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
+ * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
* <pre class="prettyprint">
- * Notification noti = new Notification.BigPictureStyle(
- * new Notification.Builder()
- * .setContentTitle("New photo from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_post)
- * .setLargeIcon(aBitmap))
- * .bigPicture(aBigBitmap)
- * .build();
+ * Notification notif = new Notification.Builder(mContext)
+ * .setContentTitle("New photo from " + sender.toString())
+ * .setContentText(subject)
+ * .setSmallIcon(R.drawable.new_post)
+ * .setLargeIcon(aBitmap)
+ * .setStyle(new Notification.BigPictureStyle()
+ * .bigPicture(aBigBitmap))
+ * .build();
* </pre>
*
* @see Notification#bigContentView
@@ -2963,16 +2963,16 @@
/**
* Helper class for generating large-format notifications that include a lot of text.
*
- * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
+ * Here's how you'd set the <code>BigTextStyle</code> on a notification:
* <pre class="prettyprint">
- * Notification noti = new Notification.BigTextStyle(
- * new Notification.Builder()
- * .setContentTitle("New mail from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail)
- * .setLargeIcon(aBitmap))
- * .bigText(aVeryLongString)
- * .build();
+ * Notification notif = new Notification.Builder(mContext)
+ * .setContentTitle("New mail from " + sender.toString())
+ * .setContentText(subject)
+ * .setSmallIcon(R.drawable.new_mail)
+ * .setLargeIcon(aBitmap)
+ * .setStyle(new Notification.BigTextStyle()
+ * .bigText(aVeryLongString))
+ * .build();
* </pre>
*
* @see Notification#bigContentView
@@ -3057,19 +3057,19 @@
/**
* Helper class for generating large-format notifications that include a list of (up to 5) strings.
*
- * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
+ * Here's how you'd set the <code>InboxStyle</code> on a notification:
* <pre class="prettyprint">
- * Notification noti = new Notification.InboxStyle(
- * new Notification.Builder()
- * .setContentTitle("5 New mails from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail)
- * .setLargeIcon(aBitmap))
- * .addLine(str1)
- * .addLine(str2)
- * .setContentTitle("")
- * .setSummaryText("+3 more")
- * .build();
+ * Notification notif = new Notification.Builder(mContext)
+ * .setContentTitle("5 New mails from " + sender.toString())
+ * .setContentText(subject)
+ * .setSmallIcon(R.drawable.new_mail)
+ * .setLargeIcon(aBitmap)
+ * .setStyle(new Notification.InboxStyle()
+ * .addLine(str1)
+ * .addLine(str2)
+ * .setContentTitle("")
+ * .setSummaryText("+3 more"))
+ * .build();
* </pre>
*
* @see Notification#bigContentView
@@ -3619,14 +3619,16 @@
* .build();</pre>
*
* <p>The activity to launch needs to allow embedding, must be exported, and
- * should have an empty task affinity.
+ * should have an empty task affinity. It is also recommended to use the device
+ * default light theme.
*
* <p>Example AndroidManifest.xml entry:
* <pre class="prettyprint">
* <activity android:name="com.example.MyDisplayActivity"
* android:exported="true"
* android:allowEmbedded="true"
- * android:taskAffinity="" /></pre>
+ * android:taskAffinity=""
+ * android:theme="@android:style/Theme.DeviceDefault.Light" /></pre>
*
* @param intent the {@link PendingIntent} for an activity
* @return this object for method chaining
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a67faa0..3c13115 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -149,9 +149,9 @@
* @param compatInfo the compability info. Must not be null.
* @param token the application token for determining stack bounds.
*/
- public Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs,
- int displayId, Configuration overrideConfiguration, CompatibilityInfo compatInfo,
- IBinder token) {
+ public Resources getTopLevelResources(String resDir, String[] splitResDirs,
+ String[] overlayDirs, String[] libDirs, int displayId,
+ Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
final float scale = compatInfo.applicationScale;
ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, token);
Resources r;
@@ -182,6 +182,14 @@
return null;
}
+ if (splitResDirs != null) {
+ for (String splitResDir : splitResDirs) {
+ if (assets.addAssetPath(splitResDir) == 0) {
+ return null;
+ }
+ }
+ }
+
if (overlayDirs != null) {
for (String idmapPath : overlayDirs) {
assets.addOverlayPath(idmapPath);
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 56a55fb..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 */
@@ -228,19 +238,35 @@
}
/**
- * Get the package name of the next application with data in the backup store.
+ * Get the package name of the next application with data in the backup store, plus
+ * a description of the structure of the restored archive: either TYPE_KEY_VALUE for
+ * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream.
*
- * @return The name of one of the packages supplied to {@link #startRestore},
- * or "" (the empty string) if no more backup data is available,
- * or null if an error occurred (the restore should be aborted and rescheduled).
+ * <p>If the package name in the returned RestoreDescription object is the singleton
+ * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available
+ * in the current restore session: all packages described in startRestore() have been
+ * processed.
+ *
+ * <p>If this method returns {@code null}, it means that a transport-level error has
+ * occurred and the entire restore operation should be abandoned.
+ *
+ * @return A RestoreDescription object containing the name of one of the packages
+ * supplied to {@link #startRestore} plus an indicator of the data type of that
+ * restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
+ * no more packages can be restored in this session; or {@code null} to indicate
+ * a transport-level error.
*/
- public String nextRestorePackage() {
+ public RestoreDescription nextRestorePackage() {
return null;
}
/**
- * Get the data for the application returned by {@link #nextRestorePackage}.
- * @param data An open, writable file into which the backup data should be stored.
+ * Get the data for the application returned by {@link #nextRestorePackage}, if that
+ * method reported {@link RestoreDescription#TYPE_KEY_VALUE} as its delivery type.
+ * If the package has only TYPE_FULL_STREAM data, then this method will return an
+ * error.
+ *
+ * @param data An open, writable file into which the key/value backup data should be stored.
* @return the same error codes as {@link #startRestore}.
*/
public int getRestoreData(ParcelFileDescriptor outFd) {
@@ -332,32 +358,11 @@
// Full restore interfaces
/**
- * Ask the transport to set up to perform a full data restore of the given packages.
+ * Ask the transport to provide data for the "current" package being restored. This
+ * is the package that was just reported by {@link #nextRestorePackage()} as having
+ * {@link RestoreDescription#TYPE_FULL_STREAM} data.
*
- * @param token A backup token as returned by {@link #getAvailableRestoreSets}
- * or {@link #getCurrentRestoreSet}.
- * @param targetPackage The names of the packages whose data is being requested.
- * @return TRANSPORT_OK to indicate that the OS may proceed with requesting
- * restore data; TRANSPORT_ERROR to indicate a fatal error condition that precludes
- * performing any restore at this time.
- */
- public int prepareFullRestore(long token, String[] targetPackages) {
- 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;
- }
-
- /**
- * Ask the transport to provide data for the "current" package being restored. The
- * transport then writes some data to the socket supplied to this call, and returns
+ * The transport writes some data to the socket supplied to this call, and returns
* the number of bytes written. The system will then read that many bytes and
* stream them to the application's agent for restore, then will call this method again
* to receive the next chunk of the archive. This sequence will be repeated until the
@@ -369,17 +374,42 @@
* {@link #getNextFullRestorePackage()} to begin the restore process for the next
* application, and the sequence begins again.
*
+ * <p>The transport should always close this socket when returning from this method.
+ * Do not cache this socket across multiple calls or you may leak file descriptors.
+ *
* @param socket The file descriptor that the transport will use for delivering the
- * streamed archive.
- * @return 0 when no more data for the current package is available. A positive value
- * indicates the presence of that much data to be delivered to the app. A 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.
+ * streamed archive. The transport must close this socket in all cases when returning
+ * from this method.
+ * @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;
}
+
+ /**
+ * 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.
+ */
+ public int abortFullRestore() {
+ return BackupTransport.TRANSPORT_OK;
+ }
+
/**
* Bridge between the actual IBackupTransport implementation and the stable API. If the
* binder interface needs to change, we use this layer to translate so that we can
@@ -450,7 +480,7 @@
}
@Override
- public String nextRestorePackage() throws RemoteException {
+ public RestoreDescription nextRestorePackage() throws RemoteException {
return BackupTransport.this.nextRestorePackage();
}
@@ -478,5 +508,15 @@
public int sendBackupData(int numBytes) throws RemoteException {
return BackupTransport.this.sendBackupData(numBytes);
}
+
+ @Override
+ public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
+ return BackupTransport.this.getNextFullRestoreDataChunk(socket);
+ }
+
+ @Override
+ public int abortFullRestore() {
+ return BackupTransport.this.abortFullRestore();
+ }
}
}
diff --git a/core/java/android/app/backup/RestoreDescription.aidl b/core/java/android/app/backup/RestoreDescription.aidl
new file mode 100644
index 0000000..9cbea78
--- /dev/null
+++ b/core/java/android/app/backup/RestoreDescription.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.app.backup;
+
+parcelable RestoreDescription;
diff --git a/core/java/android/app/backup/RestoreDescription.java b/core/java/android/app/backup/RestoreDescription.java
new file mode 100644
index 0000000..0fb4355
--- /dev/null
+++ b/core/java/android/app/backup/RestoreDescription.java
@@ -0,0 +1,101 @@
+/*
+ * 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.app.backup;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Description of the available restore data for a given package. Returned by a
+ * BackupTransport in response to a request about the next available restorable
+ * package.
+ *
+ * @see BackupTransport#nextRestorePackage()
+ *
+ * @hide
+ */
+public class RestoreDescription implements Parcelable {
+ private final String mPackageName;
+ private final int mDataType;
+
+ private static final String NO_MORE_PACKAGES_SENTINEL = "";
+
+ /**
+ * Return this constant RestoreDescription from BackupTransport.nextRestorePackage()
+ * to indicate that no more package data is available in the current restore operation.
+ */
+ public static final RestoreDescription NO_MORE_PACKAGES =
+ new RestoreDescription(NO_MORE_PACKAGES_SENTINEL, 0);
+
+ // ---------------------------------------
+ // Data type identifiers
+
+ /** This package's restore data is an original-style key/value dataset */
+ public static final int TYPE_KEY_VALUE = 1;
+
+ /** This package's restore data is a tarball-type full data stream */
+ public static final int TYPE_FULL_STREAM = 2;
+
+ // ---------------------------------------
+ // API
+
+ public RestoreDescription(String packageName, int dataType) {
+ mPackageName = packageName;
+ mDataType = dataType;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public int getDataType() {
+ return mDataType;
+ }
+
+ // ---------------------------------------
+ // Parcelable implementation - not used by transport
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mPackageName);
+ out.writeInt(mDataType);
+ }
+
+ public static final Parcelable.Creator<RestoreDescription> CREATOR
+ = new Parcelable.Creator<RestoreDescription>() {
+ public RestoreDescription createFromParcel(Parcel in) {
+ final RestoreDescription unparceled = new RestoreDescription(in);
+ return (NO_MORE_PACKAGES_SENTINEL.equals(unparceled.mPackageName))
+ ? NO_MORE_PACKAGES
+ : unparceled;
+ }
+
+ public RestoreDescription[] newArray(int size) {
+ return new RestoreDescription[size];
+ }
+ };
+
+ private RestoreDescription(Parcel in) {
+ mPackageName = in.readString();
+ mDataType = in.readInt();
+ }
+}
diff --git a/core/java/android/app/maintenance/IIdleCallback.aidl b/core/java/android/app/maintenance/IIdleCallback.aidl
deleted file mode 100644
index 582dede..0000000
--- a/core/java/android/app/maintenance/IIdleCallback.aidl
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright 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.app.maintenance;
-
-import android.app.maintenance.IIdleService;
-
-/**
- * The server side of the idle maintenance IPC protocols. The app-side implementation
- * invokes on this interface to indicate completion of the (asynchronous) instructions
- * issued by the server.
- *
- * In all cases, the 'who' parameter is the caller's service binder, used to track
- * which idle service instance is reporting.
- *
- * {@hide}
- */
-interface IIdleCallback {
- /**
- * Acknowledge receipt and processing of the asynchronous "start idle work" incall.
- * 'result' is true if the app wants some time to perform ongoing background
- * idle-time work; or false if the app declares that it does not need any time
- * for such work.
- */
- void acknowledgeStart(int token, boolean result);
-
- /**
- * Acknowledge receipt and processing of the asynchronous "stop idle work" incall.
- */
- void acknowledgeStop(int token);
-
- /*
- * Tell the idle service manager that we're done with our idle maintenance, so that
- * it can go on to the next one and stop attributing wakelock time to us etc.
- *
- * @param opToken The identifier passed in the startIdleMaintenance() call that
- * indicated the beginning of this service's idle timeslice.
- */
- void idleFinished(int token);
-}
diff --git a/core/java/android/app/maintenance/IIdleService.aidl b/core/java/android/app/maintenance/IIdleService.aidl
deleted file mode 100644
index 54abccd..0000000
--- a/core/java/android/app/maintenance/IIdleService.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Copyright 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.app.maintenance;
-
-import android.app.maintenance.IIdleCallback;
-
-/**
- * Interface that the framework uses to communicate with application code
- * that implements an idle-time "maintenance" service. End user code does
- * not implement this interface directly; instead, the app's idle service
- * implementation will extend android.app.maintenance.IdleService.
- * {@hide}
- */
-oneway interface IIdleService {
- /**
- * Begin your idle-time work.
- */
- void startIdleMaintenance(IIdleCallback callbackBinder, int token);
- void stopIdleMaintenance(IIdleCallback callbackBinder, int token);
-}
diff --git a/core/java/android/app/maintenance/IdleService.java b/core/java/android/app/maintenance/IdleService.java
deleted file mode 100644
index 2331b81..0000000
--- a/core/java/android/app/maintenance/IdleService.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.maintenance;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-
-/**
- * Idle maintenance API. Full docs TBW (to be written).
- */
-public abstract class IdleService extends Service {
- private static final String TAG = "IdleService";
-
- static final int MSG_START = 1;
- static final int MSG_STOP = 2;
- static final int MSG_FINISH = 3;
-
- IdleHandler mHandler;
- IIdleCallback mCallbackBinder;
- int mToken;
- final Object mHandlerLock = new Object();
-
- void ensureHandler() {
- synchronized (mHandlerLock) {
- if (mHandler == null) {
- mHandler = new IdleHandler(getMainLooper());
- }
- }
- }
-
- /**
- * TBW: the idle service should supply an intent-filter handling this intent
- * <p>
- * <p class="note">The application must also protect the idle service with the
- * {@code "android.permission.BIND_IDLE_SERVICE"} permission to ensure that other
- * applications cannot maliciously bind to it. If an idle service's manifest
- * declaration does not require that permission, it will never be invoked.
- * </p>
- */
- @SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE =
- "android.service.idle.IdleService";
-
- /**
- * Idle services must be protected with this permission:
- *
- * <pre class="prettyprint">
- * <service android:name="MyIdleService"
- * android:permission="android.permission.BIND_IDLE_SERVICE" >
- * ...
- * </service>
- * </pre>
- *
- * <p>If an idle service is declared in the manifest but not protected with this
- * permission, that service will be ignored by the OS.
- */
- public static final String PERMISSION_BIND =
- "android.permission.BIND_IDLE_SERVICE";
-
- // Trampoline: the callbacks are always run on the main thread
- IIdleService mBinder = new IIdleService.Stub() {
- @Override
- public void startIdleMaintenance(IIdleCallback callbackBinder, int token)
- throws RemoteException {
- ensureHandler();
- Message msg = mHandler.obtainMessage(MSG_START, token, 0, callbackBinder);
- mHandler.sendMessage(msg);
- }
-
- @Override
- public void stopIdleMaintenance(IIdleCallback callbackBinder, int token)
- throws RemoteException {
- ensureHandler();
- Message msg = mHandler.obtainMessage(MSG_STOP, token, 0, callbackBinder);
- mHandler.sendMessage(msg);
- }
- };
-
- /**
- * Your application may begin doing "idle" maintenance work in the background.
- * <p>
- * Your application may continue to run in the background until it receives a call
- * to {@link #onIdleStop()}, at which point you <i>must</i> cease doing work. The
- * OS will hold a wakelock on your application's behalf from the time this method is
- * called until after the following call to {@link #onIdleStop()} returns.
- * </p>
- * <p>
- * Returning {@code false} from this method indicates that you have no ongoing work
- * to do at present. The OS will respond by immediately calling {@link #onIdleStop()}
- * and returning your application to its normal stopped state. Returning {@code true}
- * indicates that the application is indeed performing ongoing work, so the OS will
- * let your application run in this state until it's no longer appropriate.
- * </p>
- * <p>
- * You will always receive a matching call to {@link #onIdleStop()} even if your
- * application returns {@code false} from this method.
- *
- * @return {@code true} to indicate that the application wishes to perform some ongoing
- * background work; {@code false} to indicate that it does not need to perform such
- * work at present.
- */
- public abstract boolean onIdleStart();
-
- /**
- * Your app's maintenance opportunity is over. Once the application returns from
- * this method, the wakelock held by the OS on its behalf will be released.
- */
- public abstract void onIdleStop();
-
- /**
- * Tell the OS that you have finished your idle work. Calling this more than once,
- * or calling it when you have not received an {@link #onIdleStart()} callback, is
- * an error.
- *
- * <p>It is safe to call {@link #finishIdle()} from any thread.
- */
- public final void finishIdle() {
- ensureHandler();
- mHandler.sendEmptyMessage(MSG_FINISH);
- }
-
- class IdleHandler extends Handler {
- IdleHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START: {
- // Call the concrete onIdleStart(), reporting its return value back to
- // the OS. If onIdleStart() throws, report it as a 'false' return but
- // rethrow the exception at the offending app.
- boolean result = false;
- IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
- mCallbackBinder = callbackBinder;
- final int token = mToken = msg.arg1;
- try {
- result = IdleService.this.onIdleStart();
- } catch (Exception e) {
- Log.e(TAG, "Unable to start idle workload", e);
- throw new RuntimeException(e);
- } finally {
- // don't bother if the service already called finishIdle()
- if (mCallbackBinder != null) {
- try {
- callbackBinder.acknowledgeStart(token, result);
- } catch (RemoteException re) {
- Log.e(TAG, "System unreachable to start idle workload");
- }
- }
- }
- break;
- }
-
- case MSG_STOP: {
- // Structured just like MSG_START for the stop-idle bookend call.
- IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
- final int token = msg.arg1;
- try {
- IdleService.this.onIdleStop();
- } catch (Exception e) {
- Log.e(TAG, "Unable to stop idle workload", e);
- throw new RuntimeException(e);
- } finally {
- if (mCallbackBinder != null) {
- try {
- callbackBinder.acknowledgeStop(token);
- } catch (RemoteException re) {
- Log.e(TAG, "System unreachable to stop idle workload");
- }
- }
- }
- break;
- }
-
- case MSG_FINISH: {
- if (mCallbackBinder != null) {
- try {
- mCallbackBinder.idleFinished(mToken);
- } catch (RemoteException e) {
- Log.e(TAG, "System unreachable to finish idling");
- } finally {
- mCallbackBinder = null;
- }
- } else {
- Log.e(TAG, "finishIdle() called but the idle service is not started");
- }
- break;
- }
-
- default: {
- Slog.w(TAG, "Unknown message " + msg.what);
- }
- }
- }
- }
-
- /** @hide */
- @Override
- public final IBinder onBind(Intent intent) {
- return mBinder.asBinder();
- }
-
-}
diff --git a/core/java/android/app/maintenance/package.html b/core/java/android/app/maintenance/package.html
deleted file mode 100644
index 1c9bf9d..0000000
--- a/core/java/android/app/maintenance/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body>
- {@hide}
-</body>
-</html>
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6e53a6fb..e24dc84 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -55,6 +55,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.Set;
/**
@@ -6821,72 +6822,12 @@
if (other == null) {
return false;
}
- if (mAction != other.mAction) {
- if (mAction != null) {
- if (!mAction.equals(other.mAction)) {
- return false;
- }
- } else {
- if (!other.mAction.equals(mAction)) {
- return false;
- }
- }
- }
- if (mData != other.mData) {
- if (mData != null) {
- if (!mData.equals(other.mData)) {
- return false;
- }
- } else {
- if (!other.mData.equals(mData)) {
- return false;
- }
- }
- }
- if (mType != other.mType) {
- if (mType != null) {
- if (!mType.equals(other.mType)) {
- return false;
- }
- } else {
- if (!other.mType.equals(mType)) {
- return false;
- }
- }
- }
- if (mPackage != other.mPackage) {
- if (mPackage != null) {
- if (!mPackage.equals(other.mPackage)) {
- return false;
- }
- } else {
- if (!other.mPackage.equals(mPackage)) {
- return false;
- }
- }
- }
- if (mComponent != other.mComponent) {
- if (mComponent != null) {
- if (!mComponent.equals(other.mComponent)) {
- return false;
- }
- } else {
- if (!other.mComponent.equals(mComponent)) {
- return false;
- }
- }
- }
- if (mCategories != other.mCategories) {
- if (mCategories != null) {
- if (!mCategories.equals(other.mCategories)) {
- return false;
- }
- } else {
- if (!other.mCategories.equals(mCategories)) {
- return false;
- }
- }
- }
+ if (!Objects.equals(this.mAction, other.mAction)) return false;
+ if (!Objects.equals(this.mData, other.mData)) return false;
+ if (!Objects.equals(this.mType, other.mType)) return false;
+ if (!Objects.equals(this.mPackage, other.mPackage)) return false;
+ if (!Objects.equals(this.mComponent, other.mComponent)) return false;
+ if (!Objects.equals(this.mCategories, other.mCategories)) return false;
return true;
}
@@ -7375,6 +7316,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 6b44a11..be4e8644 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -23,8 +23,12 @@
import android.os.Parcelable;
import android.util.Printer;
+import com.android.internal.util.ArrayUtils;
+
import java.text.Collator;
+import java.util.Arrays;
import java.util.Comparator;
+import java.util.Objects;
/**
* Information you can retrieve about a particular application. This
@@ -321,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.
*
@@ -398,17 +411,30 @@
public int largestWidthLimitDp = 0;
/**
- * Full path to the location of this package.
+ * Full path to the base APK for this application.
*/
public String sourceDir;
/**
- * Full path to the location of the publicly available parts of this
- * package (i.e. the primary resource package and manifest). For
- * non-forward-locked apps this will be the same as {@link #sourceDir).
+ * Full path to the publicly available parts of {@link #sourceDir},
+ * including resources and manifest. This may be different from
+ * {@link #sourceDir} if an application is forward locked.
*/
public String publicSourceDir;
-
+
+ /**
+ * Full paths to zero or more split APKs that, when combined with the base
+ * APK defined in {@link #sourceDir}, form a complete application.
+ */
+ public String[] splitSourceDirs;
+
+ /**
+ * Full path to the publicly available parts of {@link #splitSourceDirs},
+ * including resources and manifest. This may be different from
+ * {@link #splitSourceDirs} if an application is forward locked.
+ */
+ public String[] splitPublicSourceDirs;
+
/**
* Full paths to the locations of extra resource packages this application
* uses. This field is only used if there are extra resource packages,
@@ -512,13 +538,16 @@
+ " compatibleWidthLimitDp=" + compatibleWidthLimitDp
+ " largestWidthLimitDp=" + largestWidthLimitDp);
pw.println(prefix + "sourceDir=" + sourceDir);
- if (sourceDir == null) {
- if (publicSourceDir != null) {
- pw.println(prefix + "publicSourceDir=" + publicSourceDir);
- }
- } else if (!sourceDir.equals(publicSourceDir)) {
+ if (!Objects.equals(sourceDir, publicSourceDir)) {
pw.println(prefix + "publicSourceDir=" + publicSourceDir);
}
+ if (!ArrayUtils.isEmpty(splitSourceDirs)) {
+ pw.println(prefix + "splitSourceDirs=" + Arrays.toString(splitSourceDirs));
+ }
+ if (!ArrayUtils.isEmpty(splitPublicSourceDirs)
+ && !Arrays.equals(splitSourceDirs, splitPublicSourceDirs)) {
+ pw.println(prefix + "splitPublicSourceDirs=" + Arrays.toString(splitPublicSourceDirs));
+ }
if (resourceDirs != null) {
pw.println(prefix + "resourceDirs=" + resourceDirs);
}
@@ -591,6 +620,8 @@
largestWidthLimitDp = orig.largestWidthLimitDp;
sourceDir = orig.sourceDir;
publicSourceDir = orig.publicSourceDir;
+ splitSourceDirs = orig.splitSourceDirs;
+ splitPublicSourceDirs = orig.splitPublicSourceDirs;
nativeLibraryDir = orig.nativeLibraryDir;
cpuAbi = orig.cpuAbi;
resourceDirs = orig.resourceDirs;
@@ -633,6 +664,8 @@
dest.writeInt(largestWidthLimitDp);
dest.writeString(sourceDir);
dest.writeString(publicSourceDir);
+ dest.writeStringArray(splitSourceDirs);
+ dest.writeStringArray(splitPublicSourceDirs);
dest.writeString(nativeLibraryDir);
dest.writeString(cpuAbi);
dest.writeStringArray(resourceDirs);
@@ -674,6 +707,8 @@
largestWidthLimitDp = source.readInt();
sourceDir = source.readString();
publicSourceDir = source.readString();
+ splitSourceDirs = source.readStringArray();
+ splitPublicSourceDirs = source.readStringArray();
nativeLibraryDir = source.readString();
cpuAbi = source.readString();
resourceDirs = source.readStringArray();
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/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java
index a977e41..dab0caf 100644
--- a/core/java/android/content/pm/InstrumentationInfo.java
+++ b/core/java/android/content/pm/InstrumentationInfo.java
@@ -30,17 +30,32 @@
* "package" attribute.
*/
public String targetPackage;
-
+
/**
- * Full path to the location of this package.
+ * Full path to the base APK for this application.
*/
public String sourceDir;
-
+
/**
- * Full path to the location of the publicly available parts of this package (i.e. the resources
- * and manifest). For non-forward-locked apps this will be the same as {@link #sourceDir).
+ * Full path to the publicly available parts of {@link #sourceDir},
+ * including resources and manifest. This may be different from
+ * {@link #sourceDir} if an application is forward locked.
*/
public String publicSourceDir;
+
+ /**
+ * Full paths to zero or more split APKs that, when combined with the base
+ * APK defined in {@link #sourceDir}, form a complete application.
+ */
+ public String[] splitSourceDirs;
+
+ /**
+ * Full path to the publicly available parts of {@link #splitSourceDirs},
+ * including resources and manifest. This may be different from
+ * {@link #splitSourceDirs} if an application is forward locked.
+ */
+ public String[] splitPublicSourceDirs;
+
/**
* Full path to a directory assigned to the package for its persistent
* data.
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/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 20dcf83..d2146ac 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -885,9 +885,9 @@
/**
* Extracts theme attributes from a typed array for later resolution using
- * {@link Theme#resolveAttributes(int[], int[])}. Removes the entries from
- * the typed array so that subsequent calls to typed getters will return the
- * default value without crashing.
+ * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}.
+ * Removes the entries from the typed array so that subsequent calls to typed
+ * getters will return the default value without crashing.
*
* @return an array of length {@link #getIndexCount()} populated with theme
* attributes, or null if there are no theme attributes in the typed
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 0705e0c..cc8503b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -167,11 +167,16 @@
private boolean mOneShot;
private boolean mWithBuffer;
private boolean mFaceDetectionRunning = false;
- private Object mAutoFocusCallbackLock = new Object();
+ private final Object mAutoFocusCallbackLock = new Object();
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.
+ */
+ private static final int CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -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,14 +352,82 @@
return null;
}
- Camera(int cameraId) {
- int err = cameraInit(cameraId);
+ /**
+ * 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.
+ * @return a new Camera object, connected, locked and ready for use.
+ *
+ * @throws IllegalArgumentException if the {@code halVersion} is invalid
+ *
+ * @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)
+ * @see #CAMERA_HAL_API_VERSION_1_0
+ *
+ * @hide
+ */
+ public static Camera openLegacy(int cameraId, int halVersion) {
+ if (halVersion < CAMERA_HAL_API_VERSION_1_0) {
+ throw new IllegalArgumentException("Invalid HAL version " + 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 = cameraInitVersion(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");
@@ -346,10 +435,7 @@
}
}
- /**
- * @hide
- */
- public int cameraInit(int cameraId) {
+ private int cameraInitVersion(int cameraId, int halVersion) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
@@ -369,9 +455,48 @@
String packageName = ActivityThread.currentPackageName();
- return native_setup(new WeakReference<Camera>(this), cameraId, packageName);
+ return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
}
+ private int cameraInitNormal(int cameraId) {
+ return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
+ }
+
+ /**
+ * Connect to the camera service using #connectLegacy
+ *
+ * <p>
+ * This acts the same as normal except that it will return
+ * the detailed error code if open fails instead of
+ * converting everything into {@code NO_INIT}.</p>
+ *
+ * <p>Intended to use by the camera2 shim only, do <i>not</i> use this for other code.</p>
+ *
+ * @return a detailed errno error code, or {@code NO_ERROR} on success
+ *
+ * @hide
+ */
+ public int cameraInitUnspecified(int cameraId) {
+ return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_UNSPECIFIED);
+ }
+
+ /** used by Camera#open, Camera#open(int) */
+ Camera(int cameraId) {
+ int err = cameraInitNormal(cameraId);
+ if (checkInitErrors(err)) {
+ switch(err) {
+ case EACCESS:
+ throw new RuntimeException("Fail to connect to camera service");
+ case ENODEV:
+ throw new RuntimeException("Camera initialization failed");
+ default:
+ // Should never hit this.
+ throw new RuntimeException("Unknown camera error");
+ }
+ }
+ }
+
+
/**
* @hide
*/
@@ -392,11 +517,12 @@
Camera() {
}
+ @Override
protected void finalize() {
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();
@@ -929,7 +1055,7 @@
private class EventHandler extends Handler
{
- private Camera mCamera;
+ private final Camera mCamera;
public EventHandler(Camera c, Looper looper) {
super(looper);
@@ -2210,6 +2336,7 @@
* @hide
* @deprecated
*/
+ @Deprecated
public void dump() {
Log.e(TAG, "dump: size=" + mMap.size());
for (String k : mMap.keySet()) {
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index 4c50dda..2bc3dd4 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -69,4 +69,16 @@
* well-formatted in the generated java method.
*/
int getCameraVendorTagDescriptor(out BinderHolder desc);
+
+ // Writes the camera1 parameters into a single-element array.
+ 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/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4a87680..e2f88eb 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -579,6 +579,7 @@
* of the lens that can be focused correctly.</p>
* <p>If the lens is fixed-focus, this should be
* 0.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*/
public static final Key<Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE =
new Key<Float>("android.lens.info.minimumFocusDistance", float.class);
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 0901562..9a3d806 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -19,8 +19,10 @@
import android.content.Context;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
+import android.hardware.CameraInfo;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.CameraDeviceUserShim;
+import android.hardware.camera2.legacy.LegacyMetadataMapper;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.hardware.camera2.utils.BinderHolder;
@@ -57,6 +59,10 @@
private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
private static final int USE_CALLING_UID = -1;
+ @SuppressWarnings("unused")
+ private static final int API_VERSION_1 = 1;
+ private static final int API_VERSION_2 = 2;
+
private final ICameraService mCameraService;
private ArrayList<String> mDeviceIdList;
@@ -142,6 +148,9 @@
synchronized (mLock) {
mListenerMap.put(listener, handler);
+
+ // TODO: fire the current oldest known state when adding a new listener
+ // (must be done while holding lock)
}
}
@@ -185,16 +194,46 @@
}
}
- CameraMetadataNative info = new CameraMetadataNative();
- try {
- mCameraService.getCameraCharacteristics(Integer.valueOf(cameraId), info);
- } catch(CameraRuntimeException e) {
- throw e.asChecked();
- } catch(RemoteException e) {
- // impossible
- return null;
+ int id = Integer.valueOf(cameraId);
+
+ /*
+ * Get the camera characteristics from the camera service directly if it supports it,
+ * otherwise get them from the legacy shim instead.
+ */
+
+ if (!supportsCamera2Api(cameraId)) {
+ // Legacy backwards compatibility path; build static info from the camera parameters
+ String[] outParameters = new String[1];
+ try {
+ mCameraService.getLegacyParameters(id, /*out*/outParameters);
+ String parameters = outParameters[0];
+
+ CameraInfo info = new CameraInfo();
+ mCameraService.getCameraInfo(id, /*out*/info);
+
+ return LegacyMetadataMapper.createCharacteristics(parameters, info);
+ } catch (RemoteException e) {
+ // Impossible
+ return null;
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ }
+
+ } else {
+ // Normal path: Get the camera characteristics directly from the camera service
+ CameraMetadataNative info = new CameraMetadataNative();
+
+ try {
+ mCameraService.getCameraCharacteristics(id, info);
+ } catch(CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch(RemoteException e) {
+ // impossible
+ return null;
+ }
+
+ return new CameraCharacteristics(info);
}
- return new CameraCharacteristics(info);
}
/**
@@ -239,14 +278,19 @@
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
int id = Integer.parseInt(cameraId);
try {
- mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
- USE_CALLING_UID, holder);
- cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
- } catch (CameraRuntimeException e) {
- if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+ if (supportsCamera2Api(cameraId)) {
+ // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
+ mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
+ USE_CALLING_UID, holder);
+ cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
+ } else {
// Use legacy camera implementation for HAL1 devices
Log.i(TAG, "Using legacy camera HAL.");
cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
+ }
+ } catch (CameraRuntimeException e) {
+ if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+ throw new AssertionError("Should've gone down the shim path");
} else if (e.getReason() == CameraAccessException.CAMERA_IN_USE ||
e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE ||
e.getReason() == CameraAccessException.CAMERA_DISABLED ||
@@ -456,6 +500,53 @@
}
}
+ /**
+ * Queries the camera service if it supports the camera2 api directly, or needs a shim.
+ *
+ * @param cameraId a non-{@code null} camera identifier
+ * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
+ */
+ private boolean supportsCamera2Api(String cameraId) {
+ return supportsCameraApi(cameraId, API_VERSION_2);
+ }
+
+ /**
+ * Queries the camera service if it supports a camera api directly, or needs a shim.
+ *
+ * @param cameraId a non-{@code null} camera identifier
+ * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
+ * @return {@code true} if connecting will work for that device version.
+ */
+ private boolean supportsCameraApi(String cameraId, int apiVersion) {
+ int id = Integer.parseInt(cameraId);
+
+ /*
+ * Possible return values:
+ * - NO_ERROR => Camera2 API is supported
+ * - CAMERA_DEPRECATED_HAL => Camera2 API is *not* supported (thrown as an exception)
+ *
+ * Anything else is an unexpected error we don't want to recover from.
+ */
+
+ try {
+ int res = mCameraService.supportsCameraApi(id, apiVersion);
+
+ if (res != CameraBinderDecorator.NO_ERROR) {
+ throw new AssertionError("Unexpected value " + res);
+ }
+
+ return true;
+ } catch (CameraRuntimeException e) {
+ if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+ return false;
+ } else {
+ throw e;
+ }
+ } catch (RemoteException e) {
+ throw new AssertionError("Camera service unreachable", e);
+ }
+ }
+
// TODO: this class needs unit tests
// TODO: extract class into top level
private class CameraServiceListener extends ICameraServiceListener.Stub {
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/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 2cfb611..5bc59dc 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -18,6 +18,7 @@
import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.TypeReference;
import android.os.Parcel;
import android.os.Parcelable;
@@ -280,7 +281,7 @@
@Override
public int hashCode() {
- return mSettings.hashCode();
+ return HashCodeHelpers.hashCode(mSettings, mSurfaceSet, mUserTag);
}
public static final Parcelable.Creator<CaptureRequest> CREATOR =
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d9f3af4..2e59eee 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -28,6 +28,8 @@
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
+import android.hardware.camera2.utils.CloseableLock;
+import android.hardware.camera2.utils.CloseableLock.ScopedLock;
import android.hardware.camera2.utils.LongParcelable;
import android.os.Handler;
import android.os.IBinder;
@@ -57,13 +59,14 @@
// TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
private ICameraDeviceUser mRemoteDevice;
- private final Object mLock = new Object();
+ private final CloseableLock mCloseLock;
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
private final StateListener mDeviceListener;
private volatile StateListener mSessionStateListener;
private final Handler mDeviceHandler;
+ private volatile boolean mClosing = false;
private boolean mInError = false;
private boolean mIdle = true;
@@ -100,7 +103,9 @@
private final Runnable mCallOnOpened = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onOpened(CameraDeviceImpl.this);
@@ -113,7 +118,9 @@
private final Runnable mCallOnUnconfigured = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onUnconfigured(CameraDeviceImpl.this);
@@ -126,7 +133,9 @@
private final Runnable mCallOnActive = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onActive(CameraDeviceImpl.this);
@@ -139,7 +148,9 @@
private final Runnable mCallOnBusy = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onBusy(CameraDeviceImpl.this);
@@ -150,20 +161,29 @@
};
private final Runnable mCallOnClosed = new Runnable() {
+ private boolean mClosedOnce = false;
+
@Override
public void run() {
+ if (mClosedOnce) {
+ throw new AssertionError("Don't post #onClosed more than once");
+ }
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onClosed(CameraDeviceImpl.this);
}
mDeviceListener.onClosed(CameraDeviceImpl.this);
+ mClosedOnce = true;
}
};
private final Runnable mCallOnIdle = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onIdle(CameraDeviceImpl.this);
@@ -176,7 +196,9 @@
private final Runnable mCallOnDisconnected = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onDisconnected(CameraDeviceImpl.this);
@@ -195,6 +217,7 @@
mDeviceListener = listener;
mDeviceHandler = handler;
mCharacteristics = characteristics;
+ mCloseLock = new CloseableLock(/*name*/"CD-" + mCameraId);
final int MAX_TAG_LEN = 23;
String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -210,8 +233,8 @@
}
public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
// TODO: Move from decorator to direct binder-mediated exceptions
- synchronized(mLock) {
// If setRemoteFailure already called, do nothing
if (mInError) return;
@@ -254,7 +277,9 @@
}
final int code = failureCode;
final boolean isError = failureIsError;
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed, can't go to error state
+
mInError = true;
mDeviceHandler.post(new Runnable() {
@Override
@@ -280,7 +305,7 @@
if (outputs == null) {
outputs = new ArrayList<Surface>();
}
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
@@ -344,7 +369,7 @@
public void createCaptureSession(List<Surface> outputs,
CameraCaptureSession.StateListener listener, Handler handler)
throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
if (DEBUG) {
Log.d(TAG, "createCaptureSession");
}
@@ -389,7 +414,7 @@
@Override
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
CameraMetadataNative templatedRequest = new CameraMetadataNative();
@@ -435,7 +460,7 @@
* starting and stopping repeating request and flushing.
*
* <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
- * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
+ * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
* If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
* is added to the list mFrameNumberRequestPairs.</p>
*
@@ -446,7 +471,7 @@
private void checkEarlyTriggerSequenceComplete(
final int requestId, final long lastFrameNumber) {
// lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
- // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
+ // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
final CaptureListenerHolder holder;
int index = mCaptureListenerMap.indexOfKey(requestId);
@@ -463,7 +488,7 @@
if (holder != null) {
if (DEBUG) {
- Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
+ Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
+ " request did not reach HAL");
}
@@ -480,10 +505,9 @@
|| lastFrameNumber > Integer.MAX_VALUE) {
throw new AssertionError(lastFrameNumber + " cannot be cast to int");
}
- holder.getListener().onCaptureSequenceCompleted(
+ holder.getListener().onCaptureSequenceAborted(
CameraDeviceImpl.this,
- requestId,
- lastFrameNumber);
+ requestId);
}
}
};
@@ -509,7 +533,7 @@
handler = checkHandler(handler);
}
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
int requestId;
@@ -581,7 +605,7 @@
@Override
public void stopRepeating() throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
if (mRepeatingRequestId != REQUEST_ID_NONE) {
@@ -612,7 +636,7 @@
private void waitUntilIdle() throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
if (mRepeatingRequestId != REQUEST_ID_NONE) {
throw new IllegalStateException("Active repeating request ongoing");
@@ -633,7 +657,7 @@
@Override
public void flush() throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
mDeviceHandler.post(mCallOnBusy);
@@ -656,8 +680,15 @@
@Override
public void close() {
- synchronized (mLock) {
+ mClosing = true;
+ // Acquire exclusive lock, close, release (idempotent)
+ mCloseLock.close();
+ /*
+ * The rest of this is safe, since no other methods will be able to execute
+ * (they will throw ISE instead; the callbacks will get dropped)
+ */
+ {
try {
if (mRemoteDevice != null) {
mRemoteDevice.disconnect();
@@ -805,7 +836,12 @@
// remove request from mCaptureListenerMap
final int requestId = frameNumberRequestPair.getValue();
final CaptureListenerHolder holder;
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) {
+ Log.w(TAG, "Camera closed while checking sequences");
+ return;
+ }
+
int index = mCaptureListenerMap.indexOfKey(requestId);
holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
: null;
@@ -890,9 +926,12 @@
@Override
public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
Runnable r = null;
- if (isClosed()) return;
- synchronized(mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) {
+ return; // Camera already closed
+ }
+
mInError = true;
switch (errorCode) {
case ERROR_CAMERA_DISCONNECTED:
@@ -914,25 +953,24 @@
break;
}
CameraDeviceImpl.this.mDeviceHandler.post(r);
- }
- // Fire onCaptureSequenceCompleted
- if (DEBUG) {
- Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+ // Fire onCaptureSequenceCompleted
+ if (DEBUG) {
+ Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+ }
+ mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
+ checkAndFireSequenceComplete();
}
- mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
- checkAndFireSequenceComplete();
-
}
@Override
public void onCameraIdle() {
- if (isClosed()) return;
-
if (DEBUG) {
Log.d(TAG, "Camera now idle");
}
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
if (!CameraDeviceImpl.this.mIdle) {
CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
}
@@ -948,30 +986,33 @@
}
final CaptureListenerHolder holder;
- // Get the listener for this frame ID, if there is one
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
+ // Get the listener for this frame ID, if there is one
holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
- }
- if (holder == null) {
- return;
- }
+ if (holder == null) {
+ return;
+ }
- if (isClosed()) return;
+ if (isClosed()) return;
- // Dispatch capture start notice
- holder.getHandler().post(
- new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
- holder.getListener().onCaptureStarted(
- CameraDeviceImpl.this,
- holder.getRequest(resultExtras.getSubsequenceId()),
- timestamp);
+ // Dispatch capture start notice
+ holder.getHandler().post(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ holder.getListener().onCaptureStarted(
+ CameraDeviceImpl.this,
+ holder.getRequest(resultExtras.getSubsequenceId()),
+ timestamp);
+ }
}
- }
- });
+ });
+
+ }
}
@Override
@@ -984,88 +1025,91 @@
+ requestId);
}
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
- // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
- result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
- getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
+ // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
+ result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
+ getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
- final CaptureListenerHolder holder;
- synchronized (mLock) {
- holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
- }
+ final CaptureListenerHolder holder =
+ CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
- Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
- boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
+ Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
+ boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
- // Update tracker (increment counter) when it's not a partial result.
- if (!quirkIsPartialResult) {
- mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
- }
-
- // Check if we have a listener for this
- if (holder == null) {
- if (DEBUG) {
- Log.d(TAG,
- "holder is null, early return at frame "
- + resultExtras.getFrameNumber());
+ // Update tracker (increment counter) when it's not a partial result.
+ if (!quirkIsPartialResult) {
+ mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(),
+ /*error*/false);
}
- return;
- }
- if (isClosed()) {
- if (DEBUG) {
- Log.d(TAG,
- "camera is closed, early return at frame "
- + resultExtras.getFrameNumber());
+ // Check if we have a listener for this
+ if (holder == null) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "holder is null, early return at frame "
+ + resultExtras.getFrameNumber());
+ }
+ return;
}
- return;
- }
- final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
-
-
- Runnable resultDispatch = null;
-
- // Either send a partial result or the final capture completed result
- if (quirkIsPartialResult) {
- final CaptureResult resultAsCapture =
- new CaptureResult(result, request, requestId);
-
- // Partial result
- resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()){
- holder.getListener().onCapturePartial(
- CameraDeviceImpl.this,
- request,
- resultAsCapture);
- }
+ if (isClosed()) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "camera is closed, early return at frame "
+ + resultExtras.getFrameNumber());
}
- };
- } else {
- final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, requestId);
+ return;
+ }
- // Final capture result
- resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()){
- holder.getListener().onCaptureCompleted(
- CameraDeviceImpl.this,
- request,
- resultAsCapture);
+ final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
+
+
+ Runnable resultDispatch = null;
+
+ // Either send a partial result or the final capture completed result
+ if (quirkIsPartialResult) {
+ final CaptureResult resultAsCapture =
+ new CaptureResult(result, request, requestId);
+
+ // Partial result
+ resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getListener().onCapturePartial(
+ CameraDeviceImpl.this,
+ request,
+ resultAsCapture);
+ }
}
- }
- };
- }
+ };
+ } else {
+ final TotalCaptureResult resultAsCapture =
+ new TotalCaptureResult(result, request, requestId);
- holder.getHandler().post(resultDispatch);
+ // Final capture result
+ resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getListener().onCaptureCompleted(
+ CameraDeviceImpl.this,
+ request,
+ resultAsCapture);
+ }
+ }
+ };
+ }
- // Fire onCaptureSequenceCompleted
- if (!quirkIsPartialResult) {
- checkAndFireSequenceComplete();
+ holder.getHandler().post(resultDispatch);
+
+ // Fire onCaptureSequenceCompleted
+ if (!quirkIsPartialResult) {
+ checkAndFireSequenceComplete();
+ }
+
}
}
@@ -1101,10 +1145,9 @@
}
}
+ /** Whether the camera device has started to close (may not yet have finished) */
private boolean isClosed() {
- synchronized(mLock) {
- return (mRemoteDevice == null);
- }
+ return mClosing;
}
private CameraCharacteristics getCharacteristics() {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 22ff9c6..ab7e844 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -34,6 +34,7 @@
* <li>{@code CONFIGURING -> IDLE}</li>
* <li>{@code IDLE -> CONFIGURING}</li>
* <li>{@code IDLE -> CAPTURING}</li>
+ * <li>{@code IDLE -> IDLE}</li>
* <li>{@code CAPTURING -> IDLE}</li>
* <li>{@code ANY -> ERROR}</li>
* </ul>
@@ -216,12 +217,17 @@
mCurrentState = STATE_CONFIGURING;
break;
case STATE_IDLE:
+ if (mCurrentState == STATE_IDLE) {
+ break;
+ }
+
if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
doStateTransition(STATE_ERROR);
break;
}
+
if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
mCurrentListener != null) {
mCurrentHandler.post(new Runnable() {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 54d9c3c..abd69c1 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -76,13 +76,11 @@
}
// TODO: Move open/init into LegacyCameraDevice thread when API is switched to async.
Camera legacyCamera = Camera.openUninitialized();
- int initErrors = legacyCamera.cameraInit(cameraId);
+ int initErrors = legacyCamera.cameraInitUnspecified(cameraId);
+
// Check errors old HAL initialization
- if (Camera.checkInitErrors(initErrors)) {
- // TODO: Map over old camera error codes. This likely involves improving the error
- // reporting in the HAL1 connect path.
- throw new CameraRuntimeException(CameraAccessException.CAMERA_DISCONNECTED);
- }
+ CameraBinderDecorator.throwOnError(initErrors);
+
LegacyCameraDevice device = new LegacyCameraDevice(cameraId, legacyCamera, callbacks);
return new CameraDeviceUserShim(cameraId, device);
}
@@ -169,7 +167,7 @@
}
int numSurfaces = mSurfaces.size();
if (numSurfaces > 0) {
- surfaces = new ArrayList<Surface>();
+ surfaces = new ArrayList<>();
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 3fd2309..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;
@@ -148,6 +149,12 @@
Handler handler = mGLHandlerThread.getHandler();
handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
mGLHandlerThread.quitSafely();
+ try {
+ mGLHandlerThread.join();
+ } catch (InterruptedException e) {
+ Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+ mGLHandlerThread.getName(), mGLHandlerThread.getId()));
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index f9cf905..50515a2 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -23,7 +23,6 @@
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.utils.LongParcelable;
import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -34,7 +33,8 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
+
+import static android.hardware.camera2.utils.CameraBinderDecorator.*;
/**
* This class emulates the functionality of a Camera2 device using a the old Camera class.
@@ -48,18 +48,20 @@
*/
public class LegacyCameraDevice implements AutoCloseable {
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();
+ private List<Surface> mConfiguredSurfaces;
private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
- private final AtomicInteger mRequestIdCounter = new AtomicInteger(0);
- private final HandlerThread mCallbackHandlerThread = new HandlerThread("ResultThread");
+ private final HandlerThread mResultThread = new HandlerThread("ResultThread");
+ private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
private final Handler mCallbackHandler;
+ private final Handler mResultHandler;
private static final int ILLEGAL_VALUE = -1;
private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
@@ -81,53 +83,91 @@
public void onError(final int errorCode, RequestHolder holder) {
mIdle.open();
final CaptureResultExtras extras = getExtrasFromRequest(holder);
- try {
- mDeviceCallbacks.onCameraError(errorCode, extras);
- } catch (RemoteException e) {
- Log.e(TAG, "Received remote exception during onCameraError callback: ", e);
- }
-
+ mResultHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.d(TAG, "doing onError callback.");
+ }
+ try {
+ mDeviceCallbacks.onCameraError(errorCode, extras);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Received remote exception during onCameraError callback: ", e);
+ }
+ }
+ });
}
@Override
public void onConfiguring() {
// Do nothing
+ if (DEBUG) {
+ Log.d(TAG, "doing onConfiguring callback.");
+ }
}
@Override
public void onIdle() {
mIdle.open();
- try {
- mDeviceCallbacks.onCameraIdle();
- } catch (RemoteException e) {
- Log.e(TAG, "Received remote exception during onCameraIdle callback: ", e);
- }
+ mResultHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.d(TAG, "doing onIdle callback.");
+ }
+ try {
+ mDeviceCallbacks.onCameraIdle();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Received remote exception during onCameraIdle callback: ", e);
+ }
+ }
+ });
}
@Override
public void onCaptureStarted(RequestHolder holder) {
final CaptureResultExtras extras = getExtrasFromRequest(holder);
- try {
- // TODO: Don't fake timestamp
- mDeviceCallbacks.onCaptureStarted(extras, System.nanoTime());
- } catch (RemoteException e) {
- Log.e(TAG, "Received remote exception during onCameraError callback: ", e);
- }
-
+ final long timestamp = System.nanoTime();
+ 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);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Received remote exception during onCameraError callback: ", e);
+ }
+ }
+ });
}
@Override
- public void onCaptureResult(CameraMetadataNative result, RequestHolder holder) {
+ public void onCaptureResult(final CameraMetadataNative result, RequestHolder holder) {
final CaptureResultExtras extras = getExtrasFromRequest(holder);
- try {
- // TODO: Don't fake metadata
- mDeviceCallbacks.onResultReceived(result, extras);
- } catch (RemoteException e) {
- Log.e(TAG, "Received remote exception during onCameraError callback: ", e);
- }
+ 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);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Received remote exception during onCameraError callback: ", e);
+ }
+ }
+ });
}
};
@@ -161,6 +201,8 @@
mDeviceCallbacks = callbacks;
TAG = String.format("CameraDevice-%d-LE", mCameraId);
+ mResultThread.start();
+ mResultHandler = new Handler(mResultThread.getLooper());
mCallbackHandlerThread.start();
mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
@@ -172,16 +214,35 @@
/**
* Configure the device with a set of output surfaces.
*
+ * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
+ *
+ * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
+ *
* @param outputs a list of surfaces to set.
- * @return an error code for this binder operation, or {@link CameraBinderDecorator.NO_ERROR}
+ * @return an error code for this binder operation, or {@link NO_ERROR}
* on success.
*/
public int configureOutputs(List<Surface> outputs) {
+ if (outputs != null) {
+ for (Surface output : outputs) {
+ if (output == null) {
+ Log.e(TAG, "configureOutputs - null outputs are not allowed");
+ return BAD_VALUE;
+ }
+ }
+ }
+
int error = mDeviceState.setConfiguring();
- if (error == CameraBinderDecorator.NO_ERROR) {
+ if (error == NO_ERROR) {
mRequestThreadManager.configure(outputs);
error = mDeviceState.setIdle();
}
+
+ // TODO: May also want to check the surfaces more deeply (e.g. state, formats, sizes..)
+ if (error == NO_ERROR) {
+ mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
+ }
+
return error;
}
@@ -198,7 +259,35 @@
*/
public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
/*out*/LongParcelable frameNumber) {
- // TODO: validate request here
+ if (requestList == null || requestList.isEmpty()) {
+ Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
+ return BAD_VALUE;
+ }
+
+ // Make sure that there all requests have at least 1 surface; all surfaces are non-null
+ for (CaptureRequest request : requestList) {
+ if (request.getTargets().isEmpty()) {
+ Log.e(TAG, "submitRequestList - "
+ + "Each request must have at least one Surface target");
+ return BAD_VALUE;
+ }
+
+ for (Surface surface : request.getTargets()) {
+ if (surface == null) {
+ Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
+ return BAD_VALUE;
+ } else if (mConfiguredSurfaces == null) {
+ Log.e(TAG, "submitRequestList - must configure " +
+ " device with valid surfaces before submitting requests");
+ return INVALID_OPERATION;
+ } else if (!mConfiguredSurfaces.contains(surface)) {
+ Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
+ return BAD_VALUE;
+ }
+ }
+ }
+
+ // TODO: further validation of request here
mIdle.close();
return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
frameNumber);
@@ -244,6 +333,22 @@
public void close() {
mRequestThreadManager.quit();
mCallbackHandlerThread.quitSafely();
+ mResultThread.quitSafely();
+
+ try {
+ mCallbackHandlerThread.join();
+ } catch (InterruptedException e) {
+ Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+ mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
+ }
+
+ try {
+ mResultThread.join();
+ } catch (InterruptedException e) {
+ Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+ mResultThread.getName(), mResultThread.getId()));
+ }
+
// TODO: throw IllegalStateException in every method after close has been called
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
new file mode 100644
index 0000000..048878c
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -0,0 +1,351 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.hardware.Camera.Size;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.params.StreamConfiguration;
+import android.hardware.camera2.params.StreamConfigurationDuration;
+import android.util.Log;
+import android.util.Range;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.android.internal.util.Preconditions.*;
+import static android.hardware.camera2.CameraCharacteristics.*;
+
+/**
+ * Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the
+ * camera characteristics.
+ */
+public class LegacyMetadataMapper {
+ private static final String TAG = "LegacyMetadataMapper";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ // from graphics.h
+ private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
+ private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
+
+ // for metadata
+ private static final float LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS = 0.0f;
+
+ 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}
+ *
+ * @param parameters A string parseable by {@link Camera.Parameters#unflatten}
+ * @param info Camera info with camera facing direction and angle of orientation
+ * @return static camera characteristics for a camera device
+ *
+ * @throws NullPointerException if any of the args were {@code null}
+ */
+ public static CameraCharacteristics createCharacteristics(String parameters,
+ android.hardware.CameraInfo info) {
+ checkNotNull(parameters, "parameters must not be null");
+ checkNotNull(info, "info must not be null");
+ checkNotNull(info.info, "info.info must not be null");
+
+ CameraMetadataNative m = new CameraMetadataNative();
+
+ mapCameraInfo(m, info.info);
+
+ Camera.Parameters params = Camera.getEmptyParameters();
+ params.unflatten(parameters);
+ mapCameraParameters(m, params);
+
+ if (VERBOSE) {
+ Log.v(TAG, "createCharacteristics metadata:");
+ Log.v(TAG, "--------------------------------------------------- (start)");
+ m.dumpToLog();
+ Log.v(TAG, "--------------------------------------------------- (end)");
+ }
+
+ return new CameraCharacteristics(m);
+ }
+
+ private static void mapCameraInfo(CameraMetadataNative m, CameraInfo i) {
+ m.set(LENS_FACING, i.facing == CameraInfo.CAMERA_FACING_BACK ?
+ LENS_FACING_BACK : LENS_FACING_FRONT);
+ m.set(SENSOR_ORIENTATION, i.orientation);
+ }
+
+ private static void mapCameraParameters(CameraMetadataNative m, Camera.Parameters p) {
+ m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
+ mapStreamConfigs(m, p);
+ mapAeConfig(m, p);
+ mapCapabilities(m, p);
+ mapLens(m, p);
+ mapFlash(m, p);
+ // TODO: map other fields
+ }
+
+ private static void mapStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
+
+ ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>();
+ /*
+ * Implementation-defined (preview, recording, etc) -> use camera1 preview sizes
+ * YUV_420_888 cpu callbacks -> use camera1 preview sizes
+ * Other preview callbacks (CPU) -> use camera1 preview sizes
+ * JPEG still capture -> use camera1 still capture sizes
+ *
+ * Use platform-internal format constants here, since StreamConfigurationMap does the
+ * remapping to public format constants.
+ */
+ List<Size> previewSizes = p.getSupportedPreviewSizes();
+ appendStreamConfig(availableStreamConfigs,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, previewSizes);
+ appendStreamConfig(availableStreamConfigs,
+ ImageFormat.YUV_420_888, previewSizes);
+ for (int format : p.getSupportedPreviewFormats()) {
+ if (ImageFormat.isPublicFormat(format)) {
+ appendStreamConfig(availableStreamConfigs, format, previewSizes);
+ } else {
+ /*
+ * Do not add any formats unknown to us
+ * (since it would fail runtime checks in StreamConfigurationMap)
+ */
+ Log.w(TAG,
+ 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;
+ long longestStallDuration = -1;
+ for (Camera.Size s : jpegSizes) {
+ long stallDuration = calculateJpegStallDuration(s);
+ jpegStalls[i++] = new StreamConfigurationDuration(HAL_PIXEL_FORMAT_BLOB, s.width,
+ s.height, stallDuration);
+ if (longestStallDuration < stallDuration) {
+ longestStallDuration = stallDuration;
+ }
+ }
+ // Set stall durations for jpeg, other formats use default stall duration
+ m.set(SCALER_AVAILABLE_STALL_DURATIONS, jpegStalls);
+
+ m.set(SENSOR_INFO_MAX_FRAME_DURATION, longestStallDuration);
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private static void mapAeConfig(CameraMetadataNative m, Camera.Parameters p) {
+
+ List<int[]> fpsRanges = p.getSupportedPreviewFpsRange();
+ if (fpsRanges == null) {
+ throw new AssertionError("Supported FPS ranges cannot be null.");
+ }
+ int rangesSize = fpsRanges.size();
+ if (rangesSize <= 0) {
+ throw new AssertionError("At least one FPS range must be supported.");
+ }
+ Range<Integer>[] ranges = new Range[rangesSize];
+ int i = 0;
+ for (int[] r : fpsRanges) {
+ ranges[i++] = Range.create(r[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ r[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ }
+ m.set(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, ranges);
+
+ List<String> antiBandingModes = p.getSupportedAntibanding();
+ int antiBandingModesSize = antiBandingModes.size();
+ if (antiBandingModesSize > 0) {
+ int[] modes = new int[antiBandingModesSize];
+ int j = 0;
+ for (String mode : antiBandingModes) {
+ int convertedMode = convertAntiBandingMode(mode);
+ if (convertedMode == -1) {
+ Log.w(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
+ " not supported, skipping...");
+ } else {
+ modes[j++] = convertedMode;
+ }
+ }
+ m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, Arrays.copyOf(modes, j));
+ }
+ }
+
+ private static void mapCapabilities(CameraMetadataNative m, Camera.Parameters p) {
+ int[] capabilities = { REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE };
+ m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
+ }
+
+ private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
+ /*
+ * We can tell if the lens is fixed focus;
+ * but if it's not, we can't tell the minimum focus distance, so leave it null then.
+ */
+ if (p.getFocusMode() == Camera.Parameters.FOCUS_MODE_FIXED) {
+ m.set(LENS_INFO_MINIMUM_FOCUS_DISTANCE, LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS);
+ }
+ }
+
+ private static void mapFlash(CameraMetadataNative m, Camera.Parameters p) {
+ boolean flashAvailable = false;
+ List<String> supportedFlashModes = p.getSupportedFlashModes();
+ if (supportedFlashModes != null) {
+ // If only 'OFF' is available, we don't really have flash support
+ if (!(supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_OFF) &&
+ supportedFlashModes.size() == 1)) {
+ flashAvailable = true;
+ }
+ }
+
+ m.set(FLASH_INFO_AVAILABLE, flashAvailable);
+ }
+
+ private static void appendStreamConfig(
+ ArrayList<StreamConfiguration> configs, int format, List<Camera.Size> sizes) {
+ for (Camera.Size size : sizes) {
+ StreamConfiguration config =
+ new StreamConfiguration(format, size.width, size.height, /*input*/false);
+ configs.add(config);
+ }
+ }
+
+ /**
+ * Returns -1 if the anti-banding mode string is null, or not supported.
+ */
+ private static int convertAntiBandingMode(final String mode) {
+ if (mode == null) {
+ return -1;
+ }
+ switch(mode) {
+ case Camera.Parameters.ANTIBANDING_OFF: {
+ return CONTROL_AE_ANTIBANDING_MODE_OFF;
+ }
+ case Camera.Parameters.ANTIBANDING_50HZ: {
+ return CONTROL_AE_ANTIBANDING_MODE_50HZ;
+ }
+ case Camera.Parameters.ANTIBANDING_60HZ: {
+ return CONTROL_AE_ANTIBANDING_MODE_60HZ;
+ }
+ case Camera.Parameters.ANTIBANDING_AUTO: {
+ return CONTROL_AE_ANTIBANDING_MODE_AUTO;
+ }
+ default: {
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * Returns null if the anti-banding mode enum is not supported.
+ */
+ private static String convertAntiBandingModeToLegacy(int mode) {
+ switch(mode) {
+ case CONTROL_AE_ANTIBANDING_MODE_OFF: {
+ return Camera.Parameters.ANTIBANDING_OFF;
+ }
+ case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
+ return Camera.Parameters.ANTIBANDING_50HZ;
+ }
+ case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
+ return Camera.Parameters.ANTIBANDING_60HZ;
+ }
+ case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
+ return Camera.Parameters.ANTIBANDING_AUTO;
+ }
+ default: {
+ return null;
+ }
+ }
+ }
+
+
+ private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
+ int[] legacyFps = new int[2];
+ legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
+ legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
+ return legacyFps;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Generate capture result metadata from legacy camera parameters.
+ *
+ * @param params a {@link Camera.Parameters} object to generate metadata from.
+ * @param request the {@link CaptureRequest} used for this result.
+ * @param timestamp the timestamp to use for this result in nanoseconds.
+ * @return a {@link CameraMetadataNative} object containing result metadata.
+ */
+ public static CameraMetadataNative convertResultMetadata(Camera.Parameters params,
+ 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;
+ }
+
+ /**
+ * Set the legacy parameters using the request metadata.
+ *
+ * @param request a {@link CaptureRequest} object to generate parameters from.
+ * @param params the a {@link Camera.Parameters} to set parameters in.
+ */
+ public static void convertRequestMetadata(CaptureRequest request,
+ /*out*/Camera.Parameters params) {
+ Integer antiBandingMode = request.get(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE);
+ if (antiBandingMode != null) {
+ String legacyMode = convertAntiBandingModeToLegacy(antiBandingMode);
+ if (legacyMode != null) params.setAntibanding(legacyMode);
+ }
+
+ Range<Integer> aeFpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+ if (aeFpsRange != null) {
+ int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
+ params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ }
+ }
+}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index c4669f5..e0f3429 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -16,7 +16,6 @@
package android.hardware.camera2.legacy;
-import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.camera2.CaptureRequest;
@@ -28,12 +27,15 @@
import android.os.SystemClock;
import android.util.Log;
import android.util.Pair;
+import android.util.Size;
import android.view.Surface;
import java.io.IOError;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
/**
@@ -64,21 +66,29 @@
private static final int PREVIEW_FRAME_TIMEOUT = 300; // ms
private static final int JPEG_FRAME_TIMEOUT = 1000; // ms
+ 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;
- private List<Surface> mPreviewOutputs = new ArrayList<Surface>();
- private List<Surface> mCallbackOutputs = new ArrayList<Surface>();
+ private final List<Surface> mPreviewOutputs = new ArrayList<Surface>();
+ private final List<Surface> mCallbackOutputs = new ArrayList<Surface>();
private GLThreadManager mGLThreadManager;
private SurfaceTexture mPreviewTexture;
+ private Camera.Parameters mParams;
+
+ private Size mIntermediateBufferSize;
private final RequestQueue mRequestQueue = new RequestQueue();
+ private CaptureRequest mLastRequest = null;
private SurfaceTexture mDummyTexture;
private Surface mDummySurface;
private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
+ private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
/**
* Container object for Configure messages.
@@ -93,6 +103,31 @@
}
}
+
+ /**
+ * Comparator for {@link Size} objects.
+ *
+ * <p>This comparator compares by rectangle area. Tiebreaks on width.</p>
+ */
+ private static class SizeComparator implements Comparator<Size> {
+ @Override
+ public int compare(Size size, Size size2) {
+ if (size == null || size2 == null) {
+ throw new NullPointerException("Null argument passed to compare");
+ }
+ if (size.equals(size2)) return 0;
+ long width = size.getWidth();
+ long width2 = size2.getWidth();
+ long area = width * size.getHeight();
+ long area2 = width2 * size2.getHeight();
+ if (area == area2) {
+ return (width > width2) ? 1 : -1;
+ }
+ return (area > area2) ? 1 : -1;
+
+ }
+ }
+
/**
* Counter class used to calculate and log the current FPS of frame production.
*/
@@ -177,23 +212,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();
}
};
@@ -220,7 +266,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;
}
@@ -230,7 +276,13 @@
return; // Already running
}
- mPreviewTexture.setDefaultBufferSize(640, 480); // TODO: size selection based on request
+ if (mPreviewTexture == null) {
+ throw new IllegalStateException(
+ "Preview capture called with no preview surfaces configured.");
+ }
+
+ mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
+ mIntermediateBufferSize.getHeight());
mCamera.setPreviewTexture(mPreviewTexture);
Camera.Parameters params = mCamera.getParameters();
List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
@@ -248,6 +300,7 @@
startPreview();
}
+
private void configureOutputs(Collection<Surface> outputs) throws IOException {
stopPreview();
if (mGLThreadManager != null) {
@@ -261,15 +314,59 @@
mInFlightPreview = null;
mInFlightJpeg = null;
- for (Surface s : outputs) {
- int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
- switch (format) {
- case CameraMetadataNative.NATIVE_JPEG_FORMAT:
- mCallbackOutputs.add(s);
- break;
- default:
- mPreviewOutputs.add(s);
- break;
+ if (outputs != null) {
+ for (Surface s : outputs) {
+ int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
+ switch (format) {
+ case CameraMetadataNative.NATIVE_JPEG_FORMAT:
+ mCallbackOutputs.add(s);
+ break;
+ default:
+ mPreviewOutputs.add(s);
+ break;
+ }
+ }
+ }
+ mParams = mCamera.getParameters();
+ if (mPreviewOutputs.size() > 0) {
+ List<Size> outputSizes = new ArrayList<>(outputs.size());
+ for (Surface s : mPreviewOutputs) {
+ int[] dimens = {0, 0};
+ LegacyCameraDevice.nativeDetectSurfaceDimens(s, dimens);
+ outputSizes.add(new Size(dimens[0], dimens[1]));
+ }
+
+ Size largestOutput = findLargestByArea(outputSizes);
+
+ // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
+ List<Size> supportedJpegSizes = convertSizeList(mParams.getSupportedPictureSizes());
+ Size largestJpegDimen = findLargestByArea(supportedJpegSizes);
+
+ 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
+ // supported preview size.
+ long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
+ Size bestPreviewDimen = findLargestByArea(supportedPreviewSizes);
+ for (Size s : supportedPreviewSizes) {
+ long currArea = s.getWidth() * s.getHeight();
+ long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
+ if (checkAspectRatiosMatch(largestJpegDimen, s) && (currArea < bestArea &&
+ currArea >= largestOutputArea)) {
+ bestPreviewDimen = s;
+ }
+ }
+
+ mIntermediateBufferSize = bestPreviewDimen;
+ if (DEBUG) {
+ Log.d(TAG, "Intermediate buffer selected with dimens: " +
+ bestPreviewDimen.toString());
+ }
+ } else {
+ mIntermediateBufferSize = null;
+ if (DEBUG) {
+ Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
}
}
@@ -282,7 +379,28 @@
mGLThreadManager.setConfigurationAndWait(mPreviewOutputs);
mGLThreadManager.allowNewFrames();
mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
- mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
+ if (mPreviewTexture != null) {
+ mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
+ }
+ }
+
+ private static Size findLargestByArea(List<Size> sizes) {
+ return Collections.max(sizes, new SizeComparator());
+ }
+
+ private static boolean checkAspectRatiosMatch(Size a, Size b) {
+ float aAspect = a.getWidth() / (float) a.getHeight();
+ float bAspect = b.getWidth() / (float) b.getHeight();
+
+ return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
+ }
+
+ private static List<Size> convertSizeList(List<Camera.Size> sizeList) {
+ List<Size> sizes = new ArrayList<>(sizeList.size());
+ for (Camera.Size s : sizeList) {
+ sizes.add(new Size(s.width, s.height));
+ }
+ return sizes;
}
// Calculate the highest FPS range supported
@@ -312,7 +430,6 @@
private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
private boolean mCleanup = false;
- private List<RequestHolder> mRepeating = null;
@SuppressWarnings("unchecked")
@Override
@@ -321,10 +438,14 @@
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;
- Log.i(TAG, "Configure outputs: " + config.surfaces.size() +
+ int sizes = config.surfaces != null ? config.surfaces.size() : 0;
+ Log.i(TAG, "Configure outputs: " + sizes +
" surfaces configured.");
try {
configureOutputs(config.surfaces);
@@ -352,7 +473,15 @@
List<RequestHolder> requests =
nextBurst.first.produceRequestHolders(nextBurst.second);
for (RequestHolder holder : requests) {
+ CaptureRequest request = holder.getRequest();
+ if (mLastRequest == null || mLastRequest != request) {
+ mLastRequest = request;
+ LegacyMetadataMapper.convertRequestMetadata(mLastRequest,
+ /*out*/mParams);
+ mCamera.setParameters(mParams);
+ }
mDeviceState.setCaptureStart(holder);
+ long timestamp = 0;
try {
if (holder.hasPreviewTargets()) {
mReceivedPreview.close();
@@ -361,6 +490,7 @@
// TODO: report error to CameraDevice
Log.e(TAG, "Hit timeout for preview callback!");
}
+ timestamp = mLastPreviewTimestamp;
}
if (holder.hasJpegTargets()) {
mReceivedJpeg.close();
@@ -371,13 +501,18 @@
Log.e(TAG, "Hit timeout for jpeg callback!");
}
mInFlightJpeg = null;
+ timestamp = mLastJpegTimestamp;
}
} catch (IOException e) {
// TODO: err handling
throw new IOError(e);
}
- // TODO: Set fields in result.
- mDeviceState.setCaptureResult(holder, new CameraMetadataNative());
+ CameraMetadataNative result = LegacyMetadataMapper.convertResultMetadata(mParams,
+ request, timestamp);
+ mDeviceState.setCaptureResult(holder, result);
+ }
+ if (DEBUG) {
+ mRequestCounter.countAndLog();
}
break;
case MSG_CLEANUP:
@@ -437,6 +572,12 @@
Handler handler = mRequestThread.waitAndGetHandler();
handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
mRequestThread.quitSafely();
+ try {
+ mRequestThread.join();
+ } catch (InterruptedException e) {
+ Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+ mRequestThread.getName(), mRequestThread.getId()));
+ }
}
/**
@@ -473,12 +614,14 @@
/**
- * Configure with the current output Surfaces.
+ * Configure with the current list of output Surfaces.
*
* <p>
* This operation blocks until the configuration is complete.
* </p>
*
+ * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
+ *
* @param outputs a {@link java.util.Collection} of outputs to configure.
*/
public void configure(Collection<Surface> outputs) {
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 2f0f6bc..e9d32f0 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -431,6 +431,11 @@
public void configureSurfaces(Collection<Surface> surfaces) {
releaseEGLContext();
+ if (surfaces == null || surfaces.size() == 0) {
+ Log.w(TAG, "No output surfaces configured for GL drawing.");
+ return;
+ }
+
for (Surface s : surfaces) {
// If pixel conversions aren't handled by egl, use a pbuffer
if (LegacyCameraDevice.needsConversion(s)) {
@@ -481,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/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
index 40cda08..898c746 100644
--- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
+++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
@@ -47,6 +47,8 @@
* - POLICY_PROHIBITS
* - RESOURCE_BUSY
* - NO_SUCH_DEVICE
+ * - NOT_SUPPORTED
+ * - TOO_MANY_USERS
*/
public static final int EACCES = -13;
public static final int EBUSY = -16;
diff --git a/core/java/android/hardware/camera2/utils/CloseableLock.java b/core/java/android/hardware/camera2/utils/CloseableLock.java
new file mode 100644
index 0000000..af55055
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/CloseableLock.java
@@ -0,0 +1,330 @@
+/*
+ * 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.hardware.camera2.utils;
+
+import android.util.Log;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Implement a shared/exclusive lock that can be closed.
+ *
+ * <p>A shared lock can be acquired if any other shared locks are also acquired. An
+ * exclusive lock acquire will block until all shared locks have been released.</p>
+ *
+ * <p>Locks are re-entrant; trying to acquire another lock (of the same type)
+ * while a lock is already held will immediately succeed.</p>
+ *
+ * <p>Acquiring to acquire a shared lock while holding an exclusive lock or vice versa is not
+ * supported; attempting it will throw an {@link IllegalStateException}.</p>
+ *
+ * <p>If the lock is closed, all future and current acquires will immediately return {@code null}.
+ * </p>
+ */
+public class CloseableLock implements AutoCloseable {
+
+ private static final boolean VERBOSE = false;
+
+ private final String TAG = "CloseableLock";
+ private final String mName;
+
+ private volatile boolean mClosed = false;
+
+ /** If an exclusive lock is acquired by some thread. */
+ private boolean mExclusive = false;
+ /**
+ * How many shared locks are acquired by any thread:
+ *
+ * <p>Reentrant locking increments this. If an exclusive lock is held,
+ * this value will stay at 0.</p>
+ */
+ private int mSharedLocks = 0;
+
+ private final ReentrantLock mLock = new ReentrantLock();
+ /** This condition automatically releases mLock when waiting; re-acquiring it after notify */
+ private final Condition mCondition = mLock.newCondition();
+
+ /** How many times the current thread is holding the lock */
+ private final ThreadLocal<Integer> mLockCount =
+ new ThreadLocal<Integer>() {
+ @Override protected Integer initialValue() {
+ return 0;
+ }
+ };
+
+ /**
+ * Helper class to release a lock at the end of a try-with-resources statement.
+ */
+ public class ScopedLock implements AutoCloseable {
+ private ScopedLock() {}
+
+ /** Release the lock with {@link CloseableLock#releaseLock}. */
+ @Override
+ public void close() {
+ releaseLock();
+ }
+ }
+
+ /**
+ * Create a new instance; starts out with 0 locks acquired.
+ */
+ public CloseableLock() {
+ mName = "";
+ }
+
+ /**
+ * Create a new instance; starts out with 0 locks acquired.
+ *
+ * @param name set an optional name for logging functionality
+ */
+ public CloseableLock(String name) {
+ mName = name;
+ }
+
+ /**
+ * Acquires the lock exclusively (blocking), marks it as closed, then releases the lock.
+ *
+ * <p>Marking a lock as closed will fail all further acquisition attempts;
+ * it will also immediately unblock all other threads currently trying to acquire a lock.</p>
+ *
+ * <p>This operation is idempotent; calling it more than once has no effect.</p>
+ *
+ * @throws IllegalStateException
+ * if an attempt is made to {@code close} while this thread has a lock acquired
+ */
+ @Override
+ public void close() {
+ if (mClosed) {
+ log("close - already closed; ignoring");
+ return;
+ }
+
+ ScopedLock scoper = acquireExclusiveLock();
+ // Already closed by another thread?
+ if (scoper == null) {
+ return;
+ } else if (mLockCount.get() != 1) {
+ // Future: may want to add a #releaseAndClose to allow this.
+ throw new IllegalStateException(
+ "Cannot close while one or more acquired locks are being held by this " +
+ "thread; release all other locks first");
+ }
+
+ try {
+ mLock.lock();
+
+ mClosed = true;
+ mExclusive = false;
+ mSharedLocks = 0;
+ mLockCount.remove();
+
+ // Notify all threads that are waiting to unblock and return immediately
+ mCondition.signalAll();
+ } finally {
+ mLock.unlock();
+ }
+
+ log("close - completed");
+ }
+
+ /**
+ * Try to acquire the lock non-exclusively, blocking until the operation completes.
+ *
+ * <p>If the lock has already been closed, or being closed before this operation returns,
+ * the call will immediately return {@code false}.</p>
+ *
+ * <p>If other threads hold a non-exclusive lock (and the lock is not yet closed),
+ * this operation will return immediately. If another thread holds an exclusive lock,
+ * this thread will block until the exclusive lock has been released.</p>
+ *
+ * <p>This lock is re-entrant; acquiring more than one non-exclusive lock per thread is
+ * supported, and must be matched by an equal number of {@link #releaseLock} calls.</p>
+ *
+ * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock
+ * was already closed.
+ *
+ * @throws IllegalStateException if this thread is already holding an exclusive lock
+ */
+ public ScopedLock acquireLock() {
+
+ int ownedLocks;
+
+ try {
+ mLock.lock();
+
+ // Lock is already closed, all further acquisitions will fail
+ if (mClosed) {
+ log("acquire lock early aborted (already closed)");
+ return null;
+ }
+
+ ownedLocks = mLockCount.get();
+
+ // This thread is already holding an exclusive lock
+ if (mExclusive && ownedLocks > 0) {
+ throw new IllegalStateException(
+ "Cannot acquire shared lock while holding exclusive lock");
+ }
+
+ // Is another thread holding the exclusive lock? Block until we can get in.
+ while (mExclusive) {
+ mCondition.awaitUninterruptibly();
+
+ // Did another thread #close while we were waiting? Unblock immediately.
+ if (mClosed) {
+ log("acquire lock unblocked aborted (already closed)");
+ return null;
+ }
+ }
+
+ mSharedLocks++;
+
+ ownedLocks = mLockCount.get() + 1;
+ mLockCount.set(ownedLocks);
+ } finally {
+ mLock.unlock();
+ }
+
+ log("acquired lock (local own count = " + ownedLocks + "");
+ return new ScopedLock();
+ }
+
+ /**
+ * Try to acquire the lock exclusively, blocking until all other threads release their locks.
+ *
+ * <p>If the lock has already been closed, or being closed before this operation returns,
+ * the call will immediately return {@code false}.</p>
+ *
+ * <p>If any other threads are holding a lock, this thread will block until all
+ * other locks are released.</p>
+ *
+ * <p>This lock is re-entrant; acquiring more than one exclusive lock per thread is supported,
+ * and must be matched by an equal number of {@link #releaseLock} calls.</p>
+ *
+ * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock
+ * was already closed.
+ *
+ * @throws IllegalStateException
+ * if an attempt is made to acquire an exclusive lock while already holding a lock
+ */
+ public ScopedLock acquireExclusiveLock() {
+
+ int ownedLocks;
+
+ try {
+ mLock.lock();
+
+ // Lock is already closed, all further acquisitions will fail
+ if (mClosed) {
+ log("acquire exclusive lock early aborted (already closed)");
+ return null;
+ }
+
+ ownedLocks = mLockCount.get();
+
+ // This thread is already holding a shared lock
+ if (!mExclusive && ownedLocks > 0) {
+ throw new IllegalStateException(
+ "Cannot acquire exclusive lock while holding shared lock");
+ }
+
+ /*
+ * Is another thread holding the lock? Block until we can get in.
+ *
+ * If we are already holding the lock, always let it through since
+ * we are just reentering the exclusive lock.
+ */
+ while (ownedLocks == 0 && (mExclusive || mSharedLocks > 0)) {
+ mCondition.awaitUninterruptibly();
+
+ // Did another thread #close while we were waiting? Unblock immediately.
+ if (mClosed) {
+ log("acquire exclusive lock unblocked aborted (already closed)");
+ return null;
+ }
+ }
+
+ mExclusive = true;
+
+ ownedLocks = mLockCount.get() + 1;
+ mLockCount.set(ownedLocks);
+ } finally {
+ mLock.unlock();
+ }
+
+ log("acquired exclusive lock (local own count = " + ownedLocks + "");
+ return new ScopedLock();
+ }
+
+ /**
+ * Release a single lock that was acquired.
+ *
+ * <p>Any other other that is blocked and trying to acquire a lock will get a chance
+ * to acquire the lock.</p>
+ *
+ * @throws IllegalStateException if no locks were acquired, or if the lock was already closed
+ */
+ public void releaseLock() {
+ if (mLockCount.get() <= 0) {
+ throw new IllegalStateException(
+ "Cannot release lock that was not acquired by this thread");
+ }
+
+ int ownedLocks;
+
+ try {
+ mLock.lock();
+
+ // Lock is already closed, it couldn't have been acquired in the first place
+ if (mClosed) {
+ throw new IllegalStateException("Do not release after the lock has been closed");
+ }
+
+ if (!mExclusive) {
+ mSharedLocks--;
+ } else {
+ if (mSharedLocks != 0) {
+ throw new AssertionError("Too many shared locks " + mSharedLocks);
+ }
+ }
+
+ ownedLocks = mLockCount.get() - 1;
+ mLockCount.set(ownedLocks);
+
+ if (ownedLocks == 0 && mExclusive) {
+ // Wake up any threads that might be waiting for the exclusive lock to be released
+ mExclusive = false;
+ mCondition.signalAll();
+ } else if (ownedLocks == 0 && mSharedLocks == 0) {
+ // Wake up any threads that might be trying to get the exclusive lock
+ mCondition.signalAll();
+ }
+ } finally {
+ mLock.unlock();
+ }
+
+ log("released lock (local lock count " + ownedLocks + ")");
+ }
+
+ private void log(String what) {
+ if (VERBOSE) {
+ Log.v(TAG + "[" + mName + "]", what);
+ }
+ }
+
+}
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index 8ad9463..d86dd5e7 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -185,6 +185,7 @@
public static final int RESULT_TARGET_NOT_AVAILABLE = 3;
public static final int RESULT_ALREADY_IN_PROGRESS = 4;
public static final int RESULT_EXCEPTION = 5;
+ public static final int RESULT_INCORRECT_MODE = 6;
private static final int[] ADDRESS_TO_TYPE = {
DEVICE_TV, // ADDR_TV
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index dd9c39f..b7af374 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -17,6 +17,7 @@
package android.net;
import android.net.LinkAddress;
+import android.net.RouteInfo;
/**
* Callback class for receiving events from an INetworkManagementService
@@ -98,4 +99,14 @@
* @param servers The IP addresses of the DNS servers.
*/
void interfaceDnsServerInfo(String iface, long lifetime, in String[] servers);
+
+ /**
+ * A route has been added or updated.
+ */
+ void routeUpdated(in RouteInfo route);
+
+ /**
+ * A route has been removed.
+ */
+ void routeRemoved(in RouteInfo route);
}
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index a14d13f..f1fa3eb 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Pair;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -46,9 +47,18 @@
private final byte[] address; // network byte order
private final int prefixLength;
+ private void checkAndMaskAddressAndPrefixLength() {
+ if (address.length != 4 && address.length != 16) {
+ throw new IllegalArgumentException(
+ "IpPrefix has " + address.length + " bytes which is neither 4 nor 16");
+ }
+ NetworkUtils.maskRawAddress(address, prefixLength);
+ }
+
/**
* Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in
- * network byte order and a prefix length.
+ * network byte order and a prefix length. Silently truncates the address to the prefix length,
+ * so for example {@code 192.0.2.1/24} is silently converted to {@code 192.0.2.0/24}.
*
* @param address the IP address. Must be non-null and exactly 4 or 16 bytes long.
* @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
@@ -56,24 +66,46 @@
* @hide
*/
public IpPrefix(byte[] address, int prefixLength) {
- if (address.length != 4 && address.length != 16) {
- throw new IllegalArgumentException(
- "IpPrefix has " + address.length + " bytes which is neither 4 nor 16");
- }
- if (prefixLength < 0 || prefixLength > (address.length * 8)) {
- throw new IllegalArgumentException("IpPrefix with " + address.length +
- " bytes has invalid prefix length " + prefixLength);
- }
this.address = address.clone();
this.prefixLength = prefixLength;
- // TODO: Validate that the non-prefix bits are zero
+ checkAndMaskAddressAndPrefixLength();
}
/**
+ * Constructs a new {@code IpPrefix} from an IPv4 or IPv6 address and a prefix length. Silently
+ * truncates the address to the prefix length, so for example {@code 192.0.2.1/24} is silently
+ * converted to {@code 192.0.2.0/24}.
+ *
+ * @param address the IP address. Must be non-null.
+ * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
* @hide
*/
public IpPrefix(InetAddress address, int prefixLength) {
- this(address.getAddress(), prefixLength);
+ // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
+ // which is unnecessary because getAddress() already returns a clone.
+ this.address = address.getAddress();
+ this.prefixLength = prefixLength;
+ checkAndMaskAddressAndPrefixLength();
+ }
+
+ /**
+ * Constructs a new IpPrefix from a string such as "192.0.2.1/24" or "2001:db8::1/64".
+ * Silently truncates the address to the prefix length, so for example {@code 192.0.2.1/24}
+ * is silently converted to {@code 192.0.2.0/24}.
+ *
+ * @param prefix the prefix to parse
+ *
+ * @hide
+ */
+ public IpPrefix(String prefix) {
+ // We don't reuse the (InetAddress, int) constructor because "error: call to this must be
+ // first statement in constructor". We could factor out setting the member variables to an
+ // init() method, but if we did, then we'd have to make the members non-final, or "error:
+ // cannot assign a value to final variable address". So we just duplicate the code here.
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
+ this.address = ipAndMask.first.getAddress();
+ this.prefixLength = ipAndMask.second;
+ checkAndMaskAddressAndPrefixLength();
}
/**
@@ -129,7 +161,7 @@
}
/**
- * Returns the prefix length of this {@code IpAddress}.
+ * Returns the prefix length of this {@code IpPrefix}.
*
* @return the prefix length.
*/
@@ -138,6 +170,20 @@
}
/**
+ * Returns a string representation of this {@code IpPrefix}.
+ *
+ * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::"}.
+ */
+ public String toString() {
+ try {
+ return InetAddress.getByAddress(address).getHostAddress() + "/" + prefixLength;
+ } catch(UnknownHostException e) {
+ // Cosmic rays?
+ throw new IllegalStateException("IpPrefix with invalid address! Shouldn't happen.", e);
+ }
+ }
+
+ /**
* Implement the Parcelable interface.
*/
public int describeContents() {
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 5246078..f9a25f9 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Pair;
import java.net.Inet4Address;
import java.net.InetAddress;
@@ -166,23 +167,9 @@
* @hide
*/
public LinkAddress(String address, int flags, int scope) {
- InetAddress inetAddress = null;
- int prefixLength = -1;
- try {
- String [] pieces = address.split("/", 2);
- prefixLength = Integer.parseInt(pieces[1]);
- inetAddress = InetAddress.parseNumericAddress(pieces[0]);
- } catch (NullPointerException e) { // Null string.
- } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
- } catch (NumberFormatException e) { // Non-numeric prefix.
- } catch (IllegalArgumentException e) { // Invalid IP address.
- }
-
- if (inetAddress == null || prefixLength == -1) {
- throw new IllegalArgumentException("Bad LinkAddress params " + address);
- }
-
- init(inetAddress, prefixLength, flags, scope);
+ // This may throw an IllegalArgumentException; catching it is the caller's responsibility.
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
+ init(ipAndMask.first, ipAndMask.second, flags, scope);
}
/**
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 8eefa0f..e7184ed 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -31,6 +31,7 @@
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
+import java.util.Objects;
/**
* Describes the properties of a network link.
@@ -334,15 +335,17 @@
}
/**
- * Adds a {@link RouteInfo} to this {@code LinkProperties}. If the {@link RouteInfo}
- * had an interface name set and that differs from the interface set for this
- * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The
- * proper course is to add either un-named or properly named {@link RouteInfo}.
+ * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
+ * {@link RouteInfo} had an interface name set and that differs from the interface set for this
+ * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The proper
+ * course is to add either un-named or properly named {@link RouteInfo}.
*
* @param route A {@link RouteInfo} to add to this object.
+ * @return {@code false} if the route was already present, {@code true} if it was added.
+ *
* @hide
*/
- public void addRoute(RouteInfo route) {
+ public boolean addRoute(RouteInfo route) {
if (route != null) {
String routeIface = route.getInterface();
if (routeIface != null && !routeIface.equals(mIfaceName)) {
@@ -350,8 +353,28 @@
"Route added with non-matching interface: " + routeIface +
" vs. " + mIfaceName);
}
- mRoutes.add(routeWithInterface(route));
+ route = routeWithInterface(route);
+ if (!mRoutes.contains(route)) {
+ mRoutes.add(route);
+ return true;
+ }
}
+ return false;
+ }
+
+ /**
+ * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must
+ * specify an interface and the interface must match the interface of this
+ * {@code LinkProperties}, or it will not be removed.
+ *
+ * @return {@code true} if the route was removed, {@code false} if it was not present.
+ *
+ * @hide
+ */
+ public boolean removeRoute(RouteInfo route) {
+ return route != null &&
+ Objects.equals(mIfaceName, route.getInterface()) &&
+ mRoutes.remove(route);
}
/**
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/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b02f88e..15c0a71 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -24,6 +24,8 @@
import java.util.Locale;
import android.util.Log;
+import android.util.Pair;
+
/**
* Native methods for managing network interfaces.
@@ -218,24 +220,17 @@
}
/**
- * Get InetAddress masked with prefixLength. Will never return null.
- * @param IP address which will be masked with specified prefixLength
- * @param prefixLength the prefixLength used to mask the IP
+ * Masks a raw IP address byte array with the specified prefix length.
*/
- public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
- if (address == null) {
- throw new RuntimeException("getNetworkPart doesn't accept null address");
- }
-
- byte[] array = address.getAddress();
-
+ public static void maskRawAddress(byte[] array, int prefixLength) {
if (prefixLength < 0 || prefixLength > array.length * 8) {
- throw new RuntimeException("getNetworkPart - bad prefixLength");
+ throw new RuntimeException("IP address with " + array.length +
+ " bytes has invalid prefix length " + prefixLength);
}
int offset = prefixLength / 8;
- int reminder = prefixLength % 8;
- byte mask = (byte)(0xFF << (8 - reminder));
+ int remainder = prefixLength % 8;
+ byte mask = (byte)(0xFF << (8 - remainder));
if (offset < array.length) array[offset] = (byte)(array[offset] & mask);
@@ -244,6 +239,16 @@
for (; offset < array.length; offset++) {
array[offset] = 0;
}
+ }
+
+ /**
+ * Get InetAddress masked with prefixLength. Will never return null.
+ * @param address the IP address to mask with
+ * @param prefixLength the prefixLength used to mask the IP
+ */
+ public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
+ byte[] array = address.getAddress();
+ maskRawAddress(array, prefixLength);
InetAddress netPart = null;
try {
@@ -255,6 +260,30 @@
}
/**
+ * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
+ * @hide
+ */
+ public static Pair<InetAddress, Integer> parseIpAndMask(String ipAndMaskString) {
+ InetAddress address = null;
+ int prefixLength = -1;
+ try {
+ String[] pieces = ipAndMaskString.split("/", 2);
+ prefixLength = Integer.parseInt(pieces[1]);
+ address = InetAddress.parseNumericAddress(pieces[0]);
+ } catch (NullPointerException e) { // Null string.
+ } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
+ } catch (NumberFormatException e) { // Non-numeric prefix.
+ } catch (IllegalArgumentException e) { // Invalid IP address.
+ }
+
+ if (address == null || prefixLength == -1) {
+ throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString);
+ }
+
+ return new Pair<InetAddress, Integer>(address, prefixLength);
+ }
+
+ /**
* Check if IP address type is consistent between two InetAddress.
* @return true if both are the same type. False otherwise.
*/
diff --git a/core/java/android/net/PSKKeyManager.java b/core/java/android/net/PSKKeyManager.java
new file mode 100644
index 0000000..92dd141
--- /dev/null
+++ b/core/java/android/net/PSKKeyManager.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 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.net;
+
+import java.net.Socket;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Provider of key material for pre-shared key (PSK) key exchange used in TLS-PSK cipher suites.
+ *
+ * <h3>Overview of TLS-PSK</h3>
+ *
+ * <p>TLS-PSK is a set of TLS/SSL cipher suites which rely on a symmetric pre-shared key (PSK) to
+ * secure the TLS/SSL connection and mutually authenticate its peers. These cipher suites may be
+ * a more natural fit compared to conventional public key based cipher suites in some scenarios
+ * where communication between peers is bootstrapped via a separate step (for example, a pairing
+ * step) and requires both peers to authenticate each other. In such scenarios a symmetric key (PSK)
+ * can be exchanged during the bootstrapping step, removing the need to generate and exchange public
+ * key pairs and X.509 certificates.</p>
+ *
+ * <p>When a TLS-PSK cipher suite is used, both peers have to use the same key for the TLS/SSL
+ * handshake to succeed. Thus, both peers are implicitly authenticated by a successful handshake.
+ * This removes the need to use a {@code TrustManager} in conjunction with this {@code KeyManager}.
+ * </p>
+ *
+ * <h3>Supporting multiple keys</h3>
+ *
+ * <p>A peer may have multiple keys to choose from. To help choose the right key, during the handshake
+ * the server can provide a <em>PSK identity hint</em> to the client, and the client can provide a
+ * <em>PSK identity</em> to the server. The contents of these two pieces of information are specific
+ * to application-level protocols.</p>
+ *
+ * <p><em>NOTE: Both the PSK identity hint and the PSK identity are transmitted in cleartext.
+ * Moreover, these data are received and processed prior to peer having been authenticated. Thus,
+ * they must not contain or leak key material or other sensitive information, and should be
+ * treated (e.g., parsed) with caution, as untrusted data.</em></p>
+ *
+ * <p>The high-level flow leading to peers choosing a key during TLS/SSL handshake is as follows:
+ * <ol>
+ * <li>Server receives a handshake request from client.
+ * <li>Server replies, optionally providing a PSK identity hint to client.</li>
+ * <li>Client chooses the key.</li>
+ * <li>Client provides a PSK identity of the chosen key to server.</li>
+ * <li>Server chooses the key.</li>
+ * </ol></p>
+ *
+ * <p>In the flow above, either peer can signal that they do not have a suitable key, in which case
+ * the the handshake will be aborted immediately. This may enable a network attacker who does not
+ * know the key to learn which PSK identity hints or PSK identities are supported. If this is a
+ * concern then a randomly generated key should be used in the scenario where no key is available.
+ * This will lead to the handshake aborting later, due to key mismatch -- same as in the scenario
+ * where a key is available -- making it appear to the attacker that all PSK identity hints and PSK
+ * identities are supported.</p>
+ *
+ * <h3>Maximum sizes</h3>
+ *
+ * <p>The maximum supported sizes are as follows:
+ * <ul>
+ * <li>256 bytes for keys (see {@link #MAX_KEY_LENGTH_BYTES}),</li>
+ * <li>128 bytes for PSK identity and PSK identity hint (in modified UTF-8 representation) (see
+ * {@link #MAX_IDENTITY_LENGTH_BYTES} and {@link #MAX_IDENTITY_HINT_LENGTH_BYTES}).</li>
+ * </ul></p>
+ *
+ * <h3>Example</h3>
+ * The following example illustrates how to create an {@code SSLContext} which enables the use of
+ * TLS-PSK in {@code SSLSocket}, {@code SSLServerSocket} and {@code SSLEngine} instances obtained
+ * from it.
+ * <pre> {@code
+ * PSKKeyManager myPskKeyManager = ...;
+ *
+ * SSLContext sslContext = SSLContext.getInstance("TLS");
+ * sslContext.init(
+ * new KeyManager[] {myPskKeyManager},
+ * new TrustManager[0], // No TrustManagers needed in TLS-PSK
+ * null // Use the default source of entropy
+ * );
+ *
+ * SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(...);
+ * // Enable a TLS-PSK cipher suite (no TLS-PSK cipher suites are enabled by default)
+ * sslSocket.setEnabledCipherSuites(new String[] {"TLS_PSK_WITH_AES_128_CBC_SHA"});
+ * sslSocket.startHandshake();
+ * }</pre>
+ */
+public interface PSKKeyManager extends com.android.org.conscrypt.PSKKeyManager {
+ // IMPLEMENTATION DETAILS: This class exists only because the default implemenetation of the
+ // TLS/SSL JSSE provider (currently Conscrypt) cannot depend on Android framework classes.
+ // As a result, this framework class simply extends the PSKKeyManager interface from Conscrypt
+ // without adding any new methods or fields. Moreover, for technical reasons (Conscrypt classes
+ // are "hidden") this class replaces the Javadoc of Conscrypt's PSKKeyManager.
+
+ /**
+ * Maximum supported length (in bytes) for PSK identity hint (in modified UTF-8 representation).
+ */
+ int MAX_IDENTITY_HINT_LENGTH_BYTES =
+ com.android.org.conscrypt.PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES;
+
+ /** Maximum supported length (in bytes) for PSK identity (in modified UTF-8 representation). */
+ int MAX_IDENTITY_LENGTH_BYTES =
+ com.android.org.conscrypt.PSKKeyManager.MAX_IDENTITY_LENGTH_BYTES;
+
+ /** Maximum supported length (in bytes) for PSK. */
+ int MAX_KEY_LENGTH_BYTES = com.android.org.conscrypt.PSKKeyManager.MAX_KEY_LENGTH_BYTES;
+
+ /**
+ * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
+ * socket.
+ *
+ * @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
+ */
+ @Override
+ String chooseServerKeyIdentityHint(Socket socket);
+
+ /**
+ * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
+ * engine.
+ *
+ * @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
+ */
+ @Override
+ String chooseServerKeyIdentityHint(SSLEngine engine);
+
+ /**
+ * Gets the PSK identity to report to the server to help agree on the PSK for the provided
+ * socket.
+ *
+ * @param identityHint identity hint provided by the server or {@code null} if none provided.
+ *
+ * @return PSK identity to provide to the server. {@code null} is permitted but will be
+ * converted into an empty string.
+ */
+ @Override
+ String chooseClientKeyIdentity(String identityHint, Socket socket);
+
+ /**
+ * Gets the PSK identity to report to the server to help agree on the PSK for the provided
+ * engine.
+ *
+ * @param identityHint identity hint provided by the server or {@code null} if none provided.
+ *
+ * @return PSK identity to provide to the server. {@code null} is permitted but will be
+ * converted into an empty string.
+ */
+ @Override
+ String chooseClientKeyIdentity(String identityHint, SSLEngine engine);
+
+ /**
+ * Gets the PSK to use for the provided socket.
+ *
+ * @param identityHint identity hint provided by the server to help select the key or
+ * {@code null} if none provided.
+ * @param identity identity provided by the client to help select the key.
+ *
+ * @return key or {@code null} to signal to peer that no suitable key is available and to abort
+ * the handshake.
+ */
+ @Override
+ SecretKey getKey(String identityHint, String identity, Socket socket);
+
+ /**
+ * Gets the PSK to use for the provided engine.
+ *
+ * @param identityHint identity hint provided by the server to help select the key or
+ * {@code null} if none provided.
+ * @param identity identity provided by the client to help select the key.
+ *
+ * @return key or {@code null} to signal to peer that no suitable key is available and to abort
+ * the handshake.
+ */
+ @Override
+ SecretKey getKey(String identityHint, String identity, SSLEngine engine);
+}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 9218c11..53e87a6 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -25,6 +25,7 @@
import android.nfc.INfcAdapterExtras;
import android.nfc.INfcTag;
import android.nfc.INfcCardEmulation;
+import android.nfc.INfcLockscreenDispatch;
import android.os.Bundle;
/**
@@ -52,4 +53,6 @@
void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
void setP2pModes(int initatorModes, int targetModes);
+
+ void registerLockscreenDispatch(INfcLockscreenDispatch lockscreenDispatch, in int[] techList);
}
diff --git a/core/java/android/nfc/INfcLockscreenDispatch.aidl b/core/java/android/nfc/INfcLockscreenDispatch.aidl
new file mode 100644
index 0000000..27e9a8c
--- /dev/null
+++ b/core/java/android/nfc/INfcLockscreenDispatch.aidl
@@ -0,0 +1,12 @@
+package android.nfc;
+
+import android.nfc.Tag;
+
+/**
+ * @hide
+ */
+interface INfcLockscreenDispatch {
+
+ boolean onTagDetected(in Tag tag);
+
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index dd8e41c..be098a8 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -380,6 +380,16 @@
public Uri[] createBeamUris(NfcEvent event);
}
+
+ /**
+ * A callback to be invoked when an application has registered for receiving
+ * tags at the lockscreen.
+ */
+ public interface NfcLockscreenDispatch {
+ public boolean onTagDetected(Tag tag);
+ }
+
+
/**
* Helper to check if this device has FEATURE_NFC, but without using
* a context.
@@ -1417,6 +1427,26 @@
}
}
+ public boolean registerLockscreenDispatch(final NfcLockscreenDispatch lockscreenDispatch,
+ String[] techList) {
+ try {
+ sService.registerLockscreenDispatch(new INfcLockscreenDispatch.Stub() {
+ @Override
+ public boolean onTagDetected(Tag tag) throws RemoteException {
+ return lockscreenDispatch.onTagDetected(tag);
+ }
+ }, Tag.techListFromStrings(techList));
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Unable to register LockscreenDispatch", e);
+ return false;
+ }
+
+ return true;
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 0d261d1..43be702 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -35,6 +35,7 @@
import java.io.IOException;
import java.util.Arrays;
+import java.util.HashMap;
/**
* Represents an NFC tag that has been discovered.
@@ -195,6 +196,41 @@
return strings;
}
+ static int[] techListFromStrings(String[] techStringList) throws IllegalArgumentException {
+ if (techStringList == null) {
+ throw new IllegalArgumentException("List cannot be null");
+ }
+ int[] techIntList = new int[techStringList.length];
+ HashMap<String, Integer> stringToCodeMap = getTechStringToCodeMap();
+ for (int i = 0; i < techStringList.length; i++) {
+ Integer code = stringToCodeMap.get(techStringList[i]);
+
+ if (code == null) {
+ throw new IllegalArgumentException("Unknown tech type " + techStringList[i]);
+ }
+
+ techIntList[i] = code.intValue();
+ }
+ return techIntList;
+ }
+
+ private static HashMap<String, Integer> getTechStringToCodeMap() {
+ HashMap<String, Integer> techStringToCodeMap = new HashMap<String, Integer>();
+
+ techStringToCodeMap.put(IsoDep.class.getName(), TagTechnology.ISO_DEP);
+ techStringToCodeMap.put(MifareClassic.class.getName(), TagTechnology.MIFARE_CLASSIC);
+ techStringToCodeMap.put(MifareUltralight.class.getName(), TagTechnology.MIFARE_ULTRALIGHT);
+ techStringToCodeMap.put(Ndef.class.getName(), TagTechnology.NDEF);
+ techStringToCodeMap.put(NdefFormatable.class.getName(), TagTechnology.NDEF_FORMATABLE);
+ techStringToCodeMap.put(NfcA.class.getName(), TagTechnology.NFC_A);
+ techStringToCodeMap.put(NfcB.class.getName(), TagTechnology.NFC_B);
+ techStringToCodeMap.put(NfcF.class.getName(), TagTechnology.NFC_F);
+ techStringToCodeMap.put(NfcV.class.getName(), TagTechnology.NFC_V);
+ techStringToCodeMap.put(NfcBarcode.class.getName(), TagTechnology.NFC_BARCODE);
+
+ return techStringToCodeMap;
+ }
+
/**
* For use by NfcService only.
* @hide
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
new file mode 100644
index 0000000..f3a95b9
--- /dev/null
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -0,0 +1,49 @@
+/*
+ * 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.os;
+
+/**
+ * Battery manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class BatteryManagerInternal {
+ /**
+ * Returns true if the device is plugged into any of the specified plug types.
+ */
+ public abstract boolean isPowered(int plugTypeSet);
+
+ /**
+ * Returns the current plug type.
+ */
+ public abstract int getPlugType();
+
+ /**
+ * Returns battery level as a percentage.
+ */
+ public abstract int getBatteryLevel();
+
+ /**
+ * Returns whether we currently consider the battery level to be low.
+ */
+ public abstract boolean getBatteryLevelLow();
+
+ /**
+ * Returns a non-zero value if an unsupported charger is attached.
+ */
+ public abstract int getInvalidCharger();
+}
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 69b828f..08a15eb 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -16,8 +16,6 @@
package android.os;
-import android.view.WindowManagerPolicy;
-
/**
* Power manager local system service interface.
*
@@ -57,12 +55,9 @@
public abstract boolean getLowPowerModeEnabled();
+ public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
+
public interface LowPowerModeListener {
public void onLowPowerModeChanged(boolean enabled);
}
-
- public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
-
- // TODO: Remove this and retrieve as a local service instead.
- public abstract void setPolicy(WindowManagerPolicy policy);
}
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 ba66e65..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}.
*/
@@ -7963,8 +7982,7 @@
actualContext = ((ContextWrapper) actualContext).getBaseContext();
}
final int intentFlags = (actualContext instanceof Activity)
- ? Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
- : Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK;
+ ? 0 : Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK;
// Launch pivot dialog through intent for now
final Intent intent = new Intent(ACTION_QUICK_CONTACT).addFlags(intentFlags);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 06c05ee..9b6f0ea 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2460,6 +2460,18 @@
public static final String POINTER_SPEED = "pointer_speed";
/**
+ * Whether lock-to-app will be triggered by long-press on recents.
+ * @hide
+ */
+ public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled";
+
+ /**
+ * Whether lock-to-app will lock the keyguard when exiting.
+ * @hide
+ */
+ public static final String LOCK_TO_APP_EXIT_LOCKED = "lock_to_app_exit_locked";
+
+ /**
* I am the lolrus.
* <p>
* Nonzero values indicate that the user has a bukkit.
@@ -4537,12 +4549,6 @@
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
- * Specifies the package name currently configured to be the primary phone application
- * @hide
- */
- public static final String PHONE_DEFAULT_APPLICATION = "phone_default_application";
-
- /**
* Name of a package that the current user has explicitly allowed to see all of that
* user's notifications.
*
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
index 2fcec52..5fd597b 100644
--- a/core/java/android/service/fingerprint/FingerprintManager.java
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -22,12 +22,14 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
+import android.util.Slog;
/**
* A class that coordinates access to the fingerprint hardware.
@@ -36,31 +38,40 @@
public class FingerprintManager {
private static final String TAG = "FingerprintManager";
private static final boolean DEBUG = true;
- private static final String FINGERPRINT_SERVICE_PACKAGE = "com.android.service.fingerprint";
- private static final String FINGERPRINT_SERVICE_CLASS =
- "com.android.service.fingerprint.FingerprintService";
private static final int MSG_ENROLL_RESULT = 100;
- private static final int MSG_SCANNED = 101;
- private static final int MSG_ERROR = 102;
- private static final int MSG_REMOVED = 103;
+ private static final int MSG_ACQUIRED = 101;
+ private static final int MSG_PROCESSED = 102;
+ private static final int MSG_ERROR = 103;
+ private static final int MSG_REMOVED = 104;
+ // Errors generated by layers above HAL
public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10;
- public static final int FINGERPRINT_ERROR = -1; // One of the error messages below.
- // Progress messages.
- public static final int FINGERPRINT_SCANNED = 1;
- public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2;
+ // Message types. Must agree with HAL (fingerprint.h)
+ public static final int FINGERPRINT_ERROR = -1;
+ public static final int FINGERPRINT_ACQUIRED = 1;
+ public static final int FINGERPRINT_PROCESSED = 2;
+ public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
- // Error messages. Must agree with fingerprint HAL definitions.
+ // Error messages. Must agree with HAL (fingerprint.h)
public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
- public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2;
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+ // FINGERPRINT_ACQUIRED messages. Must agree with HAL (fingerprint.h)
+ public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+ public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+ public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+ public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4;
+ public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8;
+ public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16;
+
private IFingerprintService mService;
private FingerprintManagerReceiver mClientReceiver;
private Context mContext;
+ private IBinder mToken = new Binder();
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
@@ -69,8 +80,11 @@
case MSG_ENROLL_RESULT:
mClientReceiver.onEnrollResult(msg.arg1, msg.arg2);
break;
- case MSG_SCANNED:
- mClientReceiver.onScanned(msg.arg1, msg.arg2);
+ case MSG_ACQUIRED:
+ mClientReceiver.onAcquired(msg.arg1);
+ break;
+ case MSG_PROCESSED:
+ mClientReceiver.onProcessed(msg.arg1);
break;
case MSG_ERROR:
mClientReceiver.onError(msg.arg1);
@@ -82,45 +96,29 @@
}
};
- public FingerprintManager(Context context) {
+ /**
+ * @hide
+ */
+ public FingerprintManager(Context context, IFingerprintService service) {
mContext = context;
- // Connect to service...
- Intent intent = new Intent();
- intent.setClassName(FINGERPRINT_SERVICE_PACKAGE, FINGERPRINT_SERVICE_CLASS);
- if (!context.bindServiceAsUser(intent, mFingerprintConnection,
- Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF)) {
- if (DEBUG) Log.v(TAG, "Can't bind to " + FINGERPRINT_SERVICE_CLASS);
+ mService = service;
+ if (mService == null) {
+ Slog.v(TAG, "FingerprintManagerService was null");
}
}
- private final ServiceConnection mFingerprintConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) Log.v(TAG, "Connected to FingerprintService");
- mService = IFingerprintService.Stub.asInterface(service);
- try {
- mService.startListening(mServiceReceiver, getCurrentUserId());
- } catch (RemoteException e) {
- if (DEBUG) Log.v(TAG, "Failed to set callback", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Log.v(TAG, "Disconnected from FingerprintService");
- mService = null;
- }
- };
-
private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
public void onEnrollResult(int fingerprintId, int remaining) {
mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget();
}
- public void onScanned(int fingerprintId, int confidence) {
- mHandler.obtainMessage(MSG_SCANNED, fingerprintId, confidence)
- .sendToTarget();;
+ public void onAcquired(int acquireInfo) {
+ mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0).sendToTarget();
+ }
+
+ public void onProcessed(int fingerprintId) {
+ mHandler.obtainMessage(MSG_PROCESSED, fingerprintId, 0).sendToTarget();
}
public void onError(int error) {
@@ -151,12 +149,14 @@
*/
public void enroll(long timeout) {
if (mServiceReceiver == null) {
- throw new IllegalStateException("enroll: Call registerCallback() first");
+ sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
+ return;
}
if (mService != null) try {
- mService.enroll(timeout, getCurrentUserId());
+ mService.enroll(mToken, timeout, getCurrentUserId());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception while enrolling: ", e);
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
}
}
@@ -166,10 +166,19 @@
* @param fingerprintId
*/
public void remove(int fingerprintId) {
- if (mService != null) try {
- mService.remove(fingerprintId, getCurrentUserId());
- } catch (RemoteException e) {
- Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
+ if (mServiceReceiver == null) {
+ sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
+ return;
+ }
+ if (mService != null) {
+ try {
+ mService.remove(mToken, fingerprintId, getCurrentUserId());
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
+ }
+ } else {
+ Log.w(TAG, "remove(): Service not connected!");
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
}
}
@@ -181,10 +190,13 @@
mClientReceiver = receiver;
if (mService != null) {
try {
- mService.startListening(mServiceReceiver, getCurrentUserId());
+ mService.startListening(mToken, mServiceReceiver, getCurrentUserId());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in startListening(): ", e);
}
+ } else {
+ Log.w(TAG, "startListening(): Service not connected!");
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
}
}
@@ -201,15 +213,38 @@
* Stops the client from listening to fingerprint events.
*/
public void stopListening() {
- mClientReceiver = null;
if (mService != null) {
try {
- mService.stopListening(getCurrentUserId());
+ mService.stopListening(mToken, getCurrentUserId());
+ mClientReceiver = null;
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in stopListening(): ", e);
}
} else {
Log.w(TAG, "stopListening(): Service not connected!");
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
}
}
+
+ public void enrollCancel() {
+ if (mServiceReceiver == null) {
+ sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
+ return;
+ }
+ if (mService != null) {
+ try {
+ mService.enrollCancel(mToken, getCurrentUserId());
+ mClientReceiver = null;
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception in enrollCancel(): ", e);
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
+ }
+ } else {
+ Log.w(TAG, "enrollCancel(): Service not connected!");
+ }
+ }
+
+ private void sendError(int msg, int arg1, int arg2) {
+ mHandler.obtainMessage(msg, arg1, arg2);
+ }
}
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
index 34f1655..e5193f5 100644
--- a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
+++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
@@ -30,18 +30,32 @@
public void onEnrollResult(int fingerprintId, int remaining) { }
/**
- * Fingerprint scan detected. Most clients will use this function to detect a fingerprint
+ * Fingerprint touch detected, but not processed yet. Clients will use this message to
+ * determine a good or bad scan before the fingerprint is processed. This is meant for the
+ * client to provide feedback about the scan or alert the user that recognition is to follow.
*
- * @param fingerprintId is the finger the hardware has detected.
- * @param confidence from 0 (no confidence) to 65535 (high confidence). Fingerprint 0 has
- * special meaning - the finger wasn't recognized.
+ * @param acquiredInfo one of:
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_GOOD},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_PARTIAL},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_INSUFFICIENT},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_IMAGER_DIRTY},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_SLOW},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_FAST}
*/
- public void onScanned(int fingerprintId, int confidence) { }
+ public void onAcquired(int acquiredInfo) { }
+
+ /**
+ * Fingerprint has been detected and processed. A non-zero return indicates a valid
+ * fingerprint was detected.
+ *
+ * @param fingerprintId the finger id, or 0 if not recognized.
+ */
+ public void onProcessed(int fingerprintId) { }
/**
* An error was detected during scan or enrollment. One of
* {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE},
- * {@link FingerprintManager#FINGERPRINT_ERROR_BAD_CAPTURE} or
+ * {@link FingerprintManager#FINGERPRINT_ERROR_UNABLE_TO_PROCESS} or
* {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT}
* {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE}
*
diff --git a/core/java/android/service/fingerprint/FingerprintService.java b/core/java/android/service/fingerprint/FingerprintService.java
deleted file mode 100644
index c7fa7cd..0000000
--- a/core/java/android/service/fingerprint/FingerprintService.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.fingerprint;
-
-import android.app.Service;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.util.Slog;
-
-import java.io.PrintWriter;
-import java.util.HashMap;
-
-/**
- * A service to manage multiple clients that want to access the fingerprint HAL API.
- * The service is responsible for maintaining a list of clients and dispatching all
- * fingerprint -related events.
- *
- * @hide
- */
-public class FingerprintService extends Service {
- private final String TAG = FingerprintService.class.getSimpleName() +
- "[" + getClass().getSimpleName() + "]";
- private static final boolean DEBUG = true;
- HashMap<IFingerprintServiceReceiver, ClientData> mClients =
- new HashMap<IFingerprintServiceReceiver, ClientData>();
-
- private static final int MSG_NOTIFY = 10;
-
- Handler mHandler = new Handler() {
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case MSG_NOTIFY:
- handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj);
- break;
-
- default:
- Slog.w(TAG, "Unknown message:" + msg.what);
- }
- }
- };
-
- private static final int STATE_IDLE = 0;
- private static final int STATE_LISTENING = 1;
- private static final int STATE_ENROLLING = 2;
- private static final int STATE_DELETING = 3;
- private static final long MS_PER_SEC = 1000;
-
- private static final class ClientData {
- public IFingerprintServiceReceiver receiver;
- int state;
- int userId;
- }
-
- @Override
- public final IBinder onBind(Intent intent) {
- if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
- return new FingerprintServiceWrapper();
- }
-
- // JNI methods to communicate from FingerprintManagerService to HAL
- native int nativeEnroll(int timeout);
- native int nativeRemove(int fingerprintId);
-
- // JNI methods for communicating from HAL to clients
- void notify(int msg, int arg1, int arg2) {
- mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget();
- }
-
- void handleNotify(int msg, int arg1, int arg2) {
- for (int i = 0; i < mClients.size(); i++) {
- ClientData clientData = mClients.get(i);
- switch (msg) {
- case FingerprintManager.FINGERPRINT_ERROR: {
- if (clientData.state != STATE_IDLE) {
- // FINGERPRINT_ERROR_HW_UNAVAILABLE
- // FINGERPRINT_ERROR_BAD_CAPTURE
- // FINGERPRINT_ERROR_TIMEOUT
- // FINGERPRINT_ERROR_NO_SPACE
- final int error = arg1;
- clientData.state = STATE_IDLE;
- if (clientData.receiver != null) {
- try {
- clientData.receiver.onError(error);
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to client. Did it die?", e);
- }
- }
- }
- }
- break;
- case FingerprintManager.FINGERPRINT_SCANNED: {
- final int fingerId = arg1;
- final int confidence = arg2;
- if (clientData.state == STATE_LISTENING && clientData.receiver != null) {
- try {
- clientData.receiver.onScanned(fingerId, confidence);
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to client. Did it die?", e);
- }
- }
- break;
- }
- case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
- if (clientData.state == STATE_ENROLLING) {
- final int fingerId = arg1;
- final int remaining = arg2;
- if (remaining == 0) {
- FingerprintUtils.addFingerprintIdForUser(fingerId,
- getContentResolver(), clientData.userId);
- clientData.state = STATE_IDLE; // Nothing left to do
- }
- if (clientData.receiver != null) {
- try {
- clientData.receiver.onEnrollResult(fingerId, remaining);
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to client. Did it die?", e);
- }
- }
- }
- break;
- }
- case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
- int fingerId = arg1;
- if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
- if (clientData.state == STATE_DELETING) {
- FingerprintUtils.removeFingerprintIdForUser(fingerId, getContentResolver(),
- clientData.userId);
- if (clientData.receiver != null) {
- try {
- clientData.receiver.onRemoved(fingerId);
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to client. Did it die?", e);
- }
- }
- }
- }
- break;
- }
- }
- }
-
- int enroll(IFingerprintServiceReceiver receiver, long timeout, int userId) {
- ClientData clientData = mClients.get(receiver);
- if (clientData != null) {
- if (clientData.userId != userId) throw new IllegalStateException("Bad user");
- clientData.state = STATE_ENROLLING;
- return nativeEnroll((int) (timeout / MS_PER_SEC));
- }
- return -1;
- }
-
- int remove(IFingerprintServiceReceiver receiver, int fingerId, int userId) {
- ClientData clientData = mClients.get(receiver);
- if (clientData != null) {
- if (clientData.userId != userId) throw new IllegalStateException("Bad user");
- clientData.state = STATE_DELETING;
- // The fingerprint id will be removed when we get confirmation from the HAL
- return nativeRemove(fingerId);
- }
- return -1;
- }
-
- void startListening(IFingerprintServiceReceiver receiver, int userId) {
- ClientData clientData = new ClientData();
- clientData.state = STATE_LISTENING;
- clientData.receiver = receiver;
- clientData.userId = userId;
- mClients.put(receiver, clientData);
- }
-
- void stopListening(IFingerprintServiceReceiver receiver, int userId) {
- ClientData clientData = mClients.get(receiver);
- if (clientData != null) {
- clientData.state = STATE_IDLE;
- clientData.userId = -1;
- clientData.receiver = null;
- }
- mClients.remove(receiver);
- }
-
- private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
- IFingerprintServiceReceiver mReceiver;
- public int enroll(long timeout, int userId) {
- return mReceiver != null ? FingerprintService.this.enroll(mReceiver, timeout, userId)
- : FingerprintManager.FINGERPRINT_ERROR_NO_RECEIVER;
- }
-
- public int remove(int fingerprintId, int userId) {
- return FingerprintService.this.remove(mReceiver, fingerprintId, userId);
- }
-
- public void startListening(IFingerprintServiceReceiver receiver, int userId) {
- mReceiver = receiver;
- FingerprintService.this.startListening(receiver, userId);
- }
-
- public void stopListening(int userId) {
- FingerprintService.this.stopListening(mReceiver, userId);
- }
- }
-}
diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java
index 81a2aac..f4b5526 100644
--- a/core/java/android/service/fingerprint/FingerprintUtils.java
+++ b/core/java/android/service/fingerprint/FingerprintUtils.java
@@ -18,10 +18,12 @@
import android.content.ContentResolver;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import java.util.Arrays;
+public
class FingerprintUtils {
private static final boolean DEBUG = true;
private static final String TAG = "FingerprintUtils";
@@ -30,13 +32,16 @@
String fingerIdsRaw = Settings.Secure.getStringForUser(res,
Settings.Secure.USER_FINGERPRINT_IDS, userId);
- String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", ");
- int result[] = new int[fingerStringIds.length];
- for (int i = 0; i < result.length; i++) {
- try {
- result[i] = Integer.decode(fingerStringIds[i]);
- } catch (NumberFormatException e) {
- if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]);
+ int result[] = {};
+ if (!TextUtils.isEmpty(fingerIdsRaw)) {
+ String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", ");
+ result = new int[fingerStringIds.length];
+ for (int i = 0; i < result.length; i++) {
+ try {
+ result[i] = Integer.decode(fingerStringIds[i]);
+ } catch (NumberFormatException e) {
+ if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]);
+ }
}
}
return result;
diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl
index e92c20c..43d5e9a 100644
--- a/core/java/android/service/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/service/fingerprint/IFingerprintService.aidl
@@ -22,17 +22,20 @@
* Communication channel from client to the fingerprint service.
* @hide
*/
-interface IFingerprintService {
- // Returns 0 if successfully started, -1 otherwise
- int enroll(long timeout, int userId);
+oneway interface IFingerprintService {
+ // Any errors resulting from this call will be returned to the listener
+ void enroll(IBinder token, long timeout, int userId);
+
+ // Any errors resulting from this call will be returned to the listener
+ void enrollCancel(IBinder token, int userId);
- // Returns 0 if fingerprintId's template can be removed, -1 otherwise
- int remove(int fingerprintId, int userId);
+ // Any errors resulting from this call will be returned to the listener
+ void remove(IBinder token, int fingerprintId, int userId);
// Start listening for fingerprint events. This has the side effect of starting
// the hardware if not already started.
- oneway void startListening(IFingerprintServiceReceiver receiver, int userId);
+ void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId);
// Stops listening for fingerprints
- oneway void stopListening(int userId);
+ void stopListening(IBinder token, int userId);
}
diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
index 4826b59..af4128f 100644
--- a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
@@ -24,7 +24,8 @@
*/
oneway interface IFingerprintServiceReceiver {
void onEnrollResult(int fingerprintId, int remaining);
- void onScanned(int fingerprintId, int confidence);
+ void onAcquired(int acquiredInfo);
+ void onProcessed(int fingerprintId);
void onError(int error);
void onRemoved(int fingerprintId);
}
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 0a4f641..e936232 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -206,6 +206,10 @@
// like CircularPropagation
EpicenterCallback mEpicenterCallback;
+ // For Fragment shared element transitions, linking views explicitly by mismatching
+ // viewNames.
+ ArrayMap<String, String> mNameOverrides;
+
/**
* Constructs a Transition object with no target objects. A transition with
* no targets defaults to running on all target objects in the scene hierarchy
@@ -669,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);
@@ -1347,6 +1351,21 @@
} else {
captureHierarchy(sceneRoot, start);
}
+ if (!start && mNameOverrides != null) {
+ int numOverrides = mNameOverrides.size();
+ ArrayList<View> overriddenViews = new ArrayList<View>(numOverrides);
+ for (int i = 0; i < numOverrides; i++) {
+ String fromName = mNameOverrides.keyAt(i);
+ overriddenViews.add(mStartValues.nameValues.remove(fromName));
+ }
+ for (int i = 0; i < numOverrides; i++) {
+ View view = overriddenViews.get(i);
+ if (view != null) {
+ String toName = mNameOverrides.valueAt(i);
+ mStartValues.nameValues.put(toName, view);
+ }
+ }
+ }
}
static void addViewValues(TransitionValuesMaps transitionValuesMaps,
@@ -1400,10 +1419,12 @@
mStartValues.viewValues.clear();
mStartValues.idValues.clear();
mStartValues.itemIdValues.clear();
+ mStartValues.nameValues.clear();
} else {
mEndValues.viewValues.clear();
mEndValues.idValues.clear();
mEndValues.itemIdValues.clear();
+ mEndValues.nameValues.clear();
}
}
@@ -1566,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) {
@@ -1611,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()
@@ -1866,6 +1890,20 @@
return mCanRemoveViews;
}
+ /**
+ * Sets the shared element names -- a mapping from a name at the start state to
+ * a different name at the end state.
+ * @hide
+ */
+ public void setNameOverrides(ArrayMap<String, String> overrides) {
+ mNameOverrides = overrides;
+ }
+
+ /** @hide */
+ public ArrayMap<String, String> getNameOverrides() {
+ return mNameOverrides;
+ }
+
@Override
public String toString() {
return toString("");
@@ -2035,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/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index 698b563..c04be4f 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -168,30 +168,106 @@
@Override
public TransitionSet addTarget(View target) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).addTarget(target);
+ }
return (TransitionSet) super.addTarget(target);
}
@Override
public TransitionSet addTarget(int targetId) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).addTarget(targetId);
+ }
return (TransitionSet) super.addTarget(targetId);
}
@Override
+ public TransitionSet addTarget(String targetName) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).addTarget(targetName);
+ }
+ return (TransitionSet) super.addTarget(targetName);
+ }
+
+ @Override
+ public TransitionSet addTarget(Class targetType) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).addTarget(targetType);
+ }
+ return (TransitionSet) super.addTarget(targetType);
+ }
+
+ @Override
public TransitionSet addListener(TransitionListener listener) {
return (TransitionSet) super.addListener(listener);
}
@Override
public TransitionSet removeTarget(int targetId) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).removeTarget(targetId);
+ }
return (TransitionSet) super.removeTarget(targetId);
}
@Override
public TransitionSet removeTarget(View target) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).removeTarget(target);
+ }
return (TransitionSet) super.removeTarget(target);
}
@Override
+ public TransitionSet removeTarget(Class target) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).removeTarget(target);
+ }
+ return (TransitionSet) super.removeTarget(target);
+ }
+
+ @Override
+ public TransitionSet removeTarget(String target) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).removeTarget(target);
+ }
+ return (TransitionSet) super.removeTarget(target);
+ }
+
+ @Override
+ public Transition excludeTarget(View target, boolean exclude) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).excludeTarget(target, exclude);
+ }
+ return super.excludeTarget(target, exclude);
+ }
+
+ @Override
+ public Transition excludeTarget(String targetViewName, boolean exclude) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).excludeTarget(targetViewName, exclude);
+ }
+ return super.excludeTarget(targetViewName, exclude);
+ }
+
+ @Override
+ public Transition excludeTarget(int targetId, boolean exclude) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).excludeTarget(targetId, exclude);
+ }
+ return super.excludeTarget(targetId, exclude);
+ }
+
+ @Override
+ public Transition excludeTarget(Class type, boolean exclude) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).excludeTarget(type, exclude);
+ }
+ return super.excludeTarget(type, exclude);
+ }
+
+ @Override
public TransitionSet removeListener(TransitionListener listener) {
return (TransitionSet) super.removeListener(listener);
}
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 0f7638b..947e1a7 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -18,6 +18,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
import android.view.View;
import android.view.ViewGroup;
@@ -272,15 +275,23 @@
if (startView.getParent() == null) {
// no parent - safe to use
overlayView = startView;
- } else if (startView.getParent() instanceof View &&
- startView.getParent().getParent() == null) {
+ } else if (startView.getParent() instanceof View) {
View startParent = (View) startView.getParent();
- int id = startParent.getId();
- if (id != View.NO_ID && sceneRoot.findViewById(id) != null && mCanRemoveViews) {
- // no parent, but its parent is unparented but the parent
- // hierarchy has been replaced by a new hierarchy with the same id
- // and it is safe to un-parent startView
- overlayView = startView;
+ if (!isValidTarget(startParent)) {
+ if (startView.isAttachedToWindow()) {
+ overlayView = copyViewImage(startView);
+ } else {
+ overlayView = startView;
+ }
+ } else if (startParent.getParent() == null) {
+ int id = startParent.getId();
+ if (id != View.NO_ID && sceneRoot.findViewById(id) != null
+ && mCanRemoveViews) {
+ // no parent, but its parent is unparented but the parent
+ // hierarchy has been replaced by a new hierarchy with the same id
+ // and it is safe to un-parent startView
+ overlayView = startView;
+ }
}
}
}
@@ -320,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;
@@ -378,6 +379,36 @@
return null;
}
+ private View copyViewImage(View view) {
+ int width = view.getWidth();
+ int height = view.getHeight();
+ if (width <= 0 || height <= 0) {
+ return null;
+ }
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ final BitmapDrawable drawable = new BitmapDrawable(bitmap);
+
+ View overlayView = new View(view.getContext());
+ overlayView.setBackground(drawable);
+ int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+ int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
+ overlayView.measure(widthSpec, heightSpec);
+ overlayView.layout(0, 0, width, height);
+ 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..f4a0448
--- /dev/null
+++ b/core/java/android/util/PathParser.java
@@ -0,0 +1,609 @@
+/*
+ * 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) {
+ if (pathData == null) {
+ return null;
+ }
+ 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()]);
+ }
+
+ /**
+ * @param source The array of PathDataNode to be duplicated.
+ * @return a deep copy of the <code>source</code>.
+ */
+ public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {
+ PathDataNode[] copy = new PathParser.PathDataNode[source.length];
+ for (int i = 0; i < source.length; i ++) {
+ copy[i] = new PathDataNode(source[i]);
+ }
+ return copy;
+ }
+
+ /**
+ * @param nodesFrom The source path represented in an array of PathDataNode
+ * @param nodesTo The target path represented in an array of PathDataNode
+ * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
+ */
+ public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
+ if (nodesFrom == null || nodesTo == null) {
+ return false;
+ }
+
+ if (nodesFrom.length != nodesTo.length) {
+ return false;
+ }
+
+ for (int i = 0; i < nodesFrom.length; i ++) {
+ if (nodesFrom[i].mType != nodesTo[i].mType
+ || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Update the target's data to match the source.
+ * Before calling this, make sure canMorph(target, source) is true.
+ *
+ * @param target The target path represented in an array of PathDataNode
+ * @param source The source path represented in an array of PathDataNode
+ */
+ public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
+ for (int i = 0; i < source.length; i ++) {
+ target[i].mType = source[i].mType;
+ for (int j = 0; j < source[i].mParams.length; j ++) {
+ target[i].mParams[j] = source[i].mParams[j];
+ }
+ }
+ }
+
+ 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;
+ }
+
+ /**
+ * Each PathDataNode represents one command in the "d" attribute of the svg
+ * file.
+ * An array of PathDataNode can represent the whole "d" attribute.
+ */
+ 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);
+ }
+
+ /**
+ * Convert an array of PathDataNode to Path.
+ *
+ * @param node The source array of PathDataNode.
+ * @param path The target Path object.
+ */
+ 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;
+ }
+ }
+
+ /**
+ * The current PathDataNode will be interpolated between the
+ * <code>nodeFrom</code> and <code>nodeTo</code> according to the
+ * <code>fraction</code>.
+ *
+ * @param nodeFrom The start value as a PathDataNode.
+ * @param nodeTo The end value as a PathDataNode
+ * @param fraction The fraction to interpolate.
+ */
+ public void interpolatePathDataNode(PathDataNode nodeFrom,
+ PathDataNode nodeTo, float fraction) {
+ for (int i = 0; i < nodeFrom.mParams.length; i++) {
+ mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
+ + nodeTo.mParams[i] * fraction;
+ }
+ }
+
+ 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 832d67a..9fafc48 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -404,6 +404,9 @@
@Override
public void scale(float sx, float sy) {
+ // TODO: remove
+ if (sx > 1000000 || sy > 1000000) throw new IllegalArgumentException("invalid scales passed " + sx + ", " + sy);
+
nScale(mRenderer, sx, sy);
}
@@ -544,9 +547,9 @@
///////////////////////////////////////////////////////////////////////////
@Override
- public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
- Paint paint) {
- nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
+ public void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, boolean useCenter, Paint paint) {
+ nDrawArc(mRenderer, left, top, right, bottom,
startAngle, sweepAngle, useCenter, paint.mNativePaint);
}
@@ -771,8 +774,8 @@
}
@Override
- public void drawOval(RectF oval, Paint paint) {
- nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
+ public void drawOval(float left, float top, float right, float bottom, Paint paint) {
+ nDrawOval(mRenderer, left, top, right, bottom, paint.mNativePaint);
}
private static native void nDrawOval(long renderer, float left, float top,
@@ -801,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) {
@@ -826,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) {
@@ -862,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) {
@@ -978,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 b9e56f3..d25ef16 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25,6 +25,7 @@
import android.annotation.Nullable;
import android.content.ClipData;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -3187,6 +3188,9 @@
@ViewDebug.ExportedProperty(deepExport = true, prefix = "bg_")
private Drawable mBackground;
+ private ColorStateList mBackgroundTint = null;
+ private PorterDuff.Mode mBackgroundTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasBackgroundTint = false;
/**
* Display list used for backgrounds.
@@ -4022,6 +4026,16 @@
setStateListAnimator(AnimatorInflater.loadStateListAnimator(context,
a.getResourceId(attr, 0)));
break;
+ case R.styleable.View_backgroundTint:
+ // This will get applied later during setBackground().
+ mBackgroundTint = a.getColorStateList(R.styleable.View_backgroundTint);
+ mHasBackgroundTint = true;
+ break;
+ case R.styleable.View_backgroundTintMode:
+ // This will get applied later during setBackground().
+ mBackgroundTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.View_backgroundTintMode, -1), mBackgroundTintMode);
+ break;
}
}
@@ -4807,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);
}
/**
@@ -6783,7 +6784,7 @@
*/
private void setPressed(boolean pressed, float x, float y) {
if (pressed) {
- setDrawableHotspot(x, y);
+ drawableHotspotChanged(x, y);
}
setPressed(pressed);
@@ -9134,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)) {
@@ -13432,8 +13433,6 @@
// Destroy any previous software drawing cache if needed
if (mLayerType == LAYER_TYPE_SOFTWARE) {
destroyDrawingCache();
- invalidateParentCaches();
- invalidate(true);
}
mLayerType = layerType;
@@ -13441,13 +13440,10 @@
mLayerPaint = layerDisabled ? null : (paint == null ? new Paint() : paint);
mRenderNode.setLayerPaint(mLayerPaint);
- if (mLayerType == LAYER_TYPE_SOFTWARE) {
- // LAYER_TYPE_SOFTWARE is handled by View:draw(), so need to invalidate()
- invalidateParentCaches();
- invalidate(true);
- } else {
- invalidateViewProperty(false, false);
- }
+ // draw() behaves differently if we are on a layer, so we need to
+ // invalidate() here
+ invalidateParentCaches();
+ invalidate(true);
}
/**
@@ -14244,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;
}
@@ -15521,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.
@@ -15703,7 +15714,7 @@
return;
}
- Drawable d= null;
+ Drawable d = null;
if (resid != 0) {
d = mContext.getDrawable(resid);
}
@@ -15779,8 +15790,9 @@
// Compare the minimum sizes of the old Drawable and the new. If there isn't an old or
// if it has a different minimum size, we should layout again
- if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() ||
- mBackground.getMinimumWidth() != background.getMinimumWidth()) {
+ if (mBackground == null
+ || mBackground.getMinimumHeight() != background.getMinimumHeight()
+ || mBackground.getMinimumWidth() != background.getMinimumWidth()) {
requestLayout = true;
}
@@ -15791,6 +15803,8 @@
background.setVisible(getVisibility() == VISIBLE, false);
mBackground = background;
+ applyBackgroundTint();
+
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
mPrivateFlags |= PFLAG_ONLY_DRAWS_BACKGROUND;
@@ -15846,6 +15860,88 @@
}
/**
+ * Applies a tint to the background drawable.
+ * <p>
+ * Subsequent calls to {@link #setBackground(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#View_backgroundTint
+ * @attr ref android.R.styleable#View_backgroundTintMode
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ private void setBackgroundTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mBackgroundTint = tint;
+ mBackgroundTintMode = tintMode;
+ mHasBackgroundTint = true;
+
+ applyBackgroundTint();
+ }
+
+ /**
+ * Applies a tint to the background drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setBackground(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#View_backgroundTint
+ * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setBackgroundTint(@Nullable ColorStateList tint) {
+ setBackgroundTint(tint, mBackgroundTintMode);
+ }
+
+ /**
+ * @return the tint applied to the background drawable
+ * @attr ref android.R.styleable#View_backgroundTint
+ * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getBackgroundTint() {
+ return mBackgroundTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setBackgroundTint(ColorStateList)}} to the background drawable.
+ * The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#View_backgroundTintMode
+ * @see #setBackgroundTint(ColorStateList)
+ */
+ public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setBackgroundTint(mBackgroundTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the background drawable
+ * @attr ref android.R.styleable#View_backgroundTintMode
+ * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getBackgroundTintMode() {
+ return mBackgroundTintMode;
+ }
+
+ private void applyBackgroundTint() {
+ if (mBackground != null && mHasBackgroundTint) {
+ mBackground = mBackground.mutate();
+ mBackground.setTint(mBackgroundTint, mBackgroundTintMode);
+ }
+ }
+
+ /**
* Sets the padding. The view may add on the space required to display
* the scrollbars, depending on the style and visibility of the scrollbars.
* So the values returned from {@link #getPaddingLeft}, {@link #getPaddingTop},
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/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index af1de78..3f72b4c 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -52,7 +52,7 @@
* The View whose properties are being animated by this class. This is set at
* construction time.
*/
- private final View mView;
+ final View mView;
/**
* The duration of the underlying Animator object. By default, we don't set the duration
@@ -253,10 +253,9 @@
ViewPropertyAnimator(View view) {
mView = view;
view.ensureTransformationInfo();
- // TODO: Disabled because of b/15287046
- //if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
- // mRTBackend = new ViewPropertyAnimatorRT(view);
- //}
+ if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
+ mRTBackend = new ViewPropertyAnimatorRT(view);
+ }
}
/**
@@ -434,6 +433,9 @@
}
mPendingAnimations.clear();
mView.removeCallbacks(mAnimationStarter);
+ if (mRTBackend != null) {
+ mRTBackend.cancelAll();
+ }
}
/**
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
index 709efdb..8b4277a 100644
--- a/core/java/android/view/ViewPropertyAnimatorRT.java
+++ b/core/java/android/view/ViewPropertyAnimatorRT.java
@@ -50,6 +50,15 @@
return true;
}
+ public void cancelAll() {
+ for (int i = 0; i < mAnimators.length; i++) {
+ if (mAnimators[i] != null) {
+ mAnimators[i].cancel();
+ mAnimators[i] = null;
+ }
+ }
+ }
+
private void doStartAnimation(ViewPropertyAnimator parent) {
int size = parent.mPendingAnimations.size();
@@ -63,12 +72,22 @@
NameValuesHolder holder = parent.mPendingAnimations.get(i);
int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
- RenderNodeAnimator animator = new RenderNodeAnimator(property, holder.mFromValue + holder.mDeltaValue);
+ final float finalValue = holder.mFromValue + holder.mDeltaValue;
+ RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
animator.setStartDelay(startDelay);
animator.setDuration(duration);
animator.setInterpolator(interpolator);
animator.setTarget(mView);
animator.start();
+
+ // Alpha is a special snowflake that has the canonical value stored
+ // in mTransformationInfo instead of in RenderNode, so we need to update
+ // it with the final value here.
+ if (property == RenderNodeAnimator.ALPHA) {
+ // Don't need null check because ViewPropertyAnimator's
+ // ctor calls ensureTransformationInfo()
+ parent.mView.mTransformationInfo.mAlpha = finalValue;
+ }
}
parent.mPendingAnimations.clear();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dfd5cdf..82e5ddd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -656,7 +656,7 @@
}
public boolean invokeFunctor(long functor, boolean waitForCompletion) {
- if (mAttachInfo.mHardwareRenderer == null || !mAttachInfo.mHardwareRenderer.isEnabled()) {
+ if (mAttachInfo.mHardwareRenderer == null) {
return false;
}
mAttachInfo.mHardwareRenderer.invokeFunctor(functor, waitForCompletion);
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/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index abed082..321d9d3 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -17,6 +17,7 @@
package android.webkit;
import android.net.WebAddress;
+import android.webkit.ValueCallback;
/**
* Manages the cookies used by an application's {@link WebView} instances.
@@ -72,7 +73,7 @@
* path and name will be replaced with the new cookie. The cookie being set
* will be ignored if it is expired.
*
- * @param url the URL for which the cookie is set
+ * @param url the URL for which the cookie is to be set
* @param value the cookie as a string, using the format of the 'Set-Cookie'
* HTTP response header
*/
@@ -81,6 +82,29 @@
}
/**
+ * Sets a cookie for the given URL. Any existing cookie with the same host,
+ * path and name will be replaced with the new cookie. The cookie being set
+ * will be ignored if it is expired.
+ * <p>
+ * This method is asynchronous.
+ * If a {@link ValueCallback} is provided,
+ * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
+ * thread's {@link android.os.Looper} once the operation is complete.
+ * The value provided to the callback indicates whether the cookie was set successfully.
+ * You can pass {@code null} as the callback if you don't need to know when the operation
+ * completes or whether it succeeded, and in this case it is safe to call the method from a
+ * thread without a Looper.
+ *
+ * @param url the URL for which the cookie is to be set
+ * @param value the cookie as a string, using the format of the 'Set-Cookie'
+ * HTTP response header
+ * @param callback a callback to be executed when the cookie has been set
+ */
+ public void setCookie(String url, String value, ValueCallback<Boolean> callback) {
+ throw new MustOverrideException();
+ }
+
+ /**
* Gets the cookies for the given URL.
*
* @param url the URL for which the cookies are requested
@@ -120,19 +144,57 @@
/**
* Removes all session cookies, which are cookies without an expiration
* date.
+ * @deprecated use {@link #removeSessionCookies(ValueCallback)} instead.
*/
public void removeSessionCookie() {
throw new MustOverrideException();
}
/**
- * Removes all cookies.
+ * Removes all session cookies, which are cookies without an expiration
+ * date.
+ * <p>
+ * This method is asynchronous.
+ * If a {@link ValueCallback} is provided,
+ * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
+ * thread's {@link android.os.Looper} once the operation is complete.
+ * The value provided to the callback indicates whether any cookies were removed.
+ * You can pass {@code null} as the callback if you don't need to know when the operation
+ * completes or whether any cookie were removed, and in this case it is safe to call the
+ * method from a thread without a Looper.
+ * @param callback a callback which is executed when the session cookies have been removed
*/
+ public void removeSessionCookies(ValueCallback<Boolean> callback) {
+ throw new MustOverrideException();
+ }
+
+ /**
+ * Removes all cookies.
+ * @deprecated Use {@link #removeAllCookies(ValueCallback)} instead.
+ */
+ @Deprecated
public void removeAllCookie() {
throw new MustOverrideException();
}
/**
+ * Removes all cookies.
+ * <p>
+ * This method is asynchronous.
+ * If a {@link ValueCallback} is provided,
+ * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
+ * thread's {@link android.os.Looper} once the operation is complete.
+ * The value provided to the callback indicates whether any cookies were removed.
+ * You can pass {@code null} as the callback if you don't need to know when the operation
+ * completes or whether any cookies were removed, and in this case it is safe to call the
+ * method from a thread without a Looper.
+ * @param callback a callback which is executed when the cookies have been removed
+ */
+ public void removeAllCookies(ValueCallback<Boolean> callback) {
+ throw new MustOverrideException();
+ }
+
+ /**
* Gets whether there are stored cookies.
*
* @return true if there are stored cookies
@@ -153,7 +215,9 @@
/**
* Removes all expired cookies.
+ * @deprecated The WebView handles removing expired cookies automatically.
*/
+ @Deprecated
public void removeExpiredCookie() {
throw new MustOverrideException();
}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 08931fe..7e2d809 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -16,10 +16,13 @@
package android.widget;
+import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Insets;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
@@ -31,10 +34,16 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import com.android.internal.R;
+
public abstract class AbsSeekBar extends ProgressBar {
private final Rect mTempRect = new Rect();
private Drawable mThumb;
+ private ColorStateList mThumbTint = null;
+ private PorterDuff.Mode mThumbTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasThumbTint = false;
+
private int mThumbOffset;
private boolean mSplitTrack;
@@ -83,6 +92,15 @@
final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
setThumb(thumb);
+ if (a.hasValue(R.styleable.SeekBar_thumbTint)) {
+ mThumbTint = a.getColorStateList(R.styleable.SeekBar_thumbTint);
+ mThumbTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.SeekBar_thumbTintMode, -1), mThumbTintMode);
+ mHasThumbTint = true;
+
+ applyThumbTint();
+ }
+
// Guess thumb offset if thumb != null, but allow layout to override.
final int thumbOffset = a.getDimensionPixelOffset(
com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
@@ -108,7 +126,7 @@
* @param thumb Drawable representing the thumb
*/
public void setThumb(Drawable thumb) {
- boolean needUpdate;
+ final boolean needUpdate;
// This way, calling setThumb again with the same bitmap will result in
// it recalcuating mThumbOffset (if for example it the bounds of the
// drawable changed)
@@ -118,6 +136,7 @@
} else {
needUpdate = false;
}
+
if (thumb != null) {
thumb.setCallback(this);
if (canResolveLayoutDirection()) {
@@ -136,8 +155,12 @@
requestLayout();
}
}
+
mThumb = thumb;
+
+ applyThumbTint();
invalidate();
+
if (needUpdate) {
updateThumbAndTrackPos(getWidth(), getHeight());
if (thumb != null && thumb.isStateful()) {
@@ -160,6 +183,88 @@
}
/**
+ * Applies a tint to the thumb drawable.
+ * <p>
+ * Subsequent calls to {@link #setThumb(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#SeekBar_thumbTint
+ * @attr ref android.R.styleable#SeekBar_thumbTintMode
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ private void setThumbTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mThumbTint = tint;
+ mThumbTintMode = tintMode;
+ mHasThumbTint = true;
+
+ applyThumbTint();
+ }
+
+ /**
+ * Applies a tint to the thumb drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setThumb(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#SeekBar_thumbTint
+ * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setThumbTint(@Nullable ColorStateList tint) {
+ setThumbTint(tint, mThumbTintMode);
+ }
+
+ /**
+ * @return the tint applied to the thumb drawable
+ * @attr ref android.R.styleable#SeekBar_thumbTint
+ * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getThumbTint() {
+ return mThumbTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setThumbTint(ColorStateList)}} to the thumb drawable. The
+ * default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#SeekBar_thumbTintMode
+ * @see #setThumbTint(ColorStateList)
+ */
+ public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setThumbTint(mThumbTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the thumb drawable
+ * @attr ref android.R.styleable#SeekBar_thumbTintMode
+ * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getThumbTintMode() {
+ return mThumbTintMode;
+ }
+
+ private void applyThumbTint() {
+ if (mThumb != null && mHasThumbTint) {
+ mThumb = mThumb.mutate();
+ mThumb.setTint(mThumbTint, mThumbTintMode);
+ }
+ }
+
+ /**
* @see #setThumbOffset(int)
*/
public int getThumbOffset() {
@@ -237,7 +342,10 @@
@Override
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
- if (mThumb != null) mThumb.jumpToCurrentState();
+
+ if (mThumb != null) {
+ mThumb.jumpToCurrentState();
+ }
}
@Override
@@ -255,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);
}
}
@@ -373,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
@@ -399,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 c934ad7..9ba0fe1 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -16,9 +16,12 @@
package android.widget;
+import android.annotation.Nullable;
+import android.graphics.PorterDuff;
import com.android.internal.R;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
@@ -48,7 +51,12 @@
private boolean mChecked;
private int mButtonResource;
private boolean mBroadcasting;
+
private Drawable mButtonDrawable;
+ private ColorStateList mButtonTint = null;
+ private PorterDuff.Mode mButtonTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasButtonTint = false;
+
private OnCheckedChangeListener mOnCheckedChangeListener;
private OnCheckedChangeListener mOnCheckedChangeWidgetListener;
@@ -74,13 +82,22 @@
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.CompoundButton, defStyleAttr, defStyleRes);
- Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);
+ final Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);
if (d != null) {
setButtonDrawable(d);
}
- boolean checked = a
- .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false);
+ if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
+ mButtonTint = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
+ mButtonTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.CompoundButton_buttonTintMode, -1), mButtonTintMode);
+ mHasButtonTint = true;
+
+ applyButtonTint();
+ }
+
+ final boolean checked = a.getBoolean(
+ com.android.internal.R.styleable.CompoundButton_checked, false);
setChecked(checked);
a.recycle();
@@ -173,9 +190,11 @@
}
/**
- * Set the background to a given Drawable, identified by its resource id.
+ * Set the button graphic to a given Drawable, identified by its resource
+ * id.
*
- * @param resid the resource id of the drawable to use as the background
+ * @param resid the resource id of the drawable to use as the button
+ * graphic
*/
public void setButtonDrawable(int resid) {
if (resid != 0 && resid == mButtonResource) {
@@ -192,23 +211,114 @@
}
/**
- * Set the background to a given Drawable
+ * Set the button graphic to a given Drawable
*
- * @param d The Drawable to use as the background
+ * @param d The Drawable to use as the button graphic
*/
public void setButtonDrawable(Drawable d) {
- if (d != null) {
+ if (mButtonDrawable != d) {
if (mButtonDrawable != null) {
mButtonDrawable.setCallback(null);
unscheduleDrawable(mButtonDrawable);
}
- d.setCallback(this);
- d.setVisible(getVisibility() == VISIBLE, false);
- mButtonDrawable = d;
- setMinHeight(mButtonDrawable.getIntrinsicHeight());
- }
- refreshDrawableState();
+ mButtonDrawable = d;
+
+ if (d != null) {
+ d.setCallback(this);
+ d.setLayoutDirection(getLayoutDirection());
+ if (d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ d.setVisible(getVisibility() == VISIBLE, false);
+ setMinHeight(d.getIntrinsicHeight());
+ applyButtonTint();
+ }
+ }
+ }
+
+ /**
+ * Applies a tint to the button drawable.
+ * <p>
+ * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#CompoundButton_buttonTint
+ * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setButtonTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mButtonTint = tint;
+ mButtonTintMode = tintMode;
+ mHasButtonTint = true;
+
+ applyButtonTint();
+ }
+
+ /**
+ * Applies a tint to the button drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#CompoundButton_buttonTint
+ * @see #setButtonTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ public void setButtonTint(@Nullable ColorStateList tint) {
+ setButtonTint(tint, mButtonTintMode);
+ }
+
+ /**
+ * @return the tint applied to the button drawable
+ * @attr ref android.R.styleable#CompoundButton_buttonTint
+ * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getButtonTint() {
+ return mButtonTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setButtonTint(ColorStateList)}} to the button drawable. The
+ * default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+ * @see #setButtonTint(ColorStateList)
+ */
+ public void setButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setButtonTint(mButtonTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the button drawable
+ * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+ * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getButtonTintMode() {
+ return mButtonTintMode;
+ }
+
+ private void applyButtonTint() {
+ if (mButtonDrawable != null && mHasButtonTint) {
+ mButtonDrawable = mButtonDrawable.mutate();
+ mButtonDrawable.setTint(mButtonTint, mButtonTintMode);
+ }
}
@Override
@@ -320,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 01a6b8a..34f333e 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -18,9 +18,12 @@
import java.util.ArrayList;
+import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
@@ -33,6 +36,8 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.R;
+
/**
* FrameLayout is designed to block out an area on the screen to display
@@ -62,6 +67,9 @@
@ViewDebug.ExportedProperty(category = "drawing")
private Drawable mForeground;
+ private ColorStateList mForegroundTint = null;
+ private PorterDuff.Mode mForegroundTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasForegroundTint = false;
@ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingLeft = 0;
@@ -119,6 +127,15 @@
setMeasureAllChildren(true);
}
+ if (a.hasValue(R.styleable.FrameLayout_foregroundTint)) {
+ mForegroundTint = a.getColorStateList(R.styleable.FrameLayout_foregroundTint);
+ mForegroundTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode);
+ mHasForegroundTint = true;
+
+ applyForegroundTint();
+ }
+
mForegroundInPadding = a.getBoolean(
com.android.internal.R.styleable.FrameLayout_foregroundInsidePadding, true);
@@ -205,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);
@@ -231,32 +247,34 @@
* into account by ensuring that the children are inset to be placed
* inside of the padding area.
*
- * @param drawable The Drawable to be drawn on top of the children.
+ * @param d The Drawable to be drawn on top of the children.
*
* @attr ref android.R.styleable#FrameLayout_foreground
*/
- public void setForeground(Drawable drawable) {
- if (mForeground != drawable) {
+ public void setForeground(Drawable d) {
+ if (mForeground != d) {
if (mForeground != null) {
mForeground.setCallback(null);
unscheduleDrawable(mForeground);
}
- mForeground = drawable;
+ mForeground = d;
mForegroundPaddingLeft = 0;
mForegroundPaddingTop = 0;
mForegroundPaddingRight = 0;
mForegroundPaddingBottom = 0;
- if (drawable != null) {
+ if (d != null) {
setWillNotDraw(false);
- drawable.setCallback(this);
- if (drawable.isStateful()) {
- drawable.setState(getDrawableState());
+ d.setCallback(this);
+ d.setLayoutDirection(getLayoutDirection());
+ if (d.isStateful()) {
+ d.setState(getDrawableState());
}
+ applyForegroundTint();
if (mForegroundGravity == Gravity.FILL) {
Rect padding = new Rect();
- if (drawable.getPadding(padding)) {
+ if (d.getPadding(padding)) {
mForegroundPaddingLeft = padding.left;
mForegroundPaddingTop = padding.top;
mForegroundPaddingRight = padding.right;
@@ -281,6 +299,89 @@
return mForeground;
}
+ /**
+ * Applies a tint to the foreground drawable.
+ * <p>
+ * Subsequent calls to {@link #setForeground(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#FrameLayout_foregroundTint
+ * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ private void setForegroundTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mForegroundTint = tint;
+ mForegroundTintMode = tintMode;
+ mHasForegroundTint = true;
+
+ applyForegroundTint();
+ }
+
+ /**
+ * Applies a tint to the foreground drawable. Does not modify the current
+ * tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setForeground(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#FrameLayout_foregroundTint
+ * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setForegroundTint(@Nullable ColorStateList tint) {
+ setForegroundTint(tint, mForegroundTintMode);
+ }
+
+ /**
+ * @return the tint applied to the foreground drawable
+ * @attr ref android.R.styleable#FrameLayout_foregroundTint
+ * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getForegroundTint() {
+ return mForegroundTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setForegroundTint(ColorStateList)}} to the foreground drawable.
+ * The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
+ * @see #setForegroundTint(ColorStateList)
+ */
+ public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setForegroundTint(mForegroundTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the foreground
+ * drawable
+ * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
+ * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getForegroundTintMode() {
+ return mForegroundTintMode;
+ }
+
+ private void applyForegroundTint() {
+ if (mForeground != null && mHasForegroundTint) {
+ mForeground = mForeground.mutate();
+ mForeground.setTint(mForegroundTint, mForegroundTintMode);
+ }
+ }
+
int getPaddingLeftWithForeground() {
return mForegroundInPadding ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
mPaddingLeft + mForegroundPaddingLeft;
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 a40b85e..5d578ca 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -16,8 +16,10 @@
package android.widget;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -44,6 +46,8 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.R;
+
import java.io.IOException;
import java.io.InputStream;
@@ -75,13 +79,18 @@
private int mMaxHeight = Integer.MAX_VALUE;
// these are applied to the drawable
- private ColorFilter mColorFilter;
+ private ColorFilter mColorFilter = null;
+ private boolean mHasColorFilter = false;
private Xfermode mXfermode;
private int mAlpha = 255;
private int mViewAlphaScale = 256;
private boolean mColorMod = false;
private Drawable mDrawable = null;
+ private ColorStateList mDrawableTint = null;
+ private PorterDuff.Mode mDrawableTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasDrawableTint = false;
+
private int[] mState = null;
private boolean mMergeState = false;
private int mLevel = 0;
@@ -154,17 +163,21 @@
setMaxHeight(a.getDimensionPixelSize(
com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
- int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
+ final int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
if (index >= 0) {
setScaleType(sScaleTypeArray[index]);
}
- int tint = a.getInt(com.android.internal.R.styleable.ImageView_tint, 0);
- if (tint != 0) {
- setColorFilter(tint);
+ if (a.hasValue(R.styleable.ImageView_tint)) {
+ mDrawableTint = a.getColorStateList(R.styleable.ImageView_tint);
+ mDrawableTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
+ mHasDrawableTint = true;
+
+ applyDrawableTint();
}
-
- int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255);
+
+ final int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255);
if (alpha != 255) {
setAlpha(alpha);
}
@@ -435,6 +448,88 @@
}
/**
+ * Applies a tint to the image drawable.
+ * <p>
+ * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ImageView_tint
+ * @attr ref android.R.styleable#ImageView_tintMode
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ private void setTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mDrawableTint = tint;
+ mDrawableTintMode = tintMode;
+ mHasDrawableTint = true;
+
+ applyDrawableTint();
+ }
+
+ /**
+ * Applies a tint to the image drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ImageView_tint
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setTint(@Nullable ColorStateList tint) {
+ setTint(tint, mDrawableTintMode);
+ }
+
+ /**
+ * @return the tint applied to the image drawable
+ * @attr ref android.R.styleable#ImageView_tint
+ * @see #setTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getTint() {
+ return mDrawableTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setTint(ColorStateList)}} to the image drawable. The default
+ * mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ImageView_tintMode
+ * @see #setTint(ColorStateList)
+ */
+ public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setTint(mDrawableTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the image drawable
+ * @attr ref android.R.styleable#ImageView_tintMode
+ * @see #setTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getTintMode() {
+ return mDrawableTintMode;
+ }
+
+ private void applyDrawableTint() {
+ if (mDrawable != null && mHasDrawableTint) {
+ mDrawable = mDrawable.mutate();
+ mDrawable.setTint(mDrawableTint, mDrawableTintMode);
+ }
+ }
+
+ /**
* Sets a Bitmap as the content of this ImageView.
*
* @param bm The bitmap to set
@@ -709,17 +804,20 @@
mDrawable.setCallback(null);
unscheduleDrawable(mDrawable);
}
+
mDrawable = d;
+
if (d != null) {
d.setCallback(this);
+ d.setLayoutDirection(getLayoutDirection());
if (d.isStateful()) {
d.setState(getDrawableState());
}
- d.setLevel(mLevel);
- d.setLayoutDirection(getLayoutDirection());
d.setVisible(getVisibility() == VISIBLE, true);
+ d.setLevel(mLevel);
mDrawableWidth = d.getIntrinsicWidth();
mDrawableHeight = d.getIntrinsicHeight();
+ applyDrawableTint();
applyColorMod();
configureBounds();
} else {
@@ -1010,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);
@@ -1177,6 +1274,7 @@
public void setColorFilter(ColorFilter cf) {
if (mColorFilter != cf) {
mColorFilter = cf;
+ mHasColorFilter = true;
mColorMod = true;
applyColorMod();
invalidate();
@@ -1231,7 +1329,9 @@
// re-applied if the Drawable is changed.
if (mDrawable != null && mColorMod) {
mDrawable = mDrawable.mutate();
- mDrawable.setColorFilter(mColorFilter);
+ if (mHasColorFilter) {
+ mDrawable.setColorFilter(mColorFilter);
+ }
mDrawable.setXfermode(mXfermode);
mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
}
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 af32f1c..394b255 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -16,13 +16,17 @@
package android.widget;
+import android.annotation.Nullable;
+import android.graphics.PorterDuff;
import com.android.internal.R;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
+import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Animatable;
@@ -210,8 +214,26 @@
private Transformation mTransformation;
private AlphaAnimation mAnimation;
private boolean mHasAnimation;
+
private Drawable mIndeterminateDrawable;
+ private ColorStateList mIndeterminateTint = null;
+ private PorterDuff.Mode mIndeterminateTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasIndeterminateTint = false;
+
private Drawable mProgressDrawable;
+
+ private ColorStateList mProgressTint = null;
+ private PorterDuff.Mode mProgressTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasProgressTint = false;
+
+ private ColorStateList mProgressBackgroundTint = null;
+ private PorterDuff.Mode mProgressBackgroundTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasProgressBackgroundTint = false;
+
+ private ColorStateList mSecondaryProgressTint = null;
+ private PorterDuff.Mode mSecondaryProgressTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasSecondaryProgressTint = false;
+
private Drawable mCurrentDrawable;
Bitmap mSampleTile;
private boolean mNoInvalidate;
@@ -257,11 +279,11 @@
mNoInvalidate = true;
- Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
- if (drawable != null) {
+ final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
+ if (progressDrawable != null) {
// Calling this method can set mMaxHeight, make sure the corresponding
// XML attribute for mMaxHeight is read after calling this method
- setProgressDrawableTiled(drawable);
+ setProgressDrawableTiled(progressDrawable);
}
@@ -288,9 +310,10 @@
setSecondaryProgress(
a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
- drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable);
- if (drawable != null) {
- setIndeterminateDrawableTiled(drawable);
+ final Drawable indeterminateDrawable = a.getDrawable(
+ R.styleable.ProgressBar_indeterminateDrawable);
+ if (indeterminateDrawable != null) {
+ setIndeterminateDrawableTiled(indeterminateDrawable);
}
mOnlyIndeterminate = a.getBoolean(
@@ -303,6 +326,53 @@
mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);
+ if (a.hasValue(R.styleable.ProgressBar_progressTint)) {
+ mProgressTint = a.getColorStateList(
+ R.styleable.ProgressBar_progressTint);
+ mProgressTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_progressBackgroundTintMode, -1),
+ mProgressTintMode);
+ mHasProgressTint = true;
+
+ applyProgressLayerTint(R.id.progress, mProgressTint,
+ mProgressTintMode, true);
+ }
+
+ if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) {
+ mProgressBackgroundTint = a.getColorStateList(
+ R.styleable.ProgressBar_progressBackgroundTint);
+ mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_progressTintMode, -1),
+ mProgressBackgroundTintMode);
+ mHasProgressBackgroundTint = true;
+
+ applyProgressLayerTint(R.id.background, mProgressBackgroundTint,
+ mProgressBackgroundTintMode, false);
+ }
+
+ if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) {
+ mSecondaryProgressTint = a.getColorStateList(
+ R.styleable.ProgressBar_secondaryProgressTint);
+ mSecondaryProgressTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_secondaryProgressTintMode, -1),
+ mSecondaryProgressTintMode);
+ mHasSecondaryProgressTint = true;
+
+ applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint,
+ mSecondaryProgressTintMode, false);
+ }
+
+ if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) {
+ mIndeterminateTint = a.getColorStateList(
+ R.styleable.ProgressBar_indeterminateTint);
+ mIndeterminateTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_indeterminateTintMode, -1),
+ mIndeterminateTintMode);
+ mHasIndeterminateTint = true;
+
+ applyIndeterminateTint();
+ }
+
a.recycle();
// If not explicitly specified this view is important for accessibility.
@@ -479,16 +549,111 @@
* @see #setIndeterminate(boolean)
*/
public void setIndeterminateDrawable(Drawable d) {
- if (d != null) {
- d.setCallback(this);
+ if (mIndeterminateDrawable != d) {
+ if (mIndeterminateDrawable != null) {
+ mIndeterminateDrawable.setCallback(null);
+ unscheduleDrawable(mIndeterminateDrawable);
+ }
+
+ mIndeterminateDrawable = d;
+
+ if (d != null) {
+ d.setCallback(this);
+ d.setLayoutDirection(getLayoutDirection());
+ if (d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ applyIndeterminateTint();
+ }
+
+ if (mIndeterminate) {
+ mCurrentDrawable = d;
+ postInvalidate();
+ }
}
- mIndeterminateDrawable = d;
- if (mIndeterminateDrawable != null && canResolveLayoutDirection()) {
- mIndeterminateDrawable.setLayoutDirection(getLayoutDirection());
- }
- if (mIndeterminate) {
- mCurrentDrawable = d;
- postInvalidate();
+ }
+
+ /**
+ * Applies a tint to the indeterminate drawable.
+ * <p>
+ * Subsequent calls to {@link #setVisibilminateDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and
+ * tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTint
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setIndeterminateTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mIndeterminateTint = tint;
+ mIndeterminateTintMode = tintMode;
+ mHasIndeterminateTint = true;
+
+ applyIndeterminateTint();
+ }
+
+ /**
+ * Applies a tint to the indeterminate drawable. Does not modify the
+ * current tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setIndeterminateDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and
+ * tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTint
+ * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setIndeterminateTint(@Nullable ColorStateList tint) {
+ setIndeterminateTint(tint, mIndeterminateTintMode);
+ }
+
+ /**
+ * @return the tint applied to the indeterminate drawable
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTint
+ * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getIndeterminateTint() {
+ return mIndeterminateTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setIndeterminateTint(ColorStateList)} to the indeterminate
+ * drawable. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
+ * @see #setIndeterminateTint(ColorStateList)
+ */
+ public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setIndeterminateTint(mIndeterminateTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the indeterminate drawable
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
+ * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getIndeterminateTintMode() {
+ return mIndeterminateTintMode;
+ }
+
+ private void applyIndeterminateTint() {
+ if (mIndeterminateDrawable != null && mHasIndeterminateTint) {
+ mIndeterminateDrawable = mIndeterminateDrawable.mutate();
+ mIndeterminateDrawable.setTint(mIndeterminateTint, mIndeterminateTintMode);
}
}
@@ -532,42 +697,340 @@
* @see #setIndeterminate(boolean)
*/
public void setProgressDrawable(Drawable d) {
- boolean needUpdate;
- if (mProgressDrawable != null && d != mProgressDrawable) {
- mProgressDrawable.setCallback(null);
- needUpdate = true;
- } else {
- needUpdate = false;
- }
+ if (mProgressDrawable != d) {
+ if (mProgressDrawable != null) {
+ mProgressDrawable.setCallback(null);
+ unscheduleDrawable(mProgressDrawable);
+ }
- if (d != null) {
- d.setCallback(this);
- if (canResolveLayoutDirection()) {
+ mProgressDrawable = d;
+
+ if (d != null) {
+ d.setCallback(this);
d.setLayoutDirection(getLayoutDirection());
+ if (d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+
+ // Make sure the ProgressBar is always tall enough
+ int drawableHeight = d.getMinimumHeight();
+ if (mMaxHeight < drawableHeight) {
+ mMaxHeight = drawableHeight;
+ requestLayout();
+ }
+
+ if (mHasProgressTint) {
+ applyProgressLayerTint(R.id.progress, mProgressTint, mProgressTintMode, true);
+ }
+
+ if (mHasProgressBackgroundTint) {
+ applyProgressLayerTint(R.id.background, mProgressBackgroundTint,
+ mProgressBackgroundTintMode, false);
+ }
+
+ if (mHasSecondaryProgressTint) {
+ applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint,
+ mSecondaryProgressTintMode, false);
+ }
}
- // Make sure the ProgressBar is always tall enough
- int drawableHeight = d.getMinimumHeight();
- if (mMaxHeight < drawableHeight) {
- mMaxHeight = drawableHeight;
- requestLayout();
+ if (!mIndeterminate) {
+ mCurrentDrawable = d;
+ postInvalidate();
}
- }
- mProgressDrawable = d;
- if (!mIndeterminate) {
- mCurrentDrawable = d;
- postInvalidate();
- }
- if (needUpdate) {
updateDrawableBounds(getWidth(), getHeight());
updateDrawableState();
+
doRefreshProgress(R.id.progress, mProgress, false, false);
doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false);
}
}
/**
+ * Applies a tint to the progress indicator, if one exists, or to the
+ * entire progress drawable otherwise.
+ * <p>
+ * The progress indicator should be specified as a layer with
+ * id {@link android.R.id#progress} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and
+ * tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_progressTint
+ * @attr ref android.R.styleable#ProgressBar_progressTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setProgressTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mProgressTint = tint;
+ mProgressTintMode = tintMode;
+ mHasProgressTint = true;
+
+ applyProgressLayerTint(R.id.progress, tint, tintMode, true);
+ }
+
+ /**
+ * Applies a tint to the progress indicator, if one exists, or to the
+ * entire progress drawable otherwise. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * The progress indicator should be specified as a layer with
+ * id {@link android.R.id#progress} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and
+ * tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_progressTint
+ * @see #setProgressTint(ColorStateList)
+ */
+ public void setProgressTint(@Nullable ColorStateList tint) {
+ setProgressTint(tint, mProgressTintMode);
+ }
+
+ /**
+ * @return the tint applied to the progress drawable
+ * @attr ref android.R.styleable#ProgressBar_progressTint
+ * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getProgressTint() {
+ return mProgressTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setProgressTint(ColorStateList)}} to the progress
+ * indicator. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ProgressBar_progressTintMode
+ * @see #setProgressTint(ColorStateList)
+ */
+ public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setProgressTint(mProgressTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the progress drawable
+ * @attr ref android.R.styleable#ProgressBar_progressTintMode
+ * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getProgressTintMode() {
+ return mProgressTintMode;
+ }
+
+ /**
+ * Applies a tint to the progress background, if one exists.
+ * <p>
+ * The progress background must be specified as a layer with
+ * id {@link android.R.id#background} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+ * drawable contains a progress background will automatically mutate the
+ * drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setProgressBackgroundTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mProgressBackgroundTint = tint;
+ mProgressBackgroundTintMode = tintMode;
+ mHasProgressBackgroundTint = true;
+
+ applyProgressLayerTint(R.id.background, tint, tintMode, false);
+ }
+
+ /**
+ * Applies a tint to the progress background, if one exists. Does not
+ * modify the current tint mode, which is
+ * {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * The progress background must be specified as a layer with
+ * id {@link android.R.id#background} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+ * drawable contains a progress background will automatically mutate the
+ * drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
+ * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setProgressBackgroundTint(@Nullable ColorStateList tint) {
+ setProgressBackgroundTint(tint, mProgressBackgroundTintMode);
+ }
+
+ /**
+ * @return the tint applied to the progress background
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
+ * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getProgressBackgroundTint() {
+ return mProgressBackgroundTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setProgressBackgroundTint(ColorStateList)}} to the progress
+ * background. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
+ * @see #setProgressBackgroundTint(ColorStateList)
+ */
+ public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setProgressBackgroundTint(mProgressBackgroundTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the progress
+ * background
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
+ * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getProgressBackgroundTintMode() {
+ return mProgressBackgroundTintMode;
+ }
+
+ /**
+ * Applies a tint to the secondary progress indicator, if one exists.
+ * <p>
+ * The secondary progress indicator must be specified as a layer with
+ * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+ * drawable contains a secondary progress indicator will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setSecondaryProgressTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mSecondaryProgressTint = tint;
+ mSecondaryProgressTintMode = tintMode;
+ mHasSecondaryProgressTint = true;
+
+ applyProgressLayerTint(R.id.secondaryProgress, tint, tintMode, false);
+ }
+
+ /**
+ * Applies a tint to the secondary progress indicator, if one exists.
+ * Does not modify the current tint mode, which is
+ * {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * The secondary progress indicator must be specified as a layer with
+ * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+ * drawable contains a secondary progress indicator will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
+ * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setSecondaryProgressTint(@Nullable ColorStateList tint) {
+ setSecondaryProgressTint(tint, mSecondaryProgressTintMode);
+ }
+
+ /**
+ * @return the tint applied to the secondary progress drawable
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
+ * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getSecondaryProgressTint() {
+ return mSecondaryProgressTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setSecondaryProgressTint(ColorStateList)}} to the secondary
+ * progress indicator. The default mode is
+ * {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
+ * @see #setSecondaryProgressTint(ColorStateList)
+ */
+ public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setSecondaryProgressTint(mSecondaryProgressTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the secondary
+ * progress drawable
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
+ * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getSecondaryProgressTintMode() {
+ return mSecondaryProgressTintMode;
+ }
+
+ private void applyProgressLayerTint(int layerId, @Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode, boolean shouldFallback) {
+ final Drawable d = mProgressDrawable;
+ if (d != null) {
+ mProgressDrawable = d.mutate();
+
+ Drawable layer = null;
+ if (d instanceof LayerDrawable) {
+ layer = ((LayerDrawable) d).findDrawableByLayerId(layerId);
+ }
+
+ if (shouldFallback && layer == null) {
+ layer = d;
+ }
+
+ if (layer != null) {
+ layer.setTint(tint, tintMode);
+ }
+ }
+ }
+
+ /**
* Define the tileable drawable used to draw the progress bar in
* progress mode.
* <p>
@@ -670,6 +1133,22 @@
}
}
+ private void setDrawableTint(int id, ColorStateList tint, Mode tintMode, boolean fallback) {
+ Drawable layer = null;
+
+ // We expect a layer drawable, so try to find the target ID.
+ final Drawable d = mCurrentDrawable;
+ if (d instanceof LayerDrawable) {
+ layer = ((LayerDrawable) d).findDrawableByLayerId(id);
+ }
+
+ if (fallback && layer == null) {
+ layer = d;
+ }
+
+ layer.mutate().setTint(tint, tintMode);
+ }
+
private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
boolean callBackToApp) {
float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
@@ -1144,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/Spinner.java b/core/java/android/widget/Spinner.java
index 9601d4a..9914800 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -25,6 +25,7 @@
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -427,9 +428,15 @@
* {@link Adapter#getItemViewType(int) getItemViewType(int)} on the object
* returned from {@link #getAdapter()} will always return 0. Calling
* {@link Adapter#getViewTypeCount() getViewTypeCount()} will always return
- * 1.
+ * 1. On API {@link Build.VERSION_CODES#L} and above, attempting to set an
+ * adapter with more than one view type will throw an
+ * {@link IllegalArgumentException}.
+ *
+ * @param adapter the adapter to set
*
* @see AbsSpinner#setAdapter(SpinnerAdapter)
+ * @throws IllegalArgumentException if the adapter has more than one view
+ * type
*/
@Override
public void setAdapter(SpinnerAdapter adapter) {
@@ -437,6 +444,12 @@
mRecycler.clear();
+ final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+ if (targetSdkVersion >= Build.VERSION_CODES.L
+ && adapter != null && adapter.getViewTypeCount() != 1) {
+ throw new IllegalArgumentException("Spinner adapter view type count must be 1");
+ }
+
if (mPopup != null) {
mPopup.setAdapter(new DropDownAdapter(adapter));
} else {
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 03193a2..9a8380d 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,20 +535,43 @@
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);
- final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
- + mThumbTextPadding * 2;
+ final int maxTextWidth = mShowText ? Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+ + mThumbTextPadding * 2 : 0;
mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
final int switchWidth = Math.max(mSwitchMinWidth,
@@ -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/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 960fa49..643225d7 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -16,6 +16,7 @@
package com.android.internal.backup;
+import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -168,12 +169,25 @@
int startRestore(long token, in PackageInfo[] packages);
/**
- * Get the package name of the next application with data in the backup store.
- * @return The name of one of the packages supplied to {@link #startRestore},
- * or "" (the empty string) if no more backup data is available,
- * or null if an error occurred (the restore should be aborted and rescheduled).
+ * Get the package name of the next application with data in the backup store, plus
+ * a description of the structure of the restored archive: either TYPE_KEY_VALUE for
+ * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream.
+ *
+ * <p>If the package name in the returned RestoreDescription object is the singleton
+ * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available
+ * in the current restore session: all packages described in startRestore() have been
+ * processed.
+ *
+ * <p>If this method returns {@code null}, it means that a transport-level error has
+ * occurred and the entire restore operation should be abandoned.
+ *
+ * @return A RestoreDescription object containing the name of one of the packages
+ * supplied to {@link #startRestore} plus an indicator of the data type of that
+ * restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
+ * no more packages can be restored in this session; or {@code null} to indicate
+ * a transport-level error.
*/
- String nextRestorePackage();
+ RestoreDescription nextRestorePackage();
/**
* Get the data for the application returned by {@link #nextRestorePackage}.
@@ -188,7 +202,58 @@
*/
void finishRestore();
+ // full backup stuff
+
long requestFullBackupTime();
int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket);
int sendBackupData(int numBytes);
+
+ // full restore stuff
+
+ /**
+ * Ask the transport to provide data for the "current" package being restored. This
+ * is the package that was just reported by {@link #nextRestorePackage()} as having
+ * {@link RestoreDescription#TYPE_FULL_STREAM} data.
+ *
+ * The transport writes some data to the socket supplied to this call, and returns
+ * the number of bytes written. The system will then read that many bytes and
+ * stream them to the application's agent for restore, then will call this method again
+ * to receive the next chunk of the archive. This sequence will be repeated until the
+ * transport returns zero indicating that all of the package's data has been delivered
+ * (or returns a negative value indicating some sort of hard error condition at the
+ * transport level).
+ *
+ * <p>After this method returns zero, the system will then call
+ * {@link #getNextFullRestorePackage()} to begin the restore process for the next
+ * application, and the sequence begins again.
+ *
+ * <p>The transport should always close this socket when returning from this method.
+ * Do not cache this socket across multiple calls or you may leak file descriptors.
+ *
+ * @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.
+ */
+ int getNextFullRestoreDataChunk(in ParcelFileDescriptor socket);
+
+ /**
+ * 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.
+ */
+ int abortFullRestore();
+
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index c9d621d..b098de8 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -19,6 +19,7 @@
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
+import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.content.ComponentName;
import android.content.Context;
@@ -63,18 +64,24 @@
private static final String TRANSPORT_DESTINATION_STRING
= "Backing up to debug-only private cache";
+ private static final String INCREMENTAL_DIR = "_delta";
+ private static final String FULL_DATA_DIR = "_full";
+
// The currently-active restore set always has the same (nonzero!) token
private static final long CURRENT_SET_TOKEN = 1;
private Context mContext;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
- private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, "_delta");
- private File mCurrentSetFullDir = new File(mCurrentSetDir, "_full");
+ private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR);
+ private File mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR);
private PackageInfo[] mRestorePackages = null;
private int mRestorePackage = -1; // Index into mRestorePackages
- private File mRestoreDataDir;
+ private int mRestoreType;
+ private File mRestoreSetDir;
+ private File mRestoreSetIncrementalDir;
+ private File mRestoreSetFullDir;
private long mRestoreToken;
// Additional bookkeeping for full backup
@@ -86,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;
@@ -97,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 {
@@ -184,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();
}
@@ -192,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;
}
}
@@ -215,6 +232,7 @@
}
}
+ @Override
public int clearBackupData(PackageInfo packageInfo) {
if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
@@ -236,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) {
@@ -252,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) {
@@ -284,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;
@@ -293,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) {
@@ -316,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;
@@ -351,40 +376,69 @@
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;
mRestorePackage = -1;
mRestoreToken = token;
- mRestoreDataDir = new File(mDataDir, Long.toString(token));
- return BackupTransport.TRANSPORT_OK;
+ mRestoreSetDir = new File(mDataDir, Long.toString(token));
+ mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR);
+ mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR);
+ return TRANSPORT_OK;
}
- public String nextRestorePackage() {
+ @Override
+ public RestoreDescription nextRestorePackage() {
if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
+
+ boolean found = false;
while (++mRestorePackage < mRestorePackages.length) {
String name = mRestorePackages[mRestorePackage].packageName;
+
+ // If we have key/value data for this package, deliver that
// skip packages where we have a data dir but no actual contents
- String[] contents = (new File(mRestoreDataDir, name)).list();
+ String[] contents = (new File(mRestoreSetIncrementalDir, name)).list();
if (contents != null && contents.length > 0) {
- if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name);
- return name;
+ if (DEBUG) Log.v(TAG, " nextRestorePackage(TYPE_KEY_VALUE) = " + name);
+ mRestoreType = RestoreDescription.TYPE_KEY_VALUE;
+ found = true;
+ }
+
+ if (!found) {
+ // No key/value data; check for [non-empty] full data
+ File maybeFullData = new File(mRestoreSetFullDir, name);
+ 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;
+ }
+ }
+
+ if (found) {
+ return new RestoreDescription(name, mRestoreType);
}
}
if (DEBUG) Log.v(TAG, " no more packages to restore");
- return "";
+ 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");
- File packageDir = new File(mRestoreDataDir, mRestorePackages[mRestorePackage].packageName);
+ if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) {
+ throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset");
+ }
+ File packageDir = new File(mRestoreSetDir, mRestorePackages[mRestorePackage].packageName);
// The restore set is the concatenation of the individual record blobs,
// each of which is a file in the package's directory. We return the
@@ -394,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
@@ -415,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;
}
}
@@ -455,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) {
- mRestoreDataDir = new File(mDataDir, Long.toString(token));
- mFullRestoreSetDir = new File(mRestoreDataDir, "_full");
- 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;
}
/**
@@ -511,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/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java
index 87a50a99..fda6479 100644
--- a/core/java/com/android/server/SystemServiceManager.java
+++ b/core/java/com/android/server/SystemServiceManager.java
@@ -50,8 +50,19 @@
* @return The service instance.
*/
@SuppressWarnings("unchecked")
- public SystemService startService(String className) throws ClassNotFoundException {
- return startService((Class<SystemService>) Class.forName(className));
+ public SystemService startService(String className) {
+ final Class<SystemService> serviceClass;
+ try {
+ serviceClass = (Class<SystemService>)Class.forName(className);
+ } catch (ClassNotFoundException ex) {
+ Slog.i(TAG, "Starting " + className);
+ throw new RuntimeException("Failed to create service " + className
+ + ": service class not found, usually indicates that the caller should "
+ + "have called PackageManager.hasSystemFeature() to check whether the "
+ + "feature is available on this device before trying to start the "
+ + "services that implement it", ex);
+ }
+ return startService(serviceClass);
}
/**
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 430dd63..3d9fb5c8 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -18,6 +18,7 @@
import android.net.INetworkManagementEventObserver;
import android.net.LinkAddress;
+import android.net.RouteInfo;
/**
* Base {@link INetworkManagementEventObserver} that provides no-op
@@ -70,4 +71,14 @@
public void interfaceDnsServerInfo(String iface, long lifetime, String[] servers) {
// default no-op
}
+
+ @Override
+ public void routeUpdated(RouteInfo route) {
+ // default no-op
+ }
+
+ @Override
+ public void routeRemoved(RouteInfo route) {
+ // default no-op
+ }
}
diff --git a/core/java/com/android/server/net/NetlinkTracker.java b/core/java/com/android/server/net/NetlinkTracker.java
new file mode 100644
index 0000000..7dd8dd8
--- /dev/null
+++ b/core/java/com/android/server/net/NetlinkTracker.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.RouteInfo;
+import android.util.Log;
+
+/**
+ * Keeps track of link configuration received from Netlink.
+ *
+ * Instances of this class are expected to be owned by subsystems such as Wi-Fi
+ * or Ethernet that manage one or more network interfaces. Each interface to be
+ * tracked needs its own {@code NetlinkTracker}.
+ *
+ * An instance of this class is constructed by passing in an interface name and
+ * a callback. The owner is then responsible for registering the tracker with
+ * NetworkManagementService. When the class receives update notifications from
+ * the NetworkManagementService notification threads, it applies the update to
+ * its local LinkProperties, and if something has changed, notifies its owner of
+ * the update via the callback.
+ *
+ * The owner can then call {@code getLinkProperties()} in order to find out
+ * what changed. If in the meantime the LinkProperties stored here have changed,
+ * this class will return the current LinkProperties. Because each change
+ * triggers an update callback after the change is made, the owner may get more
+ * callbacks than strictly necessary (some of which may be no-ops), but will not
+ * be out of sync once all callbacks have been processed.
+ *
+ * Threading model:
+ *
+ * - The owner of this class is expected to create it, register it, and call
+ * getLinkProperties or clearLinkProperties on its thread.
+ * - Most of the methods in the class are inherited from BaseNetworkObserver
+ * and are called by NetworkManagementService notification threads.
+ * - All accesses to mLinkProperties must be synchronized(this). All the other
+ * member variables are immutable once the object is constructed.
+ *
+ * This class currently tracks IPv4 and IPv6 addresses. In the future it will
+ * track routes and DNS servers.
+ *
+ * @hide
+ */
+public class NetlinkTracker extends BaseNetworkObserver {
+
+ private final String TAG;
+
+ public interface Callback {
+ public void update();
+ }
+
+ private final String mInterfaceName;
+ private final Callback mCallback;
+ private final LinkProperties mLinkProperties;
+
+ private static final boolean DBG = true;
+
+ public NetlinkTracker(String iface, Callback callback) {
+ TAG = "NetlinkTracker/" + iface;
+ mInterfaceName = iface;
+ mCallback = callback;
+ mLinkProperties = new LinkProperties();
+ mLinkProperties.setInterfaceName(mInterfaceName);
+ }
+
+ private void maybeLog(String operation, String iface, LinkAddress address) {
+ if (DBG) {
+ Log.d(TAG, operation + ": " + address + " on " + iface +
+ " flags " + address.getFlags() + " scope " + address.getScope());
+ }
+ }
+
+ private void maybeLog(String operation, Object o) {
+ if (DBG) {
+ Log.d(TAG, operation + ": " + o.toString());
+ }
+ }
+
+ @Override
+ public void addressUpdated(String iface, LinkAddress address) {
+ if (mInterfaceName.equals(iface)) {
+ maybeLog("addressUpdated", iface, address);
+ boolean changed;
+ synchronized (this) {
+ changed = mLinkProperties.addLinkAddress(address);
+ }
+ if (changed) {
+ mCallback.update();
+ }
+ }
+ }
+
+ @Override
+ public void addressRemoved(String iface, LinkAddress address) {
+ if (mInterfaceName.equals(iface)) {
+ maybeLog("addressRemoved", iface, address);
+ boolean changed;
+ synchronized (this) {
+ changed = mLinkProperties.removeLinkAddress(address);
+ }
+ if (changed) {
+ mCallback.update();
+ }
+ }
+ }
+
+ @Override
+ public void routeUpdated(RouteInfo route) {
+ if (mInterfaceName.equals(route.getInterface())) {
+ maybeLog("routeUpdated", route);
+ boolean changed;
+ synchronized (this) {
+ changed = mLinkProperties.addRoute(route);
+ }
+ if (changed) {
+ mCallback.update();
+ }
+ }
+ }
+
+ @Override
+ public void routeRemoved(RouteInfo route) {
+ if (mInterfaceName.equals(route.getInterface())) {
+ maybeLog("routeRemoved", route);
+ boolean changed;
+ synchronized (this) {
+ changed = mLinkProperties.removeRoute(route);
+ }
+ if (changed) {
+ mCallback.update();
+ }
+ }
+ }
+
+ /**
+ * Returns a copy of this object's LinkProperties.
+ */
+ public synchronized LinkProperties getLinkProperties() {
+ return new LinkProperties(mLinkProperties);
+ }
+
+ public synchronized void clearLinkProperties() {
+ mLinkProperties.clear();
+ mLinkProperties.setInterfaceName(mInterfaceName);
+ }
+}
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/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f2b9bac..a7a1faad 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -159,6 +159,7 @@
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_AndroidBidi(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
+extern int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
extern int register_android_server_Watchdog(JNIEnv* env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
@@ -1328,6 +1329,7 @@
REG_JNI(register_android_media_ToneGenerator),
REG_JNI(register_android_opengl_classes),
+ REG_JNI(register_android_server_fingerprint_FingerprintService),
REG_JNI(register_android_server_NetworkManagementSocketTagger),
REG_JNI(register_android_server_Watchdog),
REG_JNI(register_android_ddm_DdmHandleNativeHeap),
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/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 9998995..c139c9d 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -258,16 +258,16 @@
// can return NULL
static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
- switch (src.config()) {
- case SkBitmap::kARGB_8888_Config:
+ switch (src.colorType()) {
+ case kN32_SkColorType:
if (src.isOpaque()) return ToColor_S32_Opaque;
return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
if (src.isOpaque()) return ToColor_S4444_Opaque;
return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
return ToColor_S565;
- case SkBitmap::kIndex8_Config:
+ case kIndex_8_SkColorType:
if (src.getColorTable() == NULL) {
return NULL;
}
@@ -291,7 +291,7 @@
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
jint offset, jint stride, jint width, jint height,
jint configHandle, jboolean isMutable) {
- SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+ SkColorType colorType = SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(configHandle));
if (NULL != jColors) {
size_t n = env->GetArrayLength(jColors);
if (n < SkAbs32(stride) * (size_t)height) {
@@ -301,12 +301,12 @@
}
// ARGB_4444 is a deprecated format, convert automatically to 8888
- if (config == SkBitmap::kARGB_4444_Config) {
- config = SkBitmap::kARGB_8888_Config;
+ if (colorType == kARGB_4444_SkColorType) {
+ colorType = kN32_SkColorType;
}
SkBitmap bitmap;
- bitmap.setConfig(config, width, height);
+ bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType));
jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
if (NULL == buff) {
@@ -515,28 +515,29 @@
android::Parcel* p = android::parcelForJavaObject(env, parcel);
- const bool isMutable = p->readInt32() != 0;
- const SkBitmap::Config config = (SkBitmap::Config)p->readInt32();
- const int width = p->readInt32();
- const int height = p->readInt32();
- const int rowBytes = p->readInt32();
- const int density = p->readInt32();
+ const bool isMutable = p->readInt32() != 0;
+ const SkColorType colorType = (SkColorType)p->readInt32();
+ const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
+ const int width = p->readInt32();
+ const int height = p->readInt32();
+ const int rowBytes = p->readInt32();
+ const int density = p->readInt32();
- if (SkBitmap::kARGB_8888_Config != config &&
- SkBitmap::kRGB_565_Config != config &&
- SkBitmap::kARGB_4444_Config != config &&
- SkBitmap::kIndex8_Config != config &&
- SkBitmap::kA8_Config != config) {
- SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
+ if (kN32_SkColorType != colorType &&
+ kRGB_565_SkColorType != colorType &&
+ kARGB_4444_SkColorType != colorType &&
+ kIndex_8_SkColorType != colorType &&
+ kAlpha_8_SkColorType != colorType) {
+ SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType);
return NULL;
}
SkBitmap* bitmap = new SkBitmap;
- bitmap->setConfig(config, width, height, rowBytes);
+ bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType), rowBytes);
SkColorTable* ctable = NULL;
- if (config == SkBitmap::kIndex8_Config) {
+ if (colorType == kIndex_8_SkColorType) {
int count = p->readInt32();
if (count > 0) {
size_t size = count * sizeof(SkPMColor);
@@ -587,13 +588,14 @@
android::Parcel* p = android::parcelForJavaObject(env, parcel);
p->writeInt32(isMutable);
- p->writeInt32(bitmap->config());
+ p->writeInt32(bitmap->colorType());
+ p->writeInt32(bitmap->alphaType());
p->writeInt32(bitmap->width());
p->writeInt32(bitmap->height());
p->writeInt32(bitmap->rowBytes());
p->writeInt32(density);
- if (bitmap->config() == SkBitmap::kIndex8_Config) {
+ if (bitmap->colorType() == kIndex_8_SkColorType) {
SkColorTable* ctable = bitmap->getColorTable();
if (ctable != NULL) {
int count = ctable->count();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 5106f0d..86ed677 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -386,7 +386,7 @@
// FIXME: If the alphaType is kUnpremul and the image has alpha, the
// colors may not be correct, since Skia does not yet support drawing
// to/from unpremultiplied bitmaps.
- outputBitmap->setConfig(SkImageInfo::Make(scaledWidth, scaledHeight,
+ outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
colorType, decodingBitmap.alphaType()));
if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
return nullObjectReturn("allocation failed for scaled bitmap");
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index a5964c8..9e09280 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -569,12 +569,11 @@
canvas->drawRectCoords(left, top, right, bottom, *paint);
}
- static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jobject joval,
- jlong paintHandle) {
+ static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jlong paintHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect oval;
- GraphicsJNI::jrectf_to_rect(env, joval, &oval);
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
canvas->drawOval(oval, *paint);
}
@@ -585,13 +584,12 @@
canvas->drawCircle(cx, cy, radius, *paint);
}
- static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jobject joval,
- jfloat startAngle, jfloat sweepAngle,
- jboolean useCenter, jlong paintHandle) {
+ static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, jboolean useCenter,
+ jlong paintHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect oval;
- GraphicsJNI::jrectf_to_rect(env, joval, &oval);
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
canvas->drawArc(oval, startAngle, sweepAngle, useCenter, *paint);
}
@@ -705,10 +703,11 @@
jboolean hasAlpha, jlong paintHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkImageInfo info = SkImageInfo::Make(width, height,
+ hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+ kPremul_SkAlphaType);
SkBitmap bitmap;
- bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config :
- SkBitmap::kRGB_565_Config, width, height);
- if (!bitmap.allocPixels()) {
+ if (!bitmap.allocPixels(info)) {
return;
}
@@ -1125,29 +1124,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_);
}
@@ -1241,11 +1293,9 @@
{"native_drawLines", "(J[FIIJ)V", (void*) SkCanvasGlue::drawLines},
{"native_drawLine","(JFFFFJ)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
{"native_drawRect","(JFFFFJ)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
- {"native_drawOval","(JLandroid/graphics/RectF;J)V",
- (void*) SkCanvasGlue::drawOval},
+ {"native_drawOval","(JFFFFJ)V", (void*) SkCanvasGlue::drawOval},
{"native_drawCircle","(JFFFJ)V", (void*) SkCanvasGlue::drawCircle},
- {"native_drawArc","(JLandroid/graphics/RectF;FFZJ)V",
- (void*) SkCanvasGlue::drawArc},
+ {"native_drawArc","(JFFFFFFZJ)V", (void*) SkCanvasGlue::drawArc},
{"native_drawRoundRect","(JFFFFFFJ)V",
(void*) SkCanvasGlue::drawRoundRect},
{"native_drawPath","(JJJ)V", (void*) SkCanvasGlue::drawPath},
@@ -1271,13 +1321,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 1e40d94..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,
@@ -936,32 +943,62 @@
return paint->getLooper() && paint->getLooper()->asABlurShadow(NULL);
}
- static int breakText(JNIEnv* env, SkPaint& paint, const jchar text[],
+ static int breakText(JNIEnv* env, const SkPaint& paint, TypefaceImpl* typeface, const jchar text[],
int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
- SkPaint::TextBufferDirection tbd) {
+ SkPaint::TextBufferDirection textBufferDirection) {
+ size_t measuredCount = 0;
+ float measured = 0;
+
+#ifdef USE_MINIKIN
+ Layout layout;
+ std::string css = MinikinUtils::setLayoutProperties(&layout, &paint, bidiFlags, typeface);
+ layout.doLayout(text, 0, count, count, css);
+ float* advances = new float[count];
+ layout.getAdvances(advances);
+ const bool forwardScan = (textBufferDirection == SkPaint::kForward_TextBufferDirection);
+ for (int i = 0; i < count; i++) {
+ // traverse in the given direction
+ int index = forwardScan ? i : (count - i - 1);
+ float width = advances[index];
+ if (measured + width > maxWidth) {
+ break;
+ }
+ // properly handle clusters when scanning backwards
+ if (forwardScan || width != 0.0f) {
+ measuredCount = i + 1;
+ }
+ measured += width;
+ }
+ delete[] advances;
+#else
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
text, 0, count, count, bidiFlags);
if (value == NULL) {
return 0;
}
- SkScalar measured;
- size_t bytes = paint.breakText(value->getGlyphs(), value->getGlyphsCount() << 1,
- maxWidth, &measured, tbd);
+ SkScalar m;
+ size_t bytes = paint.breakText(value->getGlyphs(), value->getGlyphsCount() << 1,
+ maxWidth, &m, textBufferDirection);
SkASSERT((bytes & 1) == 0);
+ measuredCount = bytes >> 1;
+ measured = SkScalarToFloat(m);
+#endif
if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
jfloat* array = autoMeasured.ptr();
- array[0] = SkScalarToFloat(measured);
+ array[0] = measured;
}
- return bytes >> 1;
+ return measuredCount;
}
- static jint breakTextC(JNIEnv* env, jobject jpaint, jcharArray jtext,
+ static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray jtext,
jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
- NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, jtext);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
SkPaint::TextBufferDirection tbd;
if (count < 0) {
tbd = SkPaint::kBackward_TextBufferDirection;
@@ -976,28 +1013,28 @@
return 0;
}
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
const jchar* text = env->GetCharArrayElements(jtext, NULL);
- count = breakText(env, *paint, text + index, count, maxWidth,
+ count = breakText(env, *paint, typeface, text + index, count, maxWidth,
bidiFlags, jmeasuredWidth, tbd);
env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
JNI_ABORT);
return count;
}
- static jint breakTextS(JNIEnv* env, jobject jpaint, jstring jtext,
+ static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring jtext,
jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
- NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, jtext);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
SkPaint::TextBufferDirection tbd = forwards ?
SkPaint::kForward_TextBufferDirection :
SkPaint::kBackward_TextBufferDirection;
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
int count = env->GetStringLength(jtext);
const jchar* text = env->GetStringChars(jtext, NULL);
- count = breakText(env, *paint, text, count, maxWidth, bidiFlags, jmeasuredWidth, tbd);
+ count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, tbd);
env->ReleaseStringChars(jtext, text);
return count;
}
@@ -1108,8 +1145,8 @@
{"native_measureText","([CIII)F", (void*) SkPaintGlue::measureText_CIII},
{"native_measureText","(Ljava/lang/String;I)F", (void*) SkPaintGlue::measureText_StringI},
{"native_measureText","(Ljava/lang/String;III)F", (void*) SkPaintGlue::measureText_StringIII},
- {"native_breakText","([CIIFI[F)I", (void*) SkPaintGlue::breakTextC},
- {"native_breakText","(Ljava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS},
+ {"native_breakText","(JJ[CIIFI[F)I", (void*) SkPaintGlue::breakTextC},
+ {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS},
{"native_getTextWidths","(JJ[CIII[F)I", (void*) SkPaintGlue::getTextWidths___CIII_F},
{"native_getTextWidths","(JJLjava/lang/String;III[F)I", (void*) SkPaintGlue::getTextWidths__StringIII_F},
{"native_getTextRunAdvances","(JJ[CIIIIZ[FI)F",
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index 420a17f..6ef1d2c 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -2,22 +2,22 @@
**
** Copyright 2006, 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
+** 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
+** 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
+** 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.
*/
// This file was generated from the C++ include file: SkPath.h
// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
+// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
// or one of the auxilary file specifications in device/tools/gluemaker.
#include "jni.h"
@@ -92,7 +92,7 @@
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
return obj->isEmpty();
}
-
+
static jboolean isRect(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect) {
SkRect rect;
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
@@ -100,65 +100,66 @@
GraphicsJNI::rect_to_jrectf(rect, env, jrect);
return result;
}
-
+
static void computeBounds(JNIEnv* env, jobject clazz, jlong objHandle, jobject jbounds) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
const SkRect& bounds = obj->getBounds();
GraphicsJNI::rect_to_jrectf(bounds, env, jbounds);
}
-
+
static void incReserve(JNIEnv* env, jobject clazz, jlong objHandle, jint extraPtCount) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->incReserve(extraPtCount);
}
-
+
static void moveTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->moveTo(x, y);
}
-
+
static void rMoveTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->rMoveTo(dx, dy);
}
-
+
static void lineTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->lineTo(x, y);
}
-
+
static void rLineTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->rLineTo(dx, dy);
}
-
+
static void quadTo__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->quadTo(x1, y1, x2, y2);
}
-
+
static void rQuadTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->rQuadTo(dx1, dy1, dx2, dy2);
}
-
+
static void cubicTo__FFFFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->cubicTo(x1, y1, x2, y2, x3, y3);
}
-
+
static void rCubicTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->rCubicTo(x1, y1, x2, y2, x3, y3);
}
-
- static void arcTo(JNIEnv* env, jobject clazz, jlong objHandle, jobject oval, jfloat startAngle, jfloat sweepAngle, jboolean forceMoveTo) {
+
+ static void arcTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
+ jboolean forceMoveTo) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
- SkRect oval_;
- GraphicsJNI::jrectf_to_rect(env, oval, &oval_);
- obj->arcTo(oval_, startAngle, sweepAngle, forceMoveTo);
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+ obj->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
}
-
+
static void close(JNIEnv* env, jobject clazz, jlong objHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->close();
@@ -185,28 +186,26 @@
obj->addCircle(x, y, radius, dir);
}
- static void addArc(JNIEnv* env, jobject clazz, jlong objHandle, jobject oval, jfloat startAngle, jfloat sweepAngle) {
- SkRect oval_;
+ static void addArc(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle) {
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
- GraphicsJNI::jrectf_to_rect(env, oval, &oval_);
- obj->addArc(oval_, startAngle, sweepAngle);
+ obj->addArc(oval, startAngle, sweepAngle);
}
- static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect,
- jfloat rx, jfloat ry, jint dirHandle) {
- SkRect rect;
+ static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat rx, jfloat ry, jint dirHandle) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
- GraphicsJNI::jrectf_to_rect(env, jrect, &rect);
obj->addRoundRect(rect, rx, ry, dir);
}
-
- static void addRoundRect8(JNIEnv* env, jobject, jlong objHandle, jobject jrect,
- jfloatArray array, jint dirHandle) {
- SkRect rect;
+
+ static void addRoundRect8(JNIEnv* env, jobject, jlong objHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
- GraphicsJNI::jrectf_to_rect(env, jrect, &rect);
AutoJavaFloatArray afa(env, array, 8);
#ifdef SK_SCALAR_IS_FLOAT
const float* src = afa.ptr();
@@ -215,32 +214,32 @@
#endif
obj->addRoundRect(rect, src, dir);
}
-
+
static void addPath__PathFF(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jfloat dx, jfloat dy) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
obj->addPath(*src, dx, dy);
}
-
+
static void addPath__Path(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
obj->addPath(*src);
}
-
+
static void addPath__PathMatrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jlong matrixHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
obj->addPath(*src, *matrix);
}
-
+
static void offset__FFPath(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy, jlong dstHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
obj->offset(dx, dy, dst);
}
-
+
static void offset__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->offset(dx, dy);
@@ -250,14 +249,14 @@
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->setLastPt(dx, dy);
}
-
+
static void transform__MatrixPath(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle, jlong dstHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
obj->transform(*matrix, dst);
}
-
+
static void transform__Matrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
@@ -487,14 +486,14 @@
{"native_rQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo},
{"native_cubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF},
{"native_rCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
- {"native_arcTo","(JLandroid/graphics/RectF;FFZ)V", (void*) SkPathGlue::arcTo},
+ {"native_arcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo},
{"native_close","(J)V", (void*) SkPathGlue::close},
{"native_addRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
{"native_addOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
{"native_addCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
- {"native_addArc","(JLandroid/graphics/RectF;FF)V", (void*) SkPathGlue::addArc},
- {"native_addRoundRect","(JLandroid/graphics/RectF;FFI)V", (void*) SkPathGlue::addRoundRectXY},
- {"native_addRoundRect","(JLandroid/graphics/RectF;[FI)V", (void*) SkPathGlue::addRoundRect8},
+ {"native_addArc","(JFFFFFF)V", (void*) SkPathGlue::addArc},
+ {"native_addRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY},
+ {"native_addRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8},
{"native_addPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF},
{"native_addPath","(JJ)V", (void*) SkPathGlue::addPath__Path},
{"native_addPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix},
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..f8bab24 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -36,6 +36,11 @@
using namespace android;
+enum {
+ // Keep up to date with Camera.java
+ CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -2,
+};
+
struct fields_t {
jfieldID context;
jfieldID facing;
@@ -466,7 +471,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 +479,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 == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
+ // Default path: hal version is don't care, do normal camera connect.
+ 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 +525,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 +905,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_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index ee4c619..849531c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -287,6 +287,8 @@
env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz,
"errorCallbackFromNative","(I)V"),
check_AudioSystem_Command(err));
+
+ env->DeleteLocalRef(clazz);
}
static jint
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_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 86207f0..87ee618 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -174,21 +174,21 @@
ssize_t pss = memtrack_proc_graphics_pss(p);
if (pss < 0) {
- ALOGW("failed to get graphics pss: %d", pss);
+ ALOGW("failed to get graphics pss: %zd", pss);
return pss;
}
graphics_mem->graphics = pss / 1024;
pss = memtrack_proc_gl_pss(p);
if (pss < 0) {
- ALOGW("failed to get gl pss: %d", pss);
+ ALOGW("failed to get gl pss: %zd", pss);
return pss;
}
graphics_mem->gl = pss / 1024;
pss = memtrack_proc_other_pss(p);
if (pss < 0) {
- ALOGW("failed to get other pss: %d", pss);
+ ALOGW("failed to get other pss: %zd", pss);
return pss;
}
graphics_mem->other = pss / 1024;
@@ -231,9 +231,9 @@
unsigned referenced = 0;
unsigned temp;
- unsigned long int start;
- unsigned long int end = 0;
- unsigned long int prevEnd = 0;
+ uint64_t start;
+ uint64_t end = 0;
+ uint64_t prevEnd = 0;
char* name;
int name_pos;
@@ -255,7 +255,7 @@
if (len < 1) return;
line[--len] = 0;
- if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
+ if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
skip = true;
} else {
while (isspace(line[name_pos])) {
@@ -371,7 +371,7 @@
referenced = temp;
} else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
swapped_out = temp;
- } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') {
+ } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
// looks like a new mapping
// example: "10000000-10001000 ---p 10000000 00:00 0"
break;
diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp
index f8a1fd9..ad36843c 100644
--- a/core/jni/android_server_FingerprintManager.cpp
+++ b/core/jni/android_server_FingerprintManager.cpp
@@ -20,30 +20,10 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
#include <utils/Log.h>
-namespace android {
-
-static struct {
- jclass clazz;
- jmethodID notify;
-} gFingerprintManagerClassInfo;
-
-static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
- return -1; // TODO
-}
-
-static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) {
- return -1; // TODO
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod g_methods[] = {
- { "nativeEnroll", "(I)I", (void*)nativeEnroll },
- { "nativeRemove", "(I)I", (void*)nativeRemove },
-};
-
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className); \
@@ -61,13 +41,174 @@
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
-int register_android_server_FingerprintManager(JNIEnv* env) {
- FIND_CLASS(gFingerprintManagerClassInfo.clazz,
- "android/service/fingerprint/FingerprintManager");
- GET_METHOD_ID(gFingerprintManagerClassInfo.notify, gFingerprintManagerClassInfo.clazz,
- "notify", "(III)V");
- return AndroidRuntime::registerNativeMethods(
- env, "com/android/service/fingerprint/FingerprintManager", g_methods, NELEM(g_methods));
+namespace android {
+
+static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(1, 0);
+
+static const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService";
+static struct {
+ jclass clazz;
+ jmethodID notify;
+ jobject callbackObject;
+} gFingerprintServiceClassInfo;
+
+static struct {
+ fingerprint_module_t const* module;
+ fingerprint_device_t *device;
+} gContext;
+
+// TODO: remove after driver update to use new HAL
+fingerprint_msg_type_t hackTilFpDriverUpdate(fingerprint_msg_type_t t) {
+ switch(static_cast<int>(t)) {
+ case 1: return FINGERPRINT_PROCESSED;
+ case 2: return FINGERPRINT_TEMPLATE_ENROLLING;
+ default: return t;
+ }
+}
+
+// Called by the HAL to notify us of fingerprint events
+static void hal_notify_callback(fingerprint_msg_t msg) {
+ uint32_t arg1 = 0;
+ uint32_t arg2 = 0;
+ uint32_t arg3 = 0; // TODO
+ msg.type = hackTilFpDriverUpdate(msg.type);
+ switch (msg.type) {
+ case FINGERPRINT_ERROR:
+ arg1 = msg.data.error;
+ break;
+ case FINGERPRINT_ACQUIRED:
+ arg1 = msg.data.acquired.acquired_info;
+ break;
+ case FINGERPRINT_PROCESSED:
+ arg1 = msg.data.processed.id;
+ break;
+ case FINGERPRINT_TEMPLATE_ENROLLING:
+ arg1 = msg.data.enroll.id;
+ arg2 = msg.data.enroll.samples_remaining;
+ arg3 = msg.data.enroll.data_collected_bmp;
+ break;
+ case FINGERPRINT_TEMPLATE_REMOVED:
+ arg1 = msg.data.removed.id;
+ break;
+ default:
+ ALOGE("fingerprint: invalid msg: %d", msg.type);
+ return;
+ }
+ //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2);
+
+ // TODO: fix gross hack to attach JNI to calling thread
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ int result = vm->AttachCurrentThread(&env, (void*) &args);
+ if (result != JNI_OK) {
+ ALOGE("Can't call JNI method: attach failed: %#x", result);
+ return;
+ }
+ }
+ env->CallVoidMethod(gFingerprintServiceClassInfo.callbackObject,
+ gFingerprintServiceClassInfo.notify, msg.type, arg1, arg2);
+}
+
+static void nativeInit(JNIEnv *env, jobject clazz, jobject callbackObj) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeInit()\n");
+ FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
+ GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz,
+ "notify", "(III)V");
+ gFingerprintServiceClassInfo.callbackObject = env->NewGlobalRef(callbackObj);
+}
+
+static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll()\n");
+ int ret = gContext.device->enroll(gContext.device, timeout);
+ return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeEnrollCancel(JNIEnv* env, jobject clazz) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnrollCancel()\n");
+ int ret = gContext.device->enroll_cancel(gContext.device);
+ return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeRemove(%d)\n", fingerprintId);
+ int ret = gContext.device->remove(gContext.device, fingerprintId);
+ return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeOpenHal(JNIEnv* env, jobject clazz) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
+ int err;
+ const hw_module_t *hw_module = NULL;
+ if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
+ ALOGE("Can't open fingerprint HW Module, error: %d", err);
+ return 0;
+ }
+ if (NULL == hw_module) {
+ ALOGE("No valid fingerprint module");
+ return 0;
+ }
+
+ gContext.module = reinterpret_cast<const fingerprint_module_t*>(hw_module);
+
+ if (gContext.module->common.methods->open == NULL) {
+ ALOGE("No valid open method");
+ return 0;
+ }
+
+ hw_device_t *device = NULL;
+
+ if (0 != (err = gContext.module->common.methods->open(hw_module, NULL, &device))) {
+ ALOGE("Can't open fingerprint methods, error: %d", err);
+ return 0;
+ }
+
+ if (kVersion != device->version) {
+ ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
+ // return 0; // FIXME
+ }
+
+ gContext.device = reinterpret_cast<fingerprint_device_t*>(device);
+ err = gContext.device->set_notify(gContext.device, hal_notify_callback);
+ if (err < 0) {
+ ALOGE("Failed in call to set_notify(), err=%d", err);
+ return 0;
+ }
+
+ // Sanity check - remove
+ if (gContext.device->notify != hal_notify_callback) {
+ ALOGE("NOTIFY not set properly: %p != %p", gContext.device->notify, hal_notify_callback);
+ }
+
+ ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
+ return reinterpret_cast<jlong>(gContext.device);
+}
+
+static jint nativeCloseHal(JNIEnv* env, jobject clazz) {
+ return -ENOSYS; // TODO
+}
+
+// ----------------------------------------------------------------------------
+
+// TODO: clean up void methods
+static const JNINativeMethod g_methods[] = {
+ { "nativeEnroll", "(I)I", (void*)nativeEnroll },
+ { "nativeEnrollCancel", "()I", (void*)nativeEnroll },
+ { "nativeRemove", "(I)I", (void*)nativeRemove },
+ { "nativeOpenHal", "()I", (void*)nativeOpenHal },
+ { "nativeCloseHal", "()I", (void*)nativeCloseHal },
+ { "nativeInit", "(Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit }
+};
+
+int register_android_server_fingerprint_FingerprintService(JNIEnv* env) {
+ FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
+ GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz, "notify",
+ "(III)V");
+ int result = AndroidRuntime::registerNativeMethods(
+ env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods));
+ ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n");
+ return result;
}
} // namespace android
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 4a6e117..4362018 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -388,11 +388,11 @@
static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
jlong rendererPtr, jintArray colors, jint offset, jint stride,
jfloat left, jfloat top, jint width, jint height, jboolean hasAlpha, jlong paintPtr) {
+ const SkImageInfo info = SkImageInfo::Make(width, height,
+ hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+ kPremul_SkAlphaType);
SkBitmap* bitmap = new SkBitmap;
- bitmap->setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config : SkBitmap::kRGB_565_Config,
- width, height);
-
- if (!bitmap->allocPixels()) {
+ if (!bitmap->allocPixels(info)) {
delete bitmap;
return;
}
@@ -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_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 0210bd9..5ebed9c 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -142,16 +142,16 @@
// Canvas management
// ----------------------------------------------------------------------------
-static inline SkBitmap::Config convertPixelFormat(int32_t format) {
+static inline SkColorType convertPixelFormat(int32_t format) {
switch (format) {
case PIXEL_FORMAT_RGBA_8888:
- return SkBitmap::kARGB_8888_Config;
+ return kN32_SkColorType;
case PIXEL_FORMAT_RGBX_8888:
- return SkBitmap::kARGB_8888_Config;
+ return kN32_SkColorType;
case PIXEL_FORMAT_RGB_565:
- return SkBitmap::kRGB_565_Config;
+ return kRGB_565_SkColorType;
default:
- return SkBitmap::kNo_Config;
+ return kUnknown_SkColorType;
}
}
@@ -188,8 +188,10 @@
ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
SkBitmap bitmap;
- bitmap.setConfig(convertPixelFormat(buffer->getPixelFormat()),
- buffer->getWidth(), buffer->getHeight(), bytesCount);
+ bitmap.setInfo(SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
+ convertPixelFormat(buffer->getPixelFormat()),
+ kPremul_SkAlphaType),
+ bytesCount);
if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
bitmap.setPixels(bits);
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3d14aaf5..7018751 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -173,17 +173,17 @@
return value;
}
-static inline SkBitmap::Config convertPixelFormat(PixelFormat format) {
+static inline SkColorType convertPixelFormat(PixelFormat format) {
/* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then
- we can map to SkBitmap::kARGB_8888_Config, and optionally call
+ we can map to kN32_SkColorType, and optionally call
bitmap.setAlphaType(kOpaque_SkAlphaType) on the resulting SkBitmap
(as an accelerator)
*/
switch (format) {
- case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config;
- case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config;
- case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config;
- default: return SkBitmap::kNo_Config;
+ case PIXEL_FORMAT_RGBX_8888: return kN32_SkColorType;
+ case PIXEL_FORMAT_RGBA_8888: return kN32_SkColorType;
+ case PIXEL_FORMAT_RGB_565: return kRGB_565_SkColorType;
+ default: return kUnknown_SkColorType;
}
}
@@ -220,12 +220,16 @@
// Associate a SkCanvas object to this surface
env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format);
+ SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
+ convertPixelFormat(outBuffer.format),
+ kPremul_SkAlphaType);
+ if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
+ info.fAlphaType = kOpaque_SkAlphaType;
+ }
+
SkBitmap bitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
- bitmap.setConfig(convertPixelFormat(outBuffer.format), outBuffer.width, outBuffer.height, bpr);
- if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
- bitmap.setAlphaType(kOpaque_SkAlphaType);
- }
+ bitmap.setInfo(info, bpr);
if (outBuffer.width > 0 && outBuffer.height > 0) {
bitmap.setPixels(outBuffer.bits);
} else {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index c0d5221..9783e91 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 {
@@ -173,7 +175,7 @@
screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
SkBitmap* bitmap = new SkBitmap();
- bitmap->setConfig(screenshotInfo, (size_t)rowBytes);
+ bitmap->setInfo(screenshotInfo, (size_t)rowBytes);
if (screenshotInfo.fWidth > 0 && screenshotInfo.fHeight > 0) {
// takes ownership of ScreenshotClient
SkMallocPixelRef* pixels = SkMallocPixelRef::NewWithProc(screenshotInfo,
@@ -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_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index c1ab515..5c04a78 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -73,17 +73,28 @@
// Native layer
// ----------------------------------------------------------------------------
-static inline SkBitmap::Config convertPixelFormat(int32_t format) {
- switch (format) {
+// FIXME: consider exporting this to share (e.g. android_view_Surface.cpp)
+static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) {
+ SkImageInfo info;
+ info.fWidth = buffer.width;
+ info.fHeight = buffer.height;
+ switch (buffer.format) {
case WINDOW_FORMAT_RGBA_8888:
- return SkBitmap::kARGB_8888_Config;
+ info.fColorType = kN32_SkColorType;
+ info.fAlphaType = kPremul_SkAlphaType;
+ break;
case WINDOW_FORMAT_RGBX_8888:
- return SkBitmap::kARGB_8888_Config;
+ info.fColorType = kN32_SkColorType;
+ info.fAlphaType = kOpaque_SkAlphaType;
case WINDOW_FORMAT_RGB_565:
- return SkBitmap::kRGB_565_Config;
+ info.fColorType = kRGB_565_SkColorType;
+ info.fAlphaType = kOpaque_SkAlphaType;
default:
- return SkBitmap::kNo_Config;
+ info.fColorType = kUnknown_SkColorType;
+ info.fAlphaType = kIgnore_SkAlphaType;
+ break;
}
+ return info;
}
/**
@@ -148,11 +159,7 @@
ssize_t bytesCount = buffer.stride * bytesPerPixel(buffer.format);
SkBitmap bitmap;
- bitmap.setConfig(convertPixelFormat(buffer.format), buffer.width, buffer.height, bytesCount);
-
- if (buffer.format == WINDOW_FORMAT_RGBX_8888) {
- bitmap.setAlphaType(kOpaque_SkAlphaType);
- }
+ bitmap.setInfo(convertPixelFormat(buffer), bytesCount);
if (buffer.width > 0 && buffer.height > 0) {
bitmap.setPixels(buffer.bits);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 815c4a75..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 edca101..fa1a563 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -657,6 +657,13 @@
android:label="@string/permlab_addVoicemail"
android:description="@string/permdesc_addVoicemail" />
+ <!-- Allows an application to read all the voicemails in the system. -->
+ <permission android:name="com.android.voicemail.permission.READ_ALL_VOICEMAIL"
+ android:permissionGroup="android.permission-group.VOICEMAIL"
+ android:protectionLevel="dangerous"
+ android:label="@string/permlab_readAllVoicemail"
+ android:description="@string/permdesc_readAllVoicemail" />
+
<!-- =============================================== -->
<!-- Permissions for enabling accessibility features -->
<!-- =============================================== -->
@@ -2735,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" />
@@ -2928,6 +2936,12 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service
+ android:name="com.android.server.pm.BackgroundDexOptService"
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
</application>
</manifest>
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-hdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-hdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..4d97045
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-hdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..46fb463
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_outline_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_recent.png b/core/res/res/drawable-hdpi/ic_recent.png
new file mode 100644
index 0000000..8866539
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_recent.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-mdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..163f4a0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-mdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..bbfb83c7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_lock_outline_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_recent.png b/core/res/res/drawable-mdpi/ic_recent.png
new file mode 100644
index 0000000..2b607df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_recent.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-xhdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..21d4d53
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-xhdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..2aeb9a2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lock_outline_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_recent.png b/core/res/res/drawable-xhdpi/ic_recent.png
new file mode 100644
index 0000000..86316db
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_recent.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-xxhdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..1b11b59
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-xxhdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..ae0d655
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_lock_outline_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_recent.png b/core/res/res/drawable-xxhdpi/ic_recent.png
new file mode 100644
index 0000000..e6bd125
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_recent.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-xxxhdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..8774412
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-xxxhdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..1375acc
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_lock_outline_wht_24dp.png
Binary files differ
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/drawable/lock_task_notify_bg.xml b/core/res/res/drawable/lock_task_notify_bg.xml
new file mode 100644
index 0000000..3a8fab5
--- /dev/null
+++ b/core/res/res/drawable/lock_task_notify_bg.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"
+ android:shape="rectangle" >
+
+ <solid android:color="@android:color/black" />
+ <corners
+ android:radius="8dip" />
+</shape>
diff --git a/core/res/res/drawable/view_accessibility_focused.xml b/core/res/res/drawable/view_accessibility_focused.xml
index 0da9a81..68e3f1e 100644
--- a/core/res/res/drawable/view_accessibility_focused.xml
+++ b/core/res/res/drawable/view_accessibility_focused.xml
@@ -17,9 +17,11 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke
- android:width="2dp"
- android:color="@color/accessibility_focus_highlight" />
+ android:width="4dp"
+ android:color="@color/accessibility_focus_highlight"
+ android:dashWidth="4dp"
+ android:dashGap="2dp" />
- <corners android:radius="2dp"/>
+ <corners android:radius="2dp" />
</shape>
diff --git a/core/res/res/layout/lock_to_app_enter.xml b/core/res/res/layout/lock_to_app_enter.xml
new file mode 100644
index 0000000..c034536
--- /dev/null
+++ b/core/res/res/layout/lock_to_app_enter.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="20dp"
+ android:paddingRight="20dp"
+ android:paddingBottom="12dp"
+ android:alpha=".8"
+ android:background="@drawable/lock_task_notify_bg" >
+
+ <ImageView
+ android:id="@+id/lock_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="20dp"
+ android:alpha=".8"
+ android:src="@drawable/ic_lock_outline_wht_24dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/lock_icon"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="2dp"
+ android:textColor="@android:color/white"
+ android:alpha=".8"
+ android:text="@string/lock_to_app_start" />
+</RelativeLayout>
diff --git a/core/res/res/layout/lock_to_app_exit.xml b/core/res/res/layout/lock_to_app_exit.xml
new file mode 100644
index 0000000..4a60c80
--- /dev/null
+++ b/core/res/res/layout/lock_to_app_exit.xml
@@ -0,0 +1,45 @@
+<?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.
+-->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="20dp"
+ android:paddingRight="20dp"
+ android:paddingBottom="12dp"
+ android:alpha=".8"
+ android:background="@drawable/lock_task_notify_bg" >
+
+ <ImageView
+ android:id="@+id/lock_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="20dp"
+ android:alpha=".8"
+ android:src="@drawable/ic_lock_open_wht_24dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/lock_icon"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="2dp"
+ android:textColor="@android:color/white"
+ android:alpha=".8"
+ android:text="@string/lock_to_app_exit" />
+</RelativeLayout>
diff --git a/core/res/res/transition/explode.xml b/core/res/res/transition/explode.xml
new file mode 100644
index 0000000..fe22284
--- /dev/null
+++ b/core/res/res/transition/explode.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<explode xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/core/res/res/transition/slide_bottom.xml b/core/res/res/transition/slide_bottom.xml
new file mode 100644
index 0000000..46dc0d6
--- /dev/null
+++ b/core/res/res/transition/slide_bottom.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="bottom"/>
diff --git a/core/res/res/transition/slide_left.xml b/core/res/res/transition/slide_left.xml
new file mode 100644
index 0000000..997bd97
--- /dev/null
+++ b/core/res/res/transition/slide_left.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="left"/>
diff --git a/core/res/res/transition/slide_right.xml b/core/res/res/transition/slide_right.xml
new file mode 100644
index 0000000..98f8f6a
--- /dev/null
+++ b/core/res/res/transition/slide_right.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="right"/>
diff --git a/core/res/res/transition/slide_top.xml b/core/res/res/transition/slide_top.xml
new file mode 100644
index 0000000..07ab945
--- /dev/null
+++ b/core/res/res/transition/slide_top.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="top"/>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 21fd37d..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,6 +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>
+ <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 abeadfa..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>
@@ -1362,6 +1364,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>
+ <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-ar/strings.xml b/core/res/res/values-ar/strings.xml
index aa47774..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>
@@ -1362,6 +1364,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>
+ <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-bg/strings.xml b/core/res/res/values-bg/strings.xml
index a8ceac6..b9c4f46 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/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,6 +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>
+ <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 f6aaf73d..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,6 +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>
+ <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 d18c107..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,6 +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>
+ <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 8695451..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Tillader, at en applikation registrerer ændringer i trust-tilstand."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Angiv en tillidsagent."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Tillader, at en applikation angiver en tillidsagent."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Åbn indstillingsmenuen for tillidsagenten"</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Giver en applikation tilladelse til at starte en aktivitet, som ændrer adfærden for tillidsagenten."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Knytte sig til en trust agent-tjeneste"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Tillader, at en applikation knytter sig til en trust agent-tjeneste."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interager med opdaterings- og gendannelsessystemet"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index bffbd1e..fe5c139 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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Ermöglicht einer App die Überwachungen von Änderungen des Trust-Status"</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Trust Agent bereitstellen"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ermöglich die Bereitstellung eines Trust Agents durch eine App"</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Einstellungsmenü des Trust Agents starten"</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Ermöglicht einer App das Starten einer Aktivität, die das Verhalten des Trust Agents ändert"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"An Trust Agent-Service anbinden"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ermöglicht einer App die Anbindung an einen Trust Agent-Service"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Mit Update- und Wiederherstellungssystem interagieren"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 59e9085..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>
@@ -1362,6 +1364,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>
+ <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 agents."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Αλληλεπίδραση με το σύστημα ενημέρωσης και ανάκτησης"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index c7d6149..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Allows an application to listen for changes in trust state."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Provide a trust agent."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Allows an application to provide a trust agent."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lunch trust agent settings menu."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Allows an application to lunch an activity that changes the trust agent behaviour."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Bind to a trust agent service"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Allows an application to bind to a trust agent service."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interact with update and recovery system"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index c7d6149..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Allows an application to listen for changes in trust state."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Provide a trust agent."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Allows an application to provide a trust agent."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lunch trust agent settings menu."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Allows an application to lunch an activity that changes the trust agent behaviour."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Bind to a trust agent service"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Allows an application to bind to a trust agent service."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interact with update and recovery system"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 8db1114..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>
@@ -1362,6 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que una aplicación detecte cambios en el estado de confianza."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Proporcionar un agente de confianza"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que una aplicación proporcione un agente de confianza."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Iniciar menú de configuración de agente de confianza"</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permite que una aplicación inicie una actividad que modifica el comportamiento del agente de confianza."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Vincular con un servicio de agente de confianza"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite que una aplicación se vincule con un servicio de agente de confianza."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interaccionar con el sistema de recuperación y las actualizaciones"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 5ad1cd3..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que una aplicación detecte cambios en el estado de confianza."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Proporcionar un agente de confianza."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que una aplicación proporcione un agente de confianza."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Abre el menú de ajustes del agente de confianza."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permite que una aplicación inicie una actividad que cambie el comportamiento del agente de confianza."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Enlazar con un servicio de agente de confianza"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite a una aplicación enlazar con un servicio de agente de confianza."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interactuar con el sistema de recuperación y las actualizaciones"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 7a4659b..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Lubab rakendusel tuvastada muudatusi usaldusväärses olekus."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Usaldusväärse agendi esitamine."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Võimaldab rakendusel esitada usaldusväärset agenti."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Käivita usaldusväärse agendi seadete menüü."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Võimaldab rakendusel käivitada tegevuse, mis muudab usaldusväärse agendi käitumist."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Usaldusväärse agendi teenusega sidumine"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Lubab rakendusel ennast siduda usaldusväärse agendi teenusega."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Suhtlemine värskenduse ja taastesüsteemiga"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index e9628ec..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,6 +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>
+ <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 09f1f45..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,6 +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>
+ <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 5982bca..ec4c2d6 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Applications personnelles"</string>
- <!-- no translation found for managed_profile_label (4287077106125758391) -->
- <skip />
+ <string name="managed_profile_label" msgid="4287077106125758391">"Android pour les activités professionnelles"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Services payants"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Effectuer des opérations payantes"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vos messages"</string>
@@ -1002,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>
@@ -1363,6 +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>
+ <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 89ef90c..57ee762 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Applications personnelles"</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">"Services payants"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Effectuer des opérations payantes"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vos messages"</string>
@@ -1002,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>
@@ -1363,6 +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>
+ <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 056f63f..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>
@@ -1362,6 +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>
+ <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-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 8dbe791..65e64ec 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sustav Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Osobne aplikacije"</string>
- <!-- no translation found for managed_profile_label (4287077106125758391) -->
- <skip />
+ <string name="managed_profile_label" msgid="4287077106125758391">"Android za posao"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Usluge koje se plaćaju"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Radite stvari koje će uzrokovati novčane troškove."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vaše poruke"</string>
@@ -1002,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>
@@ -1363,6 +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>
+ <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 cbe36be..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Lehetővé teszi, hogy az alkalmazás figyelje a trust-állapot változásait."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Trust agent szoftver megadása"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Lehetővé teszi, hogy az alkalmazás megadjon egy trust agent szoftvert."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Trust agent szoftver beállításmenüjének indítása."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Lehetővé teszi az alkalmazások számára olyan tevékenységek indítását, amelyek megváltoztatják a trust agent szoftver viselkedését."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Csatlakozás egy trust agent szolgáltatáshoz"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Lehetővé teszi, hogy az alkalmazás egy trust agent szolgáltatáshoz csatlakozzon."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Kapcsolatfelvétel a frissítési és helyreállítási rendszerrel"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 17779b7..97e9ffc1 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/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>
@@ -401,10 +400,8 @@
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Թույլ է տալիս սեփականատիրոջը միանալ պաստառի վերին մակարդակի ինտերֆեյսին: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"կապվել ձայնային փոխազդիչին"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Թույլ է տալիս սեփականատիրոջը միանալ ձայնային փոխազդիչի բազային միջերեսին: Սովորական ծրագրերի համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
- <!-- no translation found for permlab_manageVoiceKeyphrases (1252285102392793548) -->
- <skip />
- <!-- no translation found for permdesc_manageVoiceKeyphrases (8476560722907530008) -->
- <skip />
+ <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"կառավարել ձայնային բանալի բառակապակցությունները"</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>
@@ -1004,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>
@@ -1365,6 +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>
+ <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 2fb9f8a..1c80430 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Mode aman"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Aplikasi pribadi"</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">"Layanan berbayar"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Lakukan hal yang dapat dikenai biaya."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Pesan Anda"</string>
@@ -1002,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>
@@ -1363,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Mengizinkan aplikasi mendengarkan perubahan dalam status kepercayaan."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Berikan agen tepercaya."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Memungkinkan aplikasi memberikan agen tepercaya."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Luncurkan menu setelan agen tepercaya."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Memungkinkan aplikasi meluncurkan aktivitas yang mengubah perilaku agen tepercaya."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Ikat ke layanan agen kepercayaan"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Mengizinkan aplikasi mengikat ke layanan agen kepercayaan."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Berinteraksi dengan sistem pemulihan dan pembaruan"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 8a9ff58..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Consente a un\'applicazione di rilevare le modifiche nello stato trust."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Indica un trust agent."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Consente a un\'applicazione di indicare un trust agent."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Avvio del menu di impostazioni del trust agent."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Consente a un\'applicazione di avviare un\'attività che modifica il comportamento del trust agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Associazione a un servizio trust agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Consente a un\'applicazione di associarsi a un servizio trust agent."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interazione con il sistema di ripristino e aggiornamento"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 8de9971..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>
@@ -1362,6 +1364,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>
+ <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-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 3fde38a..a4b95a6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/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,6 +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>
+ <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-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 01a7194..694e1c7 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/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 სამსახურისთვის"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"სერვისები, რომელშიც ფულის გადახდა გიწევთ"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"ისეთი აქტივობების განხორციელება, რომლებშიც ფულის გადახდა მოგიწევთ."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"თქვენი შეტყობინებები"</string>
@@ -401,10 +400,8 @@
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"მფლობელს შეეძლება ფონის ზედა დონის ინტერფეისთან დაკავშირება. არასდროს გამოიყენება ჩვეულებრივ აპებში."</string>
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"ხმის ინტერაქტორთან შეკავშირება"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"მფლობელს შეეძლება შეკავშირდეს ხმის ინტერაქციის სერვისების ზედა დონის ინტერფეისთან. ჩვეულებრივ აპს ეს წესით არასოდეს უნდა დასჭირდეს."</string>
- <!-- no translation found for permlab_manageVoiceKeyphrases (1252285102392793548) -->
- <skip />
- <!-- no translation found for permdesc_manageVoiceKeyphrases (8476560722907530008) -->
- <skip />
+ <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"ხმოვანი საიდუმლო ფრაზების მართვა"</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>
@@ -1004,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>
@@ -1365,6 +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>
+ <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 62b2e8a..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>
@@ -1362,6 +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>
+ <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>
@@ -1371,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>
@@ -1380,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>
@@ -1403,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>
@@ -1434,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>
@@ -1450,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>
@@ -1469,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>
@@ -1491,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>
@@ -1548,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>
@@ -1580,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>
@@ -1689,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 88327ca..cffe6bb 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/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,6 +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>
+ <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 44f5e0d..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>
@@ -1362,6 +1364,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>
+ <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>
@@ -1701,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 09fbdfb..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Programai leidžiama atsižvelgti į patikimos būsenos pakeitimus."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Teikti patikimos priemonės paslaugą."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Programai leidžiama teikti patikimos priemonės paslaugą."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Paleisti patikimo atstovo nustatymų meniu."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Leidžiama programai paleisti veiklą, keičiančią patikimo atstovo elgseną."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Susisaistyti su „trust agent“ paslauga"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Programai leidžiama susisaistyti su „trust agent“ paslauga."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Sąveikauti su naujiniu ir atkūrimo sistema"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 2cb5735..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>
@@ -1362,6 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Ļauj lietojumprogrammai klausīties uzticamības statusa izmaiņas."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Nodrošināt uzticamības pārbaudes programmu"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ļauj lietojumprogrammai nodrošināt uzticamības pārbaudes programmu."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Palaist uzticamības pārbaudes programmas iestatījumu izvēlni."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Ļauj lietojumprogrammai palaist darbību, kas maina uzticamības pārbaudes programmas rīcību."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Izveidot savienojumu ar uzticamības pārbaudes pakalpojumu"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ļauj lietojumprogrammai izveidot savienojumu ar uzticamības pārbaudes pakalpojumu."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Mijiedarbošanās ar atjauninājumu un atkopšanas sistēmu"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index b030060..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>
@@ -1362,6 +1364,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>
+ <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-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 9ad8f37..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>
@@ -401,10 +400,8 @@
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi kertas dinding. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<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>
- <!-- no translation found for permlab_manageVoiceKeyphrases (1252285102392793548) -->
- <skip />
- <!-- no translation found for permdesc_manageVoiceKeyphrases (8476560722907530008) -->
- <skip />
+ <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"urus frasa kunci suara"</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>
@@ -1004,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>
@@ -1365,6 +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>
+ <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 1ae5c5e..40764c7 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Sikkermodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Personlige apper"</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">"Betaltjenester"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Gjøre ting som kan koste deg penger."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Meldinger"</string>
@@ -402,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>
@@ -1002,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>
@@ -1363,6 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Gir appen tillatelse til å oppdage endringer i tillitsstatusen."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Angivelse av en pålitelig agent."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Tillater appen å angi en pålitelig agent."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Start innstillingsmenyen til tillitsagenten."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Tillater apper å starte en aktivitet som endrer atferden til tillitsagenter."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Tilknytt en tillitsagent-tjeneste."</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Gir appen tillatelse til å knyttes til en tillitsagent-tjeneste."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Samhandling med oppdateringer og gjenopprettingssystem"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e42d714..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Toestaan dat een app controleert op wijzigingen in de trust-status."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Een trust-agent aanleveren."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Hiermee kan een app een trust-agent aanleveren."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Menu met instellingen voor vertrouwensagent starten."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Hiermee kan een app een activiteit starten waarmee het gedrag van de vertrouwensagent wordt gewijzigd."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Binden aan een trust-agentservice"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Toestaan dat een app wordt gebonden aan een trust-agentservice."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interactie met update- en herstelsysteem"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 64609df..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Zezwala aplikacji na monitorowanie zmian w stanie zaufania."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Dostarczaj agenta zaufania."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Zezwala aplikacji na dostarczanie agenta zaufania."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Uruchom menu ustawień zaufanego agenta."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Umożliwia aplikacji uruchamianie czynności, która zmienia działanie zaufanego agenta."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Powiąż z usługą agenta zaufania"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Zezwala aplikacji na powiązanie z usługą agenta zaufania."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakcja z systemem odzyskiwania i aktualizacjami"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 059a85f..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,6 +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>
+ <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 ec805f5..59328f5 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Aplicativos pessoais"</string>
- <!-- no translation found for managed_profile_label (4287077106125758391) -->
- <skip />
+ <string name="managed_profile_label" msgid="4287077106125758391">"Android para trabalho"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Serviços que geram gastos"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Faça coisas que podem custar dinheiro."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Suas mensagens"</string>
@@ -401,10 +400,8 @@
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite que o proprietário utilize interface de nível superior de um plano de fundo. Nunca deve ser necessário para aplicativos normais."</string>
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"associar a um interagente de voz"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Permite que o proprietário use a interface de nível superior de um serviço de interação de voz. Não deve ser necessário para aplicativos comuns."</string>
- <!-- no translation found for permlab_manageVoiceKeyphrases (1252285102392793548) -->
- <skip />
- <!-- no translation found for permdesc_manageVoiceKeyphrases (8476560722907530008) -->
- <skip />
+ <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"gerenciar frases-chave de voz"</string>
+ <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Permite ao titular gerenciar as frases-chave para detecção de hotword por voz. Isso nunca será necessário para aplicativos normais."</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"usar uma tela remota"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permite que o proprietário use a interface de nível superior de uma tela remota. Não deve ser necessário para aplicativos comuns."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"sujeitar-se a um serviço de widget"</string>
@@ -1004,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>
@@ -1365,6 +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>
+ <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 ba25b52..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) -->
@@ -2190,6 +2194,10 @@
<skip />
<!-- no translation found for permdesc_provide_trust_agent (3865702641053068148) -->
<skip />
+ <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
+ <skip />
+ <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
+ <skip />
<!-- no translation found for permlab_bind_trust_agent_service (8242093169457695334) -->
<skip />
<!-- no translation found for permdesc_bind_trust_agent_service (7041930026024507515) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 07c32fb..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>
@@ -401,10 +400,8 @@
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unei imagini de fundal. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"conectare la un serviciu de interacțiune vocală"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu de interacțiune vocală. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
- <!-- no translation found for permlab_manageVoiceKeyphrases (1252285102392793548) -->
- <skip />
- <!-- no translation found for permdesc_manageVoiceKeyphrases (8476560722907530008) -->
- <skip />
+ <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"gestionarea expresiilor cheie vocale"</string>
+ <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Permite titularului să gestioneze expresiile cheie pentru detectarea expresiei de activare. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"conectare la un ecran la distanță"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permite proprietarului să se conecteze la interfața de nivel superior a unui ecran la distanță. Nu ar trebui să fie niciodată necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"conectare la un serviciu widget"</string>
@@ -1004,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>
@@ -1365,6 +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>
+ <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 a7ca57b..c5f4c13 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/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 для бизнеса"</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,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>
@@ -1363,6 +1364,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>
+ <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-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 5631559..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>
@@ -1362,6 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Umožňuje aplikácii reagovať na zmeny stavu dôveryhodnosti."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Poskytnúť dôveryhodného agenta"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Umožňuje aplikácii poskytnúť dôveryhodného agenta."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Spustenie ponuky nastavení dôveryhodného agenta"</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Umožňuje aplikácii spustiť aktivitu, ktorá zmení správanie dôveryhodného agenta."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Viazanie sa na službu zástupcu dôveryhodnosti"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Umožňuje aplikácii viazať sa na službu zástupcu dôveryhodnosti."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakcia so systémom aktualizácií a obnovenia"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index a51e909..47568d6 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Varni način"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Osebne aplikacije"</string>
- <!-- no translation found for managed_profile_label (4287077106125758391) -->
- <skip />
+ <string name="managed_profile_label" msgid="4287077106125758391">"Android – službeno"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Plačljive storitve"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Dovolite stvari, za katere bo morda treba plačati."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vaša sporočila"</string>
@@ -1002,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>
@@ -1363,6 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Aplikaciji dovoli spremljanje sprememb stanja zaupanja."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Navedba posrednika zaupanja."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Aplikaciji dovoli navesti posrednika zaupanja."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Odpri meni z nastavitvami posrednika zaupanja."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Dovoli aplikaciji zagon dejavnosti, ki spremeni način delovanja posrednika zaupanja."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Povezovanje s storitvijo posrednikov zaupanja"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Aplikaciji dovoli povezovanje s storitvijo posrednikov zaupanja."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Uporaba sistema za posodobitev in obnovitev"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 4b2aa24..4758c94 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/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,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>
@@ -1363,6 +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>
+ <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 f9cd57a..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,6 +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>
+ <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 9573e151..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,6 +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>
+ <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 68d5442..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>
@@ -1362,6 +1364,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>
+ <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-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 5c926ba..af88c3c 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Mga personal na app"</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">"Mga serbisyong ginagastusan mo"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Gumawa ng mga bagay na magpapagastos sa iyo."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Iyong mga mensahe"</string>
@@ -1002,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>
@@ -1363,6 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Pinapayagan ang isang application na makinig para sa mga pagbabago sa estado ng trust."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Magbigay ng trust agent."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Pinapayagan ang isang application na magbigay ng trust agent."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Ilunsad ang menu ng mga setting ng trust agent."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Nagbibigay-daan sa isang application na maglunsad ng aktibidad na nagbabago sa pagkilos ng trust agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Sumailalim sa isang serbisyo ng trust agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Pinapayagan ang isang application na sumailalim sa isang serbisyo ng trust agent."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Makipag-ugnay sa system ng pag-update at pagbawi"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 3583fb0..ee8c464 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Güvenli mod"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android Sistemi"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Kişisel uygulamalar"</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">"Size maliyet getiren hizmetler"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Size maliyet getirebilecek işlemler yapma."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Mesajlarınız"</string>
@@ -1002,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>
@@ -1363,6 +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>
+ <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 fdb40b5..b85fc99 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/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 для бізнесу"</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,6 +1364,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>
+ <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-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 022efcc..b38fb87 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Chế độ an toàn"</string>
<string name="android_system_label" msgid="6577375335728551336">"Hệ thống Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Ứng dụng cá nhân"</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">"Dịch vụ tính tiền của bạn"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Thực hiện những tác vụ mà bạn có thể phải trả tiền."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Tin nhắn của bạn"</string>
@@ -1002,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>
@@ -1363,6 +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>
+ <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 f2090a2..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>
@@ -401,10 +400,8 @@
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"允许用户绑定到壁纸的顶级接口。普通应用绝不需要此权限。"</string>
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"绑定到语音互动器"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"允许应用绑定到语音互动服务的顶级接口。普通应用绝不需要此权限。"</string>
- <!-- no translation found for permlab_manageVoiceKeyphrases (1252285102392793548) -->
- <skip />
- <!-- no translation found for permdesc_manageVoiceKeyphrases (8476560722907530008) -->
- <skip />
+ <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"管理语音关键短语"</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>
@@ -1004,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>
@@ -1365,6 +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>
+ <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 3ebf416..43a487b 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/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 企業版"</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,6 +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>
+ <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-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 85e47ac..6658689 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/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 企業版"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"需要額外費用的服務。"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"執行需付費的作業或服務。"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"您的簡訊"</string>
@@ -449,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>
@@ -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,6 +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>
+ <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-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 639d400..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>
@@ -1362,6 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Ivumela uhlelo lokusebenza ukuthi lilalelele izinguquko kusimo sethemba."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Nikeza umsebenzeli owethembekile."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ivumela uhlelo lokusebenza ukunikeza umsebenzeli owethembekile."</string>
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Qalisa imenyu yezilungiselelo zomsebenzeli wethemba."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Ivumela uhlelo lokusebenza ukuthi luqalise umsebenzi oguqula ukuziphatha komsebenzeli wethemba."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Bophezela kusevisi yomenzeli wethemba"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ivumela uhlelo lokusebenza ukuthi libophezeleke kusevisi yomenzeli wethemba."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Ixhumana nesibuyekezo nesistimu yokutakula"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e807d69..4a6311a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2432,6 +2432,30 @@
<!-- Sets the state-based animator for the View. -->
<attr name="stateListAnimator" format="reference"/>
+
+ <!-- Tint to apply to the background. -->
+ <attr name="backgroundTint" format="color" />
+
+ <!-- Blending mode used to apply the background tint. -->
+ <attr name="backgroundTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -3021,7 +3045,29 @@
<!-- Indicates the initial checked state of this button. -->
<attr name="checked" format="boolean" />
<!-- Drawable used for the button graphic (e.g. checkbox, radio button, etc). -->
- <attr name="button" format="reference"/>
+ <attr name="button" format="reference" />
+ <!-- Tint to apply to the button graphic. -->
+ <attr name="buttonTint" format="color" />
+ <!-- Blending mode used to apply the button graphic tint. -->
+ <attr name="buttonTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
</declare-styleable>
<declare-styleable name="CheckedTextView">
<!-- Indicates the initial checked state of this text. -->
@@ -3106,6 +3152,28 @@
<!-- Determines whether to measure all children or just those in
the VISIBLE or INVISIBLE state when measuring. Defaults to false. -->
<attr name="measureAllChildren" format="boolean" />
+ <!-- Tint to apply to the foreground. -->
+ <attr name="foregroundTint" format="color" />
+ <!-- Blending mode used to apply the foreground tint. -->
+ <attr name="foregroundTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
</declare-styleable>
<declare-styleable name="ExpandableListView">
<!-- Indicator shown beside the group View. This can be a stateful Drawable. -->
@@ -3211,6 +3279,10 @@
<!-- @hide The alpha value (0-255) set on the ImageView's drawable. Equivalent
to calling ImageView.setAlpha(int), not the same as View.setAlpha(float). -->
<attr name="drawableAlpha" format="integer" />
+ <!-- Tint to apply to the image. -->
+ <attr name="tint" />
+ <!-- Blending mode used to apply the image tint. -->
+ <attr name="tintMode" />
</declare-styleable>
<declare-styleable name="ToggleButton">
<!-- The text for the button when it is checked. -->
@@ -3401,6 +3473,98 @@
<!-- Defines if the associated drawables need to be mirrored when in RTL mode.
Default is false -->
<attr name="mirrorForRtl" format="boolean" />
+ <!-- Tint to apply to the progress indicator. -->
+ <attr name="progressTint" format="color" />
+ <!-- Blending mode used to apply the progress indicator tint. -->
+ <attr name="progressTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
+ <!-- Tint to apply to the progress indicator background. -->
+ <attr name="progressBackgroundTint" format="color" />
+ <!-- Blending mode used to apply the progress indicator background tint. -->
+ <attr name="progressBackgroundTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
+ <!-- Tint to apply to the secondary progress indicator. -->
+ <attr name="secondaryProgressTint" format="color" />
+ <!-- Blending mode used to apply the secondary progress indicator tint. -->
+ <attr name="secondaryProgressTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
+ <!-- Tint to apply to the indepterminate progress indicator. -->
+ <attr name="indeterminateTint" format="color" />
+ <!-- Blending mode used to apply the indeterminate progress indicator tint. -->
+ <attr name="indeterminateTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
+ <!-- Tint to apply to the background. -->
+ <attr name="backgroundTint" />
+ <!-- Blending mode used to apply the background tint. -->
+ <attr name="backgroundTintMode" />
</declare-styleable>
<declare-styleable name="SeekBar">
@@ -3410,6 +3574,28 @@
<attr name="thumbOffset" format="dimension" />
<!-- Whether to split the track and leave a gap for the thumb drawable. -->
<attr name="splitTrack" format="boolean" />
+ <!-- Tint to apply to the button graphic. -->
+ <attr name="thumbTint" format="color" />
+ <!-- Blending mode used to apply the button graphic tint. -->
+ <attr name="thumbTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
</declare-styleable>
<declare-styleable name="StackView">
@@ -4792,6 +4978,16 @@
<!-- ========================== -->
<eat-comment />
+ <!-- Drawable used to draw vector paths. -->
+ <declare-styleable name="VectorDrawable">
+ <!-- If set, specifies the color to apply to the drawable as a tint. By default,
+ no tint is applied. May be a color state list. -->
+ <attr name="tint" />
+ <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
+ default value is src_in, which treats the drawable as an alpha mask. -->
+ <attr name="tintMode" />
+ </declare-styleable>
+
<!-- Define the virtual size of the drawing surface paths will draw to. -->
<declare-styleable name="VectorDrawableViewport">
<!-- The width of the canvas the drawing is on. -->
@@ -5064,10 +5260,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>
<!-- ========================== -->
@@ -5206,6 +5410,9 @@
<enum name="floatType" value="0" />
<!-- valueFrom and valueTo are integers. -->
<enum name="intType" value="1" />
+ <!-- valueFrom and valueTo are paths defined as strings.
+ This type is used for path morphing in AnimatedVectorDrawable. -->
+ <enum name="pathType" value="2" />
</attr>
</declare-styleable>
@@ -5217,6 +5424,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>
@@ -6547,6 +6760,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 814d8fc0..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/colors.xml b/core/res/res/values/colors.xml
index 9f6c7ad..f304514 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -143,7 +143,7 @@
<color name="keyguard_avatar_nick_color">#ffffffff</color>
<color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color>
- <color name="accessibility_focus_highlight">#80ffff00</color>
+ <color name="accessibility_focus_highlight">#bf39b500</color>
</resources>
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 202c127..c3e4d94 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" />
@@ -2190,6 +2190,26 @@
<public type="attr" name="searchKeyphraseSupportedLocales" />
<public type="attr" name="windowTransitionBackgroundFadeDuration" />
<public type="attr" name="overlapAnchor" />
+ <public type="attr" name="progressTint" />
+ <public type="attr" name="progressTintMode" />
+ <public type="attr" name="progressBackgroundTint" />
+ <public type="attr" name="progressBackgroundTintMode" />
+ <public type="attr" name="secondaryProgressTint" />
+ <public type="attr" name="secondaryProgressTintMode" />
+ <public type="attr" name="indeterminateTint" />
+ <public type="attr" name="indeterminateTintMode" />
+ <public type="attr" name="backgroundTint" />
+ <public type="attr" name="backgroundTintMode" />
+ <public type="attr" name="foregroundTint" />
+ <public type="attr" name="foregroundTintMode" />
+ <public type="attr" name="buttonTint" />
+ <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" />
@@ -2441,5 +2461,26 @@
<!-- An interpolator which accelerates fast and keeps accelerating until the end. -->
<public type="interpolator" name="fast_out_linear_in" />
+ <!-- Used for Activity Transitions, this transition indicates that no Transition
+ should be used. -->
<public type="transition" name="no_transition" id="0x010f0000"/>
+ <!-- A transition that moves and resizes a view -->
+ <public type="transition" name="move"/>
+ <!-- A transition that fades views in and out. -->
+ <public type="transition" name="fade"/>
+ <!-- A transition that moves views in or out of the scene to or from the edges when
+ a view visibility changes. -->
+ <public type="transition" name="explode"/>
+ <!-- A transition that moves views in or out of the scene to or from the bottom edge when
+ a view visibility changes. -->
+ <public type="transition" name="slide_bottom"/>
+ <!-- A transition that moves views in or out of the scene to or from the top edge when
+ a view visibility changes. -->
+ <public type="transition" name="slide_top"/>
+ <!-- A transition that moves views in or out of the scene to or from the right edge when
+ a view visibility changes. -->
+ <public type="transition" name="slide_right"/>
+ <!-- A transition that moves views in or out of the scene to or from the left edge when
+ a view visibility changes. -->
+ <public type="transition" name="slide_left"/>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6e1b5ef..e017f53 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2971,6 +2971,13 @@
to your voicemail inbox.</string>
<!-- Title of an application permission, listed so the user can choose whether
+ they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+ <string name="permlab_readAllVoicemail">read all voicemail</string>
+ <!-- Description of an application permission, listed so the user can choose whether
+ they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_readAllVoicemail">Allows the app to read all your voicemails.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether
they want to allow the application to do this. -->
<string name="permlab_writeGeolocationPermissions">modify Browser geolocation permissions</string>
<!-- Description of an application permission, listed so the user can choose whether
@@ -4721,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>
@@ -4732,4 +4746,17 @@
<!-- DO NOT TRANSLATE -->
<string name="day_of_week_label_typeface">sans-serif</string>
+ <!-- Lock-to-app dialog title. -->
+ <string name="lock_to_app_title">Use lock-to-app?</string>
+ <!-- Lock-to-app dialog description. The $ is not actually shown or translated, it is a marker of where the recents icon shows up. -->
+ <string name="lock_to_app_description">Lock-to-app locks the display in a single app.\n\nTo exit press and hold the recent apps button $</string>
+ <!-- Lock-to-app negative response. -->
+ <string name="lock_to_app_negative">NO</string>
+ <!-- Lock-to-app positive response. -->
+ <string name="lock_to_app_positive">START</string>
+ <!-- Starting lock-to-app indication. -->
+ <string name="lock_to_app_start">Start Lock-to-app</string>
+ <!-- Exting lock-to-app indication. -->
+ <string name="lock_to_app_exit">Exit Lock-to-app</string>
+
</resources>
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..3e82d08 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" />
@@ -603,6 +604,14 @@
<java-symbol type="string" name="kilobyteShort" />
<java-symbol type="string" name="last_month" />
<java-symbol type="string" name="launchBrowserDefault" />
+ <java-symbol type="string" name="lock_to_app_title" />
+ <java-symbol type="string" name="lock_to_app_description" />
+ <java-symbol type="string" name="lock_to_app_negative" />
+ <java-symbol type="string" name="lock_to_app_positive" />
+ <java-symbol type="drawable" name="ic_recent" />
+ <java-symbol type="layout" name="lock_to_app_enter" />
+ <java-symbol type="layout" name="lock_to_app_exit" />
+ <java-symbol type="drawable" name="lock_task_notify_bg" />
<java-symbol type="string" name="lockscreen_access_pattern_cell_added" />
<java-symbol type="string" name="lockscreen_access_pattern_cleared" />
<java-symbol type="string" name="lockscreen_access_pattern_detected" />
@@ -863,6 +872,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 +1010,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/core/tests/coretests/src/android/net/IpPrefixTest.java b/core/tests/coretests/src/android/net/IpPrefixTest.java
new file mode 100644
index 0000000..cf278fb
--- /dev/null
+++ b/core/tests/coretests/src/android/net/IpPrefixTest.java
@@ -0,0 +1,281 @@
+/*
+ * 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.net;
+
+import android.net.IpPrefix;
+import android.os.Parcel;
+import static android.test.MoreAsserts.assertNotEqual;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.junit.Assert.assertArrayEquals;
+import java.net.InetAddress;
+import java.util.Random;
+import junit.framework.TestCase;
+
+
+public class IpPrefixTest extends TestCase {
+
+ // Explicitly cast everything to byte because "error: possible loss of precision".
+ private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4};
+ private static final byte[] IPV6_BYTES = {
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
+ (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0
+ };
+
+ @SmallTest
+ public void testConstructor() {
+ IpPrefix p;
+ try {
+ p = new IpPrefix((byte[]) null, 9);
+ fail("Expected NullPointerException: null byte array");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix((InetAddress) null, 10);
+ fail("Expected NullPointerException: null InetAddress");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix((String) null);
+ fail("Expected NullPointerException: null String");
+ } catch(RuntimeException expected) {}
+
+
+ try {
+ byte[] b2 = {1, 2, 3, 4, 5};
+ p = new IpPrefix(b2, 29);
+ fail("Expected IllegalArgumentException: invalid array length");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1.2.3.4");
+ fail("Expected IllegalArgumentException: no prefix length");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1.2.3.4/");
+ fail("Expected IllegalArgumentException: empty prefix length");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("foo/32");
+ fail("Expected IllegalArgumentException: invalid address");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1/32");
+ fail("Expected IllegalArgumentException: deprecated IPv4 format");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1.2.3.256/32");
+ fail("Expected IllegalArgumentException: invalid IPv4 address");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("foo/32");
+ fail("Expected IllegalArgumentException: non-address");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("f00:::/32");
+ fail("Expected IllegalArgumentException: invalid IPv6 address");
+ } catch(IllegalArgumentException expected) {}
+ }
+
+ public void testTruncation() {
+ IpPrefix p;
+
+ p = new IpPrefix(IPV4_BYTES, 32);
+ assertEquals("192.0.2.4/32", p.toString());
+
+ p = new IpPrefix(IPV4_BYTES, 29);
+ assertEquals("192.0.2.0/29", p.toString());
+
+ p = new IpPrefix(IPV4_BYTES, 8);
+ assertEquals("192.0.0.0/8", p.toString());
+
+ p = new IpPrefix(IPV4_BYTES, 0);
+ assertEquals("0.0.0.0/0", p.toString());
+
+ try {
+ p = new IpPrefix(IPV4_BYTES, 33);
+ fail("Expected IllegalArgumentException: invalid prefix length");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix(IPV4_BYTES, 128);
+ fail("Expected IllegalArgumentException: invalid prefix length");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix(IPV4_BYTES, -1);
+ fail("Expected IllegalArgumentException: negative prefix length");
+ } catch(RuntimeException expected) {}
+
+ p = new IpPrefix(IPV6_BYTES, 128);
+ assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 122);
+ assertEquals("2001:db8:dead:beef:f00::80/122", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 64);
+ assertEquals("2001:db8:dead:beef::/64", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 3);
+ assertEquals("2000::/3", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 0);
+ assertEquals("::/0", p.toString());
+
+ try {
+ p = new IpPrefix(IPV6_BYTES, -1);
+ fail("Expected IllegalArgumentException: negative prefix length");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix(IPV6_BYTES, 129);
+ fail("Expected IllegalArgumentException: negative prefix length");
+ } catch(RuntimeException expected) {}
+
+ }
+
+ private void assertAreEqual(Object o1, Object o2) {
+ assertTrue(o1.equals(o2));
+ assertTrue(o2.equals(o1));
+ }
+
+ private void assertAreNotEqual(Object o1, Object o2) {
+ assertFalse(o1.equals(o2));
+ assertFalse(o2.equals(o1));
+ }
+
+ @SmallTest
+ public void testEquals() {
+ IpPrefix p1, p2;
+
+ p1 = new IpPrefix("192.0.2.251/23");
+ p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23);
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("192.0.2.5/23");
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("192.0.2.5/24");
+ assertAreNotEqual(p1, p2);
+
+ p1 = new IpPrefix("192.0.4.5/23");
+ assertAreNotEqual(p1, p2);
+
+
+ p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122");
+ p2 = new IpPrefix(IPV6_BYTES, 122);
+ assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString());
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122");
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123");
+ assertAreNotEqual(p1, p2);
+
+ p1 = new IpPrefix("2001:db8:dead:beef::/122");
+ assertAreNotEqual(p1, p2);
+
+ // 192.0.2.4/32 != c000:0204::/32.
+ byte[] ipv6bytes = new byte[16];
+ System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length);
+ p1 = new IpPrefix(ipv6bytes, 32);
+ assertAreEqual(p1, new IpPrefix("c000:0204::/32"));
+
+ p2 = new IpPrefix(IPV4_BYTES, 32);
+ assertAreNotEqual(p1, p2);
+ }
+
+ @SmallTest
+ public void testHashCode() {
+ IpPrefix p;
+ int oldCode = -1;
+ Random random = new Random();
+ for (int i = 0; i < 100; i++) {
+ if (random.nextBoolean()) {
+ // IPv4.
+ byte[] b = new byte[4];
+ random.nextBytes(b);
+ p = new IpPrefix(b, random.nextInt(33));
+ assertNotEqual(oldCode, p.hashCode());
+ oldCode = p.hashCode();
+ } else {
+ // IPv6.
+ byte[] b = new byte[16];
+ random.nextBytes(b);
+ p = new IpPrefix(b, random.nextInt(129));
+ assertNotEqual(oldCode, p.hashCode());
+ oldCode = p.hashCode();
+ }
+ }
+ }
+
+ @SmallTest
+ public void testMappedAddressesAreBroken() {
+ // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress,
+ // we are unable to comprehend that.
+ byte[] ipv6bytes = {
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff,
+ (byte) 192, (byte) 0, (byte) 2, (byte) 0};
+ IpPrefix p = new IpPrefix(ipv6bytes, 120);
+ assertEquals(16, p.getRawAddress().length); // Fine.
+ assertArrayEquals(ipv6bytes, p.getRawAddress()); // Fine.
+
+ // Broken.
+ assertEquals("192.0.2.0/120", p.toString());
+ assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress());
+ }
+
+ public IpPrefix passThroughParcel(IpPrefix p) {
+ Parcel parcel = Parcel.obtain();
+ IpPrefix p2 = null;
+ try {
+ p.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ p2 = IpPrefix.CREATOR.createFromParcel(parcel);
+ } finally {
+ parcel.recycle();
+ }
+ assertNotNull(p2);
+ return p2;
+ }
+
+ public void assertParcelingIsLossless(IpPrefix p) {
+ IpPrefix p2 = passThroughParcel(p);
+ assertEquals(p, p2);
+ }
+
+ public void testParceling() {
+ IpPrefix p;
+
+ p = new IpPrefix("2001:4860:db8::/64");
+ assertParcelingIsLossless(p);
+
+ p = new IpPrefix("192.0.2.0/25");
+ assertParcelingIsLossless(p);
+ }
+}
diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java
index c80d0bf..af6a32b 100644
--- a/core/tests/coretests/src/android/net/RouteInfoTest.java
+++ b/core/tests/coretests/src/android/net/RouteInfoTest.java
@@ -19,7 +19,7 @@
import java.lang.reflect.Method;
import java.net.InetAddress;
-import android.net.LinkAddress;
+import android.net.IpPrefix;
import android.net.RouteInfo;
import android.os.Parcel;
@@ -32,9 +32,8 @@
return InetAddress.parseNumericAddress(addr);
}
- private LinkAddress Prefix(String prefix) {
- String[] parts = prefix.split("/");
- return new LinkAddress(Address(parts[0]), Integer.parseInt(parts[1]));
+ private IpPrefix Prefix(String prefix) {
+ return new IpPrefix(prefix);
}
@SmallTest
@@ -43,17 +42,17 @@
// Invalid input.
try {
- r = new RouteInfo((LinkAddress) null, null, "rmnet0");
+ r = new RouteInfo((IpPrefix) null, null, "rmnet0");
fail("Expected RuntimeException: destination and gateway null");
} catch(RuntimeException e) {}
// Null destination is default route.
- r = new RouteInfo((LinkAddress) null, Address("2001:db8::1"), null);
+ r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null);
assertEquals(Prefix("::/0"), r.getDestination());
assertEquals(Address("2001:db8::1"), r.getGateway());
assertNull(r.getInterface());
- r = new RouteInfo((LinkAddress) null, Address("192.0.2.1"), "wlan0");
+ r = new RouteInfo((IpPrefix) null, Address("192.0.2.1"), "wlan0");
assertEquals(Prefix("0.0.0.0/0"), r.getDestination());
assertEquals(Address("192.0.2.1"), r.getGateway());
assertEquals("wlan0", r.getInterface());
@@ -74,7 +73,7 @@
class PatchedRouteInfo {
private final RouteInfo mRouteInfo;
- public PatchedRouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
+ public PatchedRouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
mRouteInfo = new RouteInfo(destination, gateway, iface);
}
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
index 58b7db9..42de9ea 100644
--- a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
@@ -36,8 +36,7 @@
}
}
- private void setLocale(String code) {
- Locale locale = new Locale(code);
+ private void setLocale(Locale locale) {
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
@@ -268,193 +267,193 @@
*/
public void testMatrix100000() throws Throwable {
final int resId = R.integer.matrix_100000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 100);
}
public void testMatrix100001() throws Throwable {
final int resId = R.integer.matrix_100001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
public void testMatrix100010() throws Throwable {
final int resId = R.integer.matrix_100010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 500);
}
public void testMatrix100011() throws Throwable {
final int resId = R.integer.matrix_100011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
public void testMatrix100100() throws Throwable {
final int resId = R.integer.matrix_100100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix100101() throws Throwable {
final int resId = R.integer.matrix_100101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix100110() throws Throwable {
final int resId = R.integer.matrix_100110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix100111() throws Throwable {
final int resId = R.integer.matrix_100111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix101000() throws Throwable {
final int resId = R.integer.matrix_101000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 300);
}
public void testMatrix101001() throws Throwable {
final int resId = R.integer.matrix_101001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
public void testMatrix101010() throws Throwable {
final int resId = R.integer.matrix_101010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 500);
}
public void testMatrix101011() throws Throwable {
final int resId = R.integer.matrix_101011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
public void testMatrix101100() throws Throwable {
final int resId = R.integer.matrix_101100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix101101() throws Throwable {
final int resId = R.integer.matrix_101101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix101110() throws Throwable {
final int resId = R.integer.matrix_101110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix101111() throws Throwable {
final int resId = R.integer.matrix_101111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix110000() throws Throwable {
final int resId = R.integer.matrix_110000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix110001() throws Throwable {
final int resId = R.integer.matrix_110001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix110010() throws Throwable {
final int resId = R.integer.matrix_110010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix110011() throws Throwable {
final int resId = R.integer.matrix_110011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix110100() throws Throwable {
final int resId = R.integer.matrix_110100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix110101() throws Throwable {
final int resId = R.integer.matrix_110101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
public void testMatrix110110() throws Throwable {
final int resId = R.integer.matrix_110110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix110111() throws Throwable {
final int resId = R.integer.matrix_110111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
public void testMatrix111000() throws Throwable {
final int resId = R.integer.matrix_111000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix111001() throws Throwable {
final int resId = R.integer.matrix_111001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix111010() throws Throwable {
final int resId = R.integer.matrix_111010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix111011() throws Throwable {
final int resId = R.integer.matrix_111011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix111100() throws Throwable {
final int resId = R.integer.matrix_111100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix111101() throws Throwable {
final int resId = R.integer.matrix_111101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
public void testMatrix111110() throws Throwable {
final int resId = R.integer.matrix_111110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix111111() throws Throwable {
final int resId = R.integer.matrix_111111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
}
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 a9df00585..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/auth/api-client.jd b/docs/html/google/auth/api-client.jd
index 402a95f..5331d1e 100644
--- a/docs/html/google/auth/api-client.jd
+++ b/docs/html/google/auth/api-client.jd
@@ -1,4 +1,4 @@
-page.title=Accessing Google Play Services APIs
+page.title=Accessing Google APIs
page.tags="oauth 2.0","GoogleAuthUtil"
trainingnavtop=true
diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs
index 6ff00c0..b4028bd 100644
--- a/docs/html/google/google_toc.cs
+++ b/docs/html/google/google_toc.cs
@@ -3,11 +3,11 @@
######## ATTENTION ###############
######## ###############
#########################################################
-
+
IF YOU MAKE CHANGES TO THIS FILE, YOU MUST GENERATE THE
GMS REFERENCE DOCS, BECAUSE THEY ARE NOT INCLUDED IN THE
DOCS BUILD RULE.
-
+
#########################################################
#########################################################
?>
@@ -73,11 +73,11 @@
</div>
<ul>
<li><a href="<?cs var:toroot?>google/play-services/setup.html">
- <span class="en">Setup</span></a>
+ <span class="en">Setting Up Google Play Services</span></a>
</li>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot?>google/auth/api-client.html">
- <span class="en">Accessing Google Play Services APIs</span></a>
+ <span class="en">Accessing Google APIs</span></a>
</div>
<ul>
<li>
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/google/play-services/setup.jd b/docs/html/google/play-services/setup.jd
index 744e191..4332953 100644
--- a/docs/html/google/play-services/setup.jd
+++ b/docs/html/google/play-services/setup.jd
@@ -1,4 +1,4 @@
-page.title=Set Up Google Play Services SDK
+page.title=Setting Up Google Play Services
@jd:body
@@ -8,8 +8,7 @@
<h2>In this document</h2>
<ol>
- <li><a href="#Install">Install the Google Play Services SDK</a></li>
- <li><a href="#Setup">Set Up a Project that Uses Google Play Services</a></li>
+ <li><a href="#Setup">Add Google Play Services to Your Project</a></li>
<li><a href="#Proguard">Create a Proguard Exception</a></li>
<li><a href="#ensure">Ensure Devices Have the Google Play services APK</a></li>
</ol>
@@ -18,12 +17,17 @@
</div>
</div>
+<script>
+$(document).ready(function() {
+ setupIdeDocToggle();
+});
+</script>
-
+
<p>To develop an app using the <a href="{@docRoot}reference/gms-packages.html">Google
-Play services APIs</a>, you must download the Google Play services SDK
-from the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a>.
-The download includes the client library and code samples.</p>
+Play services APIs</a>, you need to set up your project with the Google Play services SDK.
+<p>If you haven't installed the Google Play services SDK yet, go get it now by following the guide
+to <a href="{@docRoot}sdk/installing/adding-packages.html">Adding SDK Packages</a>.</p>
<p>To test your app when using the Google Play services SDK, you must use either:</p>
<ul>
@@ -33,68 +37,25 @@
that runs the Google APIs platform based on Android 4.2.2 or higher.</li>
</ul>
-<p>Ideally, you should develop and test your app on a variety of devices, including
-both phones and tablets.</p>
-<h2 id="Install">Install the Google Play Services SDK</h2>
+<h2 id="Setup">Add Google Play Services to Your Project</h2>
-<p>To install the Google Play services SDK for development:</p>
+<p>
+<select class="ide">
+ <option value="eclipse">Using Eclipse with ADT</option>
+ <option value="studio">Using Android Studio</option>
+ <option value="other">Using something else</option>
+</select>
+</p>
+
+
+<div class="select-ide studio">
<ol>
- <li>Launch the SDK Manager in one of the following ways:
- <ul>
- <li>In Android Studio, click <strong>SDK Manager</strong>
-<img src="{@docRoot}images/tools/sdk-manager-studio.png" style="vertical-align:bottom;margin:0;height:19px" />
-in the toolbar.</li>
- <li>In Eclipse (with <a href="{@docRoot}tools/help/adt.html">ADT</a>),
- select <strong>Window</strong> > <strong>Android SDK Manager</strong>.</li>
- <li>On Windows, double-click the <code>SDK Manager.exe</code> file at the root of the Android
- SDK directory.</li>
- <li>On Mac or Linux, open a terminal and navigate to the <code>tools/</code> directory in the
- Android SDK, then execute <code>android sdk</code>.</li>
- </ul>
- </li>
- <li>Install the Google Play services SDK.
- <p>Scroll to the bottom of the package list, expand <b>Extras</b>, select
- <b>Google Play services</b>, and install it. If you're using Android Studio, also install
- <b>Google Repository</b> (it provides the Maven repository used for Gradle builds).</p>
- <p>The Google Play services SDK is saved in your Android SDK environment at
- <code><android-sdk>/extras/google/google_play_services/</code>.</p>
-
-<p class="note"><strong>Note:</strong> Google Play services 4.0.30 (released
-November 2013) and newer versions require Android 2.3 or higher. If your app supports Android 2.2,
-you can continue development with the Google Play services SDK, but must instead install
-<b>Google Play services for Froyo</b> from the SDK Manager.</p>
-
- </li>
- <li>Install a compatible version of the Google APIs platform.
- <p>If you want to test your app on the emulator, expand the directory for <b>Android 4.2.2
- (API 17)</b> or a higher version, select <b>Google APIs</b>, and install it. Then create a
- new <a href="{@docRoot}tools/devices/index.html">AVD</a> with Google APIs as
- the platform target.</p>
- </li>
- <li>Make a copy of the Google Play services library project.
- <p class="note"><strong>Note:</strong> If you are using Android Studio, skip this step.</p>
- <p>Copy the library project at
- <code><android-sdk>/extras/google/google_play_services/libproject/google-play-services_lib/</code>
- to the location where you maintain your Android app projects.
- <p>If you are using Eclipse, import the library project into your workspace.
- Click <b>File > Import</b>, select <b>Android > Existing
- Android Code into Workspace</b>, and browse to the copy of the library project to import it.</p>
- </li>
-</ol>
-
-
-
-<h2 id="Setup">Set Up a Project that Uses Google Play Services</h2>
-
-<p><b>Using Android Studio:</b></p>
-
-<ol>
- <li>Open the <code>build.gradle</code> file inside your application module directory.
- <p class="note"><strong>Note:</strong> Android Studio projects contain a top-level
- <code>build.gradle</code> file and a <code>build.gradle</code> file for each module.
+ <li>Open the <code>build.gradle</code> file inside your application module directory.
+ <p class="note"><strong>Note:</strong> Android Studio projects contain a top-level
+ <code>build.gradle</code> file and a <code>build.gradle</code> file for each module.
Be sure to edit the file for your application module. See
<a href="{@docRoot}sdk/installing/studio-build.html">Building Your Project with
Gradle</a> for more information about Gradle.</p></li>
@@ -128,8 +89,11 @@
<p>You can now begin developing features with the
<a href="{@docRoot}reference/gms-packages.html">Google Play services APIs</a>.</p>
+</div><!-- end studio -->
-<p><b>Using Eclipse or another IDE:</b></p>
+
+
+<div class="select-ide eclipse other">
<p>To make the Google Play services APIs available to your app, you must reference the library
project you created in step 4 of the <a href="#Install">installation instructions</a>.</p>
@@ -156,6 +120,9 @@
you can begin developing features with the
<a href="{@docRoot}reference/gms-packages.html">Google Play services APIs</a>.</p>
+</div><!-- end eclipse and other -->
+
+
<h2 id="Proguard">Create a Proguard Exception</h2>
@@ -236,5 +203,6 @@
that takes the user to Google Play Store to install the update.</p>
-<p>To then begin a connection to Google Play services, read <a
-href="{@docRoot}google/auth/api-client.html">Accessing Google Play Services APIs</a>.</p>
+<p>To then begin a connection to Google Play services (required by most Google APIs such
+as Google Drive, Google+, and Games), read <a
+href="{@docRoot}google/auth/api-client.html">Accessing Google APIs</a>.</p>
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/index.jd b/docs/html/index.jd
index 606baf8..2177352 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -1,6 +1,5 @@
fullpage=true
page.viewport_width=970
-no_footer_links=true
excludeFromSuggestions=true
page.metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers.
page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3f61-WpRguHq-aNjtF7xJjMTSi79as" />
@@ -12,13 +11,31 @@
<div class="fullscreen-carousel-content">
<div class="vcenter">
<div class="wrap clearfix">
- <div class="resource-widget resource-flow-layout wrap col-16"
+
+ <div class="static resource-flow-layout wrap col-16">
+ <div class="resource resource-card resource-card-18x6">
+ <div class="card-bg" style="background-image: url('/preview/images/l-dev-prev.png');"></div>
+ <div class="card-info">
+ <div class="section"></div>
+ <div class="title">Android L Developer Preview</div>
+ <div class="description ellipsis" style="height: 285px;">
+ <div class="text" style="height: auto;">
+ <p style="font-size:16px;">Get an early look at the next Android release and
+ start using new APIs so your apps are ready when the platform officially launches.</p>
+ <p>
+ <a href="{@docRoot}preview/index.html" class="landing-button landing-secondary">Learn more</a></p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="resource-widget resource-flow-layout wrap col-16 no-section"
data-query="collection:index/primary"
data-resourceStyle="card"
data-sortOrder="-timestamp"
- data-numStacks="3"
- data-maxResults="4"
- data-cardSizes="18x6,6x2,6x2,6x2">
+ data-maxResults="3"
+ data-cardSizes="6x2,6x2,6x2">
</div> <!-- end .resource-widget -->
</div> <!-- end .wrap -->
</div> <!-- end .vcenter -->
@@ -39,7 +56,7 @@
<div class="landing-rest-of-page">
- <div class="landing-section" style="background-color:#f5f5f5">
+ <div class="landing-section">
<div class="wrap">
<div class="landing-section-header">
<div class="landing-h1">Android, Everywhere You Need It</div>
@@ -56,7 +73,7 @@
<div class="col-3-wide">
<img src="" alt="">
- <p>Wear</p>
+ <p>Wearable</p>
<p class="landing-small">
Provide information on-the-go for your users, whenever they need it.
</p>
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index ca19c02..6564aa8 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -2,10 +2,9 @@
"index/primary": {
"title": "",
"resources": [
- "preview/index.html",
- "preview/material/index.html",
- "preview/material/index.html",
- "preview/material/index.html"
+ "distribute/essentials/quality/tablets.html",
+ "distribute/engage/game-services.html",
+ "distribute/googleplay/edu/about.html"
]
},
"index/devices": {
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 40618a3..8ec2470 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -15,17 +15,19 @@
<ol id="toc44" class="hide-nested">
<li><a href="#Behaviors">Important Behavior Changes</a>
<ol>
+ <li><a href="#ART">New Android Runtime (ART)</a></li>
<li><a href="#BehaviorNotifications">If your app implements notifications...</a></li>
- <li><a href="#BehaviorFullscreen">If your app uses fullScreenIntent...</a></li>
+ <li><a href="#BehaviorMediaControl">If your app uses RemoteControlClient...</a></li>
<li><a href="#BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</a></li>
</ol>
</li>
<li><a href="#UI">User Interface</a>
<ol>
<li><a href="#MaterialDesign">Material design support</a></li>
+ <li><a href="#DoNotDisturb">Do Not Disturb mode</a></li>
<li><a href="#LockscreenNotifications">Lockscreen notifications</a></li>
<li><a href="#NotificationsMetadata">Notifications metadata</a></li>
- <li><a href="#Recents">Concurrent documents and activities in Recents screen</a></li>
+ <li><a href="#Recents">Concurrent documents and activities in the Recents screen</a></li>
<li><a href="#WebView">WebView updates</a></li>
</ol>
</li>
@@ -41,7 +43,7 @@
</li>
<li><a href="#Multimedia">Multimedia</a>
<ol>
- <li><a href="#Camera-v2">Camera V2</a></li>
+ <li><a href="#Camera-v2">Camera v2 API</a></li>
<li><a href="#AudioPlayback">Audio playback</a></li>
<li><a href="#MediaPlaybackControl">Media playback control</a></li>
</ol>
@@ -55,23 +57,24 @@
<ol>
<li><a href="#Multinetwork">Dynamic network selection and seamless handoff</a></li>
<li><a href="#BluetoothBroadcasting">Bluetooth broadcasting</a></li>
- <li><a href="#NFCEnhancements">NFC enhancements for payments</a></li>
+ <li><a href="#NFCEnhancements">NFC enhancements</a></li>
</ol>
</li>
<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>
<ol>
- <li><a href="#PDFRender">PDF rendering</a></li>
+ <li><a href="#PDFRender">Render PDF as bitmap</a></li>
</ol>
</li>
<li><a href="#TestingA11y">Testing & Accessibility</a>
@@ -96,73 +99,148 @@
</div>
</div>
-<p>L is an upcoming release for the Android platform
-that offers new features for users and app developers. This document provides
-an introduction to the most notable new 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>L is currently available as a <strong>developer preview</strong> intended
-for early adopters and testers. If you are interested in influencing the
-direction of the Android framework,
+<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>You should not publish apps
-using L Developer Preview to the Google Play store.</p>
+<p class="caution"><strong>Caution:</strong> Do not not publish apps
+that use the L Developer Preview to the Google Play store.</p>
+
+<p class="note"><strong>Note:</strong> This document often refers to classes and
+methods that do not yet have reference material available on <a
+href="{@docRoot}">developer.android.com</a>. These API elements are
+formatted in {@code code style} in this document (without hyperlinks). For the
+preliminary API documentation for these elements, download the <a
+href="{@docRoot}preview/l-developer-preview-reference.zip">preview
+reference</a>.</p>
<h2 id="Behaviors">Important Behavior Changes</h2>
<p>If you have previously published an app for Android, be aware that your app
- might be affected by changes in L.</p>
+ might be affected by changes in the upcoming release.</p>
+
+<h3 id="ART">New Android Runtime (ART)</h3>
+
+<p>The 4.4 release introduced a new, experimental Android runtime, ART. Under
+4.4, ART was optional, and the default runtime remained Dalvik. With the L
+Developer Preview, ART is now the default runtime.</p>
+
+<p>For an overview of ART's new features, see
+<a href="https://source.android.com/devices/tech/dalvik/art.html">Introducing
+ART</a>. Some of the major new features are:</p>
+
+<ul>
+ <li>Ahead-of-Time (AOT) compilation</li>
+ <li>Improved garbage collection (GC)</li>
+ <li>Improved debugging support</li>
+</ul>
+
+<p>Most Android apps should just work without change under ART. However, some
+techniques that work on Dalvik do not work on ART. For information about the
+most important issues, see
+<a href="{@docRoot}guide/practices/verifying-apps-art.html">Verifying App
+Behavior on the Android Runtime (ART)</a>. Pay particular attention if:</p>
+
+<ul>
+ <li>Your app uses Java Native Interface (JNI) to run C/C++ code.</li>
+ <li>You use development tools that generate non-standard code (such as some
+ obfuscators).</li>
+ <li>You use techniques that are incompatible with compacting garbage
+ collection. (ART does not currently implement compacting GC, but
+ compacting GC is under development in the Android Open-Source
+ Project.)</li>
+</ul>
<h3 id="BehaviorNotifications">If your app implements notifications...</h3>
-<p>Notifications will be drawn with dark text atop white (or very light)
+<p>Notifications are drawn with dark text atop white (or very light)
backgrounds to match the new material design widgets. Make sure that all your
-notifications look right with the new color scheme. You should remove or update
-assets and text styles that involve color. The system will automatically invert
-action icons in notifications. Use
-{@code android.app.Notification.Builder.setColor()} to set an accent color
-in a circle behind your {@code Notification.icon} image.</p>
+notifications look right with the new color scheme:</p>
-<p>The system will ignore all non-alpha channels in action icons and the main
-notification icon, so you should assume that these icons will be alpha-only.
-</p>
+<div class="figure" style="width:220px">
+ <img src="images/hun-example.png"
+ srcset="images/[email protected] 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>
+
+ <li>The system automatically inverts action icons in notifications. Use
+ {@code android.app.Notification.Builder.setColor()} to set an accent color
+ in a circle behind your {@link android.app.Notification#icon} image.</li>
+
+ <li>The system ignores all non-alpha channels in action icons and the main
+ notification icon. You should assume that these icons are alpha-only.</li>
+
+</ul>
<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, make sure to remove this code so that
-the system can present notifications correctly in Do not disturb mode. You
-should use the {@link android.app.Notification.Builder} methods instead to add
-sounds and vibration.
-</p>
+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>
+
+<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 L will 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>You must call {@code Notification.Builder.setVisibility(Notification.VISIBILITY_PUBLIC)} to mark your media notification as safe to reveal, even when the lockscreen is secured
-with a PIN, pattern, or password.</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>
-<h3 id="BehaviorFullscreen">If your app uses fullScreenIntent...</h3>
+<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>Notifications now appear in a small floating window if all these conditions
-are met: the user’s activity is in fullscreen mode, the screen is on, and the
-device is unlocked. If your app implements fullscreen activities, make sure that
-these heads-up notifications are presented correctly.</p>
+<p>Call {@code
+Notification.Builder.setVisibility(Notification.VISIBILITY_PUBLIC)} to mark a
+notification as safe to show atop any lockscreen (secure or otherwise). For more information, see
+<a href="#LockscreenNotifications">Lockscreen Notifications</a>.</p>
<h3 id="BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</h3>
-<p>With the introduction of the new document tasks feature in L (see below),
-the {@code android.app.ActivityManager.getRecentTasks()} method is now
-deprecated to improve user privacy. For backwards
-compatibility, it will still return a small subset of its data including the
+<p>With the introduction of the new <em>concurrent documents and activities tasks</em> feature in the upcoming
+release (see <a href="#Recents">Concurrent documents and activities in Recents
+screen</a> below),
+the {@link android.app.ActivityManager#getRecentTasks
+ActivityManager.getRecentTasks()} method is now
+deprecated to improve user privacy. For backward
+compatibility, this method still returns a small subset of its data, including the
calling application’s own tasks and possibly some other non-sensitive tasks
-such as home. If your app is using this method to retrieve its own tasks,
+(such as Home). If your app is using this method to retrieve its own tasks,
use {@code android.app.ActivityManager.getAppTasks()} instead to retrieve that
information.</p>
@@ -170,11 +248,14 @@
<h3 id="MaterialDesign">Material design support</h3>
-<p>The L Developer Preview adds support for the material design style. You can create
-material design apps that are visually dynamic and have UI element transitions
-which feel natural and delightful to users. This support includes:</p>
+<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
+that feel natural to users. This support includes:</p>
+
<ul>
- <li>The Material theme</li>
+
+ <li>The material theme</li>
<li>View shadows</li>
<li>The {@code RecyclerView} widget</li>
<li>Drawable animation and styling effects</li>
@@ -182,8 +263,9 @@
<li>Animators for view properties based on the state of a view</li>
<li>Customizable UI widgets and app bars with color palettes that you control</li>
</ul>
+
<p>To learn more about adding material design functionality to your app, see
-<a href="{@docRoot}preview/material/index.html">Material design on Android</a>.</p>
+<a href="{@docRoot}preview/material/index.html">Material Design</a>.</p>
<h3 id="LockscreenNotifications">Lockscreen notifications</h3>
<p>Lockscreens in the L Developer Preview have the ability to present notifications.
@@ -194,29 +276,58 @@
displayed over the secure lockscreen. To control the visibility level, call
{@code android.app.Notification.Builder.setVisibility()} and specify one of these
values:</p>
+
<ul>
<li>{@code VISIBILITY_PRIVATE}. Shows basic information, such as the
-notification’s icon, but hides the notification’s full content. If you want to
-provide a redacted public version of your notification for the system to display
-on a secure lockscreen, set the public notification object in the <code>publicVersion</code>
-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
+the user puts the device in <em>Do Not Disturb</em> mode, the device limits
+the frequency of the notifications it shows the user (when the user
+wants to avoid distractions). The user can
+customize the feature in a number of ways, such as:</p>
+
+<ul>
+ <li>Specifying important people, whose calls should go through even when
+ the device is in <em>Do Not Disturb</em> mode.</li>
+ <li>Setting custom categories to allow notifications when the device is in
+ <em>Do Not Disturb</em> mode. Examples of such categories include phone
+ calls and direct communications (like Hangouts and Skype calls).</li>
+ <li>Setting rules so <em>Do Not Disturb</em> automatically goes into effect in
+ certain conditions (like at particular times of day).</li>
+</ul>
+
+<p>You should add the appropriate metadata to your app notifications to help
+make sure <em>Do Not Disturb</em> mode handles them properly. For example, if
+your app is an alarm clock,
+you can tag the notification as an alarm so it will wake the user up even if the
+device is in <em>Do Not Disturb</em> mode. For more information, see <a
+href="#NotificationsMetadata">Notifications metadata</a>.</p>
+
<h3 id="NotificationsMetadata">Notifications metadata</h3>
<p>The L Developer Preview uses metadata associated with your app notifications
-to more intelligently sort your notifications. The metadata you set also
+to sort the notifications more intelligently. The metadata you set also
controls how the system presents your app notifications when the user is in <em>Do
-not disturb</em> mode. When constructing your notification, you can call the
-following methods in {@code android.app.Notification.Builder}:</p>
+Not Disturb</em> mode. To set the metadata, call the following methods in
+{@code android.app.Notification.Builder} when you construct the
+notification:</p>
<ul>
-<li>{@code setCategory()}. Allows the system to handle your app notifications
-in <em>Do not disturb mode</em> (for example, if your notification represents an
-incoming call, instant message, or alarm).</li>
+<li>{@code setCategory()}. Depending on the message category, this tells
+the system how to handle your app notifications when the device is
+in <em>Do Not Disturb</em> mode (for example, if your notification represents an
+incoming call, instant message, or alarm).
<li>{@code setPriority()}. Notifications with the priority field set to
{@code PRIORITY_MAX} or {@code PRIORITY_HIGH} will appear in a small floating
window if the notification also has sound or vibration.</li>
@@ -231,30 +342,35 @@
<p>In previous releases, the
<a href="{@docRoot}design/get-started/ui-overview.html">Recents screen</a>
could only display a single task for each app that the user interacted with
-most recently. The L Developer Preview allows your app to open additional tasks
-for concurrent activities or documents. This feature facilitates multitasking
+most recently. The L Developer Preview enables your app to open more tasks as
+needed for additional concurrent activities for documents.
+This feature facilitates multitasking
by letting users quickly switch between individual activities and documents
-from the Recents screen. Examples of such concurrent tasks might include web
-pages in a browser app, documents in a productivity app, concurrent matches in
+from the Recents screen, with a consistent switching experience across all apps.
+Examples of such concurrent tasks might include open tabs in a web
+browser app, documents in a productivity app, concurrent matches in
a game, or chats in a messaging app. Your app can manage its tasks
through the {@code android.app.ActivityManager.AppTask} class.</p>
<p>To insert a logical break so that the system treats your activity as a new
-document, use {@code android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT} when
+task, use {@code android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT} when
launching the activity with {@link android.app.Activity#startActivity(android.content.Intent) startActivity()}. You can also get this behavior by declaring the
<a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a>
attribute {@code documentLaunchMode="intoExisting"} or {@code ="always"} in your
manifest.</p>
<p>You can also mark that a task should be removed from the Recents screen
-when all its activities are closed by using {@code android.content.Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} when starting the root activity for
+when all its activities are closed. To do this, use {@code
+android.content.Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} when starting the
+root activity for
the task. You can also set this behavior for an activity by declaring the
<a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a>
attribute {@code autoRemoveFromRecents=“true”} in your manifest.</p>
<p>To avoid cluttering the Recents screen, you can set the maximum number of
-tasks from your app that can appear in the Recents screen through the
-<a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a> attribute {@code android:maxRecent}. The current maximum that can be specified
+tasks from your app that can appear in that screen. To do this, set the
+<a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a>
+attribute {@code android:maxRecent}. The current maximum that can be specified
is 100 tasks per user.</a></p>
<h3 id="WebView">WebView updates</h3>
@@ -274,16 +390,20 @@
<h3 id="IME">IME bug fixes and improvements</h3>
<p>Beginning in the L Developer Preview, users can more easily switch between
-all input method editors (IME) <a href="{@docRoot}guide/topics/text/creating-input-method.html">supported by the platform</a>. Performing the designated
+all <a href="{@docRoot}guide/topics/text/creating-input-method.html">input
+method editors (IME)</a> supported by the platform. Performing the designated
switching action (usually touching a Globe icon on the soft keyboard) will cycle
among all such IMEs. This change takes place in
-{@code android.view.inputmethod.InputMethodManager.shouldOfferSwitchingToNextInputMethod()}.</p>
+{@link android.view.inputmethod.InputMethodManager#shouldOfferSwitchingToNextInputMethod
+InputMethodManager.shouldOfferSwitchingToNextInputMethod()}.</p>
-<p>In addition, the framework will now check whether the next IME includes a
-switching mechanism at all, thus supporting switching to the IME after it. An
+<p>In addition, the framework now checks whether the next IME includes a
+switching mechanism at all (and, thus, whether that IME supports switching to
+the IME after it). An
IME with a switching mechanism will not cycle to an IME without one. This
change takes place in
-{@code android.view.inputmethod.InputMethodManager.switchToNextInputMethod()}.
+{@link android.view.inputmethod.InputMethodManager#switchToNextInputMethod
+InputMethodManager.switchToNextInputMethod}.
<p>To see an example of how to use the updated IME-switching APIs, refer to the
updated soft-keyboard implementation sample in this release.</p>
@@ -314,17 +434,20 @@
</manifest>
</pre>
-<p>For more information about using OpenGL ES, including how to check the device’s supported OpenGL ES version at runtime, see the <a href="{@docRoot}/guide/topics/graphics/opengl.html">OpenGL ES API guide</a>.</p>
+<p>For more information about using OpenGL ES, including how to check the device’s supported OpenGL ES version at runtime, see the <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL ES API guide</a>.</p>
<h2 id="Multimedia">Multimedia</h2>
-<h3 id="Camera=v2">Camera v2 API</h3>
+<h3 id="Camera-v2">Camera v2 API</h3>
<p>The L Developer Preview introduces the new {@code android.hardware.camera2}
-API to facilitate fine grain photo capture and image processing. You can now programmatically access the camera devices available to the system with {@code CameraManager.getCameraIdList()} and connect to a specific device with {@code CameraManager.openCamera()}. To start capturing images, you
-need to create a {@code CameraCaptureSession} and specify the
-{@link android.view.Surface} objects to send the captured images. The {@code CameraCaptureSession} can be configured to take single shots or multiple images
-in a burst.</p>
+API to facilitate fine-grain photo capture and image processing. You can now
+programmatically access the camera devices available to the system with {@code
+CameraManager.getCameraIdList()} and connect to a specific device with {@code
+CameraManager.openCamera()}. To start capturing images, create a {@code
+CameraCaptureSession} and specify the {@link android.view.Surface} objects for
+the captured images. The {@code CameraCaptureSession} can be configured to take
+single shots or multiple images in a burst.</p>
<p>To be notified when new images are captured, implement the
{@code CameraCaptureSession.CaptureListener()} interface and set it in your
@@ -334,16 +457,17 @@
{@code CaptureResult}.</p>
<h3 id="AudioPlayback">Audio playback</h3>
-<p>This release includes the following changes for
- {@code android.media.AudioTrack}:</p>
+<p>This release includes the following changes to
+ {@link android.media.AudioTrack}:</p>
<ul>
<li>Your app can now supply audio data in floating-point format
({@code android.media.AudioFormat.ENCODING_PCM_FLOAT}). This permits greater
dynamic range, more consistent precision, and greater headroom. Floating-point arithmetic is especially useful during intermediate calculations. Playback
-end-points use integer format for audio data, and with lower bit-depth. In L
-Developer Preview, portions of the internal pipeline are not yet floating-point.
- <li>Your app can now supply audio data as a {@code ByteBuffer}, in the same
-format as provided by {@code MediaCodec}.
+end-points use integer format for audio data, and with lower bit-depth. (In the
+L Developer Preview, portions of the internal pipeline are not yet
+floating-point.)
+ <li>Your app can now supply audio data as a {@link java.nio.ByteBuffer}, in the same
+format as provided by {@link android.media.MediaCodec}.
<li>The {@code WRITE_NON_BLOCKING} option can simplify buffering and
multithreading for some apps.
</ul>
@@ -352,8 +476,8 @@
<p>You can now build your own media controller app with the new
{@code android.media.session.MediaController} class, which provides
simplified transport controls APIs that replace those in
-{@code android.media.RemoteControlClient}. The {@code MediaController} class
-allows thread-safe control of playback from a non UI process, making it easier
+{@link android.media.RemoteControlClient}. The {@code MediaController} class
+allows thread-safe control of playback from a non-UI process, making it easier
to control your media playback service from your app’s user interface.
<p>You can also create multiple controllers to send playback commands,
@@ -362,65 +486,85 @@
call {@code MediaSession.getSessionToken()} to request an access
token in order for your app to interact with the session.</p>
-<p>Send transport commands such as "play", "stop", "skip", and
+<p>You can now send transport commands such as "play", "stop", "skip", and
"set rating" by using {@code MediaController.TransportControls}. To handle
-in-bound media transport commands from controllers attached to the session, you
-should override the callback methods in
+in-bound media transport commands from controllers attached to the session,
+override the callback methods in
{@code MediaSession.TransportControlsCallback}.</p>
<p>You can also create rich notifications that allow playback control tied to a
-media session with the new {@code android.app.Notification.MediaStyle} class.</p>
+media session with the new {@code android.app.Notification.MediaStyle} class. By
+using the new notification and media APIs, you will ensure that the System UI
+knows about your playback and can extract and show album art.</p>
<h2 id="Storage">Storage</h2>
<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
-{@code android.content.Context.getExternalMediaDirs()} method. No additional
+applications can store media files, call the new
+{@code android.content.Context.getExternalMediaDirs()} method. No
+additional
permissions are needed by your app to read or write to the returned paths.
-External storage devices here are those considered by the system to be a
+In this context, "external storage devices" are those devices which the system
+considers to be a
permanent part of the device, and includes emulated external storage and
physical media slots such as SD cards in battery compartments.</p>
+<p>You can bring up a system UI to allow the user to pick a directory subtree.
+To do so, send {@code android.intent.action.OPEN_DOCUMENT_TREE} in an
+{@link android.content.Intent}. If the call is successful, the system displays
+the {@link android.provider.DocumentsProvider} instances installed on the
+device for the user to select. When the user selects a directory from this UI,
+the system returns a URI representing the selected directory tree.</p>
+
<p>If you want to access a document in an existing directory, call the
-{@code android.provider.DocumentsContract.buildDocumentViaUri()} method and pass
-in a Uri representing the path to the parent directory and the target document
-ID. The method returns a new {@link android.net.Uri} with which your app can
+{@code android.provider.DocumentsContract.buildDocumentViaUri()} method.
+Pass the method a URI representing the path to the parent directory, and the
+target document
+ID. The method returns a new {@link android.net.Uri} which your app can
use to write media content with {@code DocumentsContract.createDocument()}.
<h2 id="Wireless">Wireless & Connectivity</h2>
<h3 id="Multinetwork">Dynamic network selection and seamless handoff</h3>
-<p>The L Developer Preview provides new multi-networking APIs for your app to
+<p>The L Developer Preview provides new multi-networking APIs. These let your app
dynamically scan for available networks with specific capabilities, and
establish a connection to them. This is useful when your app requires a
specialized network, such as an SUPL, MMS, or carrier-billing network, or if
you want to send data using a particular type of transport protocol.</p>
-<p>To select and connect to a network dynamically from your app, first
-instantiate a {@code android.net.ConnectivityManager}. Next, create a
-{@code android.net.NetworkRequest} to specify the network features and transport
-type your app is interested in. To start scanning for suitable networks, call
-{@code ConnectivityManager.requestNetwork()} or
-{@code ConnectivityManager.registerNetworkCallback(), and pass in the
-{@code NetworkRequest} object and an implementation of
-{@code ConnectivityManager.NetworkCallbackListener}.</p>
+<p>To select and connect to a network dynamically from your app follow these
+steps:</p>
+
+<ol>
+ <li>Create a {@link android.net.ConnectivityManager}.</li>
+ <li>Create a
+ {@code android.net.NetworkRequest} to specify the network features and transport
+ type your app is interested in.</li>
+ <li>To scan for suitable networks, call
+ {@code ConnectivityManager.requestNetwork()} or
+ {@code ConnectivityManager.registerNetworkCallback()}, and pass in the
+ {@code NetworkRequest} object and an implementation of
+ {@code ConnectivityManager.NetworkCallbackListener}.</li>
+
+</ol>
<p>When the system detects a suitable network, it connects to the network and
invokes the {@code NetworkCallbackListener.onAvailable()} callback. You can use
the {@code android.net.Network} object from the callback to get additional
-information about the network, or to establish a socket connection.</p>
+information about the network, or to direct traffic to use the selected
+network.</p>
<h3 id="BluetoothBroadcasting">Bluetooth broadcasting</h3>
<p>Android 4.3 introduced platform support for <a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html">Bluetooth Low Energy</a>
(BLE) in the central role. In the L Developer Preview, an Android device can now
-act as a Bluetooth LE <em>peripheral device</em> and make its presence known to
+act as a Bluetooth LE <em>peripheral device</em>. Apps can use this capability
+to make their presence known to
nearby devices. For instance, you can build apps that allow a device to
function as a pedometer or health monitor and communicate its data with another
BLE device.</p>
@@ -429,16 +573,19 @@
You must add the {@code android.permission.BLUETOOTH_ADMIN} permission in your
manifest in order for your app to use the new advertising and scanning features.</a>
-<p>To begin Bluetooth LE advertising so that other devices can discover the
-device running your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvisertising()} and pass in an implementation of the
-{@code android.bluetooth.le.AdvertiseCallback} class to report the success
-or failure of the advertising operation.</p>
+<p>To begin Bluetooth LE advertising so that other devices can discover
+your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvisertising()}
+and pass in an implementation of the
+{@code android.bluetooth.le.AdvertiseCallback} class. The callback object
+receives a report of the success or failure of the advertising operation.</p>
-<p>Conversely, if you want to scan for Bluetooth LE devices nearby, call
-{@code android.bluetooth.le.BluetoothLeScanner.startScan()} and pass in an
+<p> The L Developer Preview introduces the {@code
+android.bluetooth.le.ScanFilter} class so that your app can scan for only the
+specific types of devices it is interested in. To begin scanning for Bluetooth
+LE devices, call {@code android.bluetooth.le.BluetoothLeScanner.startScan()} and
+pass in a list of filters. In the method call, you must also provide an
implementation of {@code android.bluetooth.le.ScanCallback} to report if a
-Bluetooth LE advertisement is found. Optionally, you can pass in filters to scan
-for a specific type of device.</p>
+Bluetooth LE advertisement is found. </p>
<h3 id="NFCEnhancements">NFC enhancements</h3>
<p>The L Developer Preview adds these enhancements to enable wider and more
@@ -446,13 +593,12 @@
<ul>
<li>Android Beam is now available in the share menu.
-<li>Support for the <a href="http://www.wi-fi.org/discover-wi-fi/wi-fi-direct">Wi-fi Direct standard</a>.
<li>Your app can invoke the Android Beam on the user’s device to share data by
calling {@code android.nfc.NfcAdapter.invokeBeam()}. This avoids the need for
the user to manually tap the device against another NFC-capable device to
complete the data transfer.
-<li>Use the new {@code android.nfc.NdefRecord.createTextRecord()} method if
- you want to create an NDEF record containing UTF-8 text data.
+<li>You can use the new {@code android.nfc.NdefRecord.createTextRecord()} method
+to create an NDEF record containing UTF-8 text data.
<li>If you are developing a payment app, you now have the ability to
register an NFC application ID (AID) dynamically by calling
{@code android.nfc.cardemulation.CardEmulation.registerAidsForService()}.
@@ -466,18 +612,32 @@
<h3 id="JobScheduler">Scheduling jobs</h3>
<p>The L Developer Preview provides a new {@code android.app.job.JobScheduler}
API that lets you optimize battery life by defining jobs for the system to run
-asynchronously at a later time, such as when the device is charging. This is
-useful when you want to defer non user-facing units of work, have application
-code that accesses the network, or want to run a number of tasks as a batch on
-a regular schedule.</p>
+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 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>
-<p>A {@code android.app.job.JobInfo} object encapsulates such a unit of work,
-and provides an exact description of the criteria you are scheduling.</p>
+</ul>
+
+<p>A unit of work is encapsulated by a {@code android.app.job.JobInfo} object.
+This object provides an exact description of the criteria to be used for
+scheduling.</p>
<p>Use the {@code android.app.job.JobInfo.Builder} to configure how the
scheduled task should run. You can schedule the task to run under specific
-conditions such as only while the device is charging, when connected to an
-unmetered network, or when the system deems the device is idle.</p>
+conditions, such as:</p>
+
+<ul>
+ <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
unmetered network:</p>
@@ -492,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>
@@ -513,23 +673,33 @@
</ul>
<p>Use the {@code --help} option to learn about the various options for
-tailoring the output. For example, to run the tool to print battery usage
-statistics since the device was last charged for a given app package, run this
+tailoring the output. For example, to print battery usage
+statistics for a given app package since the device was last charged, run this
command:
<pre>
-$ adb shell dumpsys batterystats --charged <package-name>
+$ adb shell dumpsys batterystats --charged <package-name>
</pre>
</dd>
<dt><strong>Battery Historian</strong></dt>
<dd>
-<p>The Battery Historian tool ({@code historian.par}) analyzes L-based Android
-bug reports and creates an HTML visualization of power-related events. It can
-also visualize power consumption data from a power monitor, and will attempt to
-map power usage to the wakelocks seen. You can find the Battery Historian tool
+<p>The Battery Historian tool ({@code historian.par}) analyzes Android
+bug reports from the L Developer Preview and creates an HTML visualization of
+power-related events. It can
+also visualize power consumption data from a power monitor, and attempts to
+map power usage to the wake locks seen. You can find the Battery Historian tool
in {@code <sdk>/tools}.</p>
-<p>For best results, you should first enable full wakelock reporting to allow
+<img src="images/battery_historian.png"
+ srcset="images/[email protected] 2x"
+ alt="" width="440" height="240"
+ id="figure2" />
+<p class="img-caption">
+ <strong>Figure 2.</strong>HTML visualization generated by the Battery
+ Historian tool.
+</p>
+
+<p>For best results, you should first enable full wake lock reporting, to allow
the Battery Historian tool to monitor uninterrupted over an extended period of
time:</p>
<pre>
@@ -548,93 +718,100 @@
</pre>
</dd>
-<dt><strong>On-device power management</strong></dt>
-<dd>
-<p>You can use the {@code android.os.BatteryManager} API to obtain power
-consumption information based on the battery fuel gauge included in Android
-phones and tablets. This is useful in cases when it is not convenient to
-connect external measurement equipment to the Android device.</p>
-<p>To retrieve the battery properties, call {@code BatteryManager.getIntProperty()}
-or {@code BatteryManager.getLongProperty()}. The properties available, the
-exact resolution of the values of each, and other characteristics such as
-update frequency depend on the particular device being tested.</p>
-
-<p>The following properties can be inspected on all Android devices:</p>
-
-<table>
- <tr>
- <th>Property</th>
- <th>Description</th>
- </tr>
- <tr>
- <td>{@code BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER}</td>
- <td>Remaining battery capacity in microampere-hours.</td>
- </tr>
- <tr>
- <td>{@code BatteryManager.BATTERY_PROPERTY_CURRENT_NOW}</td>
- <td>Instantaneous battery current in microamperes.</td>
- </tr>
- <tr>
- <td>{@code BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE}</td>
- <td>Average battery current in microamperes</td>
- </tr>
- <tr>
- <td>{@code BatteryManager.BATTERY_PROPERTY_CAPACITY}</td>
- <td>Remaining battery capacity as an integer percentage.</td>
- </tr>
- <tr>
- <td>{@code BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER}</td>
- <td>Remaining energy in nanowatt-hours.</td>
- </tr>
-</table>
-<dd>
</dl>
<h2 id="Enterprise">Enterprise</h2>
<h3 id="ManagedProvisioning">Managed provisioning</h3>
+<div class="figure" style="width:360px">
+ <img src="images/managed_apps_launcher.png"
+ srcset="images/[email protected] 2x"
+ alt="" width="360" height="572" id="figure3" />
+ <p class="img-caption">
+ <strong>Figure 3.</strong> Launcher screen showing managed apps (marked with
+ a lock badge)
+ </p>
+</div>
+
<p>The L Developer Preview provides new functionality for running apps within
an enterprise environment:</p>
<ul>
<li><strong>Create managed user profiles</strong>. A device administrator can
-initiate a managed provisioning process to enroll a user device with an
-existing personal account into a co-present but separate managed profile that
-the administrator controls.
-<li><strong>Set device owner scope</strong>. Device administrators can also
-apply managed provisioning to configure a device that has no previous user
-accounts installed, so that they have full control over the device.
+initiate a managed provisioning process to add a co-present but separate managed
+profile to a device with an existing personal account. The administrator has
+control over the managed profile.</li>
+<li><strong>Set device owner</strong>. Device administrators can also initiate a
+managed provisioning process to automatically provision a
+currently-unprovisioned device such that they have full control over the
+device.</li>
</ul>
-<p>To start the manged provisioning process, send
-{@code ACTION_PROVISION_MANAGED_PROFILE} in an {@link android.content.Intent}. A
-user may be associated with more than one managed profile. To get a list of the
-managed profiles associated with the user, call
-{@code android.os.UserManager.getUserProfiles()}.</p>
+<p>To start the managed provisioning process, send {@code
+ACTION_PROVISION_MANAGED_PROFILE} in an {@link android.content.Intent}. If the
+call is successful, the system triggers the {@code
+android.app.admin.DeviceAdminReceiver. onProfileProvisioningComplete()} callback.
+You can then call {@code app.admin.DevicePolicyManager. setProfileEnabled()} to
+set this profile to the enabled state.</p>
+
+<p>A user may be associated with more than one managed profile. To get a list of
+the managed profiles associated with the user, call
+{@code android.os.UserManager. getUserProfiles()}.</p>
<p>Once a managed profile is created for a user, apps that are managed by the
device administrator will appear alongside non-managed apps in the user’s
-Launcher, Recent apps screen, and notifications. A device policy management app
-can make the managed apps visually prominent by appending a “work” badge to the
-icon drawable with {@code android.os.UserManager.getBadgeDrawableForUser()}.</p>
+Launcher, Recent apps screen, and notifications.</p>
-<p>If you are developing a Launcher app, you can use the new {@code android.content.pm.LauncherApps} class to get a list of launchable activities for the current user
-and any associated managed profiles.</p>
+<p>If you are developing a Launcher app, you can use the new {@code
+android.content.pm.LauncherApps} class to get a list of launchable activities
+for the current user and any associated managed profiles. Your Launcher can make
+the managed apps visually prominent by appending a “work” badge to the icon
+drawable with {@code android.os.UserManager.getBadgeDrawableForUser()}.</p>
+
+<h3 id="LockToAppMode">Lock-to-App mode</h3>
+<p>The L Developer Preview introduces a new <em>Lock-to-App</em> mode that
+lets you temporarily restrict users from leaving your app or being interrupted
+by notifications. Once your app activates this mode, users will not be able to
+see notifications, access other apps, or return to the Home screen, until your
+app exits the mode.</p>
+
+<p>To prevent unauthorized usage, the device on which you want to activate
+this mode must have managed profiles or must be fully controlled by a device administrator (see <a href="#ManagedProvisioning">Managed Provisioning</a> for more information). Furthermore, the device or managed profile owner must
+authorize apps to use this mode by calling {@code android.app.admin.DevicePolicyManager.setLockTaskComponents()}.</p>
+
+<p>Before activating this mode in your app, verify that your activity is authorized by calling {@code DevicePolicyManager.isLockTaskPermitted()}.</p>
+
+<p>To activate <em>Lock-to-App</em> mode, call
+{@code android.app.Activity.startLockTask()} from your authorized activity.</p>
+
+<p>When <em>Lock-to-App</em> mode is active, the following behavior takes
+effect:</p>
+
+<ul>
+<li>The status bar is blank, and user notifications and status information is hidden.</li>
+<li>The Home and Recent Apps button is hidden.</li>
+<li>Other apps may not launch new activities.</li>
+<li>The current app may start new activities, as long as doing so does not
+create new tasks.</li>
+</ul>
+
+<p>The device will remain in this mode until an authorized activity calls
+{@code Activity.stopLockTask()}.
<h2 id="Printing">Printing Framework</h2>
<h3 id="PDFRender">Render PDF as bitmap</h3>
<p>You can now render PDF document pages into bitmap images for printing by
using the new {@code android.graphics.pdf.PdfRenderer} class. You must specify a
-{@code ParcelFileDescriptor} that is seekable (that is, the file can be randomly
+{@link android.os.ParcelFileDescriptor} that is seekable (that is, the content can be randomly
accessed) on which the system writes the the printable content. Your app can
obtain a page for rendering with {@code openPage()}, then call {@code render()}
to turn the opened {@code PdfRenderer.Page} into a bitmap. You can also set
-additional parameters if you only wan 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>
+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">tiled rendering</a> in order to zoom in on the document).</p>
<h2 id="TestingA11y">Testing & Accessibility </h2>
-<h3 id="Testing A11yImprovements">Testing and accessibility improvements</h3>
+<h3 id="TestingA11yImprovements">Testing and accessibility improvements</h3>
<p>The L Developer Preview adds the following support for testing and
accessibility:</p>
@@ -644,44 +821,44 @@
capture frame statistics for window animations and content. This lets you
write instrumentation tests to evaluate if the app under test is rendering
frames at a sufficient refresh frequency to provide a smooth user experience.
+
<li>You can execute shell commands from your instrumentation test with the new
{@code android.app.UiAutomation.executeShellCommand()}. The command execution
-is similar to running 'adb shell' from a host connected to the device. This
+is similar to running {@code adb shell} from a host connected to the device. This
allows you to use shell based tools such as {@code dumpsys}, {@code am},
{@code content}, and {@code pm}.
+
<li>Accessibility services and test tools that use the accessibility APIs
-(such as <a href="{@docRoot}tools/help/uiautomator/index.html">UiAutomator</a>)
+(such as <a href="{@docRoot}tools/help/uiautomator/index.html">uiautomator</a>)
can now retrieve detailed information about the properties of windows on the
screen that sighted users can interact with. To retrieve a list of
-{@code android.view.accessibility.AccessibilityWindowInfo} representing the
+{@code android.view.accessibility.AccessibilityWindowInfo} objects
+representing the
windows information, call the new
{@code android.accessibilityservice.AccessibilityService.getWindows()} method.
<li>You can use the new {@code android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction} to define standard or customized
-actions to perform on an {@code android.view.accessibility.AccessibilityNodeInfo}.
+actions to perform on an {@link android.view.accessibility.AccessibilityNodeInfo}.
The new {@code AccessibilityAction} class replaces the actions-related APIs
previously found in {@code AccessibilityNodeInfo}.
</ul>
-<h2 id="manifest">Manifest Declarations</h2>
+<h2 id="Manifest">Manifest Declarations</h2>
<h3 id="ManifestFeatures">Declarable required features</h3>
-<p>The following values are now supported in the <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a> element so you
+<p>The following values are now supported in the <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a> element, so you
can ensure that your app is installed only on devices that provide the features
your app needs.</p>
<ul>
-<li>{@code FEATURE_LEANBACK}. Declares that your app must be installed only on devices that support the <a href="{@docRoot}tv}">Android TV</a> user interface. Example:
+<li>{@code FEATURE_LEANBACK}. Declares that your app must be installed only on
+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" />
</pre>
-<li>{@code FEATURE_MANAGEDPROFILES}. Declares that your app must only be installed on devices that support managed profiles for enterprise users. Example:
-<pre>
-<uses-feature android:name="android.software.managedprofiles"
- android:required="true" />
-</pre>
-<li>{@code FEATURE_WEBVIEW}. Declares that your app must only be installed on devices that fully implement the android.webkit.* APIs. Example:
+<li>{@code FEATURE_WEBVIEW}. Declares that your app must only be installed on
+devices that fully implement the {@code android.webkit.*} APIs. Example:
<pre>
<uses-feature android:name="android.software.webview"
android:required="true" />
diff --git a/docs/html/preview/images/battery_historian.png b/docs/html/preview/images/battery_historian.png
new file mode 100644
index 0000000..5b0db74
--- /dev/null
+++ b/docs/html/preview/images/battery_historian.png
Binary files differ
diff --git a/docs/html/preview/images/[email protected] b/docs/html/preview/images/[email protected]
new file mode 100644
index 0000000..dbb5d5e
--- /dev/null
+++ b/docs/html/preview/images/[email protected]
Binary files differ
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/[email protected] b/docs/html/preview/images/[email protected]
new file mode 100644
index 0000000..3cb8f5b
--- /dev/null
+++ b/docs/html/preview/images/[email protected]
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..eae6ede
--- /dev/null
+++ b/docs/html/preview/images/l-dev-prev.png
Binary files differ
diff --git a/docs/html/preview/images/managed_apps_launcher.png b/docs/html/preview/images/managed_apps_launcher.png
new file mode 100644
index 0000000..983d904
--- /dev/null
+++ b/docs/html/preview/images/managed_apps_launcher.png
Binary files differ
diff --git a/docs/html/preview/images/[email protected] b/docs/html/preview/images/[email protected]
new file mode 100644
index 0000000..d298fd2
--- /dev/null
+++ b/docs/html/preview/images/[email protected]
Binary files differ
diff --git a/docs/html/preview/index.html b/docs/html/preview/index.html
new file mode 100644
index 0000000..be2d360
--- /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:0px 0 0 40px" width="860px"/>
+ <div class="col-6" style="margin-left:660px; margin-top:-105px">
+ <a href="/preview/setup-sdk.html" class="landing-button landing-secondary" style="position:absolute;z-index:100;float:right;margin-top: 0px;">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:-135px; 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
+ <b>material design</b>, 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 New 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 control over where notifications appear,
+ how they look, and how they sync to non-handheld devices.
+ </p>
+ <p class="landing-small">
+ <a href="/preview/api-overview.html#UI">Learn about notifications</a>
+ </p>
+ </div>
+ <div class="col-4">
+ <p>Increased Efficiency</p>
+ <p class="landing-small">
+ <b>Project Volta</b> is our effort to make the platform energy efficient and
+ to give you more control over resource usage.
+ </p>
+ <p class="landing-small">
+ <a href="/preview/api-overview.html#Power">Learn about Project Volta</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 84a2d85..0000000
--- a/docs/html/preview/index.jd
+++ /dev/null
@@ -1,250 +0,0 @@
-page.title=Android L Developer Preview
-page.viewport_width=970
-fullpage=true
-no_footer_links=true
-page.type=about
-
-@jd:body
-
-<style>
-.fullpage>#footer,
-#jd-content>.content-footer.wrap {
- display:none;
-}
-</style>
-
-<style>
-#footer {
- display: none;
-}
-.content-footer {
- display: none;
-}
-</style>
-
-<div id="video-container">
- <div id="video-frame">
- <div class="video-close">
- <span id="icon-video-close"> </span>
- </div>
- <script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
- <div id="ytapiplayer">
- <a href="http://www.youtube.com/watch?v=0xQ3y902DEQ"><img width=940
- src="https://i1.ytimg.com/vi/0xQ3y902DEQ/maxresdefault.jpg"></a><!--You need Flash player 8+ and JavaScript enabled to view this video. -->
- </div>
- </div>
-</div>
-
-<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
- <b>Material</b>, the new Google design standard.
- </p>
- <p class="landing-small">
- <a href="/preview/quantum/index.html">Learn about</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 more</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#graphics">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#multimedia.html">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>Submit Bugs</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!">
- Submit Bugs</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>Discuss on 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">
- Socialize 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>Get Updates</p>
- <p class="landing-small">
- Updates to the L Developer Preview are delivered
- in the Android SDK Manager. Check back here
- for news about the changes.
- </p>
- <p class="landing-small">
- <a target="_blank" href="{@docRoot}preview/release-notes.html">See Release Notes</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/preview/material/animations.jd b/docs/html/preview/material/animations.jd
index 8297c65..cee782a 100644
--- a/docs/html/preview/material/animations.jd
+++ b/docs/html/preview/material/animations.jd
@@ -2,9 +2,22 @@
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#touch">Touch Feedback</a></li>
+ <li><a href="#reveal">Reveal Effect</a></li>
+ <li><a href="#transitions">Activity Transitions</a></li>
+ <li><a href="#curvedmotion">Curved Motion</a></li>
+ <li><a href="#viewstate">View State Changes</a></li>
+ <li><a href="#drawabletint">Drawable Tinting</a></li>
+</ol>
+</div>
+</div>
<p>Animations in material design give users feedback on their actions and provide visual
-continuity as users interact with your app. The Material theme provides some default animations
+continuity as users interact with your app. The material theme provides some default animations
for buttons and activity transitions, and the Android L Developer Preview provides additional
APIs that let you customize these animations and create new ones:</p>
@@ -17,7 +30,7 @@
</ul>
-<h2 style="margin-top:35px">Touch Feedback</h2>
+<h2 id="touch">Touch Feedback</h2>
<p>In the Android L Developer Preview the default touch feedback animations for buttons use the new
<code>RippleDrawable</code> class, which transitions between different states with a ripple
@@ -28,7 +41,7 @@
using the <code>ripple</code> element.</p>
-<h2 style="margin-top:35px">Reveal Effect</h2>
+<h2 id="reveal">Reveal Effect</h2>
<p>The <code>View.createRevealAnimator</code> method enables you to animate a clipping circle
to reveal or hide a view.</p>
@@ -82,7 +95,7 @@
</pre>
-<h2 style="margin-top:35px">Activity Transitions</h2>
+<h2 id="transitions">Activity Transitions</h2>
<p>The Android L Developer Preview enables your app to customize the default animations for
activity transitions. You can specify custom animations for enter and exit transitions and for
@@ -109,10 +122,10 @@
<strong>Figure 1</strong> - A scene transition with one shared element.
</p>
-<h3 style="margin-top:30px">Specify custom transitions</h3>
+<h3>Specify custom transitions</h3>
<p>First, enable window content transitions with the <code>android:windowContentTransitions</code>
-attribute when you define a style that inherits from the Material theme:</p>
+attribute when you define a style that inherits from the material theme:</p>
<pre>
<style name="BaseAppTheme" parent="android:Theme.Material">
@@ -174,14 +187,14 @@
<li><code>Window.setSharedElementExitTransition</code></li>
</ul>
-<h3 style="margin-top:30px">Start an activity using transitions</h3>
+<h3>Start an activity using transitions</h3>
<p>If you enable transitions and set an exit transition for an activity, the transition is activated
when you launch another activity with the <code>startActivity</code> method. If you have set an
enter transition for the second activity, the transition is also activated when the activity
starts.</p>
-<h3 style="margin-top:30px">Shared elements transitions</h3>
+<h3>Shared elements transitions</h3>
<p>To make a screne transition animation between two activities that have a shared element:</p>
@@ -219,7 +232,7 @@
<p>For shared dynamic views that you generate in your code, use the <code>View.setViewName</code>
method to specify a common element name in both activities.</p>
-<h3 style="margin-top:30px">Multiple shared elements</h3>
+<h3>Multiple shared elements</h3>
<p>To make a scene transition animation between two activities that have more than one shared
element, define the shared elements in both layouts with the <code>android:viewName</code>
@@ -237,7 +250,7 @@
</pre>
-<h2 style="margin-top:35px">Curved Motion</h2>
+<h2 id="curvedmotion">Curved Motion</h2>
<p>Animations in material design rely on curves for time interpolation and spatial movement
patterns. The Android L Developer Preview provides new APIs that enable you to define custom
@@ -280,7 +293,7 @@
</pre>
-<h2 style="margin-top:35px">View State Changes</h2>
+<h2 id="viewstate">View State Changes</h2>
<p>The new <code>StateListAnimator</code> class lets you define animators that run when the state
of a view changes. The following example shows how to define an <code>StateListAnimator</code> as
@@ -343,7 +356,7 @@
</pre>
-<h2 style="margin-top:35px">Drawable Tinting</h2>
+<h2 id="drawabletint">Drawable Tinting</h2>
<p>The Android L Developer Preview enables you to define bitmaps as an alpha mask and to tint
them using a color resource or a theme attribute that resolves to a color resource. You can
diff --git a/docs/html/preview/material/compatibility.jd b/docs/html/preview/material/compatibility.jd
index b5555ad..ce04e9e 100644
--- a/docs/html/preview/material/compatibility.jd
+++ b/docs/html/preview/material/compatibility.jd
@@ -2,22 +2,34 @@
@jd:body
-<p>The new material design features (like the Material theme and custom animations) are only
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#materialtheme">Material Theme</a></li>
+ <li><a href="#layouts">Layouts</a></li>
+ <li><a href="#widgets">UI Widgets</a></li>
+ <li><a href="#animation">Animation APIs</a></li>
+</ol>
+</div>
+</div>
+
+<p>The new material design features (like the material theme and custom animations) are only
available in the Android L Developer Preview. However, you can design your apps to make use of
these features when running on devices with the Android L Developer Preview and still be
compatible with previous releases of Android.</p>
-<h2 style="margin-top:35px">Material Theme</h2>
+<h2 id="materialtheme">Material Theme</h2>
-<p>The Material theme is only available in the Android L Developer Preview. To configure your
-app to use the Material theme on devices running the Android L Developer Preview and an older
+<p>The material theme is only available in the Android L Developer Preview. To configure your
+app to use the material theme on devices running the Android L Developer Preview and an older
theme on devices running earlier versions of Android:</p>
<ol>
<li>Define a theme that inherits from an older theme (like Holo) in
<code>res/values/styles.xml</code>.</li>
-<li>Define a theme with the same name that inherits from the Material theme in
+<li>Define a theme with the same name that inherits from the material theme in
<code>res/values-v21/styles.xml</code>.</li>
<li>Set this theme as your app's theme in the manifest file.</li>
</ol>
@@ -26,7 +38,7 @@
your app will not run on earlier versions of Android.</p>
-<h2 style="margin-top:35px">Layouts</h2>
+<h2 id="layouts">Layouts</h2>
<p>If the layouts that you design according to the material design guidelines do not use any
of the new XML attributes from the Android L Developer Preview, they will work on previous
@@ -38,13 +50,13 @@
Alternative layouts have the same file name.</p>
-<h2 style="margin-top:35px">UI Widgets</h2>
+<h2 id="widgets">UI Widgets</h2>
<p>The <code>RecyclerView</code> and <code>CardView</code> widgets are included in the Android L
Developer Preview Support Library, so they are available in earlier versions of Android.</p>
-<h2 style="margin-top:35px">Animation APIs</h2>
+<h2 id="animation">Animation APIs</h2>
<p>The new APIs for custom animations are only available in the Android L Developer Preview. To
preserve compatibility with earlier verisons of Android, check the system version at runtime before
diff --git a/docs/html/preview/material/get-started.jd b/docs/html/preview/material/get-started.jd
index c527550..9c0e55d 100644
--- a/docs/html/preview/material/get-started.jd
+++ b/docs/html/preview/material/get-started.jd
@@ -2,135 +2,145 @@
@jd:body
-<p>To create material design apps on Android:</p>
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#applytheme">Apply the Material Theme</a></li>
+ <li><a href="#layouts">Design Your Layouts</a></li>
+ <li><a href="#depth">Specify Depth in Your Views</a></li>
+ <li><a href="#widgets">Use the New UI Widgets</a></li>
+ <li><a href="#apis">Use the New APIs</a></li>
+</ol>
+</div>
+</div>
+
+<p>To create apps with material design:</p>
<ol>
- <li>Take a look at the <a href="">material design specification</a>.</li>
- <li>Apply the Material <strong>theme</strong> to your app.</li>
- <li>Define additional <strong>styles</strong> to customize the Material theme.</li>
- <li>Create your <strong>layouts</strong> following material design guidelines.</li>
- <li>Specify the <strong>depth</strong> of each component in your layouts to cast appropriate shadows.</li>
- <li>Use the new <strong>widgets</strong> for complex views, such as lists and cards.</li>
- <li>Use the new <strong>APIs</strong> to customize 3D views and animations in your app.</li>
+ <li style="margin-bottom:10px">
+ Take a look at the <a href="">material design specification</a>.</li>
+ <li style="margin-bottom:10px">
+ Apply the material <strong>theme</strong> to your app.</li>
+ <li style="margin-bottom:10px">
+ Define additional <strong>styles</strong> to customize the material theme.</li>
+ <li style="margin-bottom:10px">
+ Create your <strong>layouts</strong> following material design guidelines.</li>
+ <li style="margin-bottom:10px">
+ Specify the <strong>depth</strong> for views to cast appropriate shadows.</li>
+ <li style="margin-bottom:10px">
+ Use the new <strong>widgets</strong> for complex views, such as lists and cards.</li>
+ <li style="margin-bottom:10px">
+ Use the new <strong>APIs</strong> to customize the animations in your app.</li>
</ol>
-<h3 style="margin-top:25px;">Update Your App for the Android L Developer Preview</h3>
+<h3>Update Your App for the Android L Developer Preview</h3>
<p>To update an existing app for the Android L Developer Preview, design new layouts following
material design guidelines and consider how you can improve the user experience for your app by
incorporating depth, touch feedback and animations in your UI.</p>
-<h3 style="margin-top:25px;">Create New Apps for the Android L Developer Preview</h3>
+<h3>Create New Apps for the Android L Developer Preview</h3>
<p>If you are creating a new app for the Android L Developer Preview, the material design
guidelines provide you with a solid design framework for your app. Follow these guidelines and
use the new functionality in the Android framework to design and develop your app.</p>
-<h2 style="margin-top:35px">Material Theme</h2>
+<h2 id="applytheme">Apply the Material Theme</h2>
-<div style="float:right;margin-left:25px">
-<img src="{@docRoot}preview/material/images/ThemeColors.png" style="width:250px"/>
-<p class="img-caption"><strong>Figure 1.</strong> Customizing the Material theme.</p>
-</div>
-
-<p>The new Material theme provides:</p>
-
-<ul>
- <li>System widgets that let you set their color palette</li>
- <li>Touch feedback animations for the system widgets</li>
- <li>Activity transition animations</li>
-</ul>
-
-<p>The Android L Developer Preview lets you easily customize the look of the Material theme
-according to your brand identity with a color palette you control. You can tint the app bar and
-the status bar using theme attributes, as shown in Figure 1.</p>
-
-<p>The system widgets have a new design and touch feedback animations. Activity transitions help
-users navigate your app by providing visual continuity. You can customize the color palette,
-the touch feedback animations, and the activity transitions for your app.</p>
-
-<p>The Material theme is defined as:</p>
-
-<ul>
- <li><code>@android:style/Theme.Material</code> (dark version)</li>
- <li><code>@android:style/Theme.Material.Light</code> (light version)</li>
- <li><code>@android:style/Theme.Material.Light.DarkActionBar</code></li>
-</ul>
-
-<p>For a list of material styles that you can use, see the API reference for
-<code>android.R.styles</code>.</p>
-
-<p class="note">
-<strong>Note:</strong> The Material theme is only available in the Android L Developer Preview.
-For more information, see <a href="{@docRoot}preview/material/compatibility.html">Compatibility</a>.
-</p>
-
-<h3 style="margin-top:25px;">Theme Inheritance</h3>
-
-<p>In the Android L Developer Preview, elements in XML layout definitions can specify the
-<code>android:theme</code> attribute, which references a theme resource. This attribute modifies
-the theme for the element and any elements inflated below it, which is useful to alter theme
-color palettes in a specific portion of an interface.</p>
-
-<h3 style="margin-top:25px;">Customize the Status Bar</h3>
-
-<p>The Material theme lets you easily customize the status bar, so you can specify a
-color which fits your brand and provides enough contrast to show the white status icons. To
-set a custom color for the status bar, use the <code>android:statusBarColor</code> attribute when
-you extend the Material theme.</p>
-
-<p>To handle the color of the status bar yourself (for example, by adding a gradient in the
-background), set the <code>android:statusBarColor</code> attribute to
-<code>@android:color/transparent</code>. You can also use the
-<code>Window.setStatusBarColor</code> method for animations or fading.</p>
-
-
-<h2 style="margin-top:35px">Material Design</h2>
-
-<p>In addition to applying the Material theme, you also have to:</p>
-
-<ul>
- <li>Customize the theme's base colors to fit your brand.</li>
- <li>Design your layouts according to material design guidelines.</li>
-</ul>
-
-<p>The Android L Developer Preview provides new attributes to make it easy to customize the
-Material theme:</p>
+<p>To apply the material theme in your app, specify a style that inherits from
+<code>android:theme.Material</code>:</p>
<pre>
+<!-- res/values/styles.xml -->
<resources>
- <!-- inherit from the Material theme -->
- <style name="BaseAppTheme" parent="android:Theme.Material">
- <!-- Main theme colors -->
- <!-- your app's branding color (for the app bar) -->
- <item name="android:colorPrimary">@color/primary</item>
- <!-- darker variant of colorPrimary (for contextual app bars) -->
- <item name="android:colorPrimaryDark">@color/primary_dark</item>
-
- <!-- other theme colors -->
- <item name="android:colorBackground">@color/background</item>
- <item name="android:colorAccent">@color/accent</item>
- <item name="android:colorButtonNormal">@color/button_normal</item>
- <item name="android:colorControlHighlight">@color/button_chigh</item>
- <item name="android:windowBackground">@color/wbackground</item>
+ <!-- your app's theme inherits from the Material theme -->
+ <style name="AppTheme" parent="android:Theme.Material">
+ <!-- theme customizations -->
</style>
</resources>
</pre>
-<p>Ensure that you follow material design guidelines when choosing colors for your app.</p>
+<p>The material theme provides new system widgets that let you set their color palette and default
+animations for touch feedback and activity transitions. For more details, see
+<a href="{@docRoot}preview/material/theme.html">Material Theme</a>.</p>
-<p>Design your layouts according to the material design specification. In particular, pay
-attention to:</p>
+
+<h2 id="layouts">Design Your Layouts</h2>
+
+<p>In addition to applying and customizing the material theme, your layouts should conform to
+the material design guidelines. When you design your layouts, pay special attention to the
+following:</p>
<ul>
- <li>Baseline grids</li>
- <li>Keylines</li>
- <li>Spacing</li>
- <li>Touch target size</li>
- <li>Layout structure</li>
+<li>Baseline grids</li>
+<li>Keylines</li>
+<li>Spacing</li>
+<li>Touch target size</li>
+<li>Layout structure</li>
</ul>
<p>You still define layouts inside XML files using the standard tools from the Android framework.
-To specify the depth level of each view in your layout, use the <code>android:elevation</code>
-attribute.</p>
\ No newline at end of file
+For details on the material design guidelines, see the <a href="">material design
+specification</a>.</p>
+
+
+<h2 id="depth">Specify Depth in Your Views</h2>
+
+<p>In the Android L Developer Preview, views can cast shadows. The elevation value of a view
+determines the size of its shadow. To set the elevation of a view, use the
+<code>android:elevation</code> attribute in your layouts:</p>
+
+<pre>
+<Button
+ android:id="@+id/my_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/next"
+ <strong>android:elevation</strong>="10dp" />
+</pre>
+
+<p>For more details, see <a href="{@docRoot}preview/material/views-shadows.html">Views and
+Shadows</a>.</p>
+
+
+<h2 id="widgets">Use the New UI Widgets</h2>
+
+<p>The Android L Developer Preview includes two new UI widgets for complex views,
+<code>RecyclerView</code> and <code>CardView</code>. <code>RecyclerView</code> is a more advanced
+version of <code>ListView</code> that provides performance improvements and is easier to use.
+<code>CardView</code> lets you show pieces of information inside cards with a consistent look
+across apps. To include a <code>CardView</code> in your layout:</p>
+
+<pre>
+<android.support.v7.widget.CardView
+ android:id="@+id/card_view"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ card_view:cardCornerRadius="3dp">
+ ...
+</android.support.v7.widget.CardView>
+</pre>
+
+<p>For more information, see <a href="{@docRoot}preview/material/ui-widgets.html">UI Widgets</a>.</p>
+
+
+<h2 id="apis">Use the APIs to Customize Your Animations</h2>
+
+<p>The Android L Developer Preview includes new APIs to create custom animations in your app.
+For example, you can enable activity transitions and define an exit transition inside an
+activity:</p>
+
+<pre>
+// inside your activity
+getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+
+// set an exit transition
+getWindow().setExitTransition(new Explode());
+</pre>
+
+<p>When you start another activity from this activity, the exit transition is activated.</p>
+
+<p>To learn about all the features in the new APIs, see <a
+href="{@docRoot}preview/material/animations.html">Animations</a>.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/index.jd b/docs/html/preview/material/index.jd
index 468e4bd..b7abcb4 100644
--- a/docs/html/preview/material/index.jd
+++ b/docs/html/preview/material/index.jd
@@ -19,9 +19,9 @@
</ul>
-<h3 style="margin-top:30px">Material Theme</h3>
+<h3>Material Theme</h3>
-<p>The Material theme provides a new style for your app, system widgets that let you set
+<p>The material theme provides a new style for your app, system widgets that let you set
their color palette, and default animations for touch feedback and activity transitions.</p>
<!-- two columns -->
@@ -42,7 +42,7 @@
</div>
-<h3 style="margin-top:30px">New Widgets</h3>
+<h3>New Widgets</h3>
<p>The Android L Developer Preview includes two new widgets for displaying complex views:</p>
@@ -50,8 +50,8 @@
<div style="width:700px;margin-top:25px;margin-bottom:20px">
<div style="float:left;width:250px;margin-left:40px;margin-right:60px;">
<img src="{@docRoot}preview/material/images/list_mail.png" style="width:250px;"/>
- <p>The new <code>RecyclerView</code> widget is a container for large sets of views that can be
- recycled and scrolled very efficiently.</p>
+ <p>The new <code>RecyclerView</code> widget is a more advanced version of <code>ListView</code>
+ provides performance improvements for dynamic views and is easier to use.</p>
</div>
<div style="float:left;width:250px;margin-right:0px;">
<img src="{@docRoot}preview/material/images/card_travel.png" style="width:250px;"/>
@@ -62,21 +62,13 @@
</div>
-<h3 style="margin-top:30px">3D Views and Shadows</h3>
+<h3>View Shadows</h3>
<p>In addition to the X and Y components, views in the Android L Developer Preview have a Z
component. This new component represents the elevation of a view, which determines the size of
its shadow: views with higher Z values cast bigger shadows.</p>
-
-<h3 style="margin-top:30px">Animations</h3>
-
-<p>The Android L Developer Preview provides new APIs that let you create custom animations for
-touch feedback in UI controls, view state changes, and activity transitions.</p>
-
-<!-- two columns -->
-<div style="width:700px;margin-left:12px;margin-top:25px;margin-bottom:5px">
-<div style="float:left;width:340px;margin-left:0px;margin-right:0px;">
+<div style="width:290px;margin-left:35px;float:right">
<div class="framed-nexus5-port-span-5">
<video class="play-on-hover" autoplay>
<source src="/preview/material/videos/ContactsAnim.mp4"/>
@@ -84,52 +76,41 @@
<source src="/preview/material/videos/ContactsAnim.ogv"/>
</video>
</div>
-</div>
-<div style="float:left;width:340px;margin-right:0px;">
- <div class="framed-nexus5-port-span-5">
- <video class="play-on-hover" autoplay>
- <source src="/preview/material/videos/Dial.mp4"/>
- <source src="/preview/material/videos/Dial.webm"/>
- <source src="/preview/material/videos/Dial.ogv"/>
- </video>
+ <div style="font-size:10pt;margin-left:20px;margin-bottom:30px">
+ <em>Click on the device screen to replay the movie</em>
</div>
</div>
-<br style="clear:left"/>
-</div>
-<div style="text-align:center;font-size:10pt;margin-right:35px">
-<em>Click on the device screen to replay the movie</em>
-</div>
+<h3>Animations</h3>
-<!-- three columns -->
-<div style="width:700px;margin-top:25px;margin-bottom:0px">
-<div style="float:left;width:200px;margin-left:0px;margin-right:0px;">
- <p>Respond to touch events in your views with <strong>touch feedback</strong> animations.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Hide and show views with <strong>reveal effect</strong> animations.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Switch between activities with custom <strong>activity transition</strong> animations.</p>
-</div>
-<br style="clear:left"/>
-</div>
-<!-- three columns -->
-<div style="width:700px;margin-top:0px;margin-bottom:20px">
-<div style="float:left;width:200px;margin-left:0px;margin-right:0px;">
- <p>Create custom animation patterns with <strong>curved motion</strong>.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Animate changes in one or more view properties with <strong>view state change</strong> animations.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Show animations in <strong>state list drawables</strong> between view state changes.</p>
-</div>
-<br style="clear:left"/>
-</div>
+<p>The Android L Developer Preview provides new APIs that let you create custom animations for
+touch feedback in UI controls, view state changes, and activity transitions.</p>
+
+<p>The new animation APIs in the Android L Developer Preview let you:</p>
+
+<ul>
+<li style="margin-bottom:15px">
+Respond to touch events in your views with <strong>touch feedback</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Hide and show views with <strong>reveal effect</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Switch between activities with custom <strong>activity transition</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Create custom animation patterns with <strong>curved motion</strong>.
+</li>
+<li style="margin-bottom:15px">
+Animate changes in one or more view properties with <strong>view state change</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Show animations in <strong>state list drawables</strong> between view state changes.
+</li>
+</ul>
-<h3 style="margin-top:30px">New Capabilities for Drawables</h3>
+<h3>New Capabilities for Drawables</h3>
<p>The Android L Developer Preview supports <strong>drawable tinting</strong>: you can define
bitmaps as an alpha mask and tint them using a color resource. You can create these assets only
diff --git a/docs/html/preview/material/theme.jd b/docs/html/preview/material/theme.jd
new file mode 100644
index 0000000..b954960
--- /dev/null
+++ b/docs/html/preview/material/theme.jd
@@ -0,0 +1,100 @@
+page.title=Material Theme
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#colorpalette">Customize the Colot Palette</a></li>
+ <li><a href="#statusbar">Customize the Status Bar</a></li>
+ <li><a href="#inheritance">Theme Inheritance</a></li>
+</ol>
+</div>
+</div>
+
+<p>The new material theme provides:</p>
+
+<ul>
+ <li>System widgets that let you set their color palette</li>
+ <li>Touch feedback animations for the system widgets</li>
+ <li>Activity transition animations</li>
+</ul>
+
+<p>The Android L Developer Preview lets you easily customize the look of the material theme
+according to your brand identity with a color palette you control. You can tint the app bar and
+the status bar using theme attributes, as shown in Figure 1.</p>
+
+<div style="float:right;margin-left:25px;margin-top:-25px">
+<img src="{@docRoot}preview/material/images/ThemeColors.png" style="width:250px"/>
+<p class="img-caption"><strong>Figure 1.</strong> Customizing the material theme.</p>
+</div>
+
+<p>The system widgets have a new design and touch feedback animations. Activity transitions help
+users navigate your app by providing visual continuity. You can customize the color palette,
+the touch feedback animations, and the activity transitions for your app.</p>
+
+<p>The material theme is defined as:</p>
+
+<ul>
+ <li><code>@android:style/Theme.Material</code> (dark version)</li>
+ <li><code>@android:style/Theme.Material.Light</code> (light version)</li>
+ <li><code>@android:style/Theme.Material.Light.DarkActionBar</code></li>
+</ul>
+
+<p>For a list of material styles that you can use, see the API reference for
+<code>android.R.styles</code>.</p>
+
+<p class="note">
+<strong>Note:</strong> The material theme is only available in the Android L Developer Preview.
+For more information, see <a href="{@docRoot}preview/material/compatibility.html">Compatibility</a>.
+</p>
+
+
+<h2 id="colorpalette">Customize the Color Palette</h2>
+
+<p>To customize the theme's base colors to fit your brand, define your custom colors using
+theme attributes when you inherit from the material theme:</p>
+
+<pre>
+<resources>
+ <!-- inherit from the material theme -->
+ <style name="AppTheme" parent="android:Theme.Material">
+ <!-- Main theme colors -->
+ <!-- your app's branding color (for the app bar) -->
+ <item name="android:colorPrimary">@color/primary</item>
+ <!-- darker variant of colorPrimary (for contextual app bars) -->
+ <item name="android:colorPrimaryDark">@color/primary_dark</item>
+
+ <!-- other theme colors -->
+ <item name="android:colorButtonNormal">@color/button_normal</item>
+ <item name="android:windowBackground">@color/wbackground</item>
+ </style>
+</resources>
+</pre>
+
+
+<h2 id="statusbar">Customize the Status Bar</h2>
+
+<p>The material theme lets you easily customize the status bar, so you can specify a
+color which fits your brand and provides enough contrast to show the white status icons. To
+set a custom color for the status bar, use the <code>android:statusBarColor</code> attribute when
+you extend the material theme.</p>
+
+<p>To handle the color of the status bar yourself (for example, by adding a gradient in the
+background), set the <code>android:statusBarColor</code> attribute to
+<code>@android:color/transparent</code>. You can also use the
+<code>Window.setStatusBarColor</code> method for animations or fading.</p>
+
+<p class="note"><strong>Note:</strong>
+The status bar should almost always have a clear delineation from the primary toolbar, except for
+full-bleed imagery cases and when you use a gradient as a protection.
+</p>
+
+
+<h2 id="inheritance">Theme Inheritance</h3>
+
+<p>In the Android L Developer Preview, elements in XML layout definitions can specify the
+<code>android:theme</code> attribute, which references a theme resource. This attribute modifies
+the theme for the element and any elements inflated below it, which is useful to alter theme
+color palettes in a specific portion of an interface.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/ui-widgets.jd b/docs/html/preview/material/ui-widgets.jd
index 5c12a1a..f18bff9 100644
--- a/docs/html/preview/material/ui-widgets.jd
+++ b/docs/html/preview/material/ui-widgets.jd
@@ -2,13 +2,22 @@
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#recyclerview">RecyclerView</a></li>
+ <li><a href="#cardview">CardView</a></li>
+</ol>
+</div>
+</div>
<p>The support library in the Android L Developer Preview contains two new widgets,
<code>RecyclerView</code> and <code>CardView</code>. Use these widgets to show complex lists
and cards in your app. These widgets have material design styles and animations by default.</p>
-<h2 style="margin-top:35px">RecyclerView</h2>
+<h2 id="recyclerview">RecyclerView</h2>
<p><code>RecyclerView</code> is a more advanced version of <code>ListView</code>. This widget is
a container for large sets of views that can be recycled and scrolled very efficiently. Use the
@@ -62,7 +71,7 @@
<p>To create a custom layout, you extend the <code>RecyclerView.LayoutManager</code> class.</p>
-<h3 style="margin-top:30px">Examples</h3>
+<h3>Examples</h3>
<p>To include a <code>RecyclerView</code> in your layout:</p>
@@ -155,7 +164,7 @@
</pre>
-<h2 style="margin-top:35px">CardView</h2>
+<h2 id="cardview">CardView</h2>
<p><code>CardView</code> extends the <code>FrameLayout</code> class and lets you show information
inside a card with optional rounded corners:</p>
diff --git a/docs/html/preview/material/videos/Dial.mp4 b/docs/html/preview/material/videos/Dial.mp4
deleted file mode 100644
index cd5a6a2..0000000
--- a/docs/html/preview/material/videos/Dial.mp4
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/material/videos/Dial.ogv b/docs/html/preview/material/videos/Dial.ogv
deleted file mode 100644
index b7b29d0..0000000
--- a/docs/html/preview/material/videos/Dial.ogv
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/material/videos/Dial.webm b/docs/html/preview/material/videos/Dial.webm
deleted file mode 100644
index e30d2a5..0000000
--- a/docs/html/preview/material/videos/Dial.webm
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/material/views-shadows.jd b/docs/html/preview/material/views-shadows.jd
index 52fe83c..c5884d6 100644
--- a/docs/html/preview/material/views-shadows.jd
+++ b/docs/html/preview/material/views-shadows.jd
@@ -2,14 +2,24 @@
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#elevation">View Elevation</a></li>
+ <li><a href="#shadows">Shadows and Outlines</a></li>
+ <li><a href="#clip">Clipping Views</a></li>
+</ol>
+</div>
+</div>
-<p>In material design apps, depth has meaning. You should assign higher elevation values to more
+<p>In apps with material design, depth has meaning. You should assign higher elevation values to more
important UI elements in your app. The elevation value of a view determines the size of its
shadow: views with higher Z values cast bigger shadows. Views only cast shadows on the Z=0 plane
under an orthographic projection (the views do not scale for different values of Z).</p>
-<h2 style="margin-top:35px">View Elevation</h2>
+<h2 id="elevation">View Elevation</h2>
<p>The Z value for a view has two components, elevation and translation. The elevation is the
static component, and the translation is used for animations:</p>
@@ -29,7 +39,7 @@
<code>px</code>).</p>
-<h2 style="margin-top:35px">Shadows and Outlines</h2>
+<h2 id="shadows">Shadows and Outlines</h2>
<p>The bounds of a view's background drawable determine the default shape of its shadow. To define
a custom shape for a shadow, such as an oval, use the <code>View.setOutline</code> method:</p>
@@ -55,7 +65,7 @@
<p>To prevent a view from casting a shadow, set its outline to <code>null</code>.</p>
-<h2 style="margin-top:35px">Clipping Views</h2>
+<h2 id="clip">Clipping Views</h2>
<p>The Android L Developer Preview lets you clip a view to its outline area using the
<code>View.setClipToOutline</code> method. Only rectangle, circle, and round rectangle outlines
diff --git a/docs/html/preview/preview_toc.cs b/docs/html/preview/preview_toc.cs
index 8f6f8c1..5920ecc 100644
--- a/docs/html/preview/preview_toc.cs
+++ b/docs/html/preview/preview_toc.cs
@@ -19,6 +19,7 @@
</a></div>
<ul>
<li><a href="<?cs var:toroot ?>preview/material/get-started.html">Get Started</a></li>
+ <li><a href="<?cs var:toroot ?>preview/material/theme.html">Material Theme</a></li>
<li><a href="<?cs var:toroot ?>preview/material/ui-widgets.html">UI Widgets</a></li>
<li><a href="<?cs var:toroot ?>preview/material/views-shadows.html">Views and Shadows</a></li>
<li><a href="<?cs var:toroot ?>preview/material/animations.html">Animations</a></li>
@@ -75,7 +76,12 @@
</li>
<li class="nav-section">
<div class="nav-section-header empty">
- <a href="<?cs var:toroot ?>preview/feedback-support.html">Feedback and Support</a>
+ <a href="<?cs var:toroot ?>preview/support.html">Support</a>
</div>
</li>
-</ul>
\ No newline at end of file
+ <li class="nav-section">
+ <div class="nav-section-header empty">
+ <a href="<?cs var:toroot ?>preview/tos.html">Terms of Service</a>
+ </div>
+ </li>
+</ul>
diff --git a/docs/html/preview/samples.jd b/docs/html/preview/samples.jd
new file mode 100644
index 0000000..67404b6
--- /dev/null
+++ b/docs/html/preview/samples.jd
@@ -0,0 +1,16 @@
+page.title=Samples
+
+@jd:body
+
+<p>The code samples for the L Developer Preview are available in the Android SDK Manager under the
+L Preview section. Here is a summary of everything that is available:</p>
+
+<ul>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ul>
diff --git a/docs/html/preview/setup-devices.jd b/docs/html/preview/setup-devices.jd
index 0654685..86e4845 100644
--- a/docs/html/preview/setup-devices.jd
+++ b/docs/html/preview/setup-devices.jd
@@ -26,12 +26,6 @@
can cause your phone and installed services and applications to stop working.
</p>
-<p><!-- Will this link change before we publish (to a clean version of the doc)?
- Or will we scrub the doc's comments & revision history? -->
-<a href="https://docs.google.com/a/google.com/document/d/1OixnM1Q890ExOzDB3Z-FDD6Sb2kF4uZQiMxsYVII8F0/edit?usp=sharing">L
-Preview Terms of Service</a>
-</p>
-
<ol>
<li>Download and extract the Android Developer Preview package to a directory
diff --git a/docs/html/preview/support.jd b/docs/html/preview/support.jd
new file mode 100644
index 0000000..23ce6ff
--- /dev/null
+++ b/docs/html/preview/support.jd
@@ -0,0 +1,22 @@
+page.title=Support
+
+@jd:body
+
+<p>If you've encountered bugs or have feedback about the L Developer Preview, create
+an issue on our bug tracker</p>
+
+<p>Go to the Bug Tracker</p>
+
+<h2>Release Notes</h2>
+
+<p>June 25, 2014 - Initial Release of the L Developer Preview</p>
+
+<ul>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ul>
diff --git a/docs/html/preview/tos.jd b/docs/html/preview/tos.jd
new file mode 100644
index 0000000..602439f
--- /dev/null
+++ b/docs/html/preview/tos.jd
@@ -0,0 +1,9 @@
+page.title=License Agreement
+
+@jd:body
+
+<p><!-- Will this link change before we publish (to a clean version of the doc)?
+ Or will we scrub the doc's comments & revision history? -->
+<a href="https://docs.google.com/a/google.com/document/d/1OixnM1Q890ExOzDB3Z-FDD6Sb2kF4uZQiMxsYVII8F0/edit?usp=sharing">L
+Preview Terms of Service</a>
+</p>
\ No newline at end of file
diff --git a/docs/html/preview/tv/adt-1/index.jd b/docs/html/preview/tv/adt-1/index.jd
deleted file mode 100644
index 062968e..0000000
--- a/docs/html/preview/tv/adt-1/index.jd
+++ /dev/null
@@ -1,319 +0,0 @@
-page.title=ADT-1 Developer Kit
-page.tags="emote","e-mote","adt"
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#faq">ADT-1 Frequently Asked Questions</a></li>
- <li><a href="#regulatory">Regulatory Disclosures</a></li>
- <li><a href="#safety">Important Safety Instructions</a></li>
- </ol>
-</div>
-</div>
-
-<p class="note">
- <strong>!FIX: FOR REVIEW ONLY:</strong> link to <a href="request.html">ADT-1 Request</a> page.
-</p>
-
-
-<p>The ADT-1 Developer Kit is streaming media player and game controller designed for running
-and testing app built for Android TV. The kit is provided to a limited number of developers
-who are interested in building new apps or extending their existing apps to run on the Android TV
-platform.</p>
-
-<p class="note">
- <strong>Note:</strong> The ADT-1 kit <em>is not required</em> for building and testing apps
- for Android TV. You can build apps for TV and test them using an emulator for TV devices. The
- L-Preview SDK includes all the software needed to build TV apps and an emulator for running and
- testing them. For more information, see the
- <a href="{@docRoot}preview/tv/start/index.html">Get Started</a> guide for TV apps.
-</p>
-
-<h2 id="faq">ADT-1 Frequently Asked Questions</h2>
-
-<p>
- <strong>How do I put the gamepad that came with my ADT-1 into pairing mode?</strong>
-</p>
-<p>Press and hold the Back and Home buttons together for about three seconds, until all four
- blue LEDs flash together. When the LEDs are flashing, the gamepad is in pairing mode.</p>
-
-<p>
- <strong>How do I use the gamepad with the on-screen keyboard?</strong>
-</p>
-<p>Use the D-pad or left joystick to move the cursor, and press A to select. Press X to delete a
- character, and press Y to insert a space. Also, you can press the right joystick to toggle caps
- lock, and press the left joystick to show additional symbols.</p>
-
-<p>
- <strong>How do I perform a hard reset of ADT-1?</strong>
-</p>
-<p>Unplug the power cable from the back of ADT-1. Press and hold the small, round button on the
- back of ADT-1 as you re-insert the power cable, and continue to hold the small round button. The
- LED will begin flashing red for a few seconds, then change to multi-color cycle. When the LED
- starts the multi-color cycle, release the small, round button, and ADT-1 will boot. Note: this is
- a factory data reset, thus all downloaded apps, system and app data, and account settings will be
- lost.</p>
-
-<p>
- <strong>How do I do a soft reset?</strong>
-</p>
-<p>Go to Settings - Device - Factory data reset, and select ‘Reset device’. Note: this is a
- factory data reset, thus all downloaded apps, system and app data, and account settings will be
- lost.</p>
-
-<p>
- <strong>How do I turn my device on?</strong>
-</p>
-<p>Plug in the included power cable into the back of ADT-1. There is no on/off switch.</p>
-
-<p>
- <strong>How do I completely turn my device off? </strong>
-</p>
-<p>Unplug in the included power cable from the back of ADT-1. There is no on/off switch.
- However, ADT-1 will begin sleeping (daydream) based on user settings in Display -> Daydream.</p>
-
-<p>
- <strong>How do I connect to the network?</strong>
-</p>
-<p>ADT-1 has both Wi-Fi and Ethernet for connecting to your network. To change your Wi-Fi
- network, go to Settings -> Wi-Fi. To use an Ethernet network connection, simply plug in an
- Ethernet cable (that is connected to your network) into the port on the back of ADT-1.</p>
-
-<p>
- <strong>How do I use the developer cable?</strong>
-</p>
-<p>The developer cable has three connectors: a small, male power connector that plugs into the
- power port on the back of ADT-1, a standard male USB-A connector that connects your PC, and a
- small, female power connector that the included power supply plugs into.</p>
-
-<p>
- <strong>Is there an app for phone and tablet that I can use to control ADT-1?</strong>
-</p>
-<p>Yes, you can download the remote control app from Android phones and tablets here.</p>
-
-<p>
- <strong>Can I connect a USB keyboard/mouse to ADT-1?</strong>
-</p>
-<p>Yes, you can connect a USB keyboard/mouse to the USB port on the back of ADT-1. Note: not all
- manufacturers/models are guaranteed to work.</p>
-<hr />
-<p>
- Press the small, round button on the back of ADT-1 to make it search for Bluetooth devices in
- pairing mode. If multiple accessories are found, press the small, round button to select the
- device you want to pair. Pairing will happen automatically after a few seconds. To pair Bluetooth
- devices to ADT-1 from the UI, go to <strong>Settings > Remote & Accessories >
- Add accessory</strong>.
-</p>
-
-
-
-<h2 id="regulatory">Regulatory Disclosures</h2>
-
-
-<p>Model: W2</p>
-<p>FCC ID: A4R-W2</p>
-<p>IC: 10395A-W2</p>
-<p>U.S. Federal Communications Commission Notices</p>
-<p>To satisfy FCC and IC exposure requirements, a separation distance of at least 20 cm should
- be maintained between the antenna of this device and persons during device operation. Operations
- at closer than this distance are not recommended.</p>
-<p>The antenna used for this transmitter must not be co-located in conjunction with any other
- antenna or transmitter.</p>
-<p>This equipment has been tested and found to comply with the limits for a Class B digital
- device, pursuant to part 15 of the FCC Rules. These limits are designed to provide reasonable
- protection against harmful interference in a residential installation. This equipment generates,
- uses and can radiate radio frequency energy and, if not installed and used in accordance with the
- instructions, may cause harmful interference to radio communications. However, there is no
- guarantee that interference will not occur in a particular installation. If this equipment does
- cause harmful interference to radio or television reception, which can be determined by turning
- the equipment off and on, the user is encouraged to try to correct the interference by one or more
- of the following measures:</p>
-<p>—Reorient or relocate the receiving antenna.</p>
-<p>—Increase the separation between the equipment and receiver.</p>
-<p>—Connect the equipment into an outlet on a circuit different from that to which the receiver
- is connected.</p>
-<p>—Consult the dealer or an experienced radio/ TV technician for help.</p>
-<p>This device complies with part 15 of the FCC Rules. Operation is subject to the following two
- conditions: (1) This device may not cause harmful interference, and (2) this device must accept
- any interference received, including interference that may cause undesired operation.</p>
-<p>Changes or modifications not expressly approved by Google Inc. could void the user's
- authority to operate the equipment.</p>
-<p>Industry Canada Notices</p>
-<p>This device complies with Industry Canada licence-exempt RSS standard(s). Operation is
- subject to the following two conditions: (1) this device may not cause interference, and (2) this
- device must accept any interference, including interference that may cause undesired operation of
- the device.</p>
-<p>Under Industry Canada regulations, this radio transmitter may only operate using an antenna
- of a type and maximum (or lesser) gain approved for the transmitter by Industry Canada. To reduce
- potential radio interference to other users, the antenna type and its gain should be so chosen
- that the equivalent isotropically radiated power (e.i.r.p.) is not more than that necessary for
- successful communication.</p>
-<p>The radiated output power of the Wireless Device is below the Industry Canada (IC) radio
- frequency exposure limits. The Wireless Device should be used in such a manner such that the
- potential for human contact during normal operation is minimized.</p>
-
-<hr />
-
-<p>CAN ICES-3 (B)/NMB-3(B)</p>
-<p>
- <u>Avis d’<em>Industrie Canada</em></u>
-</p>
-<p>
- Le présent appareil est conforme aux <em>CNR</em> d'Industrie Canada applicables aux appareils
- radio exempts de licence. L'exploitation est autorisée aux deux conditions suivantes : (1)
- l'appareil ne doit pas produire de brouillage, et (2) l'appareil doit accepter tout brouillage
- radioélectrique subi, même si le brouillage est susceptible d'en compromettre le fonctionnement.
-</p>
-<p>
- En vertu de la règlementation d’<em>Industrie Canada</em>, cet émetteur radio peut
- fonctionner avec une antenne d'un type et d'un gain maximal (ou inférieur) approuvé pour
- l'émetteur par <em>Industrie Canada</em>. Dans le but de réduire les risques de brouillage
- radioélectrique à l'intention des autres utilisateurs, il faut choisir le type d'antenne et son
- gain de sorte que la puissance isotrope rayonnée équivalente (p.i.r.e.) ne dépasse pas l'intensité
- nécessaire à l'établissement d'une communication satisfaisante.
-</p>
-<p>
- La puissance rayonnée en sortie de l'appareil sans fil est inférieure aux limites fixées par
- <em>Industrie Canada</em> en matière d'exposition aux radiofréquences. L'appareil sans fil
- doit être utilisé de sorte que la possibilité d'un contact humain pendant le fonctionnement
- normal soit limitée.
-</p>
-
-
-<h2 id="safety">Important Safety Instructions</h2>
-
-<p>
- <strong>WARNING:</strong> Read all safety information below before using this device to avoid
- injury.
-</p>
-<ul>
- <li><p>Do not install near heat sources, such as heaters and other devices.</p></li>
- <li><p>Use in a well-ventilated area and plug power adapter into an easily accessible
- outlet. Only use this device with the provided power adapter.</p></li>
- <li><p>The device has no on/off switch. To disconnect from power, you must unplug the
- power adapter.</p></li>
- <li><p>Only use indoors and do not expose to rain, liquid, moisture, excessive heat, or
- naked flame.</p></li>
- <li><p>Clean only with a dry cloth.</p></li>
-</ul>
-<p>
- <strong>WARNING:</strong> Playing video games has been linked to injuries in some
- users. Read all safety and health information below before using the gamepad to avoid possible
- injury.
-</p>
-
-<p><u>Photosensitive Seizures</u></p>
-
-<p>
- A very small percentage of people may experience a seizure when exposed to certain visual images,
- including flashing lights or patterns that may appear in some video games, even people who have no
- history of seizures or epilepsy. These seizures have a variety of symptoms, including
- lightheadedness, altered vision, disorientation, loss of awareness, involuntary movements, loss of
- consciousness, or convulsions. If you experience any of these symptoms, <u>stop gaming
- immediately and consult your doctor</u>.
-</p>
-
-<p><u>Ergonomics</u></p>
-
-<p>Long periods of repetitive motion using incorrect body positioning may be associated with
- physical discomfort and injuries to nerves, tendons, and muscles. If during or after gaming you
- feel pain, numbness, weakness, swelling, burning, cramping, or stiffness,<u>stop gaming
- and consult your doctor</u>.
-
-<p>
- <strong>Healthy Gaming</strong>
-</p>
-
-<p>To reduce risk of seizures or injury, take the following precautions:</p>
-
-<ul>
- <li><p>Sit as far away from the TV screen as possible.</p></li>
- <li><p>Play in a well-lit room.</p></li>
- <li><p>Do not play when you are drowsy or fatigued.</p></li>
- <li><p>Take 10-15 minute breaks every hour if playing video games and avoid prolonged
- gaming.</p></li>
-</ul>
-
-<p>
- <strong>Do Not Attempt Repairs Yourself</strong>
-</p>
-
-<p>There are no user-serviceable parts inside. Do not attempt to open or disassemble.</p>
-
-<p>Failure to follow these safety instructions could result in fire, electric shock, damage to
- the device or other property, or personal injury.</p>
-
-<hr />
-
-<p>
- <strong>Importantes instructions concernant la sécurité</strong>
-</p>
-
-<p>
- <strong>ATTENTION:</strong> Veuillez lire toutes les informations de sécurité énoncées ci-bas
- avant d’utiliser l’appareil pour éviter des blessures.
-</p>
-
-<ul>
- <li><p>Ne pas installer à proximité d’une source de chaleur telle une chaufferette ou un
- autre appareil similaire.</p></li>
- <li><p>Utiliser dans un endroit bien aéré et brancher l’adaptateur électrique dans une
- prise de courant facilement accessible.</p></li>
- <li><p>L’appareil ne possède aucun interrupteur marché/arrêt. Pour mettre l’appareil hors
- tension, il faut débrancher l’appareil de la prise de courant.</p></li>
- <li><p>Utiliser l’appareil uniquement à l’intérieur et ne pas l’exposer à la pluie, à des
- substances liquides, à l’humidité, à la chaleur excessive ou à une flamme.</p></li>
- <li><p>Nettoyer uniquement avec un linge sec.</p></li>
-</ul>
-
-<p>
- <strong>ATTENTION:</strong> Le fait de jouer à des jeux vidéo a été relié à des blessures chez certains
- utilisateurs. Afin d’éviter de possibles blessures, veuillez lire toutes les informations
- concernant la sécurité et la santé énoncées ci-bas avant d’utiliser la tablette de jeu.
-</p>
-
-<p><u>Épilepsie photosensible</u></p>
-
-<p>L’exposition à certaines images visuelles, incluant les lumières ou motifs clignotants qui
- peuvent apparaître dans certains jeux vidéo, peut provoquer chez un très faible pourcentage de
- personnes une crise d’épilepsie, et ce, même si ces personnes n’ont aucun historique de crises ou
- d’épilepsie. Ces crises comportent divers symptômes tels que des étourdissements, une vision
- altérée, un sentiment de désorientation, la perte de conscience, des mouvements involontaires, la
- perte de connaissance ou de conscience ou des convulsions. Si vous ressentez quelconque de ces
- symptômes, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
-
-<p><u>Ergonomie</u></p>
-
-<p>Les longues périodes de mouvements répétitifs effectués dans une position corporelle
- inadéquate peuvent mener à un inconfort physique et à des blessures aux nerfs, tendons et muscles.
- Si durant ou après avoir joué à des jeux vidéo, vous ressentez de la douleur, de
- l’engourdissement, une faiblesse, de l’inflammation, une sensation de brûlure, des crampes ou de
- la rigidité, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
-
-<p>
- <strong>Le jeu sécuritaire</strong>
-</p>
-
-<p>Afin de réduire les risques de crises d’épilepsie ou de blessures, veuillez prendre les
- précautions suivantes :</p>
-
-<ul>
- <li>Asseyez-vous aussi loin de l’écran de télévision que possible.</li>
- <li>Jouez dans une pièce munie d’un éclairage adéquat.</li>
- <li>Ne jouez pas lorsque vous êtes étourdi ou fatigué.</li>
- <li>Prenez 10 à 15 minutes de pause après chaque heure de jeu et évitez les périodes de jeu
- prolongées.</li>
-</ul>
-
-<p>
- <strong>Ne pas tenter d’effectuer des réparations par vous-même</strong>
-</p>
-
-<p>L’Appareil ne contient aucune pièce pouvant être réparée par l’utilisateur. Ne pas tenter
- d’ouvrir ou de désassembler l’Appareil.</p>
-
-<p>Le défaut de suivre ces instructions de sécurité pourrait provoquer un feu, un choc
- électrique, un dommage à l’Appareil ou à d’autres objets ou des lésions corporelles.</p>
diff --git a/docs/html/preview/tv/adt-1/request.jd b/docs/html/preview/tv/adt-1/request.jd
deleted file mode 100644
index 69e3e4e..0000000
--- a/docs/html/preview/tv/adt-1/request.jd
+++ /dev/null
@@ -1,47 +0,0 @@
-page.title=Request ADT-1 Developer Kit
-
-@jd:body
-
-
-<p>The ADT-1 Developer Kit is streaming media player and game controller designed for running
-and testing app built for Android TV. The kit is offered to developers who are interested in
-building new apps or extending their existing apps to run on the Android TV platform before
-the commercial release of Android TV devices. Supplies of the ADT-1 kit are limited and
-requesting one not guarantee it will be delivered to you.</p>
-
-<p class="note">
- <strong>Note:</strong> The ADT-1 kit <em>is not required</em> for building and testing apps
- for Android TV. You can build apps for TV and test them using an emulator for TV devices. The
- L-Preview SDK includes all the software needed to build TV apps and an emulator for running and
- testing them. For more information, see the
- <a href="{@docRoot}preview/tv/start/index.html">Get Started</a> guide for TV apps.
- For more information about the ADT-1 kit, see the
- <a href="{@docRoot}preview/tv/adt-1/index.html">ADT-1 Developer Kit</a> information page.
-</p>
-
-<div class="sdk-terms" style="width:678px" onfocus="this.blur()">
-<div class="sdk-terms-padding">
-Android ADT-1 Developer Kit Request Agreement.
-
-1. These are the terms.
-
-2. You must agree to the terms.
-
-3. Otherwise you have not agreed to the terms.
-
-4. And then we don't want to talk to you.
-</div>
-</div>
-
-
-<p class="caution">
- <strong>Important:</strong> The email address your provide in this form is used to verify
- you as an Android developer. Please provide a Google account email address that is associated
- with the Google Play app you enter. We may also use your email address to provide you with
- updates about the ADT-1 Developer Kit and Android TV.
-</p>
-
-<iframe src="https://docs.google.com/a/google.com/forms/d/1MLhC39rf3aAJw-KhZw9cyjT1dWuz_k3_iC5QXpC4Cbw/viewform?embedded=true"
- width="100%" height="540" frameborder="0" marginheight="0" marginwidth="0"
- id="signupform">Loading...</iframe>
-
diff --git a/docs/html/preview/tv/games/index.jd b/docs/html/preview/tv/games/index.jd
deleted file mode 100644
index b9de3a4..0000000
--- a/docs/html/preview/tv/games/index.jd
+++ /dev/null
@@ -1,70 +0,0 @@
-page.title=Games on TV
-page.tags="controller"
-
-@jd:body
-
-<p>This section complements the [larger best-practices guidance for designing for Android TV](TODO, use formal name of referenced doc, and add link). It assumes that you have read that guidance, and seeks to minimize repetition.</p>
-
-<h2>Overview</h2>
-<p>Because of factors including its large size, its control scheme, and its nature as a shared display, the television screen presents a number of considerations that may be new to mobile developers. This document breaks these considerations down into five sections:</p>
-<ul>
-<li>Display</li>
-<li>Control</li>
-<li>Manifest</li>
-<li>Google Play Game Services</li>
-<li>Web</li>
-</ul>
-<h2>Display</h2>
-<p>Large and centrally situated, the television screen imposes limitations, but also opens up new opportunities for immersive gameplay.</p>
-<h3>A shared display</h3>
-<p>A living-room TV poses design challenges for multiplayer games, in that all players can see everything. This issue is especially germane to games (such as card games or strategy games) that rely on each player’s possession of hidden information.</p>
-<p>Some mechanisms you can implement to address the problem of one player’s “eavesdropping” on another’s information are:</p>
-<ul>
-<li>A player might place a "blinder" on the screen to help conceal information. For example, in a turn-based game like a word or card game, one player at a time might view the display. When the player finishes a move, the game allows him or her to cover the screen with a “blinder” that blocks anyone from viewing secret information. When the next player begins a turn, the blinder opens to reveal his or her own information.</li>
-<li>A second screen, such as a handset or larger device, can enable a player to conceal information. For information on implementing second-screen support, see <a href="http://developer.android.com/reference/android/app/Presentation.html">Presentation</a> on the Android developer site.</li>
-</ul>
-<h3>No touch interface</h3>
-<p>A television does not have a touch interface. Your game design, therefore, need not take into account the possibility that a player’s controlling fingers might block the on-screen action. You can assume constant visibility of the entire viewing area.</p>
-<p>See the <a href=#control>Control</a> section in this document and in [Design for TV](TODO, use formal name of referenced doc, and add link) for more implications of the lack of touch interface.</p>
-<h3>Landscape display</h3>
-<p>In mobile-device terms, a TV is always “sideways.” You can’t turn it, and there is no portrait orientation. You should always be designing your TV games to be displayed in landscape mode.</p>
-<a id=control><h2>Control</h2>
-<p>Without a touch interface, it's even more important than usual to get your controls right, so that players find them intuitive and fun to use. The separation of controller from device also introduces some other issues to pay attention to, like keeping track of multiple players' controllers, and handling disconnects gracefully.</p>
-<h3>D-pad</h3>
-<p>Because of the lack of touch interface, you should be planning your control scheme based on a D-pad. Some key points to keep in mind include:</p>
-<p>The player needs to use the gamepad in all aspects of the game–not just controlling core gameplay, but also navigating menus and ads. For this reason, you should also ensure that your Android TV game does not refer to a touch interface: for example, an Android TV game cannot tell a player to "Tap to skip".</p>
-<p>You can avoid unhappy surprises (and resulting low ratings) by using your Play Store description to communicate to the player any expectations about controllers. If a game is better suited to a gamepad with a joystick than one with only a D-pad, you should make this clear. A player who uses an ill-suited controller for a game is likely to have a subpar experience–and penalize your game in the ratings.</p>
-<p>You can also help ensure a good player experience by ensuring that button mapping is intuitive and flexible. For example, you can adhere to accepted custom by using the A button to <code>Accept</code>, and the B button to <code>Cancel</code>. You can also offer flexibility in the form of remappability. For more information on button mapping, see <a href="http://developer.android.com/training/game-controllers/controller-input.html">Handling Controller Actions</a>.</p>
-<p>Your game can also contribute to a good match between controller and game by querying the controller about its capabilities. For example, you may intend for a player to steer an object by waving the controller in the air. If a player's controller lacks accelerometer and gyroscope hardware, however, waving will not work. But when your game queries the controller and discovers that motion detection is not supported, it can switch over to an alternative, available control scheme.</p>
-<p>For more information on querying controller capabilities, see <a href="http://developer.android.com/training/game-controllers/compatibility.html">Supporting Controllers Across Android Versions</a>.</p>
-<h3>Back-button behavior</h3>
-<p>The Back button should never act as a toggle. For example, do not use it to both open and close a menu. Its behavior should only be linear. For example: Game play > Game pause screen > Game main screen > Android home screen.</p>
-<p>With this principle of "linear navigation" in mind, you <b>may</b> use the back button to leave an in-game menu (opened by a different button) and return to gameplay.</p>
-<h3>Handling multiple controllers</h3>
-<p>When multiple players are playing a game, each with his or her own controller, it is important to map each player-controller pair. For information on how to implement controller-number identification, see <a href="http://developer.android.com/reference/android/view/InputDevice.html#getControllerNumber(">Input Devices</a>) on the Android developer site.</p>
-<h3>Handling disconnects</h3>
-<p>When a controller is disconnected in the middle of gameplay, the game should pause, and a dialog should appear prompting the disconnected player to reconnect his or her controller.</p>
-<p>The dialog should also offer troubleshooting tips (e.g., "Check your Bluetooth connection").</p>
-<h2>Manifest</h2>
-<p>Games are displayed in a separate row from regular apps in the launcher. Android TV uses the <code>android:isGame</code> flag to differentiate games from non-game apps. You can assign it a value of either <code>true</code> or <code>false</code>. For example:</p>
-<pre class="fragment"><application>
- . . .
- <meta-data android:name="isGame" android:value=["true" | "false"]/>
-android:isGame=["true" | "false"] >
- . . .
-</application>
-</pre><h2>Google Play Game Services</h2>
-<p>If your game integrates Google Play Game Services, you should keep in mind a number of considerations pertaining to achievements, sign-on, saving games, and multiplayer play.</p>
-<h3>Achievements</h3>
-<p>Your game should include at least five (earnable) achievements. Only a user controlling gameplay from a supported input device should be able to earn achievements.</p>
-<h3>Sign-on</h3>
-<p>Your game should attempt to sign the user in on launch. If the player declines sign-in several times in a row, your game should stop asking.</p>
-<h3>Saving</h3>
-<p>We highly recommend using Play Services cloud save to store your game save. Your game should bind game saves to a specific Google account, so as to be uniquely identifiable even across devices: Whether the player is using a handset or a TV, the game should be able to pull the same game-save information from his or her account.</p>
-<p>You should also provide an option in your game's UI to prompt the player to destroy save data. You might put the option in the game's <code>Settings</code> screen.</p>
-<h3>Multiplayer experience</h3>
-<p>A game offering a multiplayer experience must allow at least two players to enter a room.</p>
-<h2>Web</h2>
-<p>Android TV games do not support a full web browser. You should therefore avoid using generic URLs in your game.</p>
-<p>Webviews will work for logins to services like Google+ and Facebook. </p>
-
diff --git a/docs/html/preview/tv/images/atv.png b/docs/html/preview/tv/images/atv.png
deleted file mode 100644
index cd96164..0000000
--- a/docs/html/preview/tv/images/atv.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/images/browsefragment.png b/docs/html/preview/tv/images/browsefragment.png
deleted file mode 100644
index 8998b13..0000000
--- a/docs/html/preview/tv/images/browsefragment.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/images/detailsfragment.png b/docs/html/preview/tv/images/detailsfragment.png
deleted file mode 100644
index 014ab23..0000000
--- a/docs/html/preview/tv/images/detailsfragment.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/images/home-recommendations.png b/docs/html/preview/tv/images/home-recommendations.png
deleted file mode 100644
index 2c18827..0000000
--- a/docs/html/preview/tv/images/home-recommendations.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/index.jd b/docs/html/preview/tv/index.jd
deleted file mode 100644
index da40985..0000000
--- a/docs/html/preview/tv/index.jd
+++ /dev/null
@@ -1,12 +0,0 @@
-page.title=Android on TV
-
-@jd:body
-
-<img src="{@docRoot}preview/tv/images/atv.png" align="middle"/>
-
-<p>Android offers a rich user experience that's optimized for apps running on large screen
- devices, such as high-definition televisions. Apps on TV offer new opportunities to
- delight your users from the comfort of their couch.</p>
-
-<a href="{@docRoot}preview/tv/start/index.html">Get Started ></a>
-
diff --git a/docs/html/preview/tv/start/hardware-features.jd b/docs/html/preview/tv/start/hardware-features.jd
deleted file mode 100644
index f3b51bb..0000000
--- a/docs/html/preview/tv/start/hardware-features.jd
+++ /dev/null
@@ -1,174 +0,0 @@
-page.title=Hardware Features on TV
-page.tags="unsupported"
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#unsupported-features">Unsupported Hardware Features</a></li>
- <li><a href="#workaround-features">Handling Unsupported Features</a></li>
- <li><a href="#check-features">Checking Available Features</a>
- <ol>
- <li><a href="#no-touchscreen">Touch screen</a></li>
- <li><a href="#no-camera">Camera</a></li>
- <li><a href="#no-gps">GPS</a></li>
- </ol>
-
- </li>
- </ol>
-</div>
-</div>
-
-<p>TVs do not have some of the hardware features found on other Android devices.
-Touch screens, cameras and GPS receivers are some of the most commonly used hardware features
-which are typically not available on a TV. When you build an app for TV, you must carefully
-consider if your app can handle not having these features and, if necessary, work around them.</p>
-
-<p>This guide discusses the hardware features not available on TV devices and shows you how to
-work around those limitations in your app.</p>
-
-
-<h2 id="unsupported-features">Unsupported Hardware Features</h2>
-
-<p>TVs have a different purpose from other devices, and so they do not have hardware
-features that other Android-powered devices often have. For this reason, the Android system
-does not support the following features for a TV device:
-
-<table>
-<tr>
-<th>Hardware</th>
-<th>Android feature descriptor</th>
-</tr>
-<tr>
-<td>Camera</td>
-<td>android.hardware.camera</td>
-</tr>
-<tr>
-<td>GPS</td>
-<td>android.hardware.location.gps</td>
-</tr>
-<tr>
-<td>Microphone</td>
-<td>android.hardware.microphone</td>
-</tr>
-<tr>
-<td>Near Field Communications (NFC)</td>
-<td>android.hardware.nfc</td>
-</tr>
-<tr>
-<td>Telephony</td>
-<td>android.hardware.telephony</td>
-</tr>
-<tr>
-<td>Touchscreen</td>
-<td>android.hardware.touchscreen</td>
-</tr>
-</table>
-</p>
-
-
-<h2 id="check-features">Checking Available Features</h2>
-
-<p>To check if a feature is available at runtime, call {@link
- android.content.pm.PackageManager#hasSystemFeature(String)}. This method takes a single string
- argument that specifies the feature you want to check. For example, to check for a touch screen,
- use {@link android.content.pm.PackageManager#hasSystemFeature(String)} with the argument
- {@link android.content.pm.PackageManager#FEATURE_TOUCHSCREEN}.</p>
-
-<p>The following code example demonstrates how to detect the availability of a hardware features
- at runtime:</p>
-
-<pre>
-// Check if the telephony hardware feature is available.
-if (getPackageManager().hasSystemFeature("android.hardware.telephony")) {
- Log.d("Mobile Test", "Running on phone");
-// Check if android.hardware.touchscreen feature is available.
-} else if (getPackageManager().hasSystemFeature("android.hardware.touchscreen")) {
- Log.d("Tablet Test", "Running on devices that don't support telephony but "+
- "do have a touch screen.");
-} else {
- Log.d("TV Test", "Running on a TV!");
-}
-</pre>
-
-
-<h2 id="workaround-features">Handling Unsupported Features</h2>
-
-<p>Depending on the design and functionality of your app, you may be able to work around certain
- hardware features being unavailable. This section discusses how to workaround specific hardware
- features.</p>
-
-
-<h3 id="no-touchscreen">Touch screen</h3>
-
-<p>Android doesn't support touch screen interaction for TV devices, since most TVs don't have touch
- screens, and using a touch screen is not consistent with a viewing environment where the user is
- seated 10 feet away from the display.</p>
-
-<p>On TV devices, you should workaround this limitation by supporting navigation using a directional
- pad (D-pad) on TV remote control. For more information on properly supporting navigation using
- TV-friendly controls, see <a href="{@docRoot}preview/tv/ui/navigation.html">Navigation for
- TV</a>.</p>
-
-<p>You can explicitly declare if your application requires (or does not require) a touch screen
- by including the following entry in your manifest:</p>
-
-<pre>
-<uses-feature android:name="android.hardware.touchscreen"
- android:required="false"/>
-</pre>
-
-
-<h3 id="no-camera">Camera</h3>
-
-<p>Although a TV typically does not have a camera, you can still provide a photography-related
- application on a TV. For example, if you have an app that takes, views and edits photos, you can
- disable its picture-taking functionality for TVs and still allow users to view and even edit
- photos. If you decide that you want to enable your camera-related application to work on a
- TV device without a camera, you can add an attribute to your app manifest declaring that
- a camera is not required by your app:</p>
-
-<pre>
-<uses-feature android:name="android.hardware.camera" android:required="false" />
-</pre>
-
-<p>If you enable your application to run without a camera, you should add code to your application
-that detects if the camera feature is available and make adjustments to the operation of your app.
-The following code example demonstrates how to detect the presence of a camera:</p>
-
-<pre>
-// Check if the camera hardware feature is available.
-if (getPackageManager().hasSystemFeature("android.hardware.camera")) {
- Log.d("Camera test", "Camera available!");
-} else {
- Log.d("Camera test", "No camera available. View and edit features only.");
-}
-</pre>
-
-
-<h3 id="no-gps">GPS</h3>
-
-<p>TVs are stationary, indoor devices, and do not have built-in global positioning system (GPS)
- receivers. If your application uses location information, you can still allow users to search
- for a location or use a static location provider such as a zip code configured during the
- TV device setup.</p>
-
-<pre>
-LocationManager locationManager = (LocationManager) this.getSystemService(
- Context.LOCATION_SERVICE);
-Location location = locationManager.getLastKnownLocation("static");
-Geocoder geocoder = new Geocoder(this);
-Address address = null;
-
-try {
- address = geocoder.getFromLocation(location.getLatitude(),
- location.getLongitude(), 1).get(0);
- Log.d("Zip code", address.getPostalCode());
-
-} catch (IOException e) {
- Log.e(TAG, "Geocoder error", e);
-}
-</pre>
-
diff --git a/docs/html/preview/tv/start/index.jd b/docs/html/preview/tv/start/index.jd
deleted file mode 100644
index 7f726bd..0000000
--- a/docs/html/preview/tv/start/index.jd
+++ /dev/null
@@ -1,193 +0,0 @@
-page.title=Get Started with TV Apps
-page.tags="leanback","recyclerview","launcher"
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#prerequisites">Prerequisites</a></li>
- <li><a href="#dev-project">Setup a TV Project</a>
- <ul>
- <li><a href="#tv-activity">Create a TV Activity</a></li>
- <li><a href="#tv-libraries">Add TV Support Libraries</a></li>
- </ul>
- </li>
- <li><a href="#build-it">Build TV Apps</a></li>
- </ol>
-</div>
-</div>
-
-<p>This guide describes how to prepare your development environment and projects for building
- TV apps, including updating your existing app to run on TV devices.</p>
-
-
-<h2 id="prerequisites">Prerequisites</h2>
-
-<p>Before you begin setting up to build apps for TV, you must:</p>
-
-<ul>
- <li><strong><a href="{@docRoot}preview/setup-sdk.html">
- Setup the Preview SDK</a></strong>
- <br>
- The preview SDK provides the developer tools needed to build and test apps for TV.
- </li>
- <li><strong><a href="{@docRoot}preview/setup-sdk.html#project">
- Create a Preview SDK Project</a></strong>
- <br>
- In order to access new APIs for TV devices, you must create a project that targets the preview
- release level or modify an existing project to target the preview release.
- </li>
-</ul>
-
-
-<h2 id="dev-project">Setup a TV Project</h2>
-
-<p>TV apps use the same structure as those for phones and tablets. This means you can modify
- your existing apps to also run on TV devices or create new apps based on what you already know
- about building apps for Android. This section discusses how to modify an existing app or create a
- new app that runs on TV devices.</p>
-
-<p>There are the main steps to creating an app that runs on TV devices, only the first of these
- is required:</p>
-
-<ul>
- <li><strong>Activity for TV</strong> - (Required) Your application must have an activity that is
- is declared to run on TV devices through an app manifest entry.</li>
- <li><strong>TV Support Libraries</strong> - (Optional) There are several Support Libraries
- available for TV devices that provide user interface widgets for building user interfaces
- for use on TV.</li>
-</ul>
-
-
-<h3 id="tv-activity">Create a TV Activity</h3>
-
-<p>Applications that are intended to run on TV devices must declare a launcher activity for TV
- in their manifest using a the {@code android.intent.category.LEANBACK_LAUNCHER} intent filter.
- This filter identifies your app as being built for TV, enabling your app to be displayed in the
- Google Play app running on TV devices. Declaring this intent also identifies which activity in
- your app should be launched when a user selects your app icon on a TV.</p>
-
-<p class="caution">
- <strong>Caution:</strong> If you do not include the LEANBACK_LAUNCHER intent filter in your app,
- it will not be visible to users running the Google Play store on TV devices. If your load an app
- without this intent filter onto a TV device using developer tools, the app does not appear in
- the TV user interface.
-</p>
-
-<p>The following code snippet shows how to include this intent filter in your manifest:</p>
-
-<pre>
-<application>
- ...
- <activity
- android:name="com.example.android.MainActivity"
- android:label="@string/app_name" >
-
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
-
- <activity
- android:name="com.example.android.<strong>TvActivity</strong>"
- android:label="@string/app_name"
- android:theme="@android:style/Theme.NoTitleBar">
-
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="<strong>android.intent.category.LEANBACK_LAUNCHER</strong>" />
- </intent-filter>
-
- </activity>
-</application>
-</pre>
-
-<p>The second activity manifest entry in the example above specifies that it should be used as
- the main activity when your app launched on an TV device.</p>
-
-<p>If you have an existing app that you are modifying for TV use, your app should not use the same
- activity layout for TV that you do for phones and tablets. The user interface of your TV app (or
- TV portion of your existing app) should provide a simpler interface that can be easily navigated
- using a remote control from a couch. For guidelines on designing an app for TV, see the
- <a href="{@docRoot}design/tv/index.html">TV Design</a> guide. For more instructions on
- developing a user interface that is appropriate to TV, see the
- <a href="{@docRoot}preview/tv/ui/index.html">TV User Interface</a> guide.
-</p>
-
-
-<h3 id="tv-libraries">Add TV Support Libraries</h3>
-
-<p>The Preview SDK includes support libraries that are intended for use with TV apps. These
- libraries provide APIs and user interface widgets for use on TV devices. The libraries are
- located in the {@code <sdk>/extras/android/support/} directory where you installed the
- Preview SDK. Here is a list of the libraries and their general purpose:</p>
-
-<ul>
- <li><strong>v17 leanback library</strong> - Provides user interface widgets for TV, including
- {@code BrowseFragment}, {@code DetailsFragment}, and {@code SearchFragment}.
- <ul>
- <li>SDK location: {@code <sdk>/extras/android/support/v17/leanback}</li>
- <li>Gradle dependency: {@code com.android.support:leanback-v17:20.0.+}</li>
- <li>Contains resources: Yes</li>
- </ul>
- </li>
- <li><strong>v7 recyclerview library</strong> - Provides classes for managing display of long
- lists in a memory efficient manner. Several classes in the v17 leanback library depend on the
- classes in this library.
- <ul>
- <li>SDK location: {@code <sdk>/extras/android/support/v7/recyclerview}</li>
- <li>Gradle dependency: {@code com.android.support:recyclerview-v7:20.0.+}</li>
- <li>Contains resources: No</li>
- </ul>
- </li>
-</ul>
-
-<p class="note">
- <strong>Note:</strong> You are not required to use these support libraries for your TV app.
- However, we strongly recommend using them, particularly for apps that provide a media catalog
- browsing interface.
-</p>
-
-<p>If you decide to use the v17 leanback library for your application, you should note that it is
- dependent on the <a href="{@docRoot}tools/support-library/features.html#v7-appcompat">v7
- appcompat library</a>, which is, in turn, dependent on the
- <a href="{@docRoot}tools/support-library/features.html#v4">v4 support library</a>. This means
- that apps that use the leanback support library should include all of these support
- libraries:</p>
-
-<ul>
- <li>v17 leanback support library</li>
- <li>v7 recyclerview support library</li>
- <li>v7 appcompat support library</li>
- <li>v4 support library</li>
-</ul>
-
-<p>Two of these libraries (v17 leanback and v7 appcompat) contain resources, which require
- you to take specific steps to include them in app projects. For instructions on
- importing a support library with resources, see
- <a href="http://developer.android.com/tools/support-library/setup.html#libs-with-res">
- Support Library Setup</a>.
-</p>
-
-
-<h2 id="build-it">Build TV Apps</h2>
-
-<p>After you have completed the steps described above, it's time to start building apps for
- the big screen! Check out these additional topics to help you build your app for TV:
-
-<ul>
- <li><a href="{@docRoot}preview/tv/ui/index.html">User Interface</a> - The user interface of
- TV devices is different from those of other Android devices. See this topic to find out how
- to build TV user interfaces and the widgets provided to make it easier to build them.
- </li>
- <li><a href="{@docRoot}preview/tv/games/index.html">Games for TV</a> - TV devices are great
- platforms for games. See this topic for information on building great game experiences for
- TV.</li>
- <li><a href="{@docRoot}preview/tv/start/hardware-features.html">Hardware features</a> - TV
- devices do not contain hardware features normally found on other Android devices. See this
- topic for information on unsupported hardware features and what to do about them.
- </li>
-</ul>
diff --git a/docs/html/preview/tv/ui/browse.jd b/docs/html/preview/tv/ui/browse.jd
deleted file mode 100644
index 9d878b0..0000000
--- a/docs/html/preview/tv/ui/browse.jd
+++ /dev/null
@@ -1,217 +0,0 @@
-page.title=BrowseFragment
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-next.title=DetailsFragment
-next.link=details.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#layout">Media Browse Layout</a></li>
- <li><a href="#lists">Displaying Media Lists</a></li>
- <li><a href="#background">Updating the Background</a></li>
- </ol>
-
-</div>
-</div>
-
-<p>The <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
- provides several APIs for displaying and browsing media catalogs
- on the TV devices. This guide discusses how to use the classes provided by this library to
- implement a user interface for browsing music or videos from your app's media catalog.</p>
-
-
-<h2 id="layout">Media Browse Layout</h2>
-
-<p>The BrowseFragment class in the Leanback support library allows you to create a primary
- layout for browsing categories and rows of media items with a minimum of code. The following
- example layout shows how to create a layout that contains a BrowseFragment:</p>
-
-<pre>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
-
- <fragment
- <strong>android:name="android.support.v17.leanback.app.BrowseFragment"</strong>
- android:id="@+id/browse_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
-</LinearLayout>
-</pre>
-
-<p>In order to work with this layout in an activity, retrieve the BrowseFragment element from
- the layout. Use the BrowseFragment.Params class to set display parameters such as the icon, title
- and whether category headers are enabled. The following code sample demonstrates how to set the
- layout parameters for a BrowseFragment in a layout:</p>
-
-<pre>
-public class BrowseMediaActivity extends Activity {
-
- public static final String TAG ="BrowseActivity";
-
- protected BrowseFragment mBrowseFragment;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.browse_fragment);
-
- final FragmentManager fragmentManager = getFragmentManager();
- <strong>mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById(
- R.id.browse_fragment);</strong>
-
- // Set display parameters for the BrowseFragment
- BrowseFragment.Params params = mBrowseFragment.getBrowseParams();
- if (params == null) {
- params = new BrowseFragment.Params();
- }
- params.setHeadersState(BrowseFragment.HEADERS_ENABLED);
- params.setTitle(getString(R.string.app_name));
- params.setBadgeImage(getResources().getDrawable(R.drawable.ic_launcher));
- mBrowseFragment.setBrowseParams(params);
-
- }
-}
-</pre>
-
-
-<h2 id="lists">Displaying Media Lists</h2>
-
-<p>The BrowseFragment allows you to define and display browseable media content categories and
- media items from a media catalog using adapters and presenters. Adapters enable you to connect to
- local or online data sources that contain your media catalog information. Presenter classes hold
- data about media items and provide layout information for displaying an item on screen.</p>
-
-<p>The following example code shows an implementation of a presenter for displaying string
- data:</p>
-
-<pre>
-public class StringPresenter extends Presenter {
- private static final String TAG = "StringPresenter";
-
- public ViewHolder onCreateViewHolder(ViewGroup parent) {
- TextView textView = new TextView(parent.getContext());
- textView.setFocusable(true);
- textView.setFocusableInTouchMode(true);
- textView.setBackground(
- parent.getContext().getResources().getDrawable(R.drawable.text_bg));
- return new ViewHolder(textView);
- }
-
- public void onBindViewHolder(ViewHolder viewHolder, Object item) {
- ((TextView) viewHolder.view).setText(item.toString());
- }
-
- public void onUnbindViewHolder(ViewHolder viewHolder) {
- Log.d(TAG, "onUnbindViewHolder");
- }
-}
-</pre>
-
-<p>Once you have constructed a presenter class for your media items, you can build and attach an
- adapter to the BrowseFragment to display those items on screen for browsing by the user. The
- following example code demonstrates how to construct an adapter to display categories and items
- in those categories using the StringPresenter class shown in the previous code example:</p>
-
-<pre>
-private ArrayObjectAdapter mRowsAdapter;
-private static final int NUM_ROWS = 4;
-
-@Override
-protected void onCreate(Bundle savedInstanceState) {
- ...
-
- buildRowsAdapter();
-}
-
-private void buildRowsAdapter() {
- mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
-
- for (int i = 0; i < NUM_ROWS; ++i) {
- ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
- new StringPresenter());
- listRowAdapter.add("Media Item 1");
- listRowAdapter.add("Media Item 2");
- listRowAdapter.add("Media Item 3");
- HeaderItem header = new HeaderItem(i, "Category " + i, null);
- mRowsAdapter.add(new ListRow(header, listRowAdapter));
- }
-
- mBrowseFragment.setAdapter(mRowsAdapter);
-}
-</pre>
-
-<p>This example shows a static implementation of the adapters. A typical media browsing
- application uses data from an online database or web service. For an example of a browsing
- application that uses data retrieved from the web, see the <a href="">!FIX</a> code sample.</p>
-
-<p>The following screenshot shows the output of this code on an Android TV device:</p>
-
-<img src="{@docRoot}preview/tv/images/browsefragment.png" alt="" height="XXX" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> Display layout example based on the
- {@link android.support.v17.leanback.app.BrowseFragment} and {@code StringPresenter}
- classes.
-</p>
-
-
-<h2 id="background">Updating the Background</h2>
-
-<p>In order to add visual interest to a media browsing app on TV, you can update the background
- image as users browse through content. This technique can make interaction with your app feel more
- cinematic and enjoyable for users.</p>
-
-<p>The Leanback support library provides a {@link
- android.support.v17.leanback.app.BackgroundManager} class for changing the background of your TV
- app activity. The following example shows how to create a simple method for updating the
- background:</p>
-
-<pre>
-protected void updateBackground(Drawable drawable) {
- BackgroundManager.getInstance(this).setDrawable(drawable);
-}
-</pre>
-
-<p>Many of the existing media browse apps automatically update the background as the user
- navigates through media listings. In order to do this, you can set up a selection listener to
- automatically update the background based on the user's current selection. The following example
- shows you how to set up an {@link android.support.v17.leanback.widget.OnItemSelectedListener}
- class to catch selection events and update the background:</p>
-
-<pre>
-protected void clearBackground() {
- BackgroundManager.getInstance(this).setDrawable(mDefaultBackground);
-}
-
-protected OnItemSelectedListener getDefaultItemSelectedListener() {
- return new OnItemSelectedListener() {
- @Override
- public void onItemSelected(Object item, Row row) {
- if (item instanceof Movie ) {
- URI uri = ((Movie)item).getBackdropURI();
- updateBackground(uri);
- } else {
- clearBackground();
- }
- }
- };
-}
-</pre>
-
-<p class="note">
- <strong>Note:</strong> The implementation above is a simple example shown for purposes of
- illustration. When creating this function in your own app, you should consider running the
- background update action in a separate thread for better performance. In addition, if you are
- planning on updating the background in response to user's scrolling through items, consider adding
- a time to delay a background image update until the user settles on item, to avoid excessive
- background image updates.
-</p>
diff --git a/docs/html/preview/tv/ui/details.jd b/docs/html/preview/tv/ui/details.jd
deleted file mode 100644
index 7da3b5d..0000000
--- a/docs/html/preview/tv/ui/details.jd
+++ /dev/null
@@ -1,230 +0,0 @@
-page.title=DetailFragment
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=BrowseFragment
-previous.link=browse.html
-next.title=Searching in TV Apps
-next.link=in-app-search.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#details-presenter">Build a Details Presenter</a></li>
- <li><a href="#details-fragment">Extend the Details Fragment</a>
- <li><a href="#activity">Creating a Details Activity</a></li>
- <li><a href="#item-listener">Listener for Clicked Items</a></li>
- </li>
- </ol>
-</div>
-</div>
-
-<p>The media browsing interface classes provided by the
- <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
- include classes for displaying additional information about a media item, such as a description
- or reviews, and taking action on that item, such as purchasing it or playing its content. This
- section discusses how to create a presenter class for media item details and extend the
- {@code DetailsFragment} class to implement a details view for a media item when it
- is selected by a user.
-</p>
-
-<p class="note">
- <strong>Note:</strong> The implementation example shown here uses an additional activity to
- contain the {@code DetailsFragment}. However, it is possible to avoid creating a second activity
- by replacing the current {@code BrowseFragment} with a {@code DetailsFragment} within the <em>same</em>
- activity using fragment transactions. For more information on using fragment transactions, see the
- <a href="{@docRoot}training/basics/fragments/fragment-ui.html#Replace">Building a Dynamic
- UI with Fragments</a> training.
-</p>
-
-
-<h2 id="details-presenter">Build a Details Presenter</h2>
-
-<p>In the media browsing framework provided for by the leanback support library, you use
- presenter objects to control the display of data on screen, including media item details. The
- framework provides the {@code AbstractDetailsDescriptionPresenter} class for this purpose, which
- is a nearly complete implementation of the presenter for media item details. All you have to do is
- implement the {@code onBindDescription()} method to bind the view fields to your data objects, as shown in
- the following code sample:</p>
-
-<pre>
-public class DetailsDescriptionPresenter
- extends AbstractDetailsDescriptionPresenter {
-
- @Override
- protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
- MyMediaItemDetails details = (MyMediaItemDetails) itemData;
- // In a production app, the itemData object contains the information
- // needed to display details for the media item:
- // viewHolder.getTitle().setText(details.getShortTitle());
-
- // Here we provide static data for testing purposes:
- viewHolder.getTitle().setText(itemData.toString());
- viewHolder.getSubtitle().setText("2014 Drama TV-14");
- viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur "
- + "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
- + " et dolore magna aliqua. Ut enim ad minim veniam, quis "
- + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
- + "commodo consequat.");
- }
-}
-</pre>
-
-
-<h2 id="details-fragment">Extend the Details Fragment</h2>
-
-<p>When you use the {@code DetailsFragment} class for displaying your media item details, you
- extend that class to provide additional content such as a preview image and actions for the media
- item. You can also provide additional content, such as a list of related media items.</p>
-
-<p>The following example code demonstrates how to use the presenter class you created in the
- previous section, add a preview image and actions for the media item being viewed. This example
- also shows the addition of a related media items row, which appears below the details listing.</p>
-
-<pre>
-public class MediaItemDetailsFragment extends DetailsFragment {
- private static final String TAG = "MediaItemDetailsFragment";
- private ArrayObjectAdapter mRowsAdapter;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Log.i(TAG, "onCreate");
- super.onCreate(savedInstanceState);
-
- buildDetails();
- }
-
- private void buildDetails() {
- ClassPresenterSelector selector = new ClassPresenterSelector();
- // Attach your media item details presenter to the row presenter:
- DetailsOverviewRowPresenter rowPresenter =
- new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
-
- selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
- selector.addClassPresenter(ListRow.class,
- new ListRowPresenter());
- mRowsAdapter = new ArrayObjectAdapter(selector);
-
- Resources res = getActivity().getResources();
- DetailsOverviewRow detailsOverview = new DetailsOverviewRow(
- "Media Item Details");
-
- // Add images and action buttons to the details view
- detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans));
- detailsOverview.addAction(new Action(1, "Buy $9.99"));
- detailsOverview.addAction(new Action(2, "Rent $2.99"));
- mRowsAdapter.add(detailsOverview);
-
- // Add a Related items row
- ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
- new StringPresenter());
- listRowAdapter.add("Media Item 1");
- listRowAdapter.add("Media Item 2");
- listRowAdapter.add("Media Item 3");
- HeaderItem header = new HeaderItem(0, "Related Items", null);
- mRowsAdapter.add(new ListRow(header, listRowAdapter));
-
- setAdapter(mRowsAdapter);
- }
-}
-</pre>
-
-<p>The following screenshot shows the output of this code on a TV device:</p>
-
-<img src="{@docRoot}preview/tv/images/detailsfragment.png" alt="" height="XXX" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> Display layout example based on {@code DetailsFragment}, using a
- {@code DetailsOverviewRow} and a {@code ListRow} for related items.
-</p>
-
-
-<h3 id="activity">Creating a Details Activity</h3>
-
-<p>Fragments such as the {@code DetailsFragment} must be contained within an activity in order
- to be used for display. Creating an activity for your details view, separate from the browse
- activity, enables you to invoke your details view using an Intent. This section explains how to
- build an activity to contain your implementation of the detail view for your media items.</p>
-
-<p>Start creating the details activity by building a layout that references your implementation
- of the {@code DetailsFragment}:</p>
-
-<pre>
-<!-- file: res/layout/details.xml -->
-
-<fragment xmlns:android="http://schemas.android.com/apk/res/android"
- <strong>android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"</strong>
- android:id="@+id/details_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
-/>
-</pre>
-
-<p>Next, create an activity class that uses the layout shown in the previous code example:</p>
-
-<pre>
-public class DetailsActivity extends Activity
-{
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- <strong>setContentView(R.layout.details);</strong>
- }
-}
-</pre>
-
-<p>Finally, add this new activity to the manifest. Remember to apply the Leanback theme to
- ensure that the user interface is consistent with the media browse activity:</p>
-
-<pre>
-<application>
- ...
-
- <activity android:name=".DetailsActivity"
- android:exported="true"
- <strong>android:theme="@style/Theme.Leanback"/></strong>
-
-</application>
-</pre>
-
-
-<h3 id="item-listener">Listener for Clicked Items</h3>
-
-<p>After you have implemented the {@code DetailsFragment}, you must modify your main media
- browsing view to move to your details view when a user clicks on a media item. In order to enable
- this behavior, add an {@code OnItemClickedListener} object to the BrowseFragment that fires an
- intent to start the item details activity.</p>
-
-<p>The following example shows how to implement a listener to start the details view when a user
- clicks a media item in the main media browsing activity:</p>
-
-<pre>
-public class BrowseMediaActivity extends Activity {
- ...
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- ...
-
- // create the media item rows
- buildRowsAdapter();
-
- // add a listener for selected items
- mBrowseFragment.setOnItemClickedListener(
- new OnItemClickedListener() {
- @Override
- public void onItemClicked(Object item, Row row) {
- System.out.println("Media Item clicked: " + item.toString());
- Intent intent = new Intent(BrowseMediaActivity.this,
- DetailsActivity.class);
- // pass the item information
- intent.getExtras().putLong("id", item.getId());
- startActivity(intent);
- }
- });
- }
-}
-</pre>
diff --git a/docs/html/preview/tv/ui/in-app-search.jd b/docs/html/preview/tv/ui/in-app-search.jd
deleted file mode 100644
index b372254..0000000
--- a/docs/html/preview/tv/ui/in-app-search.jd
+++ /dev/null
@@ -1,119 +0,0 @@
-page.title=Searching in TV Apps
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=DetailsFragment
-previous.link=details.html
-next.title=Recommendations
-next.link=recommendations.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#add-search-ui">Add Search User Interface</a></li>
- </ol>
-
-</div>
-</div>
-
-
-<p>Users frequently have specific content in mind when using a media app. A search interface can
- help your users get to the content they want faster than browsing. The Leanback library provides a
- set of classes to enable a standard search interface within your app that is consistent with other
- search functions on TV and provides features such as voice input.</p>
-
-<h2 id="add-search-ui">Add Search User Interface</h2>
-<p>When you use the BrowseFragment class for your media browsing interface, you can enable the
- search icon by setting an OnClickListener to the browse fragment object. The following sample code
- demonstrates this technique.</p>
-
-<pre>
-@Override
-public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.browse_activity);
-
- mBrowseFragment = (BrowseFragment)
- getFragmentManager().findFragmentById(R.id.browse_fragment);
-
- ...
-
- mBrowseFragment.setOnSearchClickedListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(BrowseActivity.this, SearchActivity.class);
- startActivity(intent);
- }
- });
-
- mBrowseFragment.setAdapter(buildAdapter());
-}
-</pre>
-
-<p class="note">
- <strong>Note:</strong> You can set the color of the search icon using the
- {@code setSearchAffordanceColor()} method of {@code BrowseFragment}.
-</p>
-
-<p>When a user selects the search icon, the system invokes a search activity via the defined
- Intent. Your search activity should use a linear layout containing a SearchFragment. This fragment
- must also implement the SearchFragment.SearchResultProvider interface in order to display the
- results of a search. The following code sample shows how to extend the SearchFragment class to
- provide a search interface and results:</p>
-
-<pre>
-public class MySearchFragment extends SearchFragment
- implements SearchFragment.SearchResultProvider {
-
- private static final int SEARCH_DELAY_MS = 300;
- private ArrayObjectAdapter mRowsAdapter;
- private Handler mHandler = new Handler();
- private SearchRunnable mDelayedLoad;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
- setSearchResultProvider(this);
- setOnItemClickedListener(getDefaultItemClickedListener());
- mDelayedLoad = new SearchRunnable();
- }
-
- @Override
- public ObjectAdapter getResultsAdapter() {
- return mRowsAdapter;
- }
-
- @Override
- public boolean onQueryTextChange(String newQuery) {
- mRowsAdapter.clear();
- if (!TextUtils.isEmpty(newQuery)) {
- mDelayedLoad.setSearchQuery(newQuery);
- mHandler.removeCallbacks(mDelayedLoad);
- mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
- }
- return true;
- }
-
- @Override
- public boolean onQueryTextSubmit(String query) {
- mRowsAdapter.clear();
- if (!TextUtils.isEmpty(query)) {
- mDelayedLoad.setSearchQuery(query);
- mHandler.removeCallbacks(mDelayedLoad);
- mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
- }
- return true;
- }
-}
-</pre>
-
-<p>This example code shown above is meant to be used with a separate SearchRunnable class, that
- runs the search query on a separate thread. This technique keeps potentially slow-running queries
- from interfering with the main user interface thread.</p>
-
diff --git a/docs/html/preview/tv/ui/index.jd b/docs/html/preview/tv/ui/index.jd
deleted file mode 100644
index 9513bc6..0000000
--- a/docs/html/preview/tv/ui/index.jd
+++ /dev/null
@@ -1,44 +0,0 @@
-page.title=User Interfaces for TV
-page.tags="input","screens"
-
-trainingnavtop=true
-startpage=true
-
-@jd:body
-
-
-<p>
- Building an effective and engaging for TV devices requires a firm understanding what works well
- in the context of a living room. Imagine a large screen that can be seen by many people at the
- same time, controlled a few buttons by users with limited attention and you start to see the
- challenges and opportunity of building an app for TV. Building apps for this environment
- requires a different approach and different tools.</p>
-
-<p>This section discusses how to build a living room experience with your app, including
- implementation instructions and user interface widgets built for TV. Also check out the
- <a href="{@docRoot}design/tv/index.html">Design for TV</a> for information and inspiration
- on creating engaging user interfaces for TV devices.</p>
-
-<h2>Topics</h2>
-
-<dl>
- <dt><b><a href="layouts.html">Layouts</a></b></dt>
- <dd>Learn how to build app layouts for TV screens.</dd>
-
- <dt><b><a href="navigation.html">Navigation</a></b></dt>
- <dd>Learn how to build navigation for TV devices.</dd>
-
- <dt><b><a href="browse.html">BrowseFragment</a></b></dt>
- <dd>Learn how to use this fragment to build a browsing interface for media catalogs.</dd>
-
- <dt><b><a href="details.html">DetailsFragment</a></b></dt>
- <dd>Learn how to use this fragment to build a details page for media items.</dd>
-
- <dt><b><a href="search.html">In-App Search</a></b></dt>
- <dd>Learn how to use a built-for-TV user interface for searching within your app.</dd>
-
- <dt><b><a href="recommendations.html">Recommendations</a></b></dt>
- <dd>Learn how to contribute watch next suggestions and get your content noticed by users.</dd>
-</dl>
-
-
diff --git a/docs/html/preview/tv/ui/layouts.jd b/docs/html/preview/tv/ui/layouts.jd
deleted file mode 100644
index 5655152..0000000
--- a/docs/html/preview/tv/ui/layouts.jd
+++ /dev/null
@@ -1,298 +0,0 @@
-page.title=Layouts for TV
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-next.title=Navigation for TV
-next.link=navigation.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#themes">Themes</a>
- <ol>
- <li><a href="#leanback-theme">Leanback Theme</a></li>
- </ol>
- </li>
- <li><a href="#structure">Layout Structure</a>
- <ol>
- <li><a href="#overscan">Overscan</a></li>
- </ol>
- </li>
- <li><a href="#visibility">Text and Controls Visibility</a></li>
- <li><a href="#density-resources">Screen Density and Image Resources</a></li>
- <li><a href="#anti-patterns">Layout Anti-Patterns</a></li>
- <li><a href="#large-bitmaps">Handling Large Bitmaps</a></li>
- </ol>
-
-</div>
-</div>
-
-<p>
- A TV screen is typically viewed from about 10 feet away, and while it is much larger than most
- other Android device displays, this type of screen does not provide the same level of precise
- detail and color as a smaller device. These factors require that you create app layouts with
- TV devices in mind in order to create a useful and enjoyable user experience.</p>
-
-<p>This guide provides guidance and implementation details for building effective layouts for
- TV apps.</p>
-
-
-<h2 id="themes">Themes</h2>
-
-<p>Android <a href="{@docRoot}guide/topics/ui/themes.html">Themes</a> can provide a basis for
- layouts in your apps. On TV devices, you should use a theme to suppress the title bar from
- your app activities that are meant for TV. The title bar is a standard user interface element
- for Android apps on phones and tablets, but it is not appropriate for TV apps. The following
- code example from TV app manifest demonstrates how to apply this theme:
-</p>
-
-<pre>
-<application>
- ...
-
- <activity
- android:name="com.example.android.TvActivity"
- android:label="@string/app_name"
- <strong>android:theme="@android:style/Theme.NoTitleBar"</strong>>
- ...
-
- </activity>
-</application>
-
-</pre>
-
-
-<h3 id="leanback-theme">Leanback Theme</h3>
-
-<p>The Android framework provides a standard theme for TV activities, called {@code
- Leanback.Theme}, which establishes a consistent visual style for TV apps. Use of this theme is
- recommended for most apps. The following code sample shows how to apply this theme to a given
- activity within an app:</p>
-
-<pre>
-<activity
- android:name="com.example.android.TvActivity"
- android:label="@string/app_name"
- <strong>android:theme="@android:style/Theme.Leanback"</strong>>
-</pre>
-
-
-<h2 id="structure">Layout Structure</h2>
-
-<p>Layouts for TV devices should follow some basic guidelines to ensure they are useable and
- effective on large screens. Follow these tips to build landscape layouts optimized for TV screens:
-</p>
-
-<ul>
- <li>Build layouts with a landscape orientation. TV screens are always displayed in this
- orientation.</li>
- <li>Put on-screen navigation controls on the left or right side of the screen and save the
- vertical space for content.</li>
- <li>Create UIs that are divided into sections, using <a
- href="{@docRoot}guide/components/fragments.html"
- >Fragments</a> and use view groups like {@link android.widget.GridView} instead of {@link
- android.widget.ListView} to make better use of the horizontal screen space.
- </li>
- <li>Use view groups such as {@link android.widget.RelativeLayout} or {@link
- android.widget.LinearLayout} to arrange views. This approach allows the system to adjust the
- position of the views to the size, alignment, aspect ratio, and pixel density of a TV screen.</li>
- <li>Add sufficient margins between layout controls to avoid a cluttered UI.</li>
-</ul>
-
-
-<h3 id="overscan">Overscan</h3>
-
-<p>Layouts for TV have some unique requirements due to the evolution of TV standards and the
- desire to always present a full screen picture to viewers. For this reason, TV devices may
- clip the outside edge of an app layout in order to ensure a that the entire display is filled.
- This behavior is generally referred to as Overscan.</p>
-
-<p>In order to account for the impact of overscan and make sure that all the user interface
- elements you place in a layout are actually shown on screen, you should incorporate a 10% margin
- on all sides of your layout. This translates into a 27dp margin on the left and right edges and
- a 48dp margin on the top and bottom of your base layouts for activities. The following
- example layout demonstrates how to set these margins in the root layout for a TV app:
-</p>
-
-<pre>
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/base_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:layout_marginTop="27dp"
- android:layout_marginLeft="48dp"
- android:layout_marginRight="48dp"
- android:layout_marginBottom="27dp" >
-</LinearLayout>
-</pre>
-
-<p class="caution">
- <strong>Caution:</strong> Do not apply overscan margins to your layout if you are using the
- Leanback Support Library {@code BrowseFragment} or related widgets, as those layouts already
- incorporate overscan-safe margins.
-</p>
-
-
-<h2 id="visibility">Text and Controls Visibility</h2>
-
-<p>
-The text and controls in a TV app layout should be easily visible and navigable from a distance.
-Follow these tips to make them easier to see from a distance :
-</p>
-
-<ul>
- <li>Break text into small chunks that users can quickly scan.</li>
- <li>Use light text on a dark background. This style is easier to read on a TV.</li>
- <li>Avoid lightweight fonts or fonts that have both very narrow and very broad strokes.
- Use simple sans-serif fonts and anti-aliasing to increase readability.</li>
- <li>Use Android's standard font sizes:
-<pre>
-<TextView
- android:id="@+id/atext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
-</pre>
- </li>
- <li>Ensure that all your view widgets are large enough to be clearly visible to someone
- sitting 10 feet away from the screen (this distance is greater for very large screens). The
- best way to do this is to use layout-relative sizing rather than absolute sizing, and
- density-independent pixel units instead of absolute pixel units. For example, to set the
- width of a widget, use wrap_content instead of a pixel measurement, and to set the margin
- for a widget, use dip instead of px values.</li>
-</ul>
-
-
-<h2 id="density-resources">Screen Density and Image Resources</h2>
-
-<p>The common high-definition TV display resolutions are 720p, 1080i, and 1080p.
- Your TV layout should target a screen size of 1920 x 1080 pixels, and then allow the Android
- system to downscale your layout elements to 720p if necessary. In general, downscaling
- (removing pixels) does not degrade your layout presentation quality. However, upscaling can
- cause display artifacts that degrade the quality of your layout and have a negative impact on
- the user experience of your app.</p>
-
-<p>
- To get the best scaling results for images, provide them as
- <a href="{@docRoot}tools/help/draw9patch.html">9-patch image</a> elements if possible. If you
- provide low quality or small images in your layouts, they will appear pixelated, fuzzy, or
- grainy. This is not a good experience for the user. Instead, use high-quality images.
-</p>
-
-<p>
- For more information on optimizing layouts and resources for large screens see
- <a href="{@docRoot}training/multiscreen/index.html">Designing for multiple screens</a>.
-</p>
-
-
-<h2 id="anti-patterns">Layout Anti-Patterns</h2>
-
-<p>There are a few approaches to building layouts for TV that you should avoid because they do not
-work well and lead to bad user experiences. Here are some user interface approaches you
-should specifically <em>not</em> use when developing a layout for TV.
-</p>
-
-<ul>
- <li><strong>Re-using phone or tablet layouts</strong> - Do not reuse layouts from a phone or
- tablet app without modification. Layouts built for other Android device form factors are not
- well suited for TV devices and should be simplified for operation on a TV.</li>
- <li><strong>ActionBar</strong> - While this user interface convention is recommended for use
- on phones and tablets, it is not appropriate for a TV interface. In particular, using an
- antion bar options menu (or any pull-down menu for that matter) is strongly discouraged, due
- to the difficulty in navigating such a menu with a remote control.</li>
- <li><strong>ViewPager</strong> - Sliding between screens can work great on a phone or tablet,
- but don't try this on a TV!</li>
-
-</ul>
-
-<p>For more information on designing layouts that are appropriate to TV, see the
- <a href="{@docRoot}design/tv/index.html">TV Design</a> guide.</p>
-
-
-<h2 id="large-bitmaps">Handling Large Bitmaps</h2>
-
-<p>TV devices, like any other Android device, have a limited amount of memory. If you build your
- app layout with very high-resolution images or use many high-resolutions images in the operation
- of your app, it can quickly run into memory limits and cause out of memory errors.
- To avoid these types of problems, follow these tips:</p>
-
-<ul>
- <li>Load images only when they're displayed on the screen. For example, when displaying multiple images in
- a {@link android.widget.GridView} or
- {@link android.widget.Gallery}, only load an image when
- {@link android.widget.Adapter#getView(int, View, ViewGroup) getView()}
- is called on the View's {@link android.widget.Adapter}.
- </li>
- <li>Call {@link android.graphics.Bitmap#recycle()} on
- {@link android.graphics.Bitmap} views that are no longer needed.
- </li>
- <li>Use {@link java.lang.ref.WeakReference} for storing references
- to {@link android.graphics.Bitmap} objects in an in-memory
- {@link java.util.Collection}.</li>
- <li>If you fetch images from the network, use {@link android.os.AsyncTask}
- to fetch them and store them on the SD card for faster access.
- Never do network transactions on the application's UI thread.
- </li>
- <li>Scale down large images to a more appropriate size as you download them;
- otherwise, downloading the image itself may cause an out of memory exception.
- The following sample code demonstrates how to scale down images while downloading:
-<pre>
- // Get the source image's dimensions
- BitmapFactory.Options options = new BitmapFactory.Options();
- // This does not download the actual image, just downloads headers.
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
- // The actual width of the image.
- int srcWidth = options.outWidth;
- // The actual height of the image.
- int srcHeight = options.outHeight;
-
- // Only scale if the source is bigger than the width of the destination view.
- if(desiredWidth > srcWidth)
- desiredWidth = srcWidth;
-
- // Calculate the correct inSampleSize/scale value. This approach helps reduce
- // memory use. This value should be a power of 2.
- int inSampleSize = 1;
- while(srcWidth / 2 > desiredWidth){
- srcWidth /= 2;
- srcHeight /= 2;
- inSampleSize *= 2;
- }
-
- float desiredScale = (float) desiredWidth / srcWidth;
-
- // Decode with inSampleSize
- options.inJustDecodeBounds = false;
- options.inDither = false;
- options.inSampleSize = inSampleSize;
- options.inScaled = false;
- // Ensures the image stays as a 32-bit ARGB_8888 image.
- // This preserves image quality.
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-
- Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
-
- // Resize
- Matrix matrix = new Matrix();
- matrix.postScale(desiredScale, desiredScale);
- Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0,
- sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
- sampledSrcBitmap = null;
-
- // Save
- FileOutputStream out = new FileOutputStream(LOCAL_PATH_TO_STORE_IMAGE);
- scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
- scaledBitmap = null;
-</pre>
- </li>
-</ul>
-
diff --git a/docs/html/preview/tv/ui/navigation.jd b/docs/html/preview/tv/ui/navigation.jd
deleted file mode 100644
index 3041e58..0000000
--- a/docs/html/preview/tv/ui/navigation.jd
+++ /dev/null
@@ -1,144 +0,0 @@
-page.title=Navigation for TV
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=Layouts for TV
-previous.link=layouts.html
-next.title=BrowseFragment
-next.link=browse.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#d-pad-navigation">D-pad Navigation</a></li>
- <li><a href="#focus-selection">Focus and Selection</a></li>
- </ol>
-
-</div>
-</div>
-
-<p>TV devices provide a limited set of navigation controls for apps. Creating an effective
- navigation scheme for your TV app depends on understanding these limited controls and the limits
- of users perception while operating your app. As you build your Android application for TVs,
- you should pay special attention to how the user actually navigates around your application
- when using remote control buttons instead of a touch screen.</p>
-
-<p>This guide shows you how to build an effective navigation scheme for your TV app.</p>
-
-
-<h2 id="d-pad-navigation">D-pad Navigation</h2>
-
-<p>On a TV device, users navigate with controls on a remote control device, using either a
- directional pad (D-pad) or arrow keys. This type of control limits movement to up, down, left,
- and right. To build a great TV-optimized app, you must provide a navigation scheme where
- the user can quickly learn how to navigate your app using these limited controls.</p>
-
-<p>Follow these guidelines to build navigation system that works well with a D-pad on a TV device:
-</p>
-
-<ul>
- <li>Ensure that the D-pad can navigate to all the visible controls on the screen.</li>
- <li>For scrolling lists with focus, D-pad up/down keys scroll the list and Enter key selects
- an item in the list. Ensure that users can select an element in the list and that the list still
- scrolls when an element is selected.</li>
- <li>Ensure that movement between controls is straightforward and predictable.</li>
-</ul>
-
-<p>The Android framework handles directional navigation between layout elements automatically, so
- you typically do not need to do anything extra for your app. However, you should thoroughly test
- navigation with a D-pad control to discover any navigation problems. If you discover that your
- screen layout makes navigation difficult, or if you want users to move through the layout in a
- specific way, you can set up explicit directional navigation for your controls. The following
- code sample shows how to define the next control to receive focus for a
- {@link android.widget.TextView} layout object:</p>
-
-<pre>
-<TextView android:id="@+id/Category1"
- android:nextFocusDown="@+id/Category2"\>
-</pre>
-
-<p>The following table lists all of the available navigation attributes for Android user interface
-widgets:</p>
-
-<table>
- <tr>
- <th>Attribute</th>
- <th>Function</th>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusDown}</td>
- <td>Defines the next view to receive focus when the user navigates down.</td>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusLeft}</td>
- <td>Defines the next view to receive focus when the user navigates left.</td>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusRight}</td>
- <td>Defines the next view to receive focus when the user navigates right.</td>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusUp}</td>
- <td>Defines the next view to receive focus when the user navigates up.</td>
- </tr>
-</table>
-
-<p>To use one of these explicit navigation attributes, set the value to the ID ({@code android:id}
- value) of another widget in the layout. You should set up the navigation order as a loop, so that
- the last control directs focus back to the first one.</p>
-
-<p class="note">
- <strong>Note:</strong> You should only use these attributes to modify the navigation order if the
- default order that the system applies does not work well.
-</p>
-
-
-<h2 id="focus-selection">Focus and Selection</h2>
-
-<p>The success of a navigation scheme on TV devices is strongly dependent on how easy it is for a
- user to determine what user interface element is in focus on screen. If you do not provide clear
- indications of what is in focus on screen (and therefore what item they can take action on),
- users can quickly become frustrated and exit your app. By the same token, it is important
- to always have an item in focus that a user can take action on immediately after your app starts,
- and any time your app is not playing content.</p>
-
-<p>Your app layout and implementation should use color, size, animation or a combination of
- these attributes to help users easily determine what actions they can take next. Use a uniform
- scheme for indicating focus across your application.</p>
-
-<p>Android provides <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">
-Drawable State List Resources</a> to implement highlights for selected and focused controls. The
-following code example demonstates how to apply selection indication for a button object:
-</p>
-
-<pre>
-<!-- res/drawable/button.xml -->
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"
- android:drawable="@drawable/button_pressed" /> <!-- pressed -->
- <item android:state_focused="true"
- android:drawable="@drawable/button_focused" /> <!-- focused -->
- <item android:state_hovered="true"
- android:drawable="@drawable/button_focused" /> <!-- hovered -->
- <item android:drawable="@drawable/button_normal" /> <!-- default -->
-</selector>
-</pre>
-
-<p>
-This layout XML applies the above state list drawable to a {@link android.widget.Button}:
-</p>
-<pre>
-<Button
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:background="@drawable/button" />
-</pre>
-
-<p>Make sure to provide sufficient padding within the focusable and selectable controls so that
- the highlights around them are clearly visible.</p>
-
diff --git a/docs/html/preview/tv/ui/recommendations.jd b/docs/html/preview/tv/ui/recommendations.jd
deleted file mode 100644
index 52ac2e5..0000000
--- a/docs/html/preview/tv/ui/recommendations.jd
+++ /dev/null
@@ -1,234 +0,0 @@
-page.title=Making Recommendations
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=Searching in TV Apps
-previous.link=in-app-search.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#service">Create a Recommendations Service</a></li>
- <li><a href="#build">Build Recommendations</a></li>
- <li><a href="#run-service">Run Recommendations Service</a></li>
- <li><a href="#DesignLandscapeLayouts">Design Landscape Layouts</a></li>
- </ol>
-
-</div>
-</div>
-
-
-<p>Content recommendations appear as the first row of the TV launch screen after the first use
- of the device. This row is intended to help users quickly find content they enjoy. Contributing
- recommendations from your apps content catalog can help bring users back to your app.</p>
-
-
-<img src="{@docRoot}preview/tv/images/home-recommendations.png" alt="" height="XXX" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> The first row after the search widget is the system-wide
- recommendations.
-</p>
-
-
-<h2 id="service">Create a Recommendations Service</h2>
-
-<p>Content recommendations are created with background processing. In order for your application
- to contribute to recommendations, you create a service that periodically adds listings from your
- app's catalog to the system list of recommendations.</p>
-
-<p>The following code example illustrates how to extend the {@link android.app.IntentService} to
- create a recommendation service for your application.</p>
-
-<pre>
-public class RecommendationsService extends IntentService {
-
- ...
-
- public Notification buildRecommendation(Context context, Movie movie)
- throws IOException {
-
- if (mNotificationManager == null) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- Bundle extras = new Bundle();
- if (mBackgroundUri != movie.getBackgroundUri()) {
- extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
- }
-
- // build the recommendation as a Notification object
- Notification notification = new NotificationCompat.BigPictureStyle(
- new NotificationCompat.Builder(context)
- .setContentTitle(movie.getTitle())
- .setContentText(movie.getDescription())
- .setPriority(movie.getPriority())
- .setOngoing(true)
- .setCategory("recommendation")
- .setLargeIcon(movie.getImage())
- .setSmallIcon(movie.getSmallIcon())
- .setContentIntent(buildPendingIntent(movie.getId()))
- .setExtras(extras))
- .build();
-
- // post the recommendation to the NotificationManager
- mNotificationManager.notify(movie.getId(), notification);
- mNotificationManager = null;
- return notification;
- }
-
- private PendingIntent buildPendingIntent(long id) {
- Intent detailsIntent = new Intent(this, DetailsActivity.class);
- detailsIntent.putExtra("id", id);
-
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
- stackBuilder.addParentStack(DetailsActivity.class);
- stackBuilder.addNextIntent(detailsIntent);
- // Ensure each PendingIntent is unique
- detailsIntent.setAction(Long.toString(id));
-
- PendingIntent intent = stackBuilder.getPendingIntent(
- 0, PendingIntent.FLAG_UPDATE_CURRENT);
- return intent;
- }
-}
-</pre>
-
-<p>In order for this class to be recognized and run as a service, you must register this service
- using your app manifest. The following code snippet illustrates how to add this class as a
- service:</p>
-
-<pre>
-<manifest ... >
- <application ... >
- ...
-
- <service android:name=".UpdateRecommendationsService"
- android:enabled="true" android:exported="true"/>
- </application>
-</manifest>
-</pre>
-
-<h2 id="build">Build Recommendations</h2>
-
-<p>Once it starts running, your service must create recommendations and pass them to the Android
- framework. The framework receives the recommendations as {@link android.app.Notification} objects
- that use a specific style and are marked with a specific category.</p>
-
-<p>The following code example demonstrates how to get an instance of the {@link
- android.app.NotificationManager}, build a recommendation and post it to the manager:</p>
-
-<pre>
-public class RecommendationsService extends IntentService {
-
- ...
-
- public Notification buildRecommendation(Context context, Movie movie)
- throws IOException {
-
- if (mNotificationManager == null) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- Bundle extras = new Bundle();
- if (mBackgroundUri != movie.getBackgroundUri()) {
- extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
- }
-
- // build the recommendation as a Notification object
- Notification notification = new NotificationCompat.BigPictureStyle(
- new NotificationCompat.Builder(context)
- .setContentTitle(movie.getTitle())
- .setContentText(movie.getDescription())
- .setPriority(movie.getPriority())
- .setOngoing(true)
- .setCategory("recommendation")
- .setLargeIcon(movie.getImage())
- .setSmallIcon(movie.getSmallIcon())
- .setContentIntent(buildPendingIntent(movie.getId()))
- .setExtras(extras))
- .build();
-
- // post the recommendation to the NotificationManager
- mNotificationManager.notify(movie.getId(), notification);
- mNotificationManager = null;
- return notification;
- }
-
- private PendingIntent buildPendingIntent(long id) {
- Intent detailsIntent = new Intent(this, DetailsActivity.class);
- detailsIntent.putExtra("id", id);
-
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
- stackBuilder.addParentStack(DetailsActivity.class);
- stackBuilder.addNextIntent(detailsIntent);
- // Ensure each PendingIntent is unique
- detailsIntent.setAction(Long.toString(id));
-
- PendingIntent intent = stackBuilder.getPendingIntent(
- 0, PendingIntent.FLAG_UPDATE_CURRENT);
- return intent;
- }
-}
-</pre>
-
-
-<h3 id="run-service">Run Recommendations Service</h3>
-
-<p>Your app's recommendation service must run periodically in order to create current
- recommendations. In order to run your service, you should create a class that runs a timer and
- invokes it at regular intervals. The following code example extends the {@link
- android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
- every 30 minutes:</p>
-
-<pre>
-public class BootupActivity extends BroadcastReceiver {
- private static final String TAG = "BootupActivity";
-
- private static final long INITIAL_DELAY = 5000;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
- scheduleRecommendationUpdate(context);
- }
- }
-
- private void scheduleRecommendationUpdate(Context context) {
- AlarmManager alarmManager = (AlarmManager)context.getSystemService(
- Context.ALARM_SERVICE);
- Intent recommendationIntent = new Intent(context,
- UpdateRecommendationsService.class);
- PendingIntent alarmIntent = PendingIntent.getService(context, 0,
- recommendationIntent, 0);
-
- alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- INITIAL_DELAY,
- AlarmManager.INTERVAL_HALF_HOUR,
- alarmIntent);
- }
-}
-</pre>
-
-<p>In order for the {@link android.content.BroadcastReceiver} class to execute after an TV
- device starts up, you must register this class in your app manifest and attach an intent filter
- for the completion of the device boot process. This sample code demonstrates how to add this
- configuration to the manifest:</p>
-
-<pre>
-<manifest ... >
- <application ... >
- <receiver android:name=".BootupActivity" android:enabled="true"
- android:exported="false">
- <intent-filter>
- <action android:name="android.intent.action.BOOT_COMPLETED"/>
- </intent-filter>
- </receiver>
- </application>
-</manifest>
-</pre>
diff --git a/docs/html/sdk/installing/adding-packages.jd b/docs/html/sdk/installing/adding-packages.jd
index c38c927..e6c0118 100644
--- a/docs/html/sdk/installing/adding-packages.jd
+++ b/docs/html/sdk/installing/adding-packages.jd
@@ -14,7 +14,7 @@
background:#eee;
}
ol.large > li:nth-child(odd) {
-}
+}
ol.large > li:before {
display:inline;
left:-40px;
@@ -74,9 +74,14 @@
</li>
</ul>
+<p>When you open the SDK Manager for the first time, several packages will be selected by
+default. Leave these selected, but be sure you have everything you need
+to get started by following these steps:</p>
+
+
<ol class="large">
<li>
- <h2 class="norule">Get the latest SDK tools</h2>
+ <h2 id="GetTools" class="norule">Get the latest SDK tools</h2>
<img src="/images/sdk_manager_packages.png" alt="" width="350" style="float:right;margin-left:20px" />
@@ -87,7 +92,7 @@
<ul>
<li><strong>Android SDK Tools</strong></li>
<li><strong>Android SDK Platform-tools</strong></li>
- <li><strong>Android SDK Build-tools</strong></li>
+ <li><strong>Android SDK Build-tools</strong> (highest version)</li>
</ul>
</li>
<li>Open the first Android X.X folder (the latest version) and select:
@@ -97,16 +102,13 @@
<strong>ARM EABI v7a System Image</strong></li>
</ul>
</li>
- <li>Click <strong>Install</strong>.</li>
</ol>
</li>
<li>
- <h2 class="norule">Get the support library for additional APIs</h2>
+ <h2 id="GetSupportLib" class="norule">Get the support library for additional APIs</h2>
<div class="sidebox">
- <h3>Why use the support library?</h3>
-
<p>The support library is required for:</p>
<ul>
<li><a href="{@docRoot}wear/index.html">Android Wear</a></li>
@@ -114,7 +116,7 @@
<li><a href="{@docRoot}google/play-services/cast.html">Google Cast</a></li>
</ul>
- <p>The support library also provides these popular APIs:</p>
+ <p>It also provides these popular APIs:</p>
<ul>
<li><a href="{@docRoot}reference/android/support/v4/widget/DrawerLayout.html">Navigation
drawer</a></li>
@@ -127,16 +129,11 @@
<p>The <a href="{@docRoot}tools/support-library/features.html">Android Support Library</a>
provides an extended set of APIs that are compatible with most versions of Android.</p>
- <p>To download the support library:</p>
- <ol>
- <li>Open the <strong>Extras</strong> directory and select:
+ <p>Open the <strong>Extras</strong> directory and select:</p>
<ul>
<li><strong>Android Support Repository</strong></li>
<li><strong>Android Support Library</strong></li>
</ul>
- </li>
- <li>Click <strong>Install</strong>.</li>
- </ol>
<p> </p>
<p> </p>
@@ -145,10 +142,9 @@
<li>
- <h2 class="norule">Get Google Play services for even more APIs</h2>
+ <h2 id="GetGoogle" class="norule">Get Google Play services for even more APIs</h2>
<div class="sidebox">
- <h3>Why use Google Play services?</h3>
<p>The Google Play services APIs provide a variety of features and services for your Android
apps, such as:</p>
@@ -163,15 +159,11 @@
</div>
<p>To develop with Google APIs, you need the Google Play services package:</p>
- <ol>
- <li>Open the <strong>Extras</strong> directory and select:
+ <p>Open the <strong>Extras</strong> directory and select:</p>
<ul>
<li><strong>Google Repository</strong></li>
<li><strong>Google Play services</strong></li>
</ul>
- </li>
- <li>Click <strong>Install</strong>.</li>
- </ol>
<p class="note"><strong>Note:</strong> Google Play services APIs are not available on all
Android-powered devices, but are available on all devices with Google Play Store. To use these
@@ -180,9 +172,21 @@
</li>
+<li>
+ <h2 id="Install" class="norule">Install the packages</h2>
+ <p>Once you've selected all the desired packages, continue to install:</p>
+ <ol>
+ <li>Click <strong>Install X packages</strong>.</li>
+ <li>In the next window, double-click each package name on the left
+ to accept the license agreement for each.</li>
+ <li>Click <strong>Install</strong>.</li>
+ </ol>
+ <p>The download progress is shown at the bottom of the SDK Manager window.
+ <strong>Do not exit the SDK Manager</strong> or it will cancel the download.</p>
+</li>
<li>
- <h2 class="norule">Build something!</h2>
+ <h2 id="Build" class="norule">Build something!</h2>
<p>With the above packages now in your Android SDK, you're ready to build apps
for Android. As new tools and other APIs become available, simply launch the SDK Manager
@@ -208,8 +212,8 @@
<h3>Use Google APIs</h3>
<p>To start using Google APIs, such as Maps or
Play Game services, see the guide to
-<strong><a href="{@docRoot}google/auth/api-client.html">Accessing Google Play Services
-APIs</a></strong>.</p>
+<strong><a href="{@docRoot}google/play-services/setup.html">Setting Up Google Play
+Services</a></strong>.</p>
</div>
</div><!-- end cols -->
diff --git a/docs/html/sdk/installing/index.jd b/docs/html/sdk/installing/index.jd
index b6929bd..3671726 100644
--- a/docs/html/sdk/installing/index.jd
+++ b/docs/html/sdk/installing/index.jd
@@ -108,13 +108,13 @@
your JDK folder, for example <code>C:\Program Files\Java\jdk1.7.0_21</code>.</p>
</p>
</li>
-
+
</ol>
<p>The individual tools and
other SDK packages are saved within the Android Studio application directory.
-To access the tools directly, use a terminal to navigate into the application and locate
+If you need to access the tools directly, use a terminal to navigate into the application and locate
the {@code sdk/} directory. For example:</p>
<p><code>\Users\<user>\AppData\Local\Android\android-studio\sdk\</code></p>
@@ -140,7 +140,7 @@
<p>The individual tools and
other SDK packages are saved within the Android Studio application directory.
-To access the tools directly, use a terminal to navigate into the application and locate
+If you need access the tools directly, use a terminal to navigate into the application and locate
the {@code sdk/} directory. For example:</p>
<p><code>/Applications/Android\ Studio.app/sdk/</code></p>
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index af6bd75..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>
@@ -339,18 +339,12 @@
</tr>
<tr>
- <td>APK signing</td>
+ <td>APK signing and keystore management</td>
<td class="yes">Yes</td>
<td class="yes">Yes</td>
</tr>
<tr>
- <td>Keystore management</td>
- <td class="no">Coming soon</td>
- <td class="yes">Yes</td>
- </tr>
-
- <tr>
<td>NDK support</td>
<td class="no">Coming soon</td>
<td class="yes">Yes</td>
@@ -619,7 +613,7 @@
function onDownloadForRealz(link) {
if ($("input#agree").is(':checked')) {
- $("h1").text('Now downloading Android Studio...');
+ $("h1").text('Now redirecting to the install instructions...');
$("#tos").slideUp();
$("#jd-content .jd-descr").fadeOut('slow', function() {
setTimeout(function() {
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/docs/html/tv/images/hero.jpg b/docs/html/tv/images/hero.jpg
index c42a436..e951167 100644
--- a/docs/html/tv/images/hero.jpg
+++ b/docs/html/tv/images/hero.jpg
Binary files differ
diff --git a/docs/html/tv/index.jd b/docs/html/tv/index.jd
index e1cae8c..5c48e49 100644
--- a/docs/html/tv/index.jd
+++ b/docs/html/tv/index.jd
@@ -4,7 +4,6 @@
no_footer_links=true
page.type=about
-
@jd:body
<style>
@@ -14,17 +13,9 @@
}
</style>
-<style>
-#footer {
- display: none;
-}
-.content-footer {
- display: none;
-}
-</style>
-
<div class="landing-body-content">
+
<div class="landing-hero-container">
<div class="landing-section tv-hero">
@@ -42,9 +33,11 @@
Put your app on TV and bring everyone into
the action.</p>
</div>
+ </div>
<div class="landing-body">
- <a href="{@docRoot}preview/tv/index.html" class="landing-button landing-primary" style="margin-top: 40px;">
+ <a href="{@docRoot}preview/tv/start/index.html" class="landing-button
+ landing-primary" style="margin-top: 40px;">
Get Started
</a>
</div>
@@ -58,11 +51,10 @@
</a>
</div>
</div> <!-- end .landing-section .landing-hero -->
- </div> <!-- end .landing-hero-container -->
<div class="landing-rest-of-page">
- <div class="landing-section landing-gray-background" id="reimagine-your-app">
+ <div class="landing-section" style="background-color:#f5f5f5" id="reimagine-your-app">
<div class="wrap">
<div class="landing-section-header">
<div class="landing-h1">Reimagine Your App</div>
@@ -71,7 +63,6 @@
</div>
</div>
-
<div class="landing-body">
<div class="landing-breakout cols">
@@ -119,13 +110,13 @@
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
- <div class="landing-section" style="background-color:#f5f5f5">
+ <div class="landing-section landing-gray-background">
<div class="wrap">
<div class="landing-section-header">
<div class="landing-h1">Build to Entertain</div>
<div class="landing-subhead">
- Android TV let's you engage your users in a new, shared environment.<br>
- Find out how to get your app ready for it's big screen debut.
+ Android TV lets you engage your users in a new, shared environment.<br>
+ Find out how to get your app ready for its big-screen debut.
</div>
</div>
@@ -142,7 +133,7 @@
catalogs.
</p>
<p class="landing-small">
- <a href="{@docRoot}design/tv/index.html">Learn pre-built fragments</a>
+ <a href="{@docRoot}preview/tv/ui/browse.html">Learn pre-built fragments</a>
</p>
</div>
@@ -151,11 +142,10 @@
<p>Get Found</p>
<p class="landing-small">
- Give your content the attention it deserves by including it in Android TV's global
- search results.
+ Help users find your content quickly with in-app searching.
</p>
<p class="landing-small">
- <a href="{@docRoot}design/tv/index.html">Learn about TV design</a>
+ <a href="{@docRoot}preview/tv/ui/in-app-search.html">Learn about app search</a>
</p>
</div>
@@ -167,7 +157,8 @@
Suggest content from your app to keep your users coming back.
</p>
<p class="landing-small">
- <a href="{@docRoot}design/tv/index.html">Learn about design for TV</a>
+ <a href="{@docRoot}preview/tv/ui/recommendations.html">Learn about
+ recommendations</a>
</p>
</div>
@@ -182,28 +173,26 @@
<div class="landing-section-header">
<div class="landing-h1 landing-align-left">Get Started with Android TV</div>
<div class="landing-body">
- <p>You can begin building apps right away using these developer resources.</p>
+ <p>Begin building TV apps right away using these developer resources:</p>
</div>
</div>
<div class="landing-body">
<div class="landing-breakout cols">
- <div class="col-8">
- <p>Preview SDK</p>
+ <div class="col-8" style="margin-left: -8px;">
+ <p style="font-size: 24px;">L-Preview SDK</p>
<p>
- Get started building for Android TV using the Android L-preview SDK. The preview
- SDK includes the Android TV emulator so you can start building your TV app right
- away.
+ The preview SDK includes all the tools you need to build and test apps for TV.
+ Download it and start creating your big-screen app.
</p>
</div>
<div class="col-8">
- <p>ADT-1 Developer Kit</p>
+ <p style="font-size: 24px;">ADT-1 Developer Kit</p>
<p>
- While supplies last, developers can request an ADT-1 Developer Kit, a compact and
- powerful streaming media player and gamepad, ideal for developing apps for Android
- TV.
+ Request an ADT-1 Developer Kit, a compact and powerful streaming media player
+ and gamepad, ideal for developing and testing apps for TV.
</p>
</div>
@@ -215,15 +204,16 @@
<div class="landing-breakout cols">
<div class="col-8">
- <a href="{@docRoot}preview/download.html" class="landing-button landing-secondary">
+ <a href="{@docRoot}preview/setup-sdk.html" class="landing-button landing-primary">
Download the Preview SDK
</a>
</div>
<div class="col-8">
- <a href="{@docRoot}tv/adt-1/request.html" class="landing-button landing-secondary">
+ <a href="{@docRoot}preview/tv/adt-1/request.html" class="landing-button landing-primary">
Request ADT-1 Developer Kit
</a>
+ </div>
</div>
</div>
@@ -232,31 +222,33 @@
</div> <!-- end .landing-rest-of-page -->
-
- <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 class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement"
+ style="border-top: none;">
+ <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>
- <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 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>
-
- </div> <!-- end landing-body-content -->
+ </div> <!-- end .landing-hero-container -->
<script>
$("a.landing-down-arrow").on("click", function(e) {
$("body").animate({
- scrollTop: $(".wear-hero").height() + 76
+ scrollTop: $(".tv-hero").height() + 120
}, 1000, "easeOutQuint");
e.preventDefault();
});
</script>
+
+</div> <!-- end landing-body-content -->
+
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index 026698c..a4a7c88 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -51,7 +51,7 @@
</div>
<div class="landing-body">
- <a href="/training/wearables/index.html" class="landing-button landing-primary" style="margin-top: 40px;">
+ <a href="{@docRoot}training/building-wearables.html" class="landing-button landing-primary" style="margin-top: 40px;">
Get Started
</a>
</div>
@@ -62,11 +62,11 @@
</div> <!-- end .wrap -->
<div class="landing-scroll-down-affordance">
<a class="landing-down-arrow" href="#extending-android-to-wearables">
- <img src="/wear/images/carrot.png" alt="Scroll down to read more">
+ <img src="{@docRoot}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-wearables">
@@ -85,8 +85,8 @@
<div class="col-3-wide">
<div class="landing-inset-video-container">
- <img class="landing-bezel-only" src="/wear/images/screens/bezel.png" alt="">
- <img class="gif" src="/wear/images/screens/reservation_animated.gif">
+ <img class="landing-bezel-only" src="{@docRoot}wear/images/screens/bezel.png" alt="">
+ <img class="gif" src="{@docRoot}wear/images/screens/reservation_animated.gif">
</div>
<p class="landing-small">
@@ -94,7 +94,7 @@
</p>
</div>
<div class="col-3-wide">
- <img src="/wear/images/screens/circle_message2.png" itemprop="image" alt="">
+ <img src="{@docRoot}wear/images/screens/circle_message2.png" itemprop="image" alt="">
<p class="landing-small">
Get glanceable, actionable information at just the right time with notifications
that are synced from your handheld device.
@@ -102,7 +102,7 @@
</p>
</div>
<div class="col-3-wide">
- <img src="/wear/images/screens/fitness-24.png" alt="">
+ <img src="{@docRoot}wear/images/screens/fitness-24.png" alt="">
<p class="landing-small">
Design apps that can access a wide range of sensors and other hardware
directly on the wearable.
@@ -113,7 +113,7 @@
<p>
Before you start building, check out the
- <a href="/design/devices/wear.html">Android Wear Design Principles</a>
+ <a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a>
to understand how to create great experiences for this exciting, new form factor.</p>
</div>
@@ -133,49 +133,49 @@
<div class="landing-body">
<div class="landing-breakout cols">
<div class="col-4">
- <img src="/wear/images/features/ts2.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts2.png" alt="">
<p>Synced Notifications</p>
<p class="landing-small">
Notifications on handhelds can automatically sync to wearables, so design them
with both devices in mind.
</p>
<p class="landing-small">
- <a href="/training/wearables/notifications/index.html">Build notifications</a>
+ <a href="{@docRoot}training/wearables/notifications/index.html">Build notifications</a>
</p>
</div>
<div class="col-4">
- <img src="/wear/images/features/ts1.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts1.png" alt="">
<p>Wearable Apps</p>
<p class="landing-small">
Create custom experiences with activities, services, sensors, and much
more with the Android SDK.
</p>
<p class="landing-small">
- <a href="/training/wearables/apps/index.html/">Create wearable apps</a>
+ <a href="{@docRoot}training/wearables/apps/index.html">Create wearable apps</a>
</p>
</div>
<div class="col-4">
- <img src="/wear/images/features/ts2.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts2.png" alt="">
<p>Send Data</p>
<p class="landing-small">
Send data and actions between handhelds and wearables with
data replication APIs and RPCs.
</p>
<p class="landing-small">
- <a href="/training/wearables/apps/index.html/">Work with the Data Layer</a>
+ <a href="{@docRoot}training/wearables/data-layer/index.html">Work with the Data Layer</a>
</p>
</div>
<div class="col-4">
- <img src="/wear/images/features/ts4.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts4.png" alt="">
<p>Voice Actions</p>
<p class="landing-small">
Register your app to handle voice actions, like "Ok Google, take a note,"
for a hands-free experience.
</p>
<p class="landing-small">
- <a href="/training/wearables/apps/index.html/">Integrate voice actions</a>
+ <a href="{@docRoot}training/wearables/apps/voice-actions.html">Integrate voice actions</a>
</p>
</div>
</div>
@@ -183,58 +183,6 @@
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
-<!--
-
- <div class="landing-section landing-white-background">
- <div class="wrap">
- <div class="landing-section-header">
- <div class="landing-h2">Building an Ecosystem</div>
- <div class="landing-body landing-align-center">
- <p class="landing-small">
- We’re working with several partners to bring you watches powered by Android Wear later this year!
- </p>
- </div>
- </div>
-
- <div class="landing-partners cols">
- <div class="col-4">
- <img src="/wear/images/partners/asus.png" alt="Asus">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/broadcom.png" alt="Broadcom">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/fossil.png" alt="Fossil">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/htc.png" alt="HTC">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/intel.png" alt="Intel">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/lg.png" alt="LG">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/mediatek.png" alt="Mediatek">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/mips.png" alt="MIPS">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/motorola.png" alt="Motorola">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/qualcomm.png" alt="Qualcomm">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/samsung.png" alt="Samsung">
- </div>
- </div>
- </div> <!-- end .wrap
-
- </div> <!-- end .landing-section -->
-
<div class="landing-section landing-red-background">
<div class="wrap">
<div class="landing-section-header">
@@ -248,7 +196,7 @@
</div>
</div>
<div class="landing-body">
- <a href="/training/wearables/index.html" class="landing-button landing-secondary" style="margin-top: 20px;">
+ <a href="{@docRoot}training/building-wearables.html" class="landing-button landing-primary" style="margin-top: 20px;">
Get Started
</a>
</div>
@@ -277,7 +225,7 @@
</div>
<div class="col-3-wide">
<a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-landing-developer-preview.html">
- <img class="landing-social-image" src="/wear/images/blogger.png" alt="">
+ <img class="landing-social-image" src="{@docRoot}wear/images/blogger.png" alt="">
</a>
<div class="landing-social-copy">
<p>Blog Post</p>
@@ -313,12 +261,10 @@
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
</div> <!-- end .landing-rest-of-page -->
-
-
<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 id="___plusone_0" style="text-indent: 0px; margin: 0px; padding: 0px; border-style: none; float: none; line-height: normal; font-size: 1px; vertical-align: baseline; display: inline-block; width: 90px; height: 20px; background: transparent;"><iframe frameborder="0" hspace="0" marginheight="0" marginwidth="0" scrolling="no" style="position: static; top: 0px; width: 90px; margin: 0px; border-style: none; left: 0px; visibility: visible; height: 20px;" tabindex="0" vspace="0" width="100%" id="I0_1402525433965" name="I0_1402525433965" src="https://apis.google.com/u/0/_/+1/fastbutton?usegapi=1&size=medium&origin=http%3A%2F%2Frobertly.mtv%3A8080&url=http%3A%2F%2Frobertly.mtv%3A8080%2Fwear%2Findex.html&gsrc=3p&jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.en.QxHQHBkhz7M.O%2Fm%3D__features__%2Fam%3DUQ%2Frt%3Dj%2Fd%3D1%2Fz%3Dzcms%2Frs%3DAItRSTMLrMyRVKsu2FQoRingre3w1MT49A#_methods=onPlusOne%2C_ready%2C_close%2C_open%2C_resizeMe%2C_renderstart%2Concircled%2Cdrefresh%2Cerefresh%2Conload&id=I0_1402525433965&parent=http%3A%2F%2Frobertly.mtv%3A8080&pfname=&rpctoken=32453860" data-gapiattached="true" title="+1"></iframe></div>
+ <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">
@@ -326,9 +272,20 @@
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
+ restrictions, see the <a href="{@docRoot}license.html">Content
License</a>.
</div>
</div>
- </div> <!-- end landing-body-content -->
+ </div> <!-- end .landing-hero-container -->
+
+ <script>
+ $("a.landing-down-arrow").on("click", function(e) {
+ $("body").animate({
+ scrollTop: $(".wear-hero").height() + 120
+ }, 1000, "easeOutQuint");
+ e.preventDefault();
+ });
+ </script>
+
+</div> <!-- end landing-body-content -->
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 2b36016..99596ef 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1091,7 +1091,15 @@
if (oval == null) {
throw new NullPointerException();
}
- native_drawOval(mNativeCanvasWrapper, oval, paint.mNativePaint);
+ drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
+ }
+
+ /**
+ * Draw the specified oval using the specified paint. The oval will be
+ * filled or framed based on the Style in the paint.
+ */
+ public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
+ native_drawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.mNativePaint);
}
/**
@@ -1133,10 +1141,34 @@
*/
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
@NonNull Paint paint) {
- if (oval == null) {
- throw new NullPointerException();
- }
- native_drawArc(mNativeCanvasWrapper, oval, startAngle, sweepAngle,
+ drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
+ paint);
+ }
+
+ /**
+ * <p>Draw the specified arc, which will be scaled to fit inside the
+ * specified oval.</p>
+ *
+ * <p>If the start angle is negative or >= 360, the start angle is treated
+ * as start angle modulo 360.</p>
+ *
+ * <p>If the sweep angle is >= 360, then the oval is drawn
+ * completely. Note that this differs slightly from SkPath::arcTo, which
+ * treats the sweep angle modulo 360. If the sweep angle is negative,
+ * the sweep angle is treated as sweep angle modulo 360</p>
+ *
+ * <p>The arc is drawn clockwise. An angle of 0 degrees correspond to the
+ * geometric angle of 0 degrees (3 o'clock on a watch.)</p>
+ *
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise
+ * @param useCenter If true, include the center of the oval in the arc, and
+ close it if it is being stroked. This will draw a wedge
+ * @param paint The paint used to draw the arc
+ */
+ public void drawArc(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle, boolean useCenter, @NonNull Paint paint) {
+ native_drawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
useCenter, paint.mNativePaint);
}
@@ -1680,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
@@ -1695,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);
+ }
}
/**
@@ -1704,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
@@ -1712,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);
}
/**
@@ -1738,7 +1770,7 @@
}
native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
path.ni(), hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
+ paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
}
/**
@@ -1758,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);
}
}
@@ -1908,14 +1940,14 @@
float top, float right,
float bottom,
long nativePaint);
- private static native void native_drawOval(long nativeCanvas, RectF oval,
- long nativePaint);
+ private static native void native_drawOval(long nativeCanvas, float left, float top,
+ float right, float bottom, long nativePaint);
private static native void native_drawCircle(long nativeCanvas, float cx,
float cy, float radius,
long nativePaint);
- private static native void native_drawArc(long nativeCanvas, RectF oval,
- float startAngle, float sweep,
- boolean useCenter,
+ private static native void native_drawArc(long nativeCanvas, float left, float top,
+ float right, float bottom,
+ float startAngle, float sweep, boolean useCenter,
long nativePaint);
private static native void native_drawRoundRect(long nativeCanvas,
float left, float top, float right, float bottom,
@@ -1977,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/Paint.java b/graphics/java/android/graphics/Paint.java
index 8837955..17ce026 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1532,20 +1532,22 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_breakText(text, index, count, maxWidth, mBidiFlags, measuredWidth);
+ return native_breakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
+ mBidiFlags, measuredWidth);
}
final float oldSize = getTextSize();
- setTextSize(oldSize*mCompatScaling);
- int res = native_breakText(text, index, count, maxWidth*mCompatScaling, mBidiFlags,
- measuredWidth);
+ setTextSize(oldSize * mCompatScaling);
+ int res = native_breakText(mNativePaint, mNativeTypeface, text, index, count,
+ maxWidth * mCompatScaling, mBidiFlags, measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
return res;
}
- private native int native_breakText(char[] text, int index, int count,
- float maxWidth, int bidiFlags, float[] measuredWidth);
+ private static native int native_breakText(long native_object, long native_typeface,
+ char[] text, int index, int count,
+ float maxWidth, int bidiFlags, float[] measuredWidth);
/**
* Measure the text, stopping early if the measured width exceeds maxWidth.
@@ -1622,19 +1624,21 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_breakText(text, measureForwards, maxWidth, mBidiFlags, measuredWidth);
+ return native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+ maxWidth, mBidiFlags, measuredWidth);
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- int res = native_breakText(text, measureForwards, maxWidth*mCompatScaling, mBidiFlags,
- measuredWidth);
+ int res = native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+ maxWidth*mCompatScaling, mBidiFlags, measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
return res;
}
- private native int native_breakText(String text, boolean measureForwards,
+ private static native int native_breakText(long native_object, long native_typeface,
+ String text, boolean measureForwards,
float maxWidth, int bidiFlags, float[] measuredWidth);
/**
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index c600f47..c40a66d 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -62,7 +62,7 @@
}
mNativePath = init2(valNative);
}
-
+
/**
* Clear any lines and curves from the path, making it empty.
* This does NOT change the fill-type setting.
@@ -205,7 +205,7 @@
* Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside.
*/
INVERSE_EVEN_ODD(3);
-
+
FillType(int ni) {
nativeInt = ni;
}
@@ -425,7 +425,7 @@
* the path is different from the path's current last point, then an
* automatic lineTo() is added to connect the current contour to the
* start of the arc. However, if the path is empty, then we call moveTo()
- * with the first point of the arc. The sweep angle is tread mod 360.
+ * with the first point of the arc.
*
* @param oval The bounds of oval defining shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
@@ -435,10 +435,9 @@
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle,
boolean forceMoveTo) {
- isSimplePath = false;
- native_arcTo(mNativePath, oval, startAngle, sweepAngle, forceMoveTo);
+ arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, forceMoveTo);
}
-
+
/**
* Append the specified arc to the path as a new contour. If the start of
* the path is different from the path's current last point, then an
@@ -451,10 +450,27 @@
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle) {
- isSimplePath = false;
- native_arcTo(mNativePath, oval, startAngle, sweepAngle, false);
+ arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, false);
}
-
+
+ /**
+ * Append the specified arc to the path as a new contour. If the start of
+ * the path is different from the path's current last point, then an
+ * automatic lineTo() is added to connect the current contour to the
+ * start of the arc. However, if the path is empty, then we call moveTo()
+ * with the first point of the arc.
+ *
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated
+ * mod 360.
+ * @param forceMoveTo If true, always begin a new contour with the arc
+ */
+ public void arcTo(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle, boolean forceMoveTo) {
+ isSimplePath = false;
+ native_arcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
+ }
+
/**
* Close the current contour. If the current point is not equal to the
* first point of the contour, a line segment is automatically added.
@@ -473,13 +489,13 @@
CW (1), // must match enum in SkPath.h
/** counter-clockwise */
CCW (2); // must match enum in SkPath.h
-
+
Direction(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
-
+
private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) {
if (mLastDirection == null) {
mLastDirection = dir;
@@ -557,11 +573,19 @@
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
public void addArc(RectF oval, float startAngle, float sweepAngle) {
- if (oval == null) {
- throw new NullPointerException("need oval parameter");
- }
+ addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle);
+ }
+
+ /**
+ * Add the specified arc to the path as a new contour.
+ *
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise
+ */
+ public void addArc(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle) {
isSimplePath = false;
- native_addArc(mNativePath, oval, startAngle, sweepAngle);
+ native_addArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
}
/**
@@ -573,13 +597,22 @@
* @param dir The direction to wind the round-rectangle's contour
*/
public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
- if (rect == null) {
- throw new NullPointerException("need rect parameter");
- }
- isSimplePath = false;
- native_addRoundRect(mNativePath, rect, rx, ry, dir.nativeInt);
+ addRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, dir);
}
-
+
+ /**
+ * Add a closed round-rectangle contour to the path
+ *
+ * @param rx The x-radius of the rounded corners on the round-rectangle
+ * @param ry The y-radius of the rounded corners on the round-rectangle
+ * @param dir The direction to wind the round-rectangle's contour
+ */
+ public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
+ Direction dir) {
+ isSimplePath = false;
+ native_addRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
+ }
+
/**
* Add a closed round-rectangle contour to the path. Each corner receives
* two radius values [X, Y]. The corners are ordered top-left, top-right,
@@ -593,13 +626,26 @@
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
+ addRoundRect(rect.left, rect.top, rect.right, rect.bottom, radii, dir);
+ }
+
+ /**
+ * Add a closed round-rectangle contour to the path. Each corner receives
+ * two radius values [X, Y]. The corners are ordered top-left, top-right,
+ * bottom-right, bottom-left
+ *
+ * @param radii Array of 8 values, 4 pairs of [X,Y] radii
+ * @param dir The direction to wind the round-rectangle's contour
+ */
+ public void addRoundRect(float left, float top, float right, float bottom, float[] radii,
+ Direction dir) {
if (radii.length < 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
isSimplePath = false;
- native_addRoundRect(mNativePath, rect, radii, dir.nativeInt);
+ native_addRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
}
-
+
/**
* Add a copy of src to the path, offset by (dx,dy)
*
@@ -755,19 +801,24 @@
float x2, float y2, float x3, float y3);
private static native void native_rCubicTo(long nPath, float x1, float y1,
float x2, float y2, float x3, float y3);
- private static native void native_arcTo(long nPath, RectF oval,
- float startAngle, float sweepAngle, boolean forceMoveTo);
+ private static native void native_arcTo(long nPath, float left, float top,
+ float right, float bottom, float startAngle,
+ float sweepAngle, boolean forceMoveTo);
private static native void native_close(long nPath);
private static native void native_addRect(long nPath, float left, float top,
float right, float bottom, int dir);
private static native void native_addOval(long nPath, float left, float top,
float right, float bottom, int dir);
private static native void native_addCircle(long nPath, float x, float y, float radius, int dir);
- private static native void native_addArc(long nPath, RectF oval,
- float startAngle, float sweepAngle);
- private static native void native_addRoundRect(long nPath, RectF rect,
+ private static native void native_addArc(long nPath, float left, float top,
+ float right, float bottom,
+ float startAngle, float sweepAngle);
+ private static native void native_addRoundRect(long nPath, float left, float top,
+ float right, float bottom,
float rx, float ry, int dir);
- private static native void native_addRoundRect(long nPath, RectF r, float[] radii, int dir);
+ private static native void native_addRoundRect(long nPath, float left, float top,
+ float right, float bottom,
+ float[] radii, int dir);
private static native void native_addPath(long nPath, long src, float dx, float dy);
private static native void native_addPath(long nPath, long src);
private static native void native_addPath(long nPath, long src, long matrix);
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/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index cb88e3d..40b55a7 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1260,8 +1260,10 @@
/**
* Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
* attribute's enum value.
+ *
+ * @hide
*/
- static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
+ public static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
switch (value) {
case 3: return Mode.SRC_OVER;
case 5: return Mode.SRC_IN;
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/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 36ffddd..28335ea 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -671,7 +671,7 @@
mPadding = new Rect(state.mPadding);
}
- updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
setNinePatch(state.mNinePatch);
}
}
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 981efb8..369bb59 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -24,6 +24,7 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
@@ -284,31 +285,13 @@
}
@Override
- public void setTint(ColorStateList tint, Mode tintMode) {
- if (mShapeState.mTint != tint || mShapeState.mTintMode != tintMode) {
- mShapeState.mTint = tint;
- mShapeState.mTintMode = tintMode;
- updateTintFilter();
- invalidateSelf();
- }
- }
+ public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {
+ final ShapeState state = mShapeState;
+ state.mTint = tint;
+ state.mTintMode = tintMode;
- /**
- * Ensures the tint filter is consistent with the current tint color and
- * mode.
- */
- private void updateTintFilter() {
- final ColorStateList tint = mShapeState.mTint;
- final Mode tintMode = mShapeState.mTintMode;
- if (tint != null && tintMode != null) {
- if (mTintFilter == null) {
- mTintFilter = new PorterDuffColorFilter(0, tintMode);
- } else {
- mTintFilter.setMode(tintMode);
- }
- } else {
- mTintFilter = null;
- }
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ invalidateSelf();
}
@Override
@@ -349,17 +332,11 @@
@Override
protected boolean onStateChange(int[] stateSet) {
- final ColorStateList tint = mShapeState.mTint;
- if (tint != null) {
- final int newColor = tint.getColorForState(stateSet, 0);
- final int oldColor = mTintFilter.getColor();
- if (oldColor != newColor) {
- mTintFilter.setColor(newColor);
- invalidateSelf();
- return true;
- }
+ final ShapeState state = mShapeState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ return true;
}
-
return false;
}
@@ -439,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..3773a9b 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);
@@ -283,13 +286,6 @@
}
@Override
- public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
- throws XmlPullParserException, IOException {
- final VPathRenderer p = inflateInternal(res, parser, attrs, theme);
- setPathRenderer(p);
- }
-
- @Override
public boolean canApplyTheme() {
return super.canApplyTheme() || mVectorState != null && mVectorState.canApplyTheme();
}
@@ -332,13 +328,44 @@
return color;
}
- private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
- Theme theme) throws XmlPullParserException, IOException {
+
+ @Override
+ public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(res, theme, attrs,R.styleable.VectorDrawable);
+ updateStateFromTypedArray(a);
+ a.recycle();
+
+ final VectorDrawableState state = mVectorState;
+ state.mVPathRenderer = inflateInternal(res, parser, attrs, theme);
+
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ state.mVPathRenderer.setColorFilter(mTintFilter);
+ }
+
+ private void updateStateFromTypedArray(TypedArray a) {
+ final VectorDrawableState state = mVectorState;
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
+ if (tintMode != -1) {
+ state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
+ }
+
+ final ColorStateList tint = a.getColorStateList(R.styleable.VectorDrawable_tint);
+ if (tint != null) {
+ state.mTint = tint;
+ }
+ }
+
+ private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
final VPathRenderer pathRenderer = new VPathRenderer();
boolean noSizeTag = true;
boolean noViewportTag = true;
- boolean noGroupTag = true;
boolean noPathTag = true;
// Use a stack to help to build the group tree.
@@ -374,7 +401,6 @@
if (newChildGroup.getGroupName() != null) {
mVGTargetsMap.put(newChildGroup.getGroupName(), newChildGroup);
}
- noGroupTag = false;
}
} else if (eventType == XmlPullParser.END_TAG) {
final String tagName = parser.getName();
@@ -432,11 +458,8 @@
}
}
- private void setPathRenderer(VPathRenderer pathRenderer) {
- mVectorState.mVPathRenderer = pathRenderer;
- }
-
private static class VectorDrawableState extends ConstantState {
+ int[] mThemeAttrs;
int mChangingConfigurations;
VPathRenderer mVPathRenderer;
Rect mPadding;
@@ -445,6 +468,7 @@
public VectorDrawableState(VectorDrawableState copy) {
if (copy != null) {
+ mThemeAttrs = copy.mThemeAttrs;
mChangingConfigurations = copy.mChangingConfigurations;
// TODO: Make sure the constant state are handled correctly.
mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
@@ -954,7 +978,7 @@
}
- static class VPath {
+ private static class VPath {
private int[] mThemeAttrs;
int mStrokeColor = 0;
@@ -972,7 +996,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 +1006,7 @@
public void toPath(Path path) {
path.reset();
if (mNode != null) {
- VNode.createPath(mNode, path);
+ PathParser.PathDataNode.nodesToPath(mNode, path);
}
}
@@ -1016,67 +1040,98 @@
}
}
- /* Setters and Getters */
+ /* Setters and Getters, mostly used by animator from AnimatedVectorDrawable. */
+ @SuppressWarnings("unused")
+ public PathParser.PathDataNode[] getPathData() {
+ return mNode;
+ }
+
+ @SuppressWarnings("unused")
+ public void setPathData(PathParser.PathDataNode[] node) {
+ if (!PathParser.canMorph(mNode, node)) {
+ // This should not happen in the middle of animation.
+ mNode = PathParser.deepCopyNodes(node);
+ } else {
+ PathParser.updateNodes(mNode, node);
+ }
+ }
+
+ @SuppressWarnings("unused")
int getStroke() {
return mStrokeColor;
}
+ @SuppressWarnings("unused")
void setStroke(int strokeColor) {
mStrokeColor = strokeColor;
}
+ @SuppressWarnings("unused")
float getStrokeWidth() {
return mStrokeWidth;
}
+ @SuppressWarnings("unused")
void setStrokeWidth(float strokeWidth) {
mStrokeWidth = strokeWidth;
}
+ @SuppressWarnings("unused")
float getStrokeOpacity() {
return mStrokeOpacity;
}
+ @SuppressWarnings("unused")
void setStrokeOpacity(float strokeOpacity) {
mStrokeOpacity = strokeOpacity;
}
+ @SuppressWarnings("unused")
int getFill() {
return mFillColor;
}
+ @SuppressWarnings("unused")
void setFill(int fillColor) {
mFillColor = fillColor;
}
+ @SuppressWarnings("unused")
float getFillOpacity() {
return mFillOpacity;
}
+ @SuppressWarnings("unused")
void setFillOpacity(float fillOpacity) {
mFillOpacity = fillOpacity;
}
+ @SuppressWarnings("unused")
float getTrimPathStart() {
return mTrimPathStart;
}
+ @SuppressWarnings("unused")
void setTrimPathStart(float trimPathStart) {
mTrimPathStart = trimPathStart;
}
+ @SuppressWarnings("unused")
float getTrimPathEnd() {
return mTrimPathEnd;
}
+ @SuppressWarnings("unused")
void setTrimPathEnd(float trimPathEnd) {
mTrimPathEnd = trimPathEnd;
}
+ @SuppressWarnings("unused")
float getTrimPathOffset() {
return mTrimPathOffset;
}
+ @SuppressWarnings("unused")
void setTrimPathOffset(float trimPathOffset) {
mTrimPathOffset = trimPathOffset;
}
@@ -1097,7 +1152,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 +1236,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 +1273,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 91dda75..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);
}
@@ -1768,6 +1768,7 @@
}
mergeInfoLocked(pMergedInfo, pContents);
+ delete pContents;
return true;
}
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/Animator.cpp b/libs/hwui/Animator.cpp
index eff3011..dc6d852 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -160,6 +160,10 @@
(target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
}
+uint32_t RenderPropertyAnimator::dirtyMask() {
+ return mPropertyAccess->dirtyMask;
+}
+
float RenderPropertyAnimator::getValue(RenderNode* target) const {
return (target->properties().*mPropertyAccess->getter)();
}
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 203cdff..6cb72c4c 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -61,6 +61,8 @@
bool isFinished() { return mPlayState == FINISHED; }
float finalValue() { return mFinalValue; }
+ ANDROID_API virtual uint32_t dirtyMask() { return 0; }
+
protected:
BaseRenderNodeAnimator(float finalValue);
virtual ~BaseRenderNodeAnimator();
@@ -112,6 +114,8 @@
ANDROID_API virtual void onAttached(RenderNode* target);
+ ANDROID_API virtual uint32_t dirtyMask();
+
protected:
virtual float getValue(RenderNode* target) const;
virtual void setValue(RenderNode* target, float value);
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 6fd9999..402f28b 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -565,11 +565,8 @@
// call, any texture operation will be performed on the default
// texture (name=0)
- for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
- if (mBoundTextures[i] == texture) {
- mBoundTextures[i] = 0;
- }
- }
+ unbindTexture(texture);
+
glDeleteTextures(1, &texture);
}
@@ -577,6 +574,14 @@
memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
}
+void Caches::unbindTexture(GLuint texture) {
+ for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
+ if (mBoundTextures[i] == texture) {
+ mBoundTextures[i] = 0;
+ }
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Scissor
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index b4b5927..83a5d9a 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -261,6 +261,11 @@
void resetBoundTextures();
/**
+ * Clear the cache of bound textures.
+ */
+ void unbindTexture(GLuint texture);
+
+ /**
* Sets the scissor for the current surface.
*/
bool setScissor(GLint x, GLint y, GLint width, GLint height);
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 1cb87f2..8b32c40 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -33,6 +33,7 @@
}
enum TransformType {
+ TransformInvalid = 0,
TransformRenderNode,
TransformMatrix4,
TransformNone,
@@ -56,6 +57,7 @@
memset(mHead, 0, sizeof(DirtyStack));
// Create a root that we will not pop off
mHead->prev = mHead;
+ mHead->type = TransformNone;
}
void DamageAccumulator::pushCommon() {
@@ -100,6 +102,8 @@
case TransformNone:
mHead->pendingDirty.join(dirtyFrame->pendingDirty);
break;
+ default:
+ LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
}
}
@@ -166,9 +170,12 @@
}
const RenderProperties& props = frame->renderNode->properties();
+ if (props.getAlpha() <= 0) {
+ return;
+ }
// Perform clipping
- if (props.getClipToBounds() && !frame->pendingDirty.isEmpty()) {
+ if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
frame->pendingDirty.setEmpty();
}
@@ -186,8 +193,6 @@
if (projectionReceiver) {
applyTransforms(frame, projectionReceiver);
projectionReceiver->pendingDirty.join(frame->pendingDirty);
- } else {
- ALOGW("Failed to find projection receiver? Dropping on the floor...");
}
frame->pendingDirty.setEmpty();
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 4dbebc5..f8d620d 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/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index 1f84b86..fc0e8a0 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -112,6 +112,10 @@
int i1 = (int) ipart;
int i2 = MathUtils::min(i1 + 1, mSize - 1);
+ LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!"
+ " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f",
+ i1, i2, input, lutpos, mSize, mValues, ipart, weight);
+
float v1 = mValues[i1];
float v2 = mValues[i2];
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index e59e714..24dd1d3 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -185,6 +185,7 @@
}
void Layer::clearTexture() {
+ caches.unbindTexture(texture.id);
texture.id = 0;
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index df9aee5..a5fd375 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -334,8 +334,10 @@
void LayerRenderer::flushLayer(Layer* layer) {
#ifdef GL_EXT_discard_framebuffer
+ if (!layer) return;
+
GLuint fbo = layer->getFbo();
- if (layer && fbo) {
+ if (fbo) {
// If possible, discard any enqueud operations on deferred
// rendering architectures
if (Extensions::getInstance().hasDiscardFramebuffer()) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 48cc3e4..e1dfeae 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/PathCache.cpp b/libs/hwui/PathCache.cpp
index 9dd5aa5..97eb583 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -104,8 +104,7 @@
}
static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
- bitmap.setConfig(SkBitmap::kA8_Config, width, height);
- bitmap.allocPixels();
+ bitmap.allocPixels(SkImageInfo::MakeA8(width, height));
bitmap.eraseColor(0);
}
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 59e15e13..310b107 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -57,17 +57,26 @@
#define ROUND_CAP_THRESH 0.25f
#define PI 3.1415926535897932f
+// temporary error thresholds
+#define ERROR_DEPTH 20
+#define ERROR_SCALE 1e10
+#define ERROR_SQR_INV_THRESH 1e-20
+
void PathTessellator::extractTessellationScales(const Matrix4& transform,
float* scaleX, float* scaleY) {
- *scaleX = 1.0f;
- *scaleY = 1.0f;
- if (CC_UNLIKELY(!transform.isPureTranslate())) {
+ if (CC_LIKELY(transform.isPureTranslate())) {
+ *scaleX = 1.0f;
+ *scaleY = 1.0f;
+ } else {
float m00 = transform.data[Matrix4::kScaleX];
float m01 = transform.data[Matrix4::kSkewY];
float m10 = transform.data[Matrix4::kSkewX];
float m11 = transform.data[Matrix4::kScaleY];
*scaleX = sqrt(m00 * m00 + m01 * m01);
*scaleY = sqrt(m10 * m10 + m11 * m11);
+
+ LOG_ALWAYS_FATAL_IF(*scaleX > ERROR_SCALE || *scaleY > ERROR_SCALE,
+ "scales %e x %e too large for tessellation", *scaleX, *scaleY);
}
}
@@ -92,10 +101,12 @@
public:
PaintInfo(const SkPaint* paint, const mat4& transform) :
style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
- inverseScaleX(1.0f), inverseScaleY(1.0f),
halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
// compute inverse scales
- if (CC_UNLIKELY(!transform.isPureTranslate())) {
+ if (CC_LIKELY(transform.isPureTranslate())) {
+ inverseScaleX = 1.0f;
+ inverseScaleY = 1.0f;
+ } else {
float scaleX, scaleY;
PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
@@ -922,6 +933,9 @@
Vector<Vertex>& outputVertices) {
ATRACE_CALL();
+ LOG_ALWAYS_FATAL_IF(sqrInvScaleX < ERROR_SQR_INV_THRESH || sqrInvScaleY < ERROR_SQR_INV_THRESH,
+ "Invalid scale factors used for approx %e, %e", sqrInvScaleX, sqrInvScaleY);
+
// TODO: to support joins other than sharp miter, join vertices should be labelled in the
// perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
SkPath::Iter iter(path, forceClose);
@@ -975,14 +989,14 @@
// Bezier approximation
///////////////////////////////////////////////////////////////////////////////
-// Depth at which recursion is aborted
-#define ABORT_DEPTH 20
-
void PathTessellator::recursiveCubicBezierVertices(
float p1x, float p1y, float c1x, float c1y,
float p2x, float p2y, float c2x, float c2y,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Vector<Vertex>& outputVertices, int depth) {
+ LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR DEPTH exceeded: cubic approx, invscale %e x %e, vertcount %d",
+ sqrInvScaleX, sqrInvScaleY, outputVertices.size());
+
float dx = p2x - p1x;
float dy = p2y - p1y;
float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
@@ -990,8 +1004,7 @@
float d = d1 + d2;
// multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
-
- if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, p2x, p2y);
} else {
@@ -1029,11 +1042,14 @@
float cx, float cy,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Vector<Vertex>& outputVertices, int depth) {
+ LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR_DEPTH exceeded: quadratic approx, invscale %e x %e, vertcount %d",
+ sqrInvScaleX, sqrInvScaleY, outputVertices.size());
+
float dx = bx - ax;
float dy = by - ay;
float d = (cx - bx) * dy - (cy - by) * dx;
- if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, bx, by);
} else {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index f0eca3c..4cda17850 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,8 +118,8 @@
}
void RenderNode::damageSelf(TreeInfo& info) {
- if (isRenderable() && properties().getAlpha() > 0) {
- if (properties().getClipToBounds()) {
+ if (isRenderable()) {
+ if (properties().getClipDamageToBounds()) {
info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
} else {
// Hope this is big enough?
@@ -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/RenderNode.h b/libs/hwui/RenderNode.h
index ec12b9f..c510ac6 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -186,6 +186,8 @@
// UI thread only!
ANDROID_API void removeAnimator(const sp<BaseRenderNodeAnimator>& animator) {
mStagingAnimators.erase(animator);
+ // Force a sync of the staging property value
+ mDirtyPropertyFields |= animator->dirtyMask();
mNeedsAnimatorsSync = true;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 8c6cc9e..227d56e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -21,6 +21,7 @@
#include <vector>
#include <cutils/compiler.h>
#include <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
#include <SkCamera.h>
#include <SkMatrix.h>
@@ -30,6 +31,7 @@
#include "Rect.h"
#include "RevealClip.h"
#include "Outline.h"
+#include "utils/MathUtils.h"
class SkBitmap;
class SkColorFilter;
@@ -281,6 +283,7 @@
}
bool setScaleX(float scaleX) {
+ LOG_ALWAYS_FATAL_IF(scaleX > 1000000, "invalid scaleX %e", scaleX);
return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX);
}
@@ -289,6 +292,7 @@
}
bool setScaleY(float scaleY) {
+ LOG_ALWAYS_FATAL_IF(scaleY > 1000000, "invalid scaleY %e", scaleY);
return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY);
}
@@ -526,6 +530,15 @@
return mLayerProperties;
}
+ // Returns true if damage calculations should be clipped to bounds
+ // TODO: Figure out something better for getZ(), as children should still be
+ // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX
+ // for this RP's getZ() anyway, this can be optimized when we have a
+ // Z damage estimate instead of INT_MAX
+ bool getClipDamageToBounds() const {
+ return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty());
+ }
+
private:
// Rendering properties
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/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 1001cae0..9212d0a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -333,8 +333,7 @@
void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
uint32_t width, uint32_t height) {
SkBitmap rgbaBitmap;
- rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType());
- rgbaBitmap.allocPixels();
+ rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType()));
rgbaBitmap.eraseColor(0);
SkCanvas canvas(rgbaBitmap);
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/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 440f965..dc1951b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -537,9 +537,12 @@
requireGlContext();
mode = DrawGlInfo::kModeProcess;
}
- (*functor)(mode, NULL);
if (mCanvas) {
+ mCanvas->interrupt();
+ }
+ (*functor)(mode, NULL);
+ if (mCanvas) {
mCanvas->resume();
}
}
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/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 3f6ccc9..063383b 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -206,9 +206,8 @@
} else {
SkBitmap surfaceBitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
- surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
- outBuffer.width, outBuffer.height, bpr);
- surfaceBitmap.setPixels(outBuffer.bits);
+ surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
+ outBuffer.bits, bpr);
SkCanvas surfaceCanvas(surfaceBitmap);
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..fb19242 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1254,11 +1254,6 @@
* call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
* <p>Even if a SCO connection is established, the following restrictions apply on audio
* output streams so that they can be routed to SCO headset:
- * <p>NOTE: up to and including API version
- * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
- * voice call to the bluetooth headset.
- * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
- * connection is established.
* <ul>
* <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
* <li> the format must be mono </li>
@@ -1274,6 +1269,11 @@
* it will be ignored. Similarly, if a call is received or sent while an application
* is using the SCO connection, the connection will be lost for the application and NOT
* returned automatically when the call ends.
+ * <p>NOTE: up to and including API version
+ * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
+ * voice call to the bluetooth headset.
+ * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
+ * connection is established.
* @see #stopBluetoothSco()
* @see #ACTION_SCO_AUDIO_STATE_UPDATED
*/
@@ -1287,13 +1287,38 @@
}
/**
+ * Start bluetooth SCO audio connection in virtual call mode.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ * <p>Similar to {@link #startBluetoothSco()} with explicit selection of virtual call mode.
+ * Telephony and communication applications (VoIP, Video Chat) should preferably select
+ * virtual call mode.
+ * Applications using voice input for search or commands should first try raw audio connection
+ * with {@link #startBluetoothSco()} and fall back to startBluetoothScoVirtualCall() in case of
+ * failure.
+ * @see #startBluetoothSco()
+ * @see #stopBluetoothSco()
+ * @see #ACTION_SCO_AUDIO_STATE_UPDATED
+ */
+ public void startBluetoothScoVirtualCall() {
+ IAudioService service = getService();
+ try {
+ service.startBluetoothScoVirtualCall(mICallBack);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in startBluetoothScoVirtualCall", e);
+ }
+ }
+
+ /**
* Stop bluetooth SCO audio connection.
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
* <p>This method must be called by applications having requested the use of
- * bluetooth SCO audio with {@link #startBluetoothSco()}
- * when finished with the SCO connection or if connection fails.
+ * bluetooth SCO audio with {@link #startBluetoothSco()} or
+ * {@link #startBluetoothScoVirtualCall()} when finished with the SCO connection or
+ * if connection fails.
* @see #startBluetoothSco()
+ * @see #startBluetoothScoVirtualCall()
*/
public void stopBluetoothSco(){
IAudioService service = getService();
@@ -1616,26 +1641,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.
@@ -2189,48 +2194,8 @@
Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
return;
}
- IAudioService service = getService();
- try {
- // pi != null, this is currently still needed to support across
- // reboot launching of the last app.
- service.registerMediaButtonIntent(pi, eventReceiver,
- eventReceiver == null ? mToken : null);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
- }
MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.addMediaButtonListener(pi, mContext);
- }
-
- /**
- * @hide
- * Used internally by telephony package to register an intent receiver for ACTION_MEDIA_BUTTON.
- * @param eventReceiver the component that will receive the media button key events,
- * no-op if eventReceiver is null
- */
- public void registerMediaButtonEventReceiverForCalls(ComponentName eventReceiver) {
- if (eventReceiver == null) {
- return;
- }
- IAudioService service = getService();
- try {
- // eventReceiver != null
- service.registerMediaButtonEventReceiverForCalls(eventReceiver);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerMediaButtonEventReceiverForCalls", e);
- }
- }
-
- /**
- * @hide
- */
- public void unregisterMediaButtonEventReceiverForCalls() {
- IAudioService service = getService();
- try {
- service.unregisterMediaButtonEventReceiverForCalls();
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiverForCalls", e);
- }
+ helper.addMediaButtonListener(pi, eventReceiver, mContext);
}
/**
@@ -2267,12 +2232,6 @@
* @hide
*/
public void unregisterMediaButtonIntent(PendingIntent pi) {
- IAudioService service = getService();
- try {
- service.unregisterMediaButtonIntent(pi);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
- }
MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
helper.removeMediaButtonListener(pi);
}
@@ -2438,46 +2397,6 @@
}
/**
- * @hide
- * Request the user of a RemoteControlClient to seek to the given playback position.
- * @param generationId the RemoteControlClient generation counter for which this request is
- * issued. Requests for an older generation than current one will be ignored.
- * @param timeMs the time in ms to seek to, must be positive.
- */
- public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- if (timeMs < 0) {
- return;
- }
- IAudioService service = getService();
- try {
- service.setRemoteControlClientPlaybackPosition(generationId, timeMs);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setRccPlaybackPosition("+ generationId + ", "
- + timeMs + ")", e);
- }
- }
-
- /**
- * @hide
- * Notify the user of a RemoteControlClient that it should update its metadata with the
- * new value for the given key.
- * @param generationId the RemoteControlClient generation counter for which this request is
- * issued. Requests for an older generation than current one will be ignored.
- * @param key the metadata key for which a new value exists
- * @param value the new metadata value
- */
- public void updateRemoteControlClientMetadata(int generationId, int key,
- Rating value) {
- IAudioService service = getService();
- try {
- service.updateRemoteControlClientMetadata(generationId, key, value);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in updateRemoteControlClientMetadata("+ generationId + ", "
- + key +", " + value + ")", e);
- }
- }
-
- /**
* @hide
* Reload audio settings. This method is called by Settings backup
* agent when audio settings are restored and causes the AudioService
@@ -2534,6 +2453,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
@@ -2890,12 +2812,9 @@
* @hide
*/
public int getRemoteStreamVolume() {
- try {
- return getService().getRemoteStreamVolume();
- } catch (RemoteException e) {
- Log.w(TAG, "Error getting remote stream volume", e);
- return 0;
- }
+ // TODO STOPSHIP switch callers to use media sessions instead
+ Log.e(TAG, "Need to implement new Remote Volume!");
+ return 0;
}
/**
@@ -2903,12 +2822,9 @@
* @hide
*/
public int getRemoteStreamMaxVolume() {
- try {
- return getService().getRemoteStreamMaxVolume();
- } catch (RemoteException e) {
- Log.w(TAG, "Error getting remote stream max volume", e);
- return 0;
- }
+ // TODO STOPSHIP switch callers to use media sessions instead
+ Log.e(TAG, "Need to implement new Remote Volume!");
+ return 0;
}
/**
@@ -2971,7 +2887,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..0c224a6 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);
@@ -2074,7 +2056,19 @@
}
/** @see AudioManager#startBluetoothSco() */
- public void startBluetoothSco(IBinder cb, int targetSdkVersion){
+ public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
+ int scoAudioMode =
+ (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
+ SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
+ startBluetoothScoInt(cb, scoAudioMode);
+ }
+
+ /** @see AudioManager#startBluetoothScoVirtualCall() */
+ public void startBluetoothScoVirtualCall(IBinder cb) {
+ startBluetoothScoInt(cb, SCO_MODE_VIRTUAL_CALL);
+ }
+
+ void startBluetoothScoInt(IBinder cb, int scoAudioMode){
if (!checkAudioSettingsPermission("startBluetoothSco()") ||
!mSystemReady) {
return;
@@ -2086,7 +2080,7 @@
// The caller identity must be cleared after getScoClient() because it is needed if a new
// client is created.
final long ident = Binder.clearCallingIdentity();
- client.incCount(targetSdkVersion);
+ client.incCount(scoAudioMode);
Binder.restoreCallingIdentity(ident);
}
@@ -2132,9 +2126,9 @@
}
}
- public void incCount(int targetSdkVersion) {
+ public void incCount(int scoAudioMode) {
synchronized(mScoClients) {
- requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, targetSdkVersion);
+ requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
if (mStartcount == 0) {
try {
mCb.linkToDeath(this, 0);
@@ -2204,7 +2198,7 @@
}
}
- private void requestScoState(int state, int targetSdkVersion) {
+ private void requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
if (totalCount() == 0) {
if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
@@ -2219,9 +2213,7 @@
(mScoAudioState == SCO_STATE_INACTIVE ||
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_INACTIVE) {
- mScoAudioMode =
- (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
- SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
+ mScoAudioMode = scoAudioMode;
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
boolean status;
if (mScoAudioMode == SCO_MODE_RAW) {
@@ -4420,84 +4412,12 @@
mMediaFocusControl.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
}
- public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
- mMediaFocusControl.registerMediaButtonEventReceiverForCalls(c);
- }
-
- public void unregisterMediaButtonEventReceiverForCalls() {
- mMediaFocusControl.unregisterMediaButtonEventReceiverForCalls();
- }
-
- public void registerMediaButtonIntent(PendingIntent pi, ComponentName c, IBinder token) {
- mMediaFocusControl.registerMediaButtonIntent(pi, c, token);
- }
-
- public void unregisterMediaButtonIntent(PendingIntent pi) {
- mMediaFocusControl.unregisterMediaButtonIntent(pi);
- }
-
- public int registerRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient, String callingPckg) {
- return mMediaFocusControl.registerRemoteControlClient(mediaIntent, rcClient, callingPckg);
- }
-
- public void unregisterRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient) {
- mMediaFocusControl.unregisterRemoteControlClient(mediaIntent, rcClient);
- }
-
- public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- mMediaFocusControl.setRemoteControlClientPlaybackPosition(generationId, timeMs);
- }
-
- public void updateRemoteControlClientMetadata(int generationId, int key, Rating value) {
- mMediaFocusControl.updateRemoteControlClientMetadata(generationId, key, value);
- }
-
- public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
- mMediaFocusControl.registerRemoteVolumeObserverForRcc(rccId, rvo);
- }
-
- @Override
- public int getRemoteStreamVolume() {
- return mMediaFocusControl.getRemoteStreamVolume();
- }
-
- @Override
- public int getRemoteStreamMaxVolume() {
- return mMediaFocusControl.getRemoteStreamMaxVolume();
- }
-
@Override
public void setRemoteStreamVolume(int index) {
enforceSelfOrSystemUI("set the remote stream volume");
mMediaFocusControl.setRemoteStreamVolume(index);
}
- public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
- mMediaFocusControl.setPlaybackStateForRcc(rccId, state, timeMs, speed);
- }
-
- public void setPlaybackInfoForRcc(int rccId, int what, int value) {
- 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..4dcdd19 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,15 +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);
-
- oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
- oneway void unregisterMediaButtonEventReceiverForCalls();
-
/**
* Register an IRemoteControlDisplay.
* Success of registration is subject to a check on
@@ -188,43 +174,9 @@
*/
oneway void remoteControlDisplayWantsPlaybackPositionSync(in IRemoteControlDisplay rcd,
boolean wantsSync);
- /**
- * Request the user of a RemoteControlClient to seek to the given playback position.
- * @param generationId the RemoteControlClient generation counter for which this request is
- * issued. Requests for an older generation than current one will be ignored.
- * @param timeMs the time in ms to seek to, must be positive.
- */
- void setRemoteControlClientPlaybackPosition(int generationId, long timeMs);
- /**
- * Notify the user of a RemoteControlClient that it should update its metadata with the
- * new value for the given key.
- * @param generationId the RemoteControlClient generation counter for which this request is
- * issued. Requests for an older generation than current one will be ignored.
- * @param key the metadata key for which a new value exists
- * @param value the new metadata value
- */
- void updateRemoteControlClientMetadata(int generationId, int key, in Rating value);
-
- /**
- * Do not use directly, use instead
- * {@link android.media.AudioManager#registerRemoteControlClient(RemoteControlClient)}
- */
- int registerRemoteControlClient(in PendingIntent mediaIntent,
- in IRemoteControlClient rcClient, in String callingPackageName);
- /**
- * Do not use directly, use instead
- * {@link android.media.AudioManager#unregisterRemoteControlClient(RemoteControlClient)}
- */
- oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
- in IRemoteControlClient rcClient);
-
- oneway void setPlaybackInfoForRcc(int rccId, int what, int value);
- void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed);
- int getRemoteStreamMaxVolume();
- int getRemoteStreamVolume();
- oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo);
void startBluetoothSco(IBinder cb, int targetSdkVersion);
+ void startBluetoothScoVirtualCall(IBinder cb);
void stopBluetoothSco(IBinder cb);
void forceVolumeControlStream(int streamType, IBinder cb);
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 440653a..6559bc5 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -181,6 +181,27 @@
}
/**
+ * Thrown when an unrecoverable failure occurs during a MediaDrm operation.
+ * Extends java.lang.IllegalStateException with the addition of an error
+ * code that may be useful in diagnosing the failure.
+ */
+ public static final class MediaDrmStateException extends java.lang.IllegalStateException {
+ private final int mErrorCode;
+
+ public MediaDrmStateException(int errorCode, String detailMessage) {
+ super(detailMessage);
+ mErrorCode = errorCode;
+ }
+
+ /**
+ * Retrieve the associated error code
+ */
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+ }
+
+ /**
* Register a callback to be invoked when an event occurs
*
* @param listener the callback that will be run
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index 1c73c05..a4a7c4e 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -379,32 +379,11 @@
onReevaluateRemote();
break;
- case MSG_RCC_NEW_PLAYBACK_INFO:
- onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
- ((Integer)msg.obj).intValue() /* value */);
- break;
-
case MSG_RCC_NEW_VOLUME_OBS:
onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
(IRemoteVolumeObserver)msg.obj /* rvo */);
break;
- case MSG_RCC_NEW_PLAYBACK_STATE:
- onNewPlaybackStateForRcc(msg.arg1 /* rccId */,
- msg.arg2 /* state */,
- (PlayerRecord.RccPlaybackState)msg.obj /* newState */);
- break;
-
- case MSG_RCC_SEEK_REQUEST:
- onSetRemoteControlClientPlaybackPosition(
- msg.arg1 /* generationId */, ((Long)msg.obj).longValue() /* timeMs */);
- break;
-
- case MSG_RCC_UPDATE_METADATA:
- onUpdateRemoteControlClientMetadata(msg.arg1 /*genId*/, msg.arg2 /*key*/,
- (Rating) msg.obj /* value */);
- break;
-
case MSG_RCDISPLAY_INIT_INFO:
// msg.obj is guaranteed to be non null
onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
@@ -2003,217 +1982,6 @@
}
}
- protected void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- // ignore position change requests if invalid generation ID
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if (mCurrentRcClientGen != generationId) {
- return;
- }
- }
- }
- // discard any unprocessed seek request in the message queue, and replace with latest
- sendMsg(mEventHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
- 0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
- }
-
- private void onSetRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- if(DEBUG_RC) Log.d(TAG, "onSetRemoteControlClientPlaybackPosition(genId=" + generationId +
- ", timeMs=" + timeMs + ")");
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if ((mCurrentRcClient != null) && (mCurrentRcClientGen == generationId)) {
- // tell the current client to seek to the requested location
- try {
- mCurrentRcClient.seekTo(generationId, timeMs);
- } catch (RemoteException e) {
- Log.e(TAG, "Current valid remote client is dead: "+e);
- mCurrentRcClient = null;
- }
- }
- }
- }
- }
-
- protected void updateRemoteControlClientMetadata(int genId, int key, Rating value) {
- sendMsg(mEventHandler, MSG_RCC_UPDATE_METADATA, SENDMSG_QUEUE,
- genId /* arg1 */, key /* arg2 */, value /* obj */, 0 /* delay */);
- }
-
- private void onUpdateRemoteControlClientMetadata(int genId, int key, Rating value) {
- if(DEBUG_RC) Log.d(TAG, "onUpdateRemoteControlClientMetadata(genId=" + genId +
- ", what=" + key + ",rating=" + value + ")");
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if ((mCurrentRcClient != null) && (mCurrentRcClientGen == genId)) {
- try {
- switch (key) {
- case MediaMetadataEditor.RATING_KEY_BY_USER:
- mCurrentRcClient.updateMetadata(genId, key, value);
- break;
- default:
- Log.e(TAG, "unhandled metadata key " + key + " update for RCC "
- + genId);
- break;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Current valid remote client is dead", e);
- mCurrentRcClient = null;
- }
- }
- }
- }
- }
-
- protected void setPlaybackInfoForRcc(int rccId, int what, int value) {
- sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
- rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
- }
-
- // handler for MSG_RCC_NEW_PLAYBACK_INFO
- private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
- if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
- ", what=" + key + ",val=" + value + ")");
- synchronized(mPRStack) {
- // iterating from top of stack as playback information changes are more likely
- // on entries at the top of the remote control stack
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if (prse.getRccId() == rccId) {
- switch (key) {
- case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
- prse.mPlaybackType = value;
- postReevaluateRemote();
- break;
- case RemoteControlClient.PLAYBACKINFO_VOLUME:
- prse.mPlaybackVolume = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemote.mVolume = value;
- mVolumeController.postHasNewRemotePlaybackInfo();
- }
- }
- break;
- case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
- prse.mPlaybackVolumeMax = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemote.mVolumeMax = value;
- mVolumeController.postHasNewRemotePlaybackInfo();
- }
- }
- break;
- case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
- prse.mPlaybackVolumeHandling = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemote.mVolumeHandling = value;
- mVolumeController.postHasNewRemotePlaybackInfo();
- }
- }
- break;
- case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
- prse.mPlaybackStream = value;
- break;
- default:
- Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
- break;
- }
- return;
- }
- }//for
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index mPRStack on onNewPlaybackInfoForRcc, lock error? ", e);
- }
- }
- }
-
- protected void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
- sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
- rccId /* arg1 */, state /* arg2 */,
- new PlayerRecord.RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
- }
-
- private void onNewPlaybackStateForRcc(int rccId, int state,
- PlayerRecord.RccPlaybackState newState) {
- if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
- + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
- synchronized(mPRStack) {
- if (mPRStack.empty()) {
- return;
- }
- PlayerRecord oldTopPrse = mPRStack.lastElement(); // top of the stack before any changes
- PlayerRecord prse = null;
- int lastPlayingIndex = mPRStack.size();
- int inStackIndex = -1;
- try {
- // go through the stack from the top to figure out who's playing, and the position
- // of this RemoteControlClient (note that it may not be in the stack)
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- prse = mPRStack.elementAt(index);
- if (prse.getRccId() == rccId) {
- inStackIndex = index;
- prse.mPlaybackState = newState;
- }
- if (prse.isPlaybackActive()) {
- lastPlayingIndex = index;
- }
- }
-
- if (inStackIndex != -1) {
- // is in the stack
- prse = mPRStack.elementAt(inStackIndex);
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemoteIsActive = isPlaystateActive(state);
- postReevaluateRemote();
- }
- }
- if (mPRStack.size() > 1) { // no need to remove and add if stack contains only 1
- // remove it from its old location in the stack
- mPRStack.removeElementAt(inStackIndex);
- if (prse.isPlaybackActive()) {
- // and put it at the top
- mPRStack.push(prse);
- } else {
- // and put it after the ones with active playback
- if (inStackIndex > lastPlayingIndex) {
- mPRStack.add(lastPlayingIndex, prse);
- } else {
- mPRStack.add(lastPlayingIndex - 1, prse);
- }
- }
- }
-
- if (oldTopPrse != mPRStack.lastElement()) {
- // the top of the stack changed:
- final ComponentName target =
- mPRStack.lastElement().getMediaButtonReceiver();
- if (target != null) {
- // post message to persist the default media button receiver
- mEventHandler.sendMessage( mEventHandler.obtainMessage(
- MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
- }
- // reevaluate the display
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification or bad index
- Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
- + " size=" + mPRStack.size()
- + "accessing PlayerRecord stack in onNewPlaybackStateForRcc", e);
- }
- }
- }
-
- protected void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
- sendMsg(mEventHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
- rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
- }
-
// handler for MSG_RCC_NEW_VOLUME_OBS
private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
synchronized(mPRStack) {
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/MediaRouter.java b/media/java/android/media/MediaRouter.java
index ddd5b72..3336694 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -29,7 +29,6 @@
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
import android.media.session.MediaSession;
-import android.media.session.RemoteVolumeProvider;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -2204,10 +2203,10 @@
return;
}
if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
- int volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_FIXED;
+ int volumeControl = VolumeProvider.VOLUME_CONTROL_FIXED;
switch (mVolumeHandling) {
case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
- volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
break;
case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
default:
@@ -2226,7 +2225,7 @@
}
}
- class SessionVolumeProvider extends RemoteVolumeProvider {
+ class SessionVolumeProvider extends VolumeProvider {
public SessionVolumeProvider(int volumeControl, int maxVolume) {
super(volumeControl, maxVolume);
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 76c7299..be96398 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -361,18 +361,10 @@
if (timeMs < 0) {
throw new IllegalArgumentException("illegal negative time value");
}
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- if (mCurrentSession != null) {
- mCurrentSession.getTransportControls().seekTo(timeMs);
- }
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ mCurrentSession.getTransportControls().seekTo(timeMs);
}
- } else {
- final int genId;
- synchronized (mGenLock) {
- genId = mClientGenerationIdCurrent;
- }
- mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs);
}
return true;
}
@@ -534,34 +526,15 @@
if (!mMetadataChanged) {
return;
}
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- if (mCurrentSession != null) {
- if (mEditorMetadata.containsKey(
- String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
- Rating rating = (Rating) getObject(
- MediaMetadataEditor.RATING_KEY_BY_USER, null);
- if (rating != null) {
- mCurrentSession.getTransportControls().setRating(rating);
- }
- }
- }
- }
- } else {
- final int genId;
- synchronized(mGenLock) {
- genId = mClientGenerationIdCurrent;
- }
- synchronized(mInfoLock) {
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
if (mEditorMetadata.containsKey(
String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
Rating rating = (Rating) getObject(
MediaMetadataEditor.RATING_KEY_BY_USER, null);
- mAudioManager.updateRemoteControlClientMetadata(genId,
- MediaMetadataEditor.RATING_KEY_BY_USER,
- rating);
- } else {
- Log.e(TAG, "no metadata to apply");
+ if (rating != null) {
+ mCurrentSession.getTransportControls().setRating(rating);
+ }
}
}
}
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/RemoteVolumeProvider.java b/media/java/android/media/VolumeProvider.java
similarity index 92%
rename from media/java/android/media/session/RemoteVolumeProvider.java
rename to media/java/android/media/VolumeProvider.java
index 606b1d7..7d93b40 100644
--- a/media/java/android/media/session/RemoteVolumeProvider.java
+++ b/media/java/android/media/VolumeProvider.java
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
+package android.media;
+import android.media.session.MediaSession;
import android.os.RemoteException;
import android.util.Log;
@@ -24,8 +25,8 @@
* You can set a volume provider on a session by calling
* {@link MediaSession#setPlaybackToRemote}.
*/
-public abstract class RemoteVolumeProvider {
- private static final String TAG = "RemoteVolumeProvider";
+public abstract class VolumeProvider {
+ private static final String TAG = "VolumeProvider";
/**
* The volume is fixed and can not be modified. Requests to change volume
@@ -60,7 +61,7 @@
* this provider.
* @param maxVolume The maximum allowed volume.
*/
- public RemoteVolumeProvider(int volumeControl, int maxVolume) {
+ public VolumeProvider(int volumeControl, int maxVolume) {
mControlType = volumeControl;
mMaxVolume = maxVolume;
}
@@ -117,7 +118,7 @@
/**
* @hide
*/
- void setSession(MediaSession session) {
+ public void setSession(MediaSession session) {
mSession = session;
}
}
\ No newline at end of file
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 1cfc5bc..5bc0de4 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -15,6 +15,7 @@
package android.media.session;
+import android.content.ComponentName;
import android.media.MediaMetadata;
import android.media.session.ISessionController;
import android.media.session.RouteOptions;
@@ -33,6 +34,7 @@
ISessionController getController();
void setFlags(int flags);
void setActive(boolean active);
+ void setMediaButtonReceiver(in ComponentName mbr);
void destroy();
// These commands are for setting up and communicating with routes
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index f0cd785..b4c11f6 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -20,6 +20,7 @@
import android.media.Rating;
import android.media.session.ISessionControllerCallback;
import android.media.session.MediaSessionInfo;
+import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -38,6 +39,9 @@
void showRoutePicker();
MediaSessionInfo getSessionInfo();
long getFlags();
+ ParcelableVolumeInfo getVolumeAttributes();
+ void adjustVolumeBy(int delta, int flags);
+ void setVolumeTo(int value, int flags);
// These commands are for the TransportController
void play();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 5ca7daa..84dad25 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -18,6 +18,7 @@
import android.media.MediaMetadata;
import android.media.Rating;
+import android.media.VolumeProvider;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -70,14 +71,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);
}
/**
@@ -190,6 +184,23 @@
}
/**
+ * Get the current volume info for this session.
+ *
+ * @return The current volume info or null.
+ */
+ public VolumeInfo getVolumeInfo() {
+ try {
+ ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
+ return new VolumeInfo(result.volumeType, result.audioStream, result.controlType,
+ result.maxVolume, result.currentVolume);
+
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getVolumeInfo.", e);
+ }
+ return null;
+ }
+
+ /**
* Adds a callback to receive updates from the Session. Updates will be
* posted on the caller's thread.
*
@@ -305,7 +316,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 +325,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) {
@@ -507,6 +527,85 @@
}
}
+ /**
+ * Holds information about the way volume is handled for this session.
+ */
+ public static final class VolumeInfo {
+ private final int mVolumeType;
+ private final int mAudioStream;
+ private final int mVolumeControl;
+ private final int mMaxVolume;
+ private final int mCurrentVolume;
+
+ /**
+ * @hide
+ */
+ public VolumeInfo(int type, int stream, int control, int max, int current) {
+ mVolumeType = type;
+ mAudioStream = stream;
+ mVolumeControl = control;
+ mMaxVolume = max;
+ mCurrentVolume = current;
+ }
+
+ /**
+ * Get the type of volume handling, either local or remote. One of:
+ * <ul>
+ * <li>{@link MediaSession#VOLUME_TYPE_LOCAL}</li>
+ * <li>{@link MediaSession#VOLUME_TYPE_REMOTE}</li>
+ * </ul>
+ *
+ * @return The type of volume handling this session is using.
+ */
+ public int getVolumeType() {
+ return mVolumeType;
+ }
+
+ /**
+ * Get the stream this is currently controlling volume on. When the volume
+ * type is {@link MediaSession#VOLUME_TYPE_REMOTE} this value does not
+ * have meaning and should be ignored.
+ *
+ * @return The stream this session is playing on.
+ */
+ public int getAudioStream() {
+ return mAudioStream;
+ }
+
+ /**
+ * Get the type of volume control that can be used. One of:
+ * <ul>
+ * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
+ * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
+ * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
+ * </ul>
+ *
+ * @return The type of volume control that may be used with this
+ * session.
+ */
+ public int getVolumeControl() {
+ return mVolumeControl;
+ }
+
+ /**
+ * Get the maximum volume that may be set for this session.
+ *
+ * @return The maximum allowed volume where this session is playing.
+ */
+ public int getMaxVolume() {
+ return mMaxVolume;
+ }
+
+ /**
+ * Get the current volume for this session.
+ *
+ * @return The current volume where this session is playing.
+ */
+ public int getCurrentVolume() {
+ return mCurrentVolume;
+ }
+ }
+
private final static class CallbackStub extends ISessionControllerCallback.Stub {
private final WeakReference<MediaController> mController;
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 4ba1351..406b1c3 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -19,10 +19,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
+import android.media.VolumeProvider;
import android.media.session.ISessionController;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
@@ -125,18 +127,12 @@
public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
/**
- * The session uses local playback. Used for configuring volume handling
- * with the system.
- *
- * @hide
+ * The session uses local playback.
*/
public static final int VOLUME_TYPE_LOCAL = 1;
/**
- * The session uses remote playback. Used for configuring volume handling
- * with the system.
- *
- * @hide
+ * The session uses remote playback.
*/
public static final int VOLUME_TYPE_REMOTE = 2;
@@ -155,7 +151,7 @@
= new ArrayMap<String, RouteInterface.EventListener>();
private Route mRoute;
- private RemoteVolumeProvider mVolumeProvider;
+ private VolumeProvider mVolumeProvider;
private boolean mActive = false;;
@@ -232,6 +228,21 @@
}
/**
+ * Set a media button event receiver component to use to restart playback
+ * after an app has been stopped.
+ *
+ * @param mbr The receiver component to send the media button event to.
+ * @hide
+ */
+ public void setMediaButtonReceiver(ComponentName mbr) {
+ try {
+ mBinder.setMediaButtonReceiver(mbr);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
+ }
+ }
+
+ /**
* Set any flags for the session.
*
* @param flags The flags to set for this session.
@@ -272,7 +283,7 @@
* @param volumeProvider The provider that will handle volume changes. May
* not be null.
*/
- public void setPlaybackToRemote(RemoteVolumeProvider volumeProvider) {
+ public void setPlaybackToRemote(VolumeProvider volumeProvider) {
if (volumeProvider == null) {
throw new IllegalArgumentException("volumeProvider may not be null!");
}
@@ -524,12 +535,12 @@
}
/**
- * Notify the system that the remove volume changed.
+ * Notify the system that the remote volume changed.
*
* @param provider The provider that is handling volume changes.
* @hide
*/
- void notifyRemoteVolumeChanged(RemoteVolumeProvider provider) {
+ public void notifyRemoteVolumeChanged(VolumeProvider provider) {
if (provider == null || provider != mVolumeProvider) {
Log.w(TAG, "Received update from stale volume provider");
return;
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 801844f..838b857 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.MediaMetadata;
@@ -214,7 +215,7 @@
}
}
- public void addMediaButtonListener(PendingIntent pi,
+ public void addMediaButtonListener(PendingIntent pi, ComponentName mbrComponent,
Context context) {
if (pi == null) {
Log.w(TAG, "Pending intent was null, can't addMediaButtonListener.");
@@ -238,6 +239,7 @@
holder.mMediaButtonReceiver = new MediaButtonReceiver(pi, context);
holder.mSession.addCallback(holder.mMediaButtonReceiver, mHandler);
+ holder.mSession.setMediaButtonReceiver(mbrComponent);
if (DEBUG) {
Log.d(TAG, "addMediaButtonListener added " + pi);
}
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/media/java/android/media/session/ParcelableVolumeInfo.aidl
new file mode 100644
index 0000000..c4250f0
--- /dev/null
+++ b/media/java/android/media/session/ParcelableVolumeInfo.aidl
@@ -0,0 +1,18 @@
+/* Copyright 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.media.session;
+
+parcelable ParcelableVolumeInfo;
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java
new file mode 100644
index 0000000..166ccd3
--- /dev/null
+++ b/media/java/android/media/session/ParcelableVolumeInfo.java
@@ -0,0 +1,78 @@
+/* Copyright 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.media.session;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Convenience class for passing information about the audio configuration of a
+ * session. The public implementation is {@link MediaController.VolumeInfo}.
+ *
+ * @hide
+ */
+public class ParcelableVolumeInfo implements Parcelable {
+ public int volumeType;
+ public int audioStream;
+ public int controlType;
+ public int maxVolume;
+ public int currentVolume;
+
+ public ParcelableVolumeInfo(int volumeType, int audioStream, int controlType, int maxVolume,
+ int currentVolume) {
+ this.volumeType = volumeType;
+ this.audioStream = audioStream;
+ this.controlType = controlType;
+ this.maxVolume = maxVolume;
+ this.currentVolume = currentVolume;
+ }
+
+ public ParcelableVolumeInfo(Parcel from) {
+ volumeType = from.readInt();
+ audioStream = from.readInt();
+ controlType = from.readInt();
+ maxVolume = from.readInt();
+ currentVolume = from.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(volumeType);
+ dest.writeInt(audioStream);
+ dest.writeInt(controlType);
+ dest.writeInt(maxVolume);
+ dest.writeInt(currentVolume);
+ }
+
+
+ public static final Parcelable.Creator<ParcelableVolumeInfo> CREATOR
+ = new Parcelable.Creator<ParcelableVolumeInfo>() {
+ @Override
+ public ParcelableVolumeInfo createFromParcel(Parcel in) {
+ return new ParcelableVolumeInfo(in);
+ }
+
+ @Override
+ public ParcelableVolumeInfo[] newArray(int size) {
+ return new ParcelableVolumeInfo[size];
+ }
+ };
+}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 7ff2c2e..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>
@@ -144,15 +146,41 @@
/**
* Builds a URI that points to all programs on a given channel.
*
+ * @param channelId The ID of the channel to return programs for.
+ */
+ public static final Uri buildProgramsUriForChannel(long channelId) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_CHANNEL).appendPath(String.valueOf(channelId))
+ .appendPath(PATH_PROGRAM).build();
+ }
+
+ /**
+ * Builds a URI that points to all programs on a given channel.
+ *
* @param channelUri The URI of the channel to return programs for.
*/
public static final Uri buildProgramsUriForChannel(Uri channelUri) {
if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
throw new IllegalArgumentException("Not a channel: " + channelUri);
}
- String channelId = String.valueOf(ContentUris.parseId(channelUri));
- return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
- .appendPath(PATH_CHANNEL).appendPath(channelId).appendPath(PATH_PROGRAM).build();
+ return buildProgramsUriForChannel(ContentUris.parseId(channelUri));
+ }
+
+ /**
+ * Builds a URI that points to programs on a specific channel whose schedules overlap with the
+ * given time frame.
+ *
+ * @param channelId The ID of the channel to return programs for.
+ * @param startTime The start time used to filter programs. The returned programs should have
+ * {@link Programs#COLUMN_END_TIME_UTC_MILLIS} that is greater than this time.
+ * @param endTime The end time used to filter programs. The returned programs should have
+ * {@link Programs#COLUMN_START_TIME_UTC_MILLIS} that is less than this time.
+ */
+ public static final Uri buildProgramsUriForChannel(long channelId, long startTime,
+ long endTime) {
+ Uri uri = buildProgramsUriForChannel(channelId);
+ return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
+ .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
}
/**
@@ -167,9 +195,10 @@
*/
public static final Uri buildProgramsUriForChannel(Uri channelUri, long startTime,
long endTime) {
- Uri uri = buildProgramsUriForChannel(channelUri);
- return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
- .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
+ if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
+ throw new IllegalArgumentException("Not a channel: " + channelUri);
+ }
+ return buildProgramsUriForChannel(ContentUris.parseId(channelUri), startTime, endTime);
}
/**
@@ -353,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").
@@ -486,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
@@ -692,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
@@ -751,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() {}
@@ -821,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/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index edfdd60..daa7009 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -533,12 +533,11 @@
/**
* Releases this session.
- *
- * @throws IllegalStateException if the session has been already released.
*/
public void release() {
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.releaseSession(mToken, mUserId);
@@ -553,12 +552,12 @@
* Sets the {@link android.view.Surface} for this session.
*
* @param surface A {@link android.view.Surface} used to render video.
- * @throws IllegalStateException if the session has been already released.
* @hide
*/
public void setSurface(Surface surface) {
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
// surface can be null.
try {
@@ -573,11 +572,11 @@
*
* @param volume A volume value between 0.0f to 1.0f.
* @throws IllegalArgumentException if the volume value is out of range.
- * @throws IllegalStateException if the session has been already released.
*/
public void setStreamVolume(float volume) {
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
if (volume < 0.0f || volume > 1.0f) {
@@ -594,14 +593,14 @@
*
* @param channelUri The URI of a channel.
* @throws IllegalArgumentException if the argument is {@code null}.
- * @throws IllegalStateException if the session has been already released.
*/
public void tune(Uri channelUri) {
if (channelUri == null) {
throw new IllegalArgumentException("channelUri cannot be null");
}
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.tune(mToken, channelUri, mUserId);
@@ -620,8 +619,7 @@
* @param view A view playing TV.
* @param frame A position of the overlay view.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
- * @throws IllegalStateException if {@code view} is not attached to a window or
- * if the session has been already released.
+ * @throws IllegalStateException if {@code view} is not attached to a window.
*/
void createOverlayView(View view, Rect frame) {
if (view == null) {
@@ -634,7 +632,8 @@
throw new IllegalStateException("view must be attached to a window");
}
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
@@ -648,14 +647,14 @@
*
* @param frame A new position of the overlay view.
* @throws IllegalArgumentException if the arguments is {@code null}.
- * @throws IllegalStateException if the session has been already released.
*/
void relayoutOverlayView(Rect frame) {
if (frame == null) {
throw new IllegalArgumentException("frame cannot be null");
}
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.relayoutOverlayView(mToken, frame, mUserId);
@@ -666,12 +665,11 @@
/**
* Removes the current overlay view.
- *
- * @throws IllegalStateException if the session has been already released.
*/
void removeOverlayView() {
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.removeOverlayView(mToken, mUserId);
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 4fbd2a4..5f27b16 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -110,6 +110,11 @@
jfieldID certificateData;
};
+struct StateExceptionFields {
+ jmethodID init;
+ jclass classId;
+};
+
struct fields_t {
jfieldID context;
jmethodID post_event;
@@ -121,6 +126,7 @@
IteratorFields iterator;
EntryFields entry;
CertificateFields certificate;
+ StateExceptionFields stateException;
jclass certificateClassId;
jclass hashmapClassId;
jclass arraylistClassId;
@@ -212,6 +218,14 @@
}
}
+static void throwStateException(JNIEnv *env, const char *msg, status_t err) {
+ ALOGE("Illegal state exception: %s (%d)", msg, err);
+
+ jobject exception = env->NewObject(gFields.stateException.classId,
+ gFields.stateException.init, static_cast<int>(err),
+ env->NewStringUTF(msg));
+ env->Throw(static_cast<jthrowable>(exception));
+}
static bool throwExceptionAsNecessary(
JNIEnv *env, status_t err, const char *msg = NULL) {
@@ -275,8 +289,7 @@
msg = errbuf.string();
}
}
- ALOGE("Illegal state exception: %s", msg);
- jniThrowException(env, "java/lang/IllegalStateException", msg);
+ throwStateException(env, msg, err);
return true;
}
return false;
@@ -608,6 +621,10 @@
FIND_CLASS(clazz, "java/util/ArrayList");
gFields.arraylistClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+ FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
+ GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+ gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
}
static void android_media_MediaDrm_native_setup(
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 8a7e642..b6bb578 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -22,7 +22,6 @@
import android.hardware.ICameraServiceListener;
import android.hardware.IProCameraCallbacks;
import android.hardware.IProCameraUser;
-import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.impl.CameraMetadataNative;
@@ -51,8 +50,14 @@
* </pre>
*/
public class CameraBinderTest extends AndroidTestCase {
+ private static final int MAX_PARAMETERS_LENGTH = 100;
+
static String TAG = "CameraBinderTest";
+ // From ICameraService.h
+ private static final int API_VERSION_1 = 1;
+ private static final int API_VERSION_2 = 2;
+
protected CameraBinderTestUtils mUtils;
public CameraBinderTest() {
@@ -95,6 +100,56 @@
}
}
+ @SmallTest
+ public void testGetLegacyParameters() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ String[] parameters = new String[1];
+ assertEquals("Camera service returned parameters for camera " + cameraId,
+ CameraBinderTestUtils.NO_ERROR,
+ mUtils.getCameraService().getLegacyParameters(cameraId, /*out*/parameters));
+ assertNotNull(parameters[0]);
+ assertTrue("Parameters should have at least one character in it",
+ parameters[0].length() > 0);
+
+ int end = parameters[0].length();
+ if (end > MAX_PARAMETERS_LENGTH) {
+ end = MAX_PARAMETERS_LENGTH;
+ }
+
+ Log.v(TAG, "Camera " + cameraId + " parameters: " + parameters[0].substring(0, end));
+ }
+ }
+
+ /** The camera2 api is only supported on HAL3.2+ devices */
+ @SmallTest
+ public void testSupportsCamera2Api() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_2);
+
+ if (res != CameraBinderTestUtils.NO_ERROR && res != CameraBinderTestUtils.EOPNOTSUPP) {
+ fail("Camera service returned bad value when queried if it supports camera2 api: "
+ + res + " for camera ID " + cameraId);
+ }
+
+ boolean supports = res == CameraBinderTestUtils.NO_ERROR;
+ Log.v(TAG, "Camera " + cameraId + " supports api2: " + supports);
+ }
+ }
+
+ /** The camera1 api is supported on *all* devices regardless of HAL version */
+ @SmallTest
+ public void testSupportsCamera1Api() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_1);
+ assertEquals(
+ "Camera service returned bad value when queried if it supports camera1 api: "
+ + res + " for camera ID " + cameraId, CameraBinderTestUtils.NO_ERROR, res);
+ }
+ }
+
static abstract class DummyBase extends Binder implements android.os.IInterface {
@Override
public IBinder asBinder() {
@@ -150,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 {
/*
@@ -158,6 +244,7 @@
* android.hardware.camera2.ICameraDeviceCallbacks#onCameraError(int,
* android.hardware.camera2.CaptureResultExtras)
*/
+ @Override
public void onCameraError(int errorCode, CaptureResultExtras resultExtras)
throws RemoteException {
// TODO Auto-generated method stub
@@ -170,6 +257,7 @@
* android.hardware.camera2.ICameraDeviceCallbacks#onCaptureStarted(
* android.hardware.camera2.CaptureResultExtras, long)
*/
+ @Override
public void onCaptureStarted(CaptureResultExtras resultExtras, long timestamp)
throws RemoteException {
// TODO Auto-generated method stub
@@ -183,6 +271,7 @@
* android.hardware.camera2.impl.CameraMetadataNative,
* android.hardware.camera2.CaptureResultExtras)
*/
+ @Override
public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras)
throws RemoteException {
// TODO Auto-generated method stub
@@ -193,6 +282,7 @@
* (non-Javadoc)
* @see android.hardware.camera2.ICameraDeviceCallbacks#onCameraIdle()
*/
+ @Override
public void onCameraIdle() throws RemoteException {
// TODO Auto-generated method stub
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
index 1be2a62..6be538a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
@@ -22,6 +22,7 @@
protected static final int INVALID_OPERATION = -38;
protected static final int ALREADY_EXISTS = -17;
public static final int NO_ERROR = 0;
+ public static final int EOPNOTSUPP = -95;
private final Context mContext;
public CameraBinderTestUtils(Context context) {
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/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml b/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
index b769a0c..acb7767 100644
--- a/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
+++ b/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
@@ -125,6 +125,11 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/audiomanagertwo" />
+ <CheckBox
+ android:id="@+id/useVirtualCallCheckBox"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/useVirtualCallCheckText" />
</LinearLayout>
diff --git a/media/tests/ScoAudioTest/res/values/strings.xml b/media/tests/ScoAudioTest/res/values/strings.xml
index c3ff6d5..b0284e2 100644
--- a/media/tests/ScoAudioTest/res/values/strings.xml
+++ b/media/tests/ScoAudioTest/res/values/strings.xml
@@ -10,5 +10,5 @@
<string name="tts_speak">Speak TTS</string>
<string name="tts_to_file">TTS to file</string>
<string name="audiomanagertwo">Use different AudioManager for starting SCO</string>
-
+ <string name="useVirtualCallCheckText">Use Virtual Call</string>
</resources>
diff --git a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
index 0304640..7e21876 100644
--- a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
+++ b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
@@ -207,16 +207,25 @@
if (mForceScoOn != isChecked) {
mForceScoOn = isChecked;
AudioManager mngr = mAudioManager;
+ boolean useVirtualCall = false;
CheckBox box = (CheckBox) findViewById(R.id.useSecondAudioManager);
if (box.isChecked()) {
Log.i(TAG, "Using 2nd audio manager");
mngr = mAudioManager2;
}
+ box = (CheckBox) findViewById(R.id.useVirtualCallCheckBox);
+ useVirtualCall = box.isChecked();
if (mForceScoOn) {
- Log.e(TAG, "startBluetoothSco() IN");
- mngr.startBluetoothSco();
- Log.e(TAG, "startBluetoothSco() OUT");
+ if (useVirtualCall) {
+ Log.e(TAG, "startBluetoothScoVirtualCall() IN");
+ mngr.startBluetoothScoVirtualCall();
+ Log.e(TAG, "startBluetoothScoVirtualCall() OUT");
+ } else {
+ Log.e(TAG, "startBluetoothSco() IN");
+ mngr.startBluetoothSco();
+ Log.e(TAG, "startBluetoothSco() OUT");
+ }
} else {
Log.e(TAG, "stopBluetoothSco() IN");
mngr.stopBluetoothSco();
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
index a4d292a..b8b6c04 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
@@ -169,15 +169,10 @@
return true;
}
-void OmxJpegImageDecoder::configBitmapSize(SkBitmap* bm, SkBitmap::Config pref,
+void OmxJpegImageDecoder::configBitmapSize(SkBitmap* bm, SkColorType pref,
int width, int height) {
- bm->setConfig(getColorSpaceConfig(pref), width, height, 0, kOpaque_SkAlphaType);
-}
-
-SkBitmap::Config OmxJpegImageDecoder::getColorSpaceConfig(
- SkBitmap::Config pref) {
-
- // Set the color space to ARGB_8888 for now
+ // Set the color space to ARGB_8888 for now (ignoring pref)
// because of limitation in hardware support.
- return SkBitmap::kARGB_8888_Config;
+ bm->setInfo(SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType);
}
+
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
index e431e72..e487245 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
@@ -49,9 +49,7 @@
sp<MediaSource> getDecoder(OMXClient* client, const sp<MediaSource>& source);
bool decodeSource(sp<MediaSource> decoder, const sp<MediaSource>& source,
SkBitmap* bm);
- void configBitmapSize(SkBitmap* bm, SkBitmap::Config pref, int width,
- int height);
- SkBitmap::Config getColorSpaceConfig(SkBitmap::Config pref);
+ void configBitmapSize(SkBitmap* bm, SkColorType, int width, int height);
OMXClient mClient;
};
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/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index d2bf30c..ab18271 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -71,6 +71,7 @@
private AppWidgetHost mAppWidgetHost;
private AppWidgetManager mAppWidgetManager;
private KeyguardWidgetPager mAppWidgetContainer;
+ // TODO remove transport control references, these don't exist anymore
private KeyguardTransportControlView mTransportControl;
private int mAppWidgetToShow;
@@ -235,36 +236,6 @@
mKeyguardMultiUserSelectorView.finalizeActiveUserView(true);
}
}
- @Override
- public void onMusicClientIdChanged(
- int clientGeneration, boolean clearing, android.app.PendingIntent intent) {
- // Set transport state to invisible until we know music is playing (below)
- if (DEBUGXPORT && (mClientGeneration != clientGeneration || clearing)) {
- Log.v(TAG, (clearing ? "hide" : "show") + " transport, gen:" + clientGeneration);
- }
- mClientGeneration = clientGeneration;
- final int newState = (clearing ? TRANSPORT_GONE
- : (mTransportState == TRANSPORT_VISIBLE ?
- TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
- if (newState != mTransportState) {
- mTransportState = newState;
- if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
- KeyguardHostView.this.post(mSwitchPageRunnable);
- }
- }
- @Override
- public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
- if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
- if (mTransportState != TRANSPORT_GONE) {
- final int newState = (isMusicPlaying(playbackState) ?
- TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
- if (newState != mTransportState) {
- mTransportState = newState;
- if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
- KeyguardHostView.this.post(mSwitchPageRunnable);
- }
- }
- }
};
private static final boolean isMusicPlaying(int playbackState) {
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/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 668e1ef..bf34705 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -91,8 +91,6 @@
private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
protected static final int MSG_BOOT_COMPLETED = 313;
private static final int MSG_USER_SWITCH_COMPLETE = 314;
- private static final int MSG_SET_CURRENT_CLIENT_ID = 315;
- protected static final int MSG_SET_PLAYBACK_STATE = 316;
protected static final int MSG_USER_INFO_CHANGED = 317;
protected static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
private static final int MSG_SCREEN_TURNED_ON = 319;
@@ -184,12 +182,6 @@
case MSG_BOOT_COMPLETED:
handleBootCompleted();
break;
- case MSG_SET_CURRENT_CLIENT_ID:
- handleSetGenerationId(msg.arg1, msg.arg2 != 0, (PendingIntent) msg.obj);
- break;
- case MSG_SET_PLAYBACK_STATE:
- handleSetPlaybackState(msg.arg1, msg.arg2, (Long) msg.obj);
- break;
case MSG_USER_INFO_CHANGED:
handleUserInfoChanged(msg.arg1);
break;
@@ -206,8 +198,6 @@
}
};
- private AudioManager mAudioManager;
-
private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
@Override
@@ -257,49 +247,6 @@
private DisplayClientState mDisplayClientState = new DisplayClientState();
- /**
- * This currently implements the bare minimum required to enable showing and hiding
- * KeyguardTransportControl. There's a lot of client state to maintain which is why
- * KeyguardTransportControl maintains an independent connection while it's showing.
- */
- private final IRemoteControlDisplay.Stub mRemoteControlDisplay =
- new IRemoteControlDisplay.Stub() {
-
- public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
- long currentPosMs, float speed) {
- Message msg = mHandler.obtainMessage(MSG_SET_PLAYBACK_STATE,
- generationId, state, stateChangeTimeMs);
- mHandler.sendMessage(msg);
- }
-
- public void setMetadata(int generationId, Bundle metadata) {
-
- }
-
- public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
-
- }
-
- public void setArtwork(int generationId, Bitmap bitmap) {
-
- }
-
- public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
-
- }
-
- public void setEnabled(boolean enabled) {
- // no-op: this RemoteControlDisplay is not subject to being disabled.
- }
-
- public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
- boolean clearing) throws RemoteException {
- Message msg = mHandler.obtainMessage(MSG_SET_CURRENT_CLIENT_ID,
- clientGeneration, (clearing ? 1 : 0), mediaIntent);
- mHandler.sendMessage(msg);
- }
- };
-
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
@@ -501,38 +448,6 @@
}
}
- protected void handleSetGenerationId(int clientGeneration, boolean clearing, PendingIntent p) {
- mDisplayClientState.clientGeneration = clientGeneration;
- mDisplayClientState.clearing = clearing;
- mDisplayClientState.intent = p;
- if (DEBUG)
- Log.v(TAG, "handleSetGenerationId(g=" + clientGeneration + ", clear=" + clearing + ")");
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onMusicClientIdChanged(clientGeneration, clearing, p);
- }
- }
- }
-
- protected void handleSetPlaybackState(int generationId, int playbackState, long eventTime) {
- if (DEBUG)
- Log.v(TAG, "handleSetPlaybackState(gen=" + generationId
- + ", state=" + playbackState + ", t=" + eventTime + ")");
- mDisplayClientState.playbackState = playbackState;
- mDisplayClientState.playbackEventTime = eventTime;
- if (generationId == mDisplayClientState.clientGeneration) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onMusicPlaybackStateChanged(playbackState, eventTime);
- }
- }
- } else {
- Log.w(TAG, "Ignoring generation id " + generationId + " because it's not current");
- }
- }
-
private void handleUserInfoChanged(int userId) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -694,8 +609,6 @@
protected void handleBootCompleted() {
if (mBootCompleted) return;
mBootCompleted = true;
- mAudioManager = new AudioManager(mContext);
- mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1013,12 +926,6 @@
callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
callback.onClockVisibilityChanged();
callback.onSimStateChanged(mSimState);
- callback.onMusicClientIdChanged(
- mDisplayClientState.clientGeneration,
- mDisplayClientState.clearing,
- mDisplayClientState.intent);
- callback.onMusicPlaybackStateChanged(mDisplayClientState.playbackState,
- mDisplayClientState.playbackEventTime);
}
public void sendKeyguardVisibilityChanged(boolean showing) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index bcdf18f..01600d2 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -144,18 +144,6 @@
public void onBootCompleted() { }
/**
- * Called when audio client attaches or detaches from AudioManager.
- */
- public void onMusicClientIdChanged(int clientGeneration, boolean clearing, PendingIntent intent) { }
-
- /**
- * Called when the audio playback state changes.
- * @param playbackState
- * @param eventTime
- */
- public void onMusicPlaybackStateChanged(int playbackState, long eventTime) { }
-
- /**
* Called when the emergency call button is pressed.
*/
void onEmergencyCallAction() { }
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..0146ab75 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/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
index bac8cb6..305a82f 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
@@ -19,10 +19,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:zAdjustment="top">
+ android:zAdjustment="normal">
<alpha android:fromAlpha="1.0" android:toAlpha="1.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100"/>
+ android:interpolator="@android:interpolator/linear"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
index b0f8807f..863591f 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
@@ -19,10 +19,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:zAdjustment="normal">
+ android:zAdjustment="top">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100"/>
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
index 2857c04..adcefe0 100644
--- a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
@@ -23,6 +23,6 @@
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="150"/>
+ android:interpolator="@android:interpolator/fast_out_linear_in"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
index 1139e72..863591f 100644
--- a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
@@ -23,6 +23,6 @@
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="150"/>
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
index d4fdbf3..17100f7 100644
--- a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..e969d4c2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
index 9fc1a3b..b53bd8f 100644
--- a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..657f710
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
index f38de93..09606f6 100644
--- a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..a444c55
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
index 8194605..427cad9 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..29cf44b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png
Binary files differ
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/drawable/recents_button_bg.xml b/packages/SystemUI/res/drawable/recents_button_bg.xml
new file mode 100644
index 0000000..a4cb088
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_button_bg.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight" />
\ 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/recents_status_bar_scrim.xml b/packages/SystemUI/res/layout/recents_status_bar_scrim.xml
new file mode 100644
index 0000000..24928d0
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_status_bar_scrim.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
+ android:scaleType="fitXY"
+ android:src="@drawable/recents_status_gradient" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 23f2796..1bab67a 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -32,8 +32,10 @@
android:id="@+id/application_icon"
android:layout_width="@dimen/recents_task_view_application_icon_size"
android:layout_height="@dimen/recents_task_view_application_icon_size"
- android:layout_marginStart="16dp"
- android:layout_gravity="center_vertical|start" />
+ android:layout_marginStart="8dp"
+ android:layout_gravity="center_vertical|start"
+ android:padding="8dp"
+ android:background="@drawable/recents_button_bg" />
<TextView
android:id="@+id/activity_description"
android:layout_width="match_parent"
@@ -56,6 +58,7 @@
android:layout_marginEnd="4dp"
android:layout_gravity="center_vertical|end"
android:padding="18dp"
+ android:background="@drawable/recents_button_bg"
android:visibility="invisible"
android:src="@drawable/recents_dismiss_light" />
</com.android.systemui.recents.views.TaskBarView>
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 d24d21e..2e4c0ef 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -63,8 +63,9 @@
<ImageButton android:id="@+id/settings_button"
style="@android:style/Widget.Material.Button.Borderless"
android:layout_toStartOf="@id/multi_user_switch"
- android:layout_width="56dp"
+ android:layout_width="48dp"
android:layout_height="@dimen/status_bar_header_height"
+ android:layout_marginStart="8dp"
android:src="@drawable/ic_settings_24dp"
android:contentDescription="@string/accessibility_desc_quick_settings"/>
@@ -72,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="4dp"
+ android:layout_alignWithParentIfMissing="true"
android:layout_marginStart="16dp"
- />
+ android:paddingEnd="2dp" />
<TextView
android:id="@+id/header_charging_info"
@@ -117,12 +118,11 @@
android:layout_below="@id/clock"
/>
</RelativeLayout>
-
<com.android.keyguard.CarrierText
android:id="@+id/keyguard_carrier_text"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_header_height_keyguard"
- android:layout_marginLeft="8dp"
+ android:layout_marginLeft="16dp"
android:layout_toStartOf="@id/system_icons_container"
android:gravity="center_vertical"
android:ellipsize="marquee"
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-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 4021f69..71fb8b8 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -243,10 +243,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">"За една минута"</item>
<item quantity="other" msgid="6924190729213550991">"За %d минути"</item>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index b3b4c9b..b1dc302 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -42,7 +42,7 @@
<string name="battery_saver_confirmation_title" msgid="5987726159603849352">"¿Iniciar ahorro de batería?"</string>
<string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Iniciar"</string>
<string name="battery_saver_start_action" msgid="7245333922937402896">"Iniciar ahorro de batería"</string>
- <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Para ayudar a mejorar la duración de la batería, la función de ahorro de energía reducirá el rendimiento del dispositivo.\n\nEsta función estará inhabilitada cuando el dispositivo esté conectado."</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Para ayudar a mejorar la duración de la batería, la función de ahorro de energía reducirá el rendimiento del dispositivo.\n\nEsta función estará inhabilitada cuando el dispositivo esté enchufado."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Ajustes"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Modo avión"</string>
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-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 571e9ba..03ac011 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -245,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Balayez 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ée à 100 % dans <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">"Invité"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"Ajouter un invité"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Pendant une minute"</item>
<item quantity="other" msgid="6924190729213550991">"Pendant %d minutes"</item>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index bcb28e9..d11c9be 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -244,11 +244,9 @@
<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>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <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">
<item quantity="one" msgid="9040808414992812341">"Pendant une minute"</item>
<item quantity="other" msgid="6924190729213550991">"Pendant %d minutes"</item>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index bd8b829..024be828 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Prijeđite prstom ulijevo za fotoaparat"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dok ne isključite"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Gost"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ gost"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Jednu minutu"</item>
<item quantity="other" msgid="6924190729213550991">"%d min"</item>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index ee8fa8a..4424338 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -32,26 +32,17 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ծանուցումներ չկան"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ընթացիկ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ծանուցումներ"</string>
- <!-- no translation found for battery_low_title (6456385927409742437) -->
- <skip />
+ <string name="battery_low_title" msgid="6456385927409742437">"Մարտկոցը լիցքաթափվում է"</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"մնում է <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (6534746636002666456) -->
- <skip />
+ <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>
- <!-- no translation found for invalid_charger_title (3515740382572798460) -->
- <skip />
- <!-- no translation found for invalid_charger_text (5474997287953892710) -->
- <skip />
- <!-- no translation found for battery_low_why (4553600287639198111) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_title (5987726159603849352) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_ok (7283108887345688413) -->
- <skip />
- <!-- no translation found for battery_saver_start_action (7245333922937402896) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_text (8417584516834617662) -->
- <skip />
+ <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="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>
@@ -252,10 +243,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">"Մեկ րոպե"</item>
<item quantity="other" msgid="6924190729213550991">"%d րոպե"</item>
@@ -264,10 +253,7 @@
<item quantity="one" msgid="3480040795582254384">"Մեկ ժամ"</item>
<item quantity="other" msgid="5408537517529822157">"%d ժամ"</item>
</plurals>
- <!-- no translation found for battery_saver_notification_title (237918726750955859) -->
- <skip />
- <!-- no translation found for battery_saver_notification_text (7796554871101546872) -->
- <skip />
- <!-- no translation found for battery_saver_notification_action_text (7546297220816993504) -->
- <skip />
+ <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-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 3a8aab8..5dbd490 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Gesek ke kiri untuk kamera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hingga Anda menonaktifkan ini"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengisi daya (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga 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">"Tamu"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Tamu"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Selama satu menit"</item>
<item quantity="other" msgid="6924190729213550991">"Selama %d menit"</item>
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-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index d595089..6ebd5c7 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -245,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"左にスワイプしてカメラを表示"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"ユーザーがOFFにするまで"</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>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 9f7e9bf..a35c821 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -32,26 +32,17 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"შეტყობინებები არ არის."</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"მიმდინარე"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"შეტყობინებები"</string>
- <!-- no translation found for battery_low_title (6456385927409742437) -->
- <skip />
+ <string name="battery_low_title" msgid="6456385927409742437">"ბატარეა იწურება"</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"დარჩენილია <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (6534746636002666456) -->
- <skip />
+ <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>
- <!-- no translation found for invalid_charger_title (3515740382572798460) -->
- <skip />
- <!-- no translation found for invalid_charger_text (5474997287953892710) -->
- <skip />
- <!-- no translation found for battery_low_why (4553600287639198111) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_title (5987726159603849352) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_ok (7283108887345688413) -->
- <skip />
- <!-- no translation found for battery_saver_start_action (7245333922937402896) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_text (8417584516834617662) -->
- <skip />
+ <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="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>
@@ -252,10 +243,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">"ერთი წუთით"</item>
<item quantity="other" msgid="6924190729213550991">"%d წუთით"</item>
@@ -264,10 +253,7 @@
<item quantity="one" msgid="3480040795582254384">"ერთი საათით"</item>
<item quantity="other" msgid="5408537517529822157">"%d საათით"</item>
</plurals>
- <!-- no translation found for battery_saver_notification_title (237918726750955859) -->
- <skip />
- <!-- no translation found for battery_saver_notification_text (7796554871101546872) -->
- <skip />
- <!-- no translation found for battery_saver_notification_action_text (7546297220816993504) -->
- <skip />
+ <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-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-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index aa4f27d..0ffb4b8 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -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>
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 5114935..2fed4bd 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -32,26 +32,17 @@
<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>
- <!-- no translation found for battery_low_title (6456385927409742437) -->
- <skip />
+ <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>
- <!-- no translation found for battery_low_percent_format_saver_started (6534746636002666456) -->
- <skip />
+ <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>
- <!-- no translation found for invalid_charger_title (3515740382572798460) -->
- <skip />
- <!-- no translation found for invalid_charger_text (5474997287953892710) -->
- <skip />
- <!-- no translation found for battery_low_why (4553600287639198111) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_title (5987726159603849352) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_ok (7283108887345688413) -->
- <skip />
- <!-- no translation found for battery_saver_start_action (7245333922937402896) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_text (8417584516834617662) -->
- <skip />
+ <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">"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>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Mod pesawat"</string>
@@ -232,7 +223,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Sudah dicas"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Mengecas"</string>
- <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> sehingga penuh"</string>
+ <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Lagi <xliff:g id="CHARGING_TIME">%s</xliff:g> untuk penuh"</string>
<string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"Tidak mengecas"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Rangkaian mungkin\nboleh dipantau"</string>
<string name="description_target_search" msgid="3091587249776033139">"Carian"</string>
@@ -252,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>
@@ -264,10 +253,7 @@
<item quantity="one" msgid="3480040795582254384">"Selama satu jam"</item>
<item quantity="other" msgid="5408537517529822157">"Selama %d jam"</item>
</plurals>
- <!-- no translation found for battery_saver_notification_title (237918726750955859) -->
- <skip />
- <!-- no translation found for battery_saver_notification_text (7796554871101546872) -->
- <skip />
- <!-- no translation found for battery_saver_notification_action_text (7546297220816993504) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"Penjimat bateri dihidupkan"</string>
+ <string name="battery_saver_notification_text" msgid="7796554871101546872">"Prestasi peranti dikurangkan."</string>
+ <string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Buka tetapan penjimat bateri"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index be9af97..cecbe7a 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Sveip mot venstre for å åpne kameraet"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Inntil du slår av funksjonen"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Lader (fulladet om <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">"Gjest"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Gjest"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"I ett minutt"</item>
<item quantity="other" msgid="6924190729213550991">"I %d minutter"</item>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 37aae0a..657e03f 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -194,8 +194,8 @@
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brilho"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotação automática"</string>
<string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotação bloqueada"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Vertical"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Horizontal"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Retrato"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Paisagem"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de Introdução"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Localização"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localização Desativada"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a540efbb..bcf7431 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -32,26 +32,17 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
- <!-- no translation found for battery_low_title (6456385927409742437) -->
- <skip />
+ <string name="battery_low_title" msgid="6456385927409742437">"Bateria fraca"</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restante"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (6534746636002666456) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"<xliff:g id="NUMBER">%d%%</xliff:g> restante(s). A Economia de bateria está ativada."</string>
<string name="invalid_charger" msgid="4549105996740522523">"O carregamento via USB não é suportado.\nUse apenas o carregador fornecido."</string>
- <!-- no translation found for invalid_charger_title (3515740382572798460) -->
- <skip />
- <!-- no translation found for invalid_charger_text (5474997287953892710) -->
- <skip />
- <!-- no translation found for battery_low_why (4553600287639198111) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_title (5987726159603849352) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_ok (7283108887345688413) -->
- <skip />
- <!-- no translation found for battery_saver_start_action (7245333922937402896) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_text (8417584516834617662) -->
- <skip />
+ <string name="invalid_charger_title" msgid="3515740382572798460">"O carregamento via USB não é suportado."</string>
+ <string name="invalid_charger_text" msgid="5474997287953892710">"Use apenas o carregador fornecido."</string>
+ <string name="battery_low_why" msgid="4553600287639198111">"Configurações"</string>
+ <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Iniciar a economia de bateria?"</string>
+ <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Iniciar"</string>
+ <string name="battery_saver_start_action" msgid="7245333922937402896">"Iniciar economia de bateria"</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Para ajudar a melhorar a vida útil da bateria, a Economia de bateria reduzirá o desempenho do dispositivo.\n\nA Economia de bateria será desativada quando o dispositivo estiver carregando."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Configurações"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Modo avião"</string>
@@ -254,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Deslize para a esquerda para usar a câmera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Até você desativar"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até concluir)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Convidado"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ convidado"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Por 1 minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Por %d minutos"</item>
@@ -266,10 +255,7 @@
<item quantity="one" msgid="3480040795582254384">"Por 1 hora"</item>
<item quantity="other" msgid="5408537517529822157">"Por %d horas"</item>
</plurals>
- <!-- no translation found for battery_saver_notification_title (237918726750955859) -->
- <skip />
- <!-- no translation found for battery_saver_notification_text (7796554871101546872) -->
- <skip />
- <!-- no translation found for battery_saver_notification_action_text (7546297220816993504) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"A Economia de bateria está ativada"</string>
+ <string name="battery_saver_notification_text" msgid="7796554871101546872">"O desempenho do dispositivo foi reduzido."</string>
+ <string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Abrir configurações de economia de bateria"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index e057b76..8ec3621 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -32,26 +32,17 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nicio notificare"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"În desfăşurare"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificări"</string>
- <!-- no translation found for battery_low_title (6456385927409742437) -->
- <skip />
+ <string name="battery_low_title" msgid="6456385927409742437">"Bateria este aproape descărcată"</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"Rămas: <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (6534746636002666456) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"Procent rămas din baterie: <xliff:g id="NUMBER">%d%%</xliff:g>. Economisirea bateriei este activată."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Încărcarea USB nu este acceptată. \nUtilizaţi numai încărcătorul furnizat."</string>
- <!-- no translation found for invalid_charger_title (3515740382572798460) -->
- <skip />
- <!-- no translation found for invalid_charger_text (5474997287953892710) -->
- <skip />
- <!-- no translation found for battery_low_why (4553600287639198111) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_title (5987726159603849352) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_ok (7283108887345688413) -->
- <skip />
- <!-- no translation found for battery_saver_start_action (7245333922937402896) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_text (8417584516834617662) -->
- <skip />
+ <string name="invalid_charger_title" msgid="3515740382572798460">"Încărcarea prin USB nu este acceptată."</string>
+ <string name="invalid_charger_text" msgid="5474997287953892710">"Utilizați numai încărcătorul furnizat."</string>
+ <string name="battery_low_why" msgid="4553600287639198111">"Setări"</string>
+ <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Porniți economisirea bateriei?"</string>
+ <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Începeți"</string>
+ <string name="battery_saver_start_action" msgid="7245333922937402896">"Porniți economisirea bateriei"</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Pentru a mări autonomia bateriei, funcția Economisirea bateriei reduce performanța dispozitivului.\n\nEconomisirea bateriei se dezactivează când dispozitivul este conectat la priză."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Setări"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Mod Avion"</string>
@@ -252,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>
@@ -264,10 +253,7 @@
<item quantity="one" msgid="3480040795582254384">"Timp de o oră"</item>
<item quantity="other" msgid="5408537517529822157">"Timp de %d (de) ore"</item>
</plurals>
- <!-- no translation found for battery_saver_notification_title (237918726750955859) -->
- <skip />
- <!-- no translation found for battery_saver_notification_text (7796554871101546872) -->
- <skip />
- <!-- no translation found for battery_saver_notification_action_text (7546297220816993504) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"Economisirea bateriei este activată"</string>
+ <string name="battery_saver_notification_text" msgid="7796554871101546872">"Performanța dispozitivului s-a redus."</string>
+ <string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Deschideți setările pentru economisirea bateriei"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 3f95a0c..8c59e05 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -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>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index ad12b18..10ba09f 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Povlecite v levo za fotoaparat"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dokler tega ne izklopite"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Gost"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"Dodajanje gosta"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Za eno minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Za %d min"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 22a89a9..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>
@@ -243,10 +243,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">"Један минут"</item>
<item quantity="other" msgid="6924190729213550991">"%d мин"</item>
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-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index b49e1a5..42f262b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Mag-swipe pakaliwa para sa camera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hanggang sa i-off mo ito"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nagtsa-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang mapuno)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Bisita"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Bisita"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Sa loob ng isang minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Sa loob ng %d (na) minuto"</item>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 7bbf9ee..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>
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Kamera için sola kaydırın"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Siz bunu kapatana kadar"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Şarj oluyor (tamamen dolmasına <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kaldı)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Misafir"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Misafir"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Bir dakika süreyle"</item>
<item quantity="other" msgid="6924190729213550991">"%d dakika süreyle"</item>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 45677f2..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>
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Vuốt sang trái để mở máy ảnh"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Cho đến khi bạn tắt tính năng này"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Đang sạc (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> cho đến khi đầy)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Khách"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Khách"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Trong một phút"</item>
<item quantity="other" msgid="6924190729213550991">"Trong %d phút"</item>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 0d27e29..f975252 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -32,26 +32,17 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"无通知"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"正在进行的"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
- <!-- no translation found for battery_low_title (6456385927409742437) -->
- <skip />
+ <string name="battery_low_title" msgid="6456385927409742437">"电池电量偏低"</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"还剩 <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (6534746636002666456) -->
- <skip />
+ <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>
- <!-- no translation found for invalid_charger_title (3515740382572798460) -->
- <skip />
- <!-- no translation found for invalid_charger_text (5474997287953892710) -->
- <skip />
- <!-- no translation found for battery_low_why (4553600287639198111) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_title (5987726159603849352) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_ok (7283108887345688413) -->
- <skip />
- <!-- no translation found for battery_saver_start_action (7245333922937402896) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_text (8417584516834617662) -->
- <skip />
+ <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="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>
@@ -232,7 +223,7 @@
<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>
@@ -254,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>
@@ -266,10 +255,7 @@
<item quantity="one" msgid="3480040795582254384">"1小时"</item>
<item quantity="other" msgid="5408537517529822157">"%d小时"</item>
</plurals>
- <!-- no translation found for battery_saver_notification_title (237918726750955859) -->
- <skip />
- <!-- no translation found for battery_saver_notification_text (7796554871101546872) -->
- <skip />
- <!-- no translation found for battery_saver_notification_action_text (7546297220816993504) -->
- <skip />
+ <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-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index c5805a1..4b5cf08 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/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>
@@ -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>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 8f2d247..a780ad1 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -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>
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 c8851dc..48b327d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -111,9 +111,9 @@
<!-- The duration in seconds to wait before the dismiss buttons are shown. -->
<integer name="recents_task_bar_dismiss_delay_seconds">3</integer>
<!-- The min animation duration for animating views that are currently visible. -->
- <integer name="recents_filter_animate_current_views_min_duration">175</integer>
+ <integer name="recents_filter_animate_current_views_duration">250</integer>
<!-- The min animation duration for animating views that are newly visible. -->
- <integer name="recents_filter_animate_new_views_min_duration">125</integer>
+ <integer name="recents_filter_animate_new_views_duration">250</integer>
<!-- The min animation duration for animating the task bar in. -->
<integer name="recents_animate_task_bar_enter_duration">275</integer>
<!-- The animation delay for animating the first task in. This should roughly be the animation
@@ -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 7ed3e29..36c1994 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -212,7 +212,7 @@
<dimen name="glowpadview_inner_radius">15dip</dimen>
<!-- The size of the application icon in the recents task view. -->
- <dimen name="recents_task_view_application_icon_size">32dp</dimen>
+ <dimen name="recents_task_view_application_icon_size">48dp</dimen>
<!-- The size of the activity icon in the recents task view. -->
<dimen name="recents_task_view_activity_icon_size">60dp</dimen>
@@ -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>
@@ -330,4 +333,13 @@
phone hints. -->
<dimen name="edge_tap_area_width">48dp</dimen>
+ <!-- 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/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ffd76a7..b9e2e1ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -239,6 +239,12 @@
private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
/**
+ * Whether a hide is pending an we are just waiting for #startKeyguardExitAnimation to be
+ * called.
+ * */
+ private boolean mHiding;
+
+ /**
* we send this intent when the keyguard is dismissed.
*/
private static final Intent USER_PRESENT_INTENT = new Intent(Intent.ACTION_USER_PRESENT)
@@ -1169,6 +1175,7 @@
}
mStatusBarKeyguardViewManager.show(options);
+ mHiding = false;
mShowing = true;
mKeyguardDonePending = false;
updateActivityLockScreenState();
@@ -1191,7 +1198,7 @@
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleHide");
try {
-
+ mHiding = true;
if (mShowing) {
// Don't actually hide the Keyguard at the moment, wait for window manager until
@@ -1212,6 +1219,11 @@
private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
synchronized (KeyguardViewMediator.this) {
+ if (!mHiding) {
+ return;
+ }
+ mHiding = false;
+
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index c3ba349c..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/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
index b981ed6..c4bdb19 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
@@ -69,6 +69,7 @@
@Override
public void onViewAttachedToWindow(View v) {
+ vp.updateStates();
volumeComponent.setVolumePanel(vp);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 1d355cd..ddea0bf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -30,7 +30,7 @@
// Enables the screenshot app->Recents transition
public static final boolean EnableScreenshotAppTransition = false;
// Enables the filtering of tasks according to their grouping
- public static final boolean EnableTaskFiltering = false;
+ public static final boolean EnableTaskFiltering = true;
// Enables clipping of tasks against each other
public static final boolean EnableTaskStackClipping = true;
// Enables the use of theme colors as the task bar background
@@ -48,7 +48,7 @@
// For debugging, this defines the number of mock recents packages to create
public static final int SystemServicesProxyMockPackageCount = 3;
// For debugging, this defines the number of mock recents tasks to create
- public static final int SystemServicesProxyMockTaskCount = 75;
+ public static final int SystemServicesProxyMockTaskCount = 100;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d66968b..9ea346b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -72,6 +72,7 @@
FrameLayout mContainerView;
RecentsView mRecentsView;
View mEmptyView;
+ View mStatusBarScrimView;
View mNavBarScrimView;
FullScreenTransitionView mFullScreenshotView;
@@ -117,7 +118,7 @@
// Otherwise, just finish the activity without launching any other activities
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
}
}
@@ -129,7 +130,7 @@
}
} else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
// Call our callback
onEnterAnimationTriggered();
}
@@ -162,6 +163,13 @@
/** Updates the set of recent tasks */
void updateRecentsTasks(Intent launchIntent) {
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
+ ArrayList<TaskStack> stacks = root.getStacks();
+ if (!stacks.isEmpty()) {
+ mRecentsView.setBSP(root);
+ }
+
// Update the configuration based on the launch intent
mConfig.launchedFromHome = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_FROM_HOME, false);
@@ -171,31 +179,21 @@
AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false);
mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
+ mConfig.launchedWithNoRecentTasks = !root.hasTasks();
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
- ArrayList<TaskStack> stacks = root.getStacks();
- if (!stacks.isEmpty()) {
- mRecentsView.setBSP(root);
- }
-
- if (mConfig.shouldAnimateNavBarScrim()) {
- // Hide the scrim if we animate into Recents with window transitions
- mNavBarScrimView.setVisibility(View.INVISIBLE);
- } else {
- // Show the scrim if we animate into Recents without window transitions
- mNavBarScrimView.setVisibility(View.VISIBLE);
- }
+ // Show the scrim if we animate into Recents without window transitions
+ mNavBarScrimView.setVisibility(mConfig.hasNavBarScrim() &&
+ !mConfig.shouldAnimateNavBarScrim() ? View.VISIBLE : View.INVISIBLE);
+ mStatusBarScrimView.setVisibility(mConfig.hasStatusBarScrim() &&
+ !mConfig.shouldAnimateStatusBarScrim() ? View.VISIBLE : View.INVISIBLE);
// Add the default no-recents layout
- if (stacks.size() == 1 && stacks.get(0).getTaskCount() == 0) {
+ if (mConfig.launchedWithNoRecentTasks) {
mEmptyView.setVisibility(View.VISIBLE);
+ mEmptyView.setBackgroundColor(0x80000000);
} else {
mEmptyView.setVisibility(View.GONE);
}
-
- // Dim the background
- mRecentsView.setBackgroundColor(0x80000000);
}
/** Attempts to allocate and bind the search bar app widget */
@@ -279,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.startOnExitAnimation(
+ 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));
+ }
}
}
}
@@ -326,6 +332,10 @@
// Create the empty view
LayoutInflater inflater = LayoutInflater.from(this);
mEmptyView = inflater.inflate(R.layout.recents_empty, mContainerView, false);
+ mStatusBarScrimView = inflater.inflate(R.layout.recents_status_bar_scrim, mContainerView, false);
+ mStatusBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP));
mNavBarScrimView = inflater.inflate(R.layout.recents_nav_bar_scrim, mContainerView, false);
mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -337,6 +347,7 @@
}
mContainerView = new FrameLayout(this);
+ mContainerView.addView(mStatusBarScrimView);
mContainerView.addView(mRecentsView);
mContainerView.addView(mEmptyView);
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
@@ -376,7 +387,7 @@
void onConfigurationChange() {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
// Call our callback
onEnterAnimationTriggered();
}
@@ -547,7 +558,7 @@
// Just start the animation out of recents
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
} else {
// Otherwise, try and launch the first task
@@ -555,7 +566,7 @@
// If there are no tasks, then just finish recents
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
}
}
@@ -564,15 +575,38 @@
}
public void onEnterAnimationTriggered() {
- // Fade in the scrim
- if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) {
- mNavBarScrimView.setVisibility(View.VISIBLE);
- mNavBarScrimView.setAlpha(0f);
- mNavBarScrimView.animate().alpha(1f)
+ // Fade in the scrims
+ if (mConfig.hasStatusBarScrim() && mConfig.shouldAnimateStatusBarScrim()) {
+ mStatusBarScrimView.setVisibility(View.VISIBLE);
+ mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
+ mStatusBarScrimView.animate()
+ .translationY(0)
.setStartDelay(mConfig.taskBarEnterAnimDelay)
.setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .start();
+ }
+ if (mConfig.hasNavBarScrim() && mConfig.shouldAnimateNavBarScrim()) {
+ mNavBarScrimView.setVisibility(View.VISIBLE);
+ mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
+ mNavBarScrimView.animate()
+ .translationY(0)
+ .setStartDelay(mConfig.taskBarEnterAnimDelay)
+ .setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .start();
+ }
+ }
+
+ @Override
+ public void onExitAnimationTriggered() {
+ // Fade out the scrim
+ if (mConfig.hasNavBarScrim() && mConfig.shouldAnimateNavBarScrim()) {
+ mNavBarScrimView.animate()
+ .translationY(mNavBarScrimView.getMeasuredHeight())
+ .setStartDelay(0)
+ .setDuration(mConfig.taskBarExitAnimDuration)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
- .withLayer()
.start();
}
}
@@ -593,19 +627,9 @@
}
@Override
- public void onTaskLaunching(boolean isTaskInStackBounds) {
+ public void onTaskLaunching() {
mTaskLaunched = true;
- // Fade out the scrim
- if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) {
- mNavBarScrimView.animate().alpha(0f)
- .setStartDelay(0)
- .setDuration(mConfig.taskBarExitAnimDuration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .withLayer()
- .start();
- }
-
// Mark recents as no longer visible
AlternateRecentsComponent.notifyVisibilityChanged(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 3b8c6c9..10978ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -47,8 +47,8 @@
public Interpolator quintOutInterpolator;
/** Filtering */
- public int filteringCurrentViewsMinAnimDuration;
- public int filteringNewViewsMinAnimDuration;
+ public int filteringCurrentViewsAnimDuration;
+ public int filteringNewViewsAnimDuration;
/** Insets */
public Rect systemInsets = new Rect();
@@ -81,7 +81,6 @@
/** Task bar colors */
public int taskBarViewDefaultBackgroundColor;
- public int taskBarViewDefaultTextColor;
public int taskBarViewLightTextColor;
public int taskBarViewDarkTextColor;
public int taskBarViewHighlightColor;
@@ -97,6 +96,7 @@
/** Launch states */
public boolean launchedWithAltTab;
+ public boolean launchedWithNoRecentTasks;
public boolean launchedFromAppWithThumbnail;
public boolean launchedFromAppWithScreenshot;
public boolean launchedFromHome;
@@ -105,12 +105,30 @@
public boolean developerOptionsEnabled;
/** Private constructor */
- private RecentsConfiguration() {}
+ private RecentsConfiguration(Context context) {
+ // Properties that don't have to be reloaded with each configuration change can be loaded
+ // here.
+
+ // Interpolators
+ fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
+ linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
+ quintOutInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.decelerate_quint);
+
+ // Check if the developer options are enabled
+ ContentResolver cr = context.getContentResolver();
+ developerOptionsEnabled = Settings.Global.getInt(cr,
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
+ }
/** Updates the configuration to the current context */
public static RecentsConfiguration reinitialize(Context context) {
if (sInstance == null) {
- sInstance = new RecentsConfiguration();
+ sInstance = new RecentsConfiguration(context);
}
sInstance.update(context);
return sInstance;
@@ -131,21 +149,11 @@
animationPxMovementPerSecond =
res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
- // Interpolators
- fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
- fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_linear_in);
- linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.linear_out_slow_in);
- quintOutInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.decelerate_quint);
-
// Filtering
- filteringCurrentViewsMinAnimDuration =
- res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
- filteringNewViewsMinAnimDuration =
- res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
+ filteringCurrentViewsAnimDuration =
+ res.getInteger(R.integer.recents_filter_animate_current_views_duration);
+ filteringNewViewsAnimDuration =
+ res.getInteger(R.integer.recents_filter_animate_new_views_duration);
// Insets
displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
@@ -193,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 =
@@ -216,11 +222,6 @@
navBarScrimEnterDuration =
res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
- // Check if the developer options are enabled
- ContentResolver cr = context.getContentResolver();
- developerOptionsEnabled = Settings.Global.getInt(cr,
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
-
if (Console.Enabled) {
Console.log(Constants.Log.UI.MeasureAndLayout,
"[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
@@ -245,6 +246,7 @@
* members. */
public void updateOnConfigurationChange() {
launchedWithAltTab = false;
+ launchedWithNoRecentTasks = false;
launchedFromAppWithThumbnail = false;
launchedFromAppWithScreenshot = false;
launchedFromHome = false;
@@ -255,14 +257,26 @@
return searchBarAppWidgetId >= 0;
}
+ /** Returns whether the status bar scrim should be animated when shown for the first time. */
+ public boolean shouldAnimateStatusBarScrim() {
+ return launchedFromHome;
+ }
+
+ /** Returns whether the status bar scrim should be visible. */
+ public boolean hasStatusBarScrim() {
+ return !launchedWithNoRecentTasks;
+ }
+
/** Returns whether the nav bar scrim should be animated when shown for the first time. */
public boolean shouldAnimateNavBarScrim() {
- return !launchedFromHome && !launchedFromAppWithScreenshot;
+ return true;
}
/** Returns whether the nav bar scrim should be visible. */
public boolean hasNavBarScrim() {
- return !transposeRecentsLayoutWithOrientation || !isLandscape;
+ // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
+ return !launchedWithNoRecentTasks &&
+ (!transposeRecentsLayoutWithOrientation || !isLandscape);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index dbcdb94..7762111 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -118,6 +118,7 @@
TaskResourceLoadQueue mLoadQueue;
DrawableLruCache mApplicationIconCache;
BitmapLruCache mThumbnailCache;
+ Bitmap mDefaultThumbnail;
boolean mCancelled;
boolean mWaitingOnLoadQueue;
@@ -125,10 +126,12 @@
/** Constructor, creates a new loading thread that loads task resources in the background */
public TaskResourceLoader(TaskResourceLoadQueue loadQueue,
DrawableLruCache applicationIconCache,
- BitmapLruCache thumbnailCache) {
+ BitmapLruCache thumbnailCache,
+ Bitmap defaultThumbnail) {
mLoadQueue = loadQueue;
mApplicationIconCache = applicationIconCache;
mThumbnailCache = thumbnailCache;
+ mDefaultThumbnail = defaultThumbnail;
mMainThreadHandler = new Handler();
mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
@@ -238,6 +241,7 @@
loadThumbnail = thumbnail;
mThumbnailCache.put(t.key, thumbnail);
} else {
+ loadThumbnail = mDefaultThumbnail;
Console.logError(mContext,
"Failed to load task top thumbnail for: " +
t.key.baseIntent.getComponent().getPackageName());
@@ -330,6 +334,7 @@
BitmapDrawable mDefaultApplicationIcon;
Bitmap mDefaultThumbnail;
+ Bitmap mLoadingThumbnail;
/** Private Constructor */
private RecentsTaskLoader(Context context) {
@@ -356,18 +361,22 @@
mLoadQueue = new TaskResourceLoadQueue();
mApplicationIconCache = new DrawableLruCache(iconCacheSize);
mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
- mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache);
+ mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
+ mDefaultThumbnail);
// Create the default assets
Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
icon.eraseColor(0x00000000);
mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
- mDefaultThumbnail.eraseColor(0x00000000);
+ mDefaultThumbnail.eraseColor(0xFFffffff);
+ mLoadingThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ mLoadingThumbnail.eraseColor(0x00000000);
mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
if (Console.Enabled) {
Console.log(Constants.Log.App.TaskDataLoader,
"[RecentsTaskLoader|defaultBitmaps]",
- "icon: " + mDefaultApplicationIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
+ "icon: " + mDefaultApplicationIcon +
+ " default thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
}
}
@@ -394,7 +403,7 @@
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> tasks =
- ssp.getRecentTasks(25, UserHandle.CURRENT.getIdentifier());
+ ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
Collections.reverse(tasks);
if (Console.Enabled) {
Console.log(Constants.Log.App.TimeSystemCalls,
@@ -544,7 +553,7 @@
requiresLoad = true;
}
if (thumbnail == null) {
- thumbnail = mDefaultThumbnail;
+ thumbnail = mLoadingThumbnail;
requiresLoad = true;
}
if (requiresLoad) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
index 1dd1be6..20be415 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
@@ -56,6 +56,13 @@
return mStack;
}
+ /** Returns whether there are any tasks in any stacks below this node. */
+ public boolean hasTasks() {
+ return (mStack.getTaskCount() > 0) ||
+ (mStartNode != null && mStartNode.hasTasks()) ||
+ (mEndNode != null && mEndNode.hasTasks());
+ }
+
/** Returns whether this is a leaf node */
boolean isLeafNode() {
return (mStartNode == null) && (mEndNode == null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
index ad2fa8d..cadfc56 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
@@ -49,8 +50,8 @@
FullScreenTransitionViewCallbacks mCb;
ImageView mScreenshotView;
-
Rect mClipRect = new Rect();
+ Paint mLayerPaint = new Paint();
boolean mIsAnimating;
AnimatorSet mEnterAnimation;
@@ -99,12 +100,17 @@
@Override
public void draw(Canvas canvas) {
- int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.clipRect(mClipRect);
super.draw(canvas);
canvas.restoreToCount(restoreCount);
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
/** Prepares the screenshot view for the transition into Recents */
public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
if (!mConfig.launchedFromAppWithScreenshot) return;
@@ -154,7 +160,7 @@
int clipBottom = mConfig.systemInsets.top + (int) (ctx.taskRect.height() / scale);
// Enable the HW Layers on the screenshot view
- mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
// Compose the animation
mEnterAnimation = new AnimatorSet();
@@ -168,7 +174,7 @@
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on this view
- setLayerType(View.LAYER_TYPE_NONE, null);
+ setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
if (Console.Enabled) {
Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
@@ -212,7 +218,7 @@
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on the screenshot view
- mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
}
});
mEnterAnimation.setDuration(475);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 6f79683..7248758 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -54,7 +54,8 @@
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
- public void onTaskLaunching(boolean isTaskInStackBounds);
+ public void onTaskLaunching();
+ public void onExitAnimationTriggered();
}
RecentsConfiguration mConfig;
@@ -160,19 +161,19 @@
}
/** Requests all task stacks to start their enter-recents animation */
- public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child instanceof TaskStackView) {
TaskStackView stackView = (TaskStackView) child;
- stackView.startOnEnterAnimation(ctx);
+ stackView.startEnterRecentsAnimation(ctx);
}
}
}
/** Requests all task stacks to start their exit-recents animation */
- public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
// Handle the case when there are no views by incrementing and decrementing after all
// animations are started.
ctx.postAnimationTrigger.increment();
@@ -183,7 +184,7 @@
View child = getChildAt(i);
if (child instanceof TaskStackView) {
TaskStackView stackView = (TaskStackView) child;
- stackView.startOnExitAnimation(ctx);
+ stackView.startExitToHomeAnimation(ctx);
}
}
}
@@ -191,6 +192,9 @@
// Handle the case when there are no views by incrementing and decrementing after all
// animations are started.
ctx.postAnimationTrigger.decrement();
+
+ // Notify of the exit animation
+ mCb.onExitAnimationTriggered();
}
/** Adds the search bar */
@@ -385,13 +389,46 @@
final TaskStack stack, final Task task) {
// Notify any callbacks of the launching of a new task
if (mCb != null) {
- boolean isTaskInStackBounds = false;
- if (stackView != null && tv != null) {
- isTaskInStackBounds = stackView.isTaskInStackBounds(tv);
- }
- mCb.onTaskLaunching(isTaskInStackBounds);
+ mCb.onTaskLaunching();
}
+ // Upfront the processing of the thumbnail
+ TaskViewTransform transform;
+ View sourceView = tv;
+ int offsetX = 0;
+ int offsetY = 0;
+ int stackScroll = stackView.getStackScroll();
+ if (tv == null) {
+ // If there is no actual task view, then use the stack view as the source view
+ // and then offset to the expected transform rect, but bound this to just
+ // outside the display rect (to ensure we don't animate from too far away)
+ sourceView = stackView;
+ transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
+ offsetX = transform.rect.left;
+ offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
+ } else {
+ transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
+ }
+
+ // Compute the thumbnail to scale up from
+ ActivityOptions opts = null;
+ int thumbnailWidth = transform.rect.width();
+ int thumbnailHeight = transform.rect.height();
+ if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
+ task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
+ // Resize the thumbnail to the size of the view that we are animating from
+ Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
+ Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ c.drawBitmap(task.thumbnail,
+ new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
+ new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
+ c.setBitmap(null);
+ opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
+ b, offsetX, offsetY);
+ }
+
+ final ActivityOptions launchOpts = opts;
final Runnable launchRunnable = new Runnable() {
@Override
public void run() {
@@ -400,45 +437,10 @@
Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
}
- TaskViewTransform transform;
- View sourceView = tv;
- int offsetX = 0;
- int offsetY = 0;
- int stackScroll = stackView.getStackScroll();
- if (tv == null) {
- // If there is no actual task view, then use the stack view as the source view
- // and then offset to the expected transform rect, but bound this to just
- // outside the display rect (to ensure we don't animate from too far away)
- sourceView = stackView;
- transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
- offsetX = transform.rect.left;
- offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
- } else {
- transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
- }
-
- // Compute the thumbnail to scale up from
- ActivityOptions opts = null;
- int thumbnailWidth = transform.rect.width();
- int thumbnailHeight = transform.rect.height();
- if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
- task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
- // Resize the thumbnail to the size of the view that we are animating from
- Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
- Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(b);
- c.drawBitmap(task.thumbnail,
- new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
- new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
- c.setBitmap(null);
- opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
- b, offsetX, offsetY);
- }
-
if (task.isActive) {
// Bring an active task to the foreground
RecentsTaskLoader.getInstance().getSystemServicesProxy()
- .moveTaskToFront(task.key.id, opts);
+ .moveTaskToFront(task.key.id, launchOpts);
} else {
// Launch the activity anew with the desired animation
Intent i = new Intent(task.key.baseIntent);
@@ -447,8 +449,8 @@
| Intent.FLAG_ACTIVITY_NEW_TASK);
try {
UserHandle taskUser = new UserHandle(task.userId);
- if (opts != null) {
- getContext().startActivityAsUser(i, opts.toBundle(), taskUser);
+ if (launchOpts != null) {
+ getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
} else {
getContext().startActivityAsUser(i, taskUser);
}
@@ -476,7 +478,7 @@
if (tv == null) {
post(launchRunnable);
} else {
- tv.animateOnLaunchingTask(launchRunnable);
+ stackView.animateOnLaunchingTask(tv, launchRunnable);
}
}
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 d4f381b..1ef58ad 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -23,6 +23,7 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewPropertyAnimator;
@@ -50,6 +51,7 @@
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
+ Paint mLayerPaint = new Paint();
static Paint sHighlightPaint;
public TaskBarView(Context context) {
@@ -91,6 +93,13 @@
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+
+ // Hide the backgrounds if they are ripple drawables
+ if (!Constants.DebugFlags.App.EnableTaskFiltering) {
+ if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
+ mApplicationIcon.setBackground(null);
+ }
+ }
}
@Override
@@ -124,6 +133,11 @@
}
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
/** Binds the bar view to the task */
void rebindToTask(Task t, boolean animate) {
mTask = t;
@@ -137,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 */
@@ -158,12 +170,12 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateEnterRecents() {
+ public void prepareEnterRecentsAnimation() {
setVisibility(View.INVISIBLE);
}
/** Animates this task bar as it enters recents */
- public void animateOnEnterRecents(int delay, Runnable postAnimRunnable) {
+ public void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
// Animate the task bar of the first task view
setVisibility(View.VISIBLE);
setTranslationY(-getMeasuredHeight());
@@ -177,7 +189,7 @@
}
/** Animates this task bar as it exits recents */
- public void animateOnLaunchingTask(Runnable preAnimRunnable, final Runnable postAnimRunnable) {
+ public void startLaunchTaskAnimation(Runnable preAnimRunnable, final Runnable postAnimRunnable) {
// Animate the task bar out of the first task view
animate()
.translationY(-getMeasuredHeight())
@@ -194,8 +206,22 @@
.start();
}
+ /** Animates this task bar dismiss button when launching a task. */
+ public void startLaunchTaskDismissAnimation() {
+ if (mDismissButton.getVisibility() == View.VISIBLE) {
+ mDismissButton.animate().cancel();
+ mDismissButton.animate()
+ .alpha(0f)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .withLayer()
+ .start();
+ }
+ }
+
/** Animates this task bar if the user does not interact with the stack after a certain time. */
- public void animateOnNoUserInteraction() {
+ public void startNoUserInteractionAnimation() {
mDismissButton.setVisibility(View.VISIBLE);
mDismissButton.setAlpha(0f);
mDismissButton.animate()
@@ -208,7 +234,7 @@
}
/** Mark this task view that the user does has not interacted with the stack after a certain time. */
- public void setOnNoUserInteraction() {
+ public void setNoUserInteractionState() {
if (mDismissButton.getVisibility() != View.VISIBLE) {
mDismissButton.animate().cancel();
mDismissButton.setVisibility(View.VISIBLE);
@@ -218,11 +244,11 @@
/** Enable the hw layers on this task view */
void enableHwLayers() {
- mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
}
/** Disable the hw layers on this task view */
void disableHwLayers() {
- mDismissButton.setLayerType(View.LAYER_TYPE_NONE, null);
+ mDismissButton.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
}
}
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 79bfa5e..55f9335 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -116,7 +116,7 @@
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
- tv.animateOnNoUserInteraction();
+ tv.startNoUserInteractionAnimation();
}
}
});
@@ -187,12 +187,20 @@
return null;
}
- /** Update/get the transform */
+ /** Update/get the transform (creates a new TaskViewTransform) */
public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) {
TaskViewTransform transform = new TaskViewTransform();
+ return getStackTransform(indexInStack, stackScroll, transform);
+ }
+ /** Update/get the transform */
+ public TaskViewTransform getStackTransform(int indexInStack, int stackScroll,
+ TaskViewTransform transformOut) {
// Return early if we have an invalid index
- if (indexInStack < 0) return transform;
+ if (indexInStack < 0) {
+ transformOut.reset();
+ return transformOut;
+ }
// Map the items to an continuous position relative to the specified scroll
int numPeekCards = Constants.Values.TaskStackView.StackPeekNumCards;
@@ -209,35 +217,35 @@
float scale = Math.max(minScale, Math.min(1f, minScale +
((boundedT + (numPeekCards + 1)) * scaleInc)));
float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
- transform.scale = scale;
+ transformOut.scale = scale;
// Set the y translation
if (boundedT < 0f) {
- transform.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
+ transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
numPeekCards) * peekHeight - scaleYOffset);
} else {
- transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
+ transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
}
// Set the z translation
int minZ = mConfig.taskViewTranslationZMinPx;
int incZ = mConfig.taskViewTranslationZIncrementPx;
- transform.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
+ transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
// Set the alphas
- transform.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
+ transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
// Update the rect and visibility
- transform.rect.set(mTaskRect);
+ transformOut.rect.set(mTaskRect);
if (t < -(numPeekCards + 1)) {
- transform.visible = false;
+ transformOut.visible = false;
} else {
- transform.rect.offset(0, transform.translationY);
- Utilities.scaleRectAboutCenter(transform.rect, transform.scale);
- transform.visible = Rect.intersects(mRect, transform.rect);
+ transformOut.rect.offset(0, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+ transformOut.visible = Rect.intersects(mRect, transformOut.rect);
}
- transform.t = t;
- return transform;
+ transformOut.t = t;
+ return transformOut;
}
/**
@@ -250,14 +258,25 @@
boolean boundTranslationsToRect) {
// XXX: Optimization: Use binary search to find the visible range
+ int taskTransformCount = taskTransforms.size();
int taskCount = tasks.size();
int firstVisibleIndex = -1;
int lastVisibleIndex = -1;
- taskTransforms.clear();
- taskTransforms.ensureCapacity(taskCount);
+
+ // We can reuse the task transforms where possible to reduce object allocation
+ if (taskTransformCount < taskCount) {
+ // If there are less transforms than tasks, then add as many transforms as necessary
+ for (int i = taskTransformCount; i < taskCount; i++) {
+ taskTransforms.add(new TaskViewTransform());
+ }
+ } else if (taskTransformCount > taskCount) {
+ // If there are more transforms than tasks, then just subset the transform list
+ taskTransforms.subList(0, taskCount);
+ }
+
+ // Update the stack transforms
for (int i = 0; i < taskCount; i++) {
- TaskViewTransform transform = getStackTransform(i, stackScroll);
- taskTransforms.add(transform);
+ TaskViewTransform transform = getStackTransform(i, stackScroll, taskTransforms.get(i));
if (transform.visible) {
if (firstVisibleIndex < 0) {
firstVisibleIndex = i;
@@ -532,6 +551,20 @@
}
}
+ /** Animates a task view in this stack as it launches. */
+ public void animateOnLaunchingTask(TaskView tv, final Runnable r) {
+ // Hide each of the task bar dismiss buttons
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView t = (TaskView) getChildAt(i);
+ if (t == tv) {
+ t.startLaunchTaskAnimation(r, true);
+ } else {
+ t.startLaunchTaskAnimation(null, false);
+ }
+ }
+ }
+
/** Focuses the task at the specified index in the stack */
void focusTask(int taskIndex, boolean scrollToNewPosition) {
if (Console.Enabled) {
@@ -818,14 +851,14 @@
int offscreenY = mRect.bottom - (mTaskRect.top - mRect.top);
for (int i = childCount - 1; i >= 0; i--) {
TaskView tv = (TaskView) getChildAt(i);
- tv.prepareAnimateEnterRecents((i == (getChildCount() - 1)), offsetTopAlign,
+ tv.prepareEnterRecentsAnimation((i == (getChildCount() - 1)), offsetTopAlign,
offscreenY, mTaskRect);
}
// If the enter animation started already and we haven't completed a layout yet, do the
// enter animation now
if (mStartEnterAnimationRequestedAfterLayout) {
- startOnEnterAnimation(mStartEnterAnimationContext);
+ startEnterRecentsAnimation(mStartEnterAnimationContext);
mStartEnterAnimationRequestedAfterLayout = false;
mStartEnterAnimationContext = null;
}
@@ -838,7 +871,7 @@
}
/** Requests this task stacks to start it's enter-recents animation */
- public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
// If we are still waiting to layout, then just defer until then
if (mAwaitingFirstLayout) {
mStartEnterAnimationRequestedAfterLayout = true;
@@ -858,18 +891,18 @@
ctx.stackViewCount = childCount;
ctx.isFrontMost = (i == (getChildCount() - 1));
ctx.transform = transform;
- tv.animateOnEnterRecents(ctx);
+ tv.startEnterRecentsAnimation(ctx);
}
}
/** Requests this task stacks to start it's exit-recents animation. */
- public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
// Animate all the task views into view
ctx.offscreenTranslationY = mRect.bottom - (mTaskRect.top - mRect.top);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
- tv.animateOnExitRecents(ctx);
+ tv.startExitToHomeAnimation(ctx);
}
}
@@ -940,10 +973,11 @@
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
+ int offset = 0;
int movement = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -968,10 +1002,12 @@
movement = Math.max(movement, Math.abs(toTransform.translationY -
(int) tv.getTranslationY()));
}
- childViewTransformsOut.put(tv, new Pair(0, toTransform));
+
+ toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
+ childViewTransformsOut.put(tv, toTransform);
+ offset++;
}
- return Utilities.calculateTranslationAnimationDuration(movement,
- mConfig.filteringCurrentViewsMinAnimDuration);
+ return mConfig.filteringCurrentViewsAnimDuration;
}
/**
@@ -980,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();
@@ -998,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,
@@ -1009,8 +1044,7 @@
}
}
}
- return Utilities.calculateTranslationAnimationDuration(movement,
- mConfig.filteringNewViewsMinAnimDuration);
+ return mConfig.filteringNewViewsAnimDuration;
}
/** Orchestrates the animations of the current child views and any new views. */
@@ -1021,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);
@@ -1037,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() {
@@ -1057,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);
}
}
@@ -1179,7 +1211,7 @@
// If the doze trigger has already fired, then update the state for this task view
if (mDozeTrigger.hasTriggered()) {
- tv.setOnNoUserInteraction();
+ tv.setNoUserInteractionState();
}
// Add/attach the view to the hierarchy
@@ -1275,7 +1307,7 @@
TaskView tv = getChildViewForTask(t);
if (tv != null) {
// For visible children, defer removing the task until after the animation
- tv.animateRemoval(new Runnable() {
+ tv.startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
mStack.removeTask(t);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
index dc8a420..c2b2094 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
@@ -54,10 +54,13 @@
@Override
public void draw(Canvas canvas) {
if (mClipTaskBar && (mClipRect != null)) {
- // Apply the clip rect
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.clipRect(mClipRect);
+ super.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ } else {
+ super.draw(canvas);
}
- super.draw(canvas);
}
/** Updates the clip rect based on the given task bar. */
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 b7e834b..cfba74c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Outline;
+import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
@@ -63,6 +64,7 @@
Point mLastTouchDown = new Point();
Path mRoundedRectClipPath = new Path();
Rect mTmpRect = new Rect();
+ Paint mLayerPaint = new Paint();
TaskThumbnailView mThumbnailView;
TaskBarView mBarView;
@@ -200,7 +202,7 @@
if (useLayers) {
anim.withLayer();
}
- anim.setStartDelay(0)
+ anim.setStartDelay(toTransform.startDelay)
.setDuration(duration)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
.start();
@@ -246,6 +248,7 @@
// Fade the view out and slide it away
toTransform.alpha = 0f;
toTransform.translationY += 200;
+ toTransform.translationZ = 0;
}
/**
@@ -259,15 +262,15 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateEnterRecents(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
- Rect taskRect) {
+ public void prepareEnterRecentsAnimation(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
+ Rect taskRect) {
if (mConfig.launchedFromAppWithScreenshot) {
if (isTaskViewFrontMost) {
// Hide the task view as we are going to animate the full screenshot into view
// and then replace it with this view once we are done
setVisibility(View.INVISIBLE);
// Also hide the front most task bar view so we can animate it in
- mBarView.prepareAnimateEnterRecents();
+ mBarView.prepareEnterRecentsAnimation();
} else {
// Top align the task views
setTranslationY(offsetY);
@@ -278,7 +281,7 @@
} else if (mConfig.launchedFromAppWithThumbnail) {
if (isTaskViewFrontMost) {
// Hide the front most task bar view so we can animate it in
- mBarView.prepareAnimateEnterRecents();
+ mBarView.prepareEnterRecentsAnimation();
// Set the dim to 0 so we can animate it in
setDim(0);
}
@@ -286,13 +289,14 @@
} else if (mConfig.launchedFromHome) {
// Move the task view off screen (below) so we can animate it in
setTranslationY(offscreenY);
+ setTranslationZ(0);
setScaleX(1f);
setScaleY(1f);
}
}
/** Animates this task view as it enters recents */
- public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
TaskViewTransform transform = ctx.transform;
if (mConfig.launchedFromAppWithScreenshot) {
@@ -302,7 +306,7 @@
@Override
public void run() {
// Animate the task bar of the first task view
- mBarView.animateOnEnterRecents(0, mEnableThumbnailClip);
+ mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
setVisibility(View.VISIBLE);
}
});
@@ -324,7 +328,7 @@
} else if (mConfig.launchedFromAppWithThumbnail) {
if (ctx.isFrontMost) {
// Animate the task bar of the first task view
- mBarView.animateOnEnterRecents(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
+ mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
// Animate the dim into view as well
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
@@ -345,6 +349,7 @@
.scaleX(transform.scale)
.scaleY(transform.scale)
.translationY(transform.translationY)
+ .translationZ(transform.translationZ)
.setStartDelay(delay)
.setUpdateListener(null)
.setInterpolator(mConfig.quintOutInterpolator)
@@ -355,8 +360,8 @@
}
}
- /** Animates this task view as it leaves recents */
- public void animateOnExitRecents(ViewAnimation.TaskViewExitContext ctx) {
+ /** Animates this task view as it leaves recents by pressing home. */
+ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
animate()
.translationY(ctx.offscreenTranslationY)
.setStartDelay(0)
@@ -369,32 +374,27 @@
ctx.postAnimationTrigger.increment();
}
- /** Animates this task view if the user does not interact with the stack after a certain time. */
- public void animateOnNoUserInteraction() {
- mBarView.animateOnNoUserInteraction();
- }
-
- /** Mark this task view that the user does has not interacted with the stack after a certain time. */
- public void setOnNoUserInteraction() {
- mBarView.setOnNoUserInteraction();
- }
-
/** Animates this task view as it exits recents */
- public void animateOnLaunchingTask(final Runnable r) {
- // Disable the thumbnail clip and animate the bar out
- mBarView.animateOnLaunchingTask(mDisableThumbnailClip, r);
+ public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
+ if (isLaunchingTask) {
+ // Disable the thumbnail clip and animate the bar out
+ mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
- // Animate the dim
- if (mDim > 0) {
- ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
- anim.setDuration(mConfig.taskBarExitAnimDuration);
- anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
- anim.start();
+ // Animate the dim
+ if (mDim > 0) {
+ ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
+ anim.setDuration(mConfig.taskBarExitAnimDuration);
+ anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+ anim.start();
+ }
+ } else {
+ // Hide the dismiss button
+ mBarView.startLaunchTaskDismissAnimation();
}
}
/** Animates the deletion of this task view */
- public void animateRemoval(final Runnable r) {
+ public void startDeleteTaskAnimation(final Runnable r) {
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
@@ -422,6 +422,16 @@
.start();
}
+ /** Animates this task view if the user does not interact with the stack after a certain time. */
+ public void startNoUserInteractionAnimation() {
+ mBarView.startNoUserInteractionAnimation();
+ }
+
+ /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+ public void setNoUserInteractionState() {
+ mBarView.setNoUserInteractionState();
+ }
+
/** Returns the rect we want to clip (it may not be the full rect) */
Rect getClippingRect(Rect outRect) {
getHitRect(outRect);
@@ -433,13 +443,13 @@
/** Enable the hw layers on this task view */
void enableHwLayers() {
- mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
mBarView.enableHwLayers();
}
/** Disable the hw layers on this task view */
void disableHwLayers() {
- mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
mBarView.disableHwLayers();
}
@@ -489,10 +499,11 @@
@Override
public void draw(Canvas canvas) {
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
// Apply the rounded rect clip path on the whole view
canvas.clipPath(mRoundedRectClipPath);
-
super.draw(canvas);
+ canvas.restoreToCount(restoreCount);
// Apply the dim if necessary
if (mDim > 0) {
@@ -575,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;
- animateRemoval(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 6c420e1..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;
@@ -28,13 +29,14 @@
public float dismissAlpha = 1f;
public boolean visible = false;
public Rect rect = new Rect();
- float t;
+ float t = 0f;
public TaskViewTransform() {
// Do nothing
}
public TaskViewTransform(TaskViewTransform o) {
+ startDelay = o.startDelay;
translationY = o.translationY;
translationZ = o.translationZ;
scale = o.scale;
@@ -45,6 +47,19 @@
t = o.t;
}
+ /** Resets the current transform */
+ public void reset() {
+ startDelay = 0;
+ translationY = 0;
+ translationZ = 0;
+ scale = 1f;
+ alpha = 1f;
+ dismissAlpha = 1f;
+ visible = false;
+ rect.setEmpty();
+ t = 0f;
+ }
+
/** Convenience functions to compare against current property values */
public boolean hasAlphaChangedFrom(float v) {
return (Float.compare(alpha, v) != 0);
@@ -64,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/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 20684a1..5bc23b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -311,8 +311,27 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- if (mNotificationData.findByKey(sbn.getKey()) != null ||
- isHeadsUp(sbn.getKey())) {
+ Notification n = sbn.getNotification();
+ boolean isUpdate = mNotificationData.findByKey(sbn.getKey()) != null
+ || isHeadsUp(sbn.getKey());
+ boolean isGroupedChild = n.getGroup() != null
+ && (n.flags & Notification.FLAG_GROUP_SUMMARY) == 0;
+ if (isGroupedChild) {
+ if (DEBUG) {
+ Log.d(TAG, "Ignoring group child: " + sbn);
+ }
+ // Don't show grouped notifications. If this is an
+ // update, i.e. the notification existed before but
+ // wasn't a group child, remove the old instance.
+ // Otherwise just update the ranking.
+ if (isUpdate) {
+ removeNotificationInternal(sbn.getKey(), rankingMap);
+ } else {
+ updateRankingInternal(rankingMap);
+ }
+ return;
+ }
+ if (isUpdate) {
updateNotificationInternal(sbn, rankingMap);
} else {
addNotificationInternal(sbn, rankingMap);
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..7016c0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -35,6 +35,7 @@
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
@@ -64,6 +65,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.provider.Settings.SettingNotFoundException;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -82,7 +84,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 +124,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 +213,7 @@
ZenModeController mZenModeController;
CastControllerImpl mCastController;
VolumeComponent mVolumeComponent;
+ KeyguardUserSwitcher mKeyguardUserSwitcher;
int mNaturalBarHeight = -1;
int mIconSize = -1;
@@ -260,6 +263,10 @@
boolean mLeaveOpenOnKeyguardHide;
KeyguardIndicationController mKeyguardIndicationController;
+ private boolean mKeyguardFadingAway;
+ private long mKeyguardFadingAwayDelay;
+ private long mKeyguardFadingAwayDuration;
+
int mKeyguardMaxNotificationCount;
View mDateTimeView;
@@ -399,7 +406,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 +722,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);
@@ -893,6 +904,14 @@
}
};
+ private View.OnLongClickListener mLockToAppClickListener = new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ toggleLockedApp();
+ return true;
+ }
+ };
+
private int mShowSearchHoldoff = 0;
private Runnable mShowSearchPanel = new Runnable() {
public void run() {
@@ -936,6 +955,8 @@
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
+ mNavigationBarView.getRecentsButton().setLongClickable(true);
+ mNavigationBarView.getRecentsButton().setOnLongClickListener(mLockToAppClickListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
updateSearchPanel();
}
@@ -1453,7 +1474,7 @@
}
private int adjustDisableFlags(int state) {
- if (mExpandedVisible) {
+ if (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit) {
state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
state |= StatusBarManager.DISABLE_SYSTEM_INFO;
}
@@ -1501,19 +1522,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 +1557,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 +1700,7 @@
mStatusBarWindowManager.setStatusBarExpanded(true);
visibilityChanged(true);
+ mWaitingForKeyguardExit = false;
disable(mDisabledUnmodified);
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
}
@@ -1859,8 +1894,8 @@
}
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
- disable(mDisabledUnmodified);
showBouncer();
+ disable(mDisabledUnmodified);
}
public boolean interceptTouchEvent(MotionEvent event) {
@@ -2574,6 +2609,8 @@
if (mQSPanel != null) mQSPanel.updateResources();
loadDimens();
+ mLinearOutSlowIn = AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.linear_out_slow_in);
}
protected void loadDimens() {
@@ -2901,6 +2938,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 +2971,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 +3035,7 @@
private void showBouncer() {
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+ mWaitingForKeyguardExit = true;
mStatusBarKeyguardViewManager.dismiss();
}
}
@@ -3125,6 +3186,12 @@
mSystemIconArea.addView(mSystemIcons, 0);
}
+ @Override
+ public void setBouncerShowing(boolean bouncerShowing) {
+ super.setBouncerShowing(bouncerShowing);
+ disable(mDisabledUnmodified);
+ }
+
public void onScreenTurnedOff() {
mStackScroller.setAnimationsEnabled(false);
}
@@ -3133,6 +3200,28 @@
mStackScroller.setAnimationsEnabled(true);
}
+ public void toggleLockedApp() {
+ Log.d(TAG, "Trying to toggle lock-to-app");
+ try {
+ IActivityManager activityManager = ActivityManagerNative.getDefault();
+ if (activityManager.isInLockTaskMode()) {
+ activityManager.stopLockTaskModeOnCurrent();
+ } else {
+ try {
+ boolean lockToAppEnabled = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.LOCK_TO_APP_ENABLED) != 0;
+ if (lockToAppEnabled) {
+ activityManager.startLockTaskModeOnCurrent();
+ }
+ } catch (SettingNotFoundException e) {
+ // No setting, not enabled.
+ }
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Unable to toggle Lock-to-app", e);
+ }
+ }
+
private final Runnable mUserActivity = new Runnable() {
@Override
public void run() {
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 c8ab027..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;
@@ -70,6 +71,9 @@
private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT;
private int mNormalWidth;
+ private int mPadding;
+ private int mMultiUserExpandedMargin;
+ private int mSystemIconsSwitcherHiddenExpandedMargin;
private ActivityStarter mActivityStarter;
private BrightnessController mBrightnessController;
@@ -120,6 +124,11 @@
mKeyguardHeight = getResources().getDimensionPixelSize(
R.dimen.status_bar_header_height_keyguard);
mNormalWidth = getLayoutParams().width;
+ 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) {
@@ -147,6 +156,8 @@
updateZTranslation();
updateClickTargets();
updateWidth();
+ updatePadding();
+ updateMultiUserSwitch();
if (mQSPanel != null) {
mQSPanel.setExpanded(expanded && !overscrolled);
}
@@ -208,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);
@@ -222,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) {
@@ -230,6 +248,7 @@
} else {
clockLp.addRule(BELOW, mEmergencyCallsOnly.getId());
}
+ mDateTime.setLayoutParams(clockLp);
}
private void updateBrightnessControllerState() {
@@ -256,6 +275,21 @@
}
}
+ private void updatePadding() {
+ boolean padded = !mKeyguardShowing || mExpanded;
+ int padding = padded ? mPadding : 0;
+ setPaddingRelative(padding, 0, padding, 0);
+ }
+
+ private void updateMultiUserSwitch() {
+ int marginEnd = !mKeyguardShowing || mExpanded ? mMultiUserExpandedMargin : 0;
+ MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
+ if (marginEnd != lp.getMarginEnd()) {
+ lp.setMarginEnd(marginEnd);
+ mMultiUserSwitch.setLayoutParams(lp);
+ }
+ }
+
public void setExpansion(float height) {
height = (height - mCollapsedHeight) * EXPANSION_RUBBERBAND_FACTOR + mCollapsedHeight;
if (height < mCollapsedHeight) {
@@ -301,6 +335,8 @@
updateWidth();
updateVisibilities();
updateZTranslation();
+ updatePadding();
+ updateMultiUserSwitch();
}
public void setUserInfoController(UserInfoController userInfoController) {
@@ -360,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/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 330b599..0134fe8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -63,6 +63,7 @@
// Just an old-fashioned ImageView
performLongClick();
}
+ setPressed(false);
}
}
};
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 4cbb06b..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();
@@ -969,7 +970,7 @@
* @param animate Should an animation be performed.
*/
public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
- setOverScrollAmount(numPixels * getRubberBandFactor(), onTop, animate, true);
+ setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
}
/**
@@ -1005,7 +1006,7 @@
if (animate) {
mStateAnimator.animateOverScrollToAmount(amount, onTop);
} else {
- setOverScrolledPixels(amount / getRubberBandFactor(), onTop);
+ setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
mAmbientState.setOverScrollAmount(amount, onTop);
if (onTop) {
notifyOverscrollTopListener(amount);
@@ -1227,13 +1228,14 @@
mOwnScrollY -= (int) topAmount;
mDontReportNextOverScroll = true;
setOverScrollAmount(0, true, false);
- mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
* mOverflingDistance + topAmount;
} else if (velocityY > 0 && bottomAmount > 0) {
mOwnScrollY += bottomAmount;
setOverScrollAmount(0, false, false);
- mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
- * mOverflingDistance + bottomAmount;
+ mMaxOverScroll = Math.abs(velocityY) / 1000f
+ * getRubberBandFactor(false /* onTop */) * mOverflingDistance
+ + bottomAmount;
} else {
// it will be set once we reach the boundary
mMaxOverScroll = 0.0f;
@@ -1275,7 +1277,10 @@
return Math.max(desiredPadding, mIntrinsicPadding);
}
- private float getRubberBandFactor() {
+ private float getRubberBandFactor(boolean onTop) {
+ if (!onTop) {
+ return RUBBER_BAND_FACTOR_NORMAL;
+ }
if (mExpandedInThisMotion) {
return RUBBER_BAND_FACTOR_AFTER_EXPAND;
} else if (mIsExpansionChanging) {
@@ -1841,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 cbad9dc..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,
@@ -587,6 +598,7 @@
algorithmState.itemsInTopStack += algorithmState.partialInTop;
newSize = Math.max(mCollapsedSize, newSize);
if (i == 0) {
+ algorithmState.itemsInTopStack = 1.0f;
childViewState.height = (int) newSize;
}
algorithmState.lastTopStackIndex = i;
@@ -617,6 +629,20 @@
if (i < algorithmState.itemsInTopStack) {
float stackIndex = algorithmState.itemsInTopStack - i;
stackIndex = Math.min(stackIndex, MAX_ITEMS_IN_TOP_STACK + 2);
+ if (i == 0 && algorithmState.itemsInTopStack < 2.0f) {
+
+ // We only have the top item and an additional item in the top stack,
+ // Interpolate the index from 0 to 2 while the second item is
+ // translating in.
+ stackIndex -= 1.0f;
+ if (algorithmState.scrollY > mCollapsedSize) {
+
+ // Since there is a shadow treshhold, we cant just interpolate from 0 to
+ // 2 but we interpolate from 0.1f to 2.0f when scrolled in. The jump in
+ // height will not be noticable since we have padding in between.
+ stackIndex = 0.1f + stackIndex * 1.9f;
+ }
+ }
childViewState.zTranslation = mZBasicHeight
+ stackIndex * mZDistanceBetweenElements;
} else if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) {
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/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index bc2671011..637061d04 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -624,6 +624,7 @@
public void onWakeUp() {
synchronized (mLock) {
if (shouldEnableWakeGestureLp()) {
+ performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
mPowerManager.wakeUp(SystemClock.uptimeMillis());
}
}
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/Font.java b/rs/java/android/renderscript/Font.java
index b22aeb7..4318b9d 100644
--- a/rs/java/android/renderscript/Font.java
+++ b/rs/java/android/renderscript/Font.java
@@ -111,10 +111,10 @@
FontFamily serifFamily = new FontFamily();
serifFamily.mNames = sSerifNames;
- serifFamily.mNormalFileName = "DroidSerif-Regular.ttf";
- serifFamily.mBoldFileName = "DroidSerif-Bold.ttf";
- serifFamily.mItalicFileName = "DroidSerif-Italic.ttf";
- serifFamily.mBoldItalicFileName = "DroidSerif-BoldItalic.ttf";
+ serifFamily.mNormalFileName = "NotoSerif-Regular.ttf";
+ serifFamily.mBoldFileName = "NotoSerif-Bold.ttf";
+ serifFamily.mItalicFileName = "NotoSerif-Italic.ttf";
+ serifFamily.mBoldItalicFileName = "NotoSerif-BoldItalic.ttf";
addFamilyToMap(serifFamily);
FontFamily monoFamily = new FontFamily();
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/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index cef6830..3b1e88a 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -28,6 +28,7 @@
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
import android.app.backup.FullBackup;
+import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
import android.app.backup.IFullBackupRestoreObserver;
@@ -5177,7 +5178,9 @@
void restorePmMetadata() {
try {
- String packageName = mTransport.nextRestorePackage();
+ RestoreDescription desc = mTransport.nextRestorePackage();
+ // TODO: handle full-data stream restore payloads
+ String packageName = desc.getPackageName();
if (packageName == null) {
Slog.e(TAG, "Error getting first restore package");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
@@ -5245,7 +5248,9 @@
void restoreNextAgent() {
try {
- String packageName = mTransport.nextRestorePackage();
+ final RestoreDescription desc = mTransport.nextRestorePackage();
+ // TODO: handle full-data stream restore payloads
+ String packageName = desc.getPackageName();
if (packageName == null) {
Slog.e(TAG, "Error getting next restore package");
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index aeb195f..912a181 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -18,6 +18,7 @@
import android.database.ContentObserver;
import android.os.BatteryStats;
+
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
@@ -29,6 +30,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.BatteryProperties;
import android.os.Binder;
import android.os.FileUtils;
@@ -83,7 +85,7 @@
* service asynchronously itself.
* </p>
*/
-public final class BatteryService extends Binder {
+public final class BatteryService extends SystemService {
private static final String TAG = BatteryService.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -140,10 +142,12 @@
private boolean mSentLowBatteryBroadcast = false;
- public BatteryService(Context context, LightsManager lightsManager) {
+ public BatteryService(Context context) {
+ super(context);
+
mContext = context;
mHandler = new Handler(true /*async*/);
- mLed = new Led(context, lightsManager);
+ mLed = new Led(context, getLocalService(LightsManager.class));
mBatteryStats = BatteryStatsService.getService();
mCriticalBatteryLevel = mContext.getResources().getInteger(
@@ -160,7 +164,10 @@
mInvalidChargerObserver.startObserving(
"DEVPATH=/devices/virtual/switch/invalid_charger");
}
+ }
+ @Override
+ public void onStart() {
IBinder b = ServiceManager.getService("batteryproperties");
final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
IBatteryPropertiesRegistrar.Stub.asInterface(b);
@@ -169,28 +176,34 @@
} catch (RemoteException e) {
// Should never happen.
}
+
+ publishBinderService("battery", new BinderService());
+ publishLocalService(BatteryManagerInternal.class, new LocalService());
}
- void systemReady() {
- // check our power situation now that it is safe to display the shutdown dialog.
- synchronized (mLock) {
- ContentObserver obs = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- synchronized (mLock) {
- updateBatteryWarningLevelLocked();
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+ // check our power situation now that it is safe to display the shutdown dialog.
+ synchronized (mLock) {
+ ContentObserver obs = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateBatteryWarningLevelLocked();
+ }
}
- }
- };
- final ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
- false, obs, UserHandle.USER_ALL);
- updateBatteryWarningLevelLocked();
+ };
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+ false, obs, UserHandle.USER_ALL);
+ updateBatteryWarningLevelLocked();
+ }
}
}
- void updateBatteryWarningLevelLocked() {
+ private void updateBatteryWarningLevelLocked() {
final ContentResolver resolver = mContext.getContentResolver();
int defWarnLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
@@ -207,15 +220,6 @@
processValuesLocked(true);
}
- /**
- * Returns true if the device is plugged into any of the specified plug types.
- */
- public boolean isPowered(int plugTypeSet) {
- synchronized (mLock) {
- return isPoweredLocked(plugTypeSet);
- }
- }
-
private boolean isPoweredLocked(int plugTypeSet) {
// assume we are powered if battery state is unknown so
// the "stay on while plugged in" option will work.
@@ -234,34 +238,7 @@
return false;
}
- /**
- * Returns the current plug type.
- */
- public int getPlugType() {
- synchronized (mLock) {
- return mPlugType;
- }
- }
-
- /**
- * Returns battery level as a percentage.
- */
- public int getBatteryLevel() {
- synchronized (mLock) {
- return mBatteryProps.batteryLevel;
- }
- }
-
- /**
- * Returns whether we currently consider the battery level to be low.
- */
- public boolean getBatteryLevelLow() {
- synchronized (mLock) {
- return mBatteryLevelLow;
- }
- }
-
- public boolean shouldSendBatteryLowLocked() {
+ private boolean shouldSendBatteryLowLocked() {
final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
@@ -277,15 +254,6 @@
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
}
- /**
- * Returns a non-zero value if an unsupported charger is attached.
- */
- public int getInvalidCharger() {
- synchronized (mLock) {
- return mInvalidCharger;
- }
- }
-
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
@@ -640,17 +608,7 @@
}
}
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
-
- pw.println("Permission Denial: can't dump Battery service from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
+ private void dumpInternal(PrintWriter pw, String[] args) {
synchronized (mLock) {
if (args == null || args.length == 0 || "-a".equals(args[0])) {
pw.println("Current Battery Service state:");
@@ -801,4 +759,57 @@
}
}
}
+
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ pw.println("Permission Denial: can't dump Battery service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ dumpInternal(pw, args);
+ }
+ }
+
+ private final class LocalService extends BatteryManagerInternal {
+ @Override
+ public boolean isPowered(int plugTypeSet) {
+ synchronized (mLock) {
+ return isPoweredLocked(plugTypeSet);
+ }
+ }
+
+ @Override
+ public int getPlugType() {
+ synchronized (mLock) {
+ return mPlugType;
+ }
+ }
+
+ @Override
+ public int getBatteryLevel() {
+ synchronized (mLock) {
+ return mBatteryProps.batteryLevel;
+ }
+ }
+
+ @Override
+ public boolean getBatteryLevelLow() {
+ synchronized (mLock) {
+ return mBatteryLevelLow;
+ }
+ }
+
+ @Override
+ public int getInvalidCharger() {
+ synchronized (mLock) {
+ return mInvalidCharger;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/IdleMaintenanceService.java b/services/core/java/com/android/server/IdleMaintenanceService.java
deleted file mode 100644
index acc6abe..0000000
--- a/services/core/java/com/android/server/IdleMaintenanceService.java
+++ /dev/null
@@ -1,818 +0,0 @@
-/*
- * 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.server;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.maintenance.IIdleCallback;
-import android.app.maintenance.IIdleService;
-import android.app.maintenance.IdleService;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * This service observes the device state and when applicable sends
- * broadcasts at the beginning and at the end of a period during which
- * observers can perform idle maintenance tasks. Typical use of the
- * idle maintenance is to perform somehow expensive tasks that can be
- * postponed to a moment when they will not degrade user experience.
- *
- * The current implementation is very simple. The start of a maintenance
- * window is announced if: the screen is off or showing a dream AND the
- * battery level is more than twenty percent AND at least one hour passed
- * activity).
- *
- * The end of a maintenance window is announced only if: a start was
- * announced AND the screen turned on or a dream was stopped.
- *
- * Method naming note:
- * Methods whose name ends with "Tm" must only be called from the main thread.
- */
-public class IdleMaintenanceService extends BroadcastReceiver {
-
- private static final boolean DEBUG = false;
-
- private static final String TAG = IdleMaintenanceService.class.getSimpleName();
-
- private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
-
- private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
-
- private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
-
- private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
-
- private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
-
- private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
-
- private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
-
- private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
- "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
-
- private static final String ACTION_FORCE_IDLE_MAINTENANCE =
- "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";
-
- static final int MSG_OP_COMPLETE = 1;
- static final int MSG_IDLE_FINISHED = 2;
- static final int MSG_TIMEOUT = 3;
-
- // when a timeout happened, what were we expecting?
- static final int VERB_BINDING = 1;
- static final int VERB_IDLING = 2;
- static final int VERB_ENDING = 3;
-
- // What are our relevant timeouts / allocated slices?
- static final long OP_TIMEOUT = 8 * 1000; // 8 seconds to bind or ack the start
- static final long IDLE_TIMESLICE = 10 * 60 * 1000; // ten minutes for each idler
-
- private final AlarmManager mAlarmService;
- private final BatteryService mBatteryService;
- private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
- private final Context mContext;
- private final WakeLock mWakeLock;
- private final WorkSource mSystemWorkSource = new WorkSource(Process.myUid());
-
- private long mLastIdleMaintenanceStartTimeMillis;
- private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
- private boolean mIdleMaintenanceStarted;
-
- final IdleCallback mCallback;
- final Handler mHandler;
-
- final Random mTokenGenerator = new Random();
-
- int makeToken() {
- int token;
- do {
- token = mTokenGenerator.nextInt(Integer.MAX_VALUE);
- } while (token == 0);
- return token;
- }
-
- class ActiveTask {
- public IdleServiceInfo who;
- public int verb;
- public int token;
-
- ActiveTask(IdleServiceInfo target, int action) {
- who = target;
- verb = action;
- token = makeToken();
- }
-
- @Override
- public String toString() {
- return "ActiveTask{" + Integer.toHexString(this.hashCode())
- + " : verb=" + verb
- + " : token=" + token
- + " : "+ who + "}";
- }
- }
-
- // What operations are in flight?
- final SparseArray<ActiveTask> mPendingOperations = new SparseArray<ActiveTask>();
-
- // Idle service queue management
- class IdleServiceInfo {
- public final ComponentName componentName;
- public final int uid;
- public IIdleService service;
-
- IdleServiceInfo(ResolveInfo info, ComponentName cname) {
- componentName = cname; // derived from 'info' but this avoids an extra object
- uid = info.serviceInfo.applicationInfo.uid;
- service = null;
- }
-
- @Override
- public int hashCode() {
- return componentName.hashCode();
- }
-
- @Override
- public String toString() {
- return "IdleServiceInfo{" + componentName
- + " / " + (service == null ? "null" : service.asBinder()) + "}";
- }
- }
-
- final ArrayMap<ComponentName, IdleServiceInfo> mIdleServices =
- new ArrayMap<ComponentName, IdleServiceInfo>();
- final LinkedList<IdleServiceInfo> mIdleServiceQueue = new LinkedList<IdleServiceInfo>();
- IdleServiceInfo mCurrentIdler; // set when we've committed to launching an idler
- IdleServiceInfo mLastIdler; // end of queue when idling begins
-
- void reportNoTimeout(int token, boolean result) {
- final Message msg = mHandler.obtainMessage(MSG_OP_COMPLETE, result ? 1 : 0, token);
- mHandler.sendMessage(msg);
- }
-
- // Binder acknowledgment trampoline
- class IdleCallback extends IIdleCallback.Stub {
- @Override
- public void acknowledgeStart(int token, boolean result) throws RemoteException {
- reportNoTimeout(token, result);
- }
-
- @Override
- public void acknowledgeStop(int token) throws RemoteException {
- reportNoTimeout(token, false);
- }
-
- @Override
- public void idleFinished(int token) throws RemoteException {
- if (DEBUG) {
- Slog.v(TAG, "idleFinished: " + token);
- }
- final Message msg = mHandler.obtainMessage(MSG_IDLE_FINISHED, 0, token);
- mHandler.sendMessage(msg);
- }
- }
-
- // Stuff that we run on a Handler
- class IdleHandler extends Handler {
- public IdleHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- final int token = msg.arg2;
-
- switch (msg.what) {
- case MSG_OP_COMPLETE: {
- if (DEBUG) {
- Slog.i(TAG, "MSG_OP_COMPLETE of " + token);
- }
- ActiveTask task = mPendingOperations.get(token);
- if (task != null) {
- mPendingOperations.remove(token);
- removeMessages(MSG_TIMEOUT);
-
- handleOpCompleteTm(task, msg.arg1);
- } else {
- // Can happen in a race between timeout and actual
- // (belated) completion of a "begin idling" or similar
- // operation. In that state we've already processed the
- // timeout, so we intentionally no-op here.
- if (DEBUG) {
- Slog.w(TAG, "Belated op-complete of " + token);
- }
- }
- break;
- }
-
- case MSG_IDLE_FINISHED: {
- if (DEBUG) {
- Slog.i(TAG, "MSG_IDLE_FINISHED of " + token);
- }
- ActiveTask task = mPendingOperations.get(token);
- if (task != null) {
- if (DEBUG) {
- Slog.i(TAG, "... removing task " + token);
- }
- mPendingOperations.remove(token);
- removeMessages(MSG_TIMEOUT);
-
- handleIdleFinishedTm(task);
- } else {
- // Can happen "legitimately" from an app explicitly calling
- // idleFinished() after already having been told that its slice
- // has ended.
- if (DEBUG) {
- Slog.w(TAG, "Belated idle-finished of " + token);
- }
- }
- break;
- }
-
- case MSG_TIMEOUT: {
- if (DEBUG) {
- Slog.i(TAG, "MSG_TIMEOUT of " + token);
- }
- ActiveTask task = mPendingOperations.get(token);
- if (task != null) {
- mPendingOperations.remove(token);
- removeMessages(MSG_OP_COMPLETE);
-
- handleTimeoutTm(task);
- } else {
- // This one should not happen; we flushed timeout messages
- // whenever we entered a state after which we have established
- // that they are not appropriate.
- Slog.w(TAG, "Unexpected timeout of " + token);
- }
- break;
- }
-
- default:
- Slog.w(TAG, "Unknown message: " + msg.what);
- }
- }
- }
-
- void handleTimeoutTm(ActiveTask task) {
- switch (task.verb) {
- case VERB_BINDING: {
- // We were trying to bind to this service, but it wedged or otherwise
- // failed to respond in time. Let it stay in the queue for the next
- // time around, but just give up on it for now and go on to the next.
- startNextIdleServiceTm();
- break;
- }
- case VERB_IDLING: {
- // The service has reached the end of its designated idle timeslice.
- // This is not considered an error.
- if (DEBUG) {
- Slog.i(TAG, "Idler reached end of timeslice: " + task.who);
- }
- sendEndIdleTm(task.who);
- break;
- }
- case VERB_ENDING: {
- if (mCurrentIdler == task.who) {
- if (DEBUG) {
- Slog.i(TAG, "Task timed out when ending; unbind needed");
- }
- handleIdleFinishedTm(task);
- } else {
- if (DEBUG) {
- Slog.w(TAG, "Ending timeout for non-current idle service!");
- }
- }
- break;
- }
- default: {
- Slog.w(TAG, "Unknown timeout state " + task.verb);
- break;
- }
- }
- }
-
- void handleOpCompleteTm(ActiveTask task, int result) {
- if (DEBUG) {
- Slog.i(TAG, "handleOpComplete : task=" + task + " result=" + result);
- }
- if (task.verb == VERB_IDLING) {
- // If the service was told to begin idling and responded positively, then
- // it has begun idling and will eventually either explicitly finish, or
- // reach the end of its allotted timeslice. It's running free now, so we
- // just schedule the idle-expiration timeout under the token it's already been
- // given and let it keep going.
- if (result != 0) {
- scheduleOpTimeoutTm(task);
- } else {
- // The idle service has indicated that it does not, in fact,
- // need to run at present, so we immediately indicate that it's
- // to finish idling, and go on to the next idler.
- if (DEBUG) {
- Slog.i(TAG, "Idler declined idling; moving along");
- }
- sendEndIdleTm(task.who);
- }
- } else {
- // In the idling case, the task will be cleared either as the result of a timeout
- // or of an explicit idleFinished(). For all other operations (binding, ending) we
- // are done with the task as such, so we remove it from our bookkeeping.
- if (DEBUG) {
- Slog.i(TAG, "Clearing task " + task);
- }
- mPendingOperations.remove(task.token);
- if (task.verb == VERB_ENDING) {
- // The last bit of handshaking around idle cessation for this target
- handleIdleFinishedTm(task);
- }
- }
- }
-
- void handleIdleFinishedTm(ActiveTask task) {
- final IdleServiceInfo who = task.who;
- if (who == mCurrentIdler) {
- if (DEBUG) {
- Slog.i(TAG, "Current idler has finished: " + who);
- Slog.i(TAG, "Attributing wakelock to system work source");
- }
- mContext.unbindService(mConnection);
- startNextIdleServiceTm();
- } else {
- Slog.w(TAG, "finish from non-current idle service? " + who);
- }
- }
-
- void updateIdleServiceQueueTm() {
- if (DEBUG) {
- Slog.i(TAG, "Updating idle service queue");
- }
- PackageManager pm = mContext.getPackageManager();
- Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
- List<ResolveInfo> services = pm.queryIntentServices(idleIntent, 0);
- for (ResolveInfo info : services) {
- if (info.serviceInfo != null) {
- if (IdleService.PERMISSION_BIND.equals(info.serviceInfo.permission)) {
- final ComponentName componentName = new ComponentName(
- info.serviceInfo.packageName,
- info.serviceInfo.name);
- if (DEBUG) {
- Slog.i(TAG, " - " + componentName);
- }
- if (!mIdleServices.containsKey(componentName)) {
- if (DEBUG) {
- Slog.i(TAG, " + not known; adding");
- }
- IdleServiceInfo serviceInfo = new IdleServiceInfo(info, componentName);
- mIdleServices.put(componentName, serviceInfo);
- mIdleServiceQueue.add(serviceInfo);
- }
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Idle service " + info.serviceInfo
- + " does not have required permission; ignoring");
- }
- }
- }
- }
- }
-
- void startNextIdleServiceTm() {
- mWakeLock.setWorkSource(mSystemWorkSource);
-
- if (mLastIdler == null) {
- // we've run the queue; nothing more to do until the next idle interval.
- if (DEBUG) {
- Slog.i(TAG, "Queue already drained; nothing more to do");
- }
- return;
- }
-
- if (DEBUG) {
- Slog.i(TAG, "startNextIdleService : last=" + mLastIdler + " cur=" + mCurrentIdler);
- if (mIdleServiceQueue.size() > 0) {
- int i = 0;
- Slog.i(TAG, "Queue (" + mIdleServiceQueue.size() + "):");
- for (IdleServiceInfo info : mIdleServiceQueue) {
- Slog.i(TAG, " " + i + " : " + info);
- i++;
- }
- }
- }
- if (mCurrentIdler != mLastIdler) {
- if (mIdleServiceQueue.size() > 0) {
- IdleServiceInfo target = mIdleServiceQueue.pop();
- if (DEBUG) {
- Slog.i(TAG, "starting next idle service " + target);
- }
- Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
- idleIntent.setComponent(target.componentName);
- mCurrentIdler = target;
- ActiveTask task = new ActiveTask(target, VERB_BINDING);
- scheduleOpTimeoutTm(task);
- boolean bindOk = mContext.bindServiceAsUser(idleIntent, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, UserHandle.OWNER);
- if (!bindOk) {
- if (DEBUG) {
- Slog.w(TAG, "bindService() to " + target.componentName
- + " failed");
- }
- } else {
- mIdleServiceQueue.add(target); // at the end for next time
- if (DEBUG) { Slog.i(TAG, "Attributing wakelock to target uid " + target.uid); }
- mWakeLock.setWorkSource(new WorkSource(target.uid));
- }
- } else {
- // Queue is empty but mLastIdler is non-null -- eeep. Clear *everything*
- // and wind up until the next time around.
- Slog.e(TAG, "Queue unexpectedly empty; resetting. last="
- + mLastIdler + " cur=" + mCurrentIdler);
- mHandler.removeMessages(MSG_TIMEOUT);
- mPendingOperations.clear();
- stopIdleMaintenanceTm();
- }
- } else {
- // we've reached the place we started, so mark the queue as drained
- if (DEBUG) {
- Slog.i(TAG, "Reached end of queue.");
- }
- stopIdleMaintenanceTm();
- }
- }
-
- void sendStartIdleTm(IdleServiceInfo who) {
- ActiveTask task = new ActiveTask(who, VERB_IDLING);
- scheduleOpTimeoutTm(task);
- try {
- who.service.startIdleMaintenance(mCallback, task.token);
- } catch (RemoteException e) {
- // We bound to it, but now we can't reach it. Bail and go on to the
- // next service.
- mContext.unbindService(mConnection);
- if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
- mHandler.removeMessages(MSG_TIMEOUT);
- startNextIdleServiceTm();
- }
- }
-
- void sendEndIdleTm(IdleServiceInfo who) {
- ActiveTask task = new ActiveTask(who, VERB_ENDING);
- scheduleOpTimeoutTm(task);
- if (DEBUG) {
- Slog.i(TAG, "Sending end-idle to " + who);
- }
- try {
- who.service.stopIdleMaintenance(mCallback, task.token);
- } catch (RemoteException e) {
- // We bound to it, but now we can't reach it. Bail and go on to the
- // next service.
- mContext.unbindService(mConnection);
- if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
- mHandler.removeMessages(MSG_TIMEOUT);
- startNextIdleServiceTm();
- }
- }
-
- ServiceConnection mConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) {
- Slog.i(TAG, "onServiceConnected(" + name + ")");
- }
- IdleServiceInfo info = mIdleServices.get(name);
- if (info != null) {
- // Bound! Cancel the bind timeout
- mHandler.removeMessages(MSG_TIMEOUT);
- // Now tell it to start its idle work
- info.service = IIdleService.Stub.asInterface(service);
- sendStartIdleTm(info);
- } else {
- // We bound to a service we don't know about. That's ungood.
- Slog.e(TAG, "Connected to unexpected component " + name);
- mContext.unbindService(this);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) {
- Slog.i(TAG, "onServiceDisconnected(" + name + ")");
- }
- IdleServiceInfo who = mIdleServices.get(name);
- if (who == mCurrentIdler) {
- // Hm, okay; they didn't tell us they were finished but they
- // went away. Crashed, probably. Oh well. They're gone, so
- // we can't finish them cleanly; just force things along.
- Slog.w(TAG, "Idler unexpectedly vanished: " + mCurrentIdler);
- mContext.unbindService(this);
- mHandler.removeMessages(MSG_TIMEOUT);
- startNextIdleServiceTm();
- } else {
- // Not the current idler, so we don't interrupt our process...
- if (DEBUG) {
- Slog.w(TAG, "Disconnect of abandoned or unexpected service " + name);
- }
- }
- }
- };
-
- // Schedules a timeout / end-of-work based on the task verb
- void scheduleOpTimeoutTm(ActiveTask task) {
- final long timeoutMillis = (task.verb == VERB_IDLING) ? IDLE_TIMESLICE : OP_TIMEOUT;
- if (DEBUG) {
- Slog.i(TAG, "Scheduling timeout (token " + task.token
- + " : verb " + task.verb + ") for " + task + " in " + timeoutMillis);
- }
- mPendingOperations.put(task.token, task);
- mHandler.removeMessages(MSG_TIMEOUT);
- final Message msg = mHandler.obtainMessage(MSG_TIMEOUT, 0, task.token);
- mHandler.sendMessageDelayed(msg, timeoutMillis);
- }
-
- // -------------------------------------------------------------------------------
- public IdleMaintenanceService(Context context, BatteryService batteryService) {
- mContext = context;
- mBatteryService = batteryService;
-
- mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-
- PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
- mHandler = new IdleHandler(mContext.getMainLooper());
- mCallback = new IdleCallback();
-
- Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
- intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
- intent, PendingIntent.FLAG_UPDATE_CURRENT);
-
- register(mHandler);
- }
-
- public void register(Handler handler) {
- IntentFilter intentFilter = new IntentFilter();
-
- // Alarm actions.
- intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
-
- // Battery actions.
- intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
-
- // Screen actions.
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-
- // Dream actions.
- intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
- intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
-
- mContext.registerReceiverAsUser(this, UserHandle.ALL,
- intentFilter, null, mHandler);
-
- intentFilter = new IntentFilter();
- intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE);
- mContext.registerReceiverAsUser(this, UserHandle.ALL,
- intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler);
- }
-
- private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
- final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
- mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
- mUpdateIdleMaintenanceStatePendingIntent);
- }
-
- private void unscheduleUpdateIdleMaintenanceState() {
- mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
- }
-
- private void updateIdleMaintenanceStateTm(boolean noisy) {
- if (mIdleMaintenanceStarted) {
- // Idle maintenance can be interrupted by user activity, or duration
- // time out, or low battery.
- final boolean batteryOk
- = batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning();
- if (!lastUserActivityPermitsIdleMaintenanceRunning() || !batteryOk) {
- unscheduleUpdateIdleMaintenanceState();
- mIdleMaintenanceStarted = false;
- // We stopped since we don't have enough battery or timed out but the
- // user is not using the device, so we should be able to run maintenance
- // in the next maintenance window since the battery may be charged
- // without interaction and the min interval between maintenances passed.
- if (!batteryOk) {
- scheduleUpdateIdleMaintenanceState(
- getNextIdleMaintenanceIntervalStartFromNow());
- }
-
- EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
- mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
- isBatteryCharging() ? 1 : 0);
- scheduleIdleFinishTm();
- }
- } else if (deviceStatePermitsIdleMaintenanceStart(noisy)
- && lastUserActivityPermitsIdleMaintenanceStart(noisy)
- && lastRunPermitsIdleMaintenanceStart(noisy)) {
- // Now that we started idle maintenance, we should schedule another
- // update for the moment when the idle maintenance times out.
- scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
- mIdleMaintenanceStarted = true;
- EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
- mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
- isBatteryCharging() ? 1 : 0);
- mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
- startIdleMaintenanceTm();
- } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
- if (lastRunPermitsIdleMaintenanceStart(noisy)) {
- // The user does not use the device and we did not run maintenance in more
- // than the min interval between runs, so schedule an update - maybe the
- // battery will be charged latter.
- scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
- } else {
- // The user does not use the device but we have run maintenance in the min
- // interval between runs, so schedule an update after the min interval ends.
- scheduleUpdateIdleMaintenanceState(
- getNextIdleMaintenanceIntervalStartFromNow());
- }
- }
- }
-
- void startIdleMaintenanceTm() {
- if (DEBUG) {
- Slog.i(TAG, "*** Starting idle maintenance ***");
- }
- if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
- mWakeLock.setWorkSource(mSystemWorkSource);
- mWakeLock.acquire();
- updateIdleServiceQueueTm();
- mCurrentIdler = null;
- mLastIdler = (mIdleServiceQueue.size() > 0) ? mIdleServiceQueue.peekLast() : null;
- startNextIdleServiceTm();
- }
-
- // Start a graceful wind-down of the idle maintenance state: end the current idler
- // and pretend that we've finished running the queue. If there's no current idler,
- // this is a no-op.
- void scheduleIdleFinishTm() {
- if (mCurrentIdler != null) {
- if (DEBUG) {
- Slog.i(TAG, "*** Finishing idle maintenance ***");
- }
- mLastIdler = mCurrentIdler;
- sendEndIdleTm(mCurrentIdler);
- } else {
- if (DEBUG) {
- Slog.w(TAG, "Asked to finish idle maintenance but we're done already");
- }
- }
- }
-
- // Actual finalization of the idle maintenance sequence
- void stopIdleMaintenanceTm() {
- if (mLastIdler != null) {
- if (DEBUG) {
- Slog.i(TAG, "*** Idle maintenance shutdown ***");
- }
- mWakeLock.setWorkSource(mSystemWorkSource);
- mLastIdler = mCurrentIdler = null;
- updateIdleMaintenanceStateTm(false); // resets 'started' and schedules next window
- mWakeLock.release();
- } else {
- Slog.e(TAG, "ERROR: idle shutdown but invariants not held. last=" + mLastIdler
- + " cur=" + mCurrentIdler + " size=" + mIdleServiceQueue.size());
- }
- }
-
- private long getNextIdleMaintenanceIntervalStartFromNow() {
- return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
- - SystemClock.elapsedRealtime();
- }
-
- private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
- final int minBatteryLevel = isBatteryCharging()
- ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
- : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
- boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
- && mBatteryService.getBatteryLevel() > minBatteryLevel);
- if (!allowed && noisy) {
- Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power");
- }
- return allowed;
- }
-
- private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) {
- // The last time the user poked the device is above the threshold.
- boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
- && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
- > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
- if (!allowed && noisy) {
- Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity");
- }
- return allowed;
- }
-
- private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) {
- // Enough time passed since the last maintenance run.
- boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
- > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
- if (!allowed && noisy) {
- Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last");
- }
- return allowed;
- }
-
- private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
- // The user is not using the device.
- return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
- }
-
- private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
- // Battery not too low and the maintenance duration did not timeout.
- return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
- && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
- > SystemClock.elapsedRealtime());
- }
-
- private boolean isBatteryCharging() {
- return mBatteryService.getPlugType() > 0
- && mBatteryService.getInvalidCharger() == 0;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) {
- Log.i(TAG, intent.getAction());
- }
- String action = intent.getAction();
- if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
- // We care about battery only if maintenance is in progress so we can
- // stop it if battery is too low. Note that here we assume that the
- // maintenance clients are properly holding a wake lock. We will
- // refactor the maintenance to use services instead of intents for the
- // next release. The only client for this for now is internal an holds
- // a wake lock correctly.
- if (mIdleMaintenanceStarted) {
- updateIdleMaintenanceStateTm(false);
- }
- } else if (Intent.ACTION_SCREEN_ON.equals(action)
- || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
- mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
- // Unschedule any future updates since we already know that maintenance
- // cannot be performed since the user is back.
- unscheduleUpdateIdleMaintenanceState();
- // If the screen went on/stopped dreaming, we know the user is using the
- // device which means that idle maintenance should be stopped if running.
- updateIdleMaintenanceStateTm(false);
- } else if (Intent.ACTION_SCREEN_OFF.equals(action)
- || Intent.ACTION_DREAMING_STARTED.equals(action)) {
- mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
- // If screen went off/started dreaming, we may be able to start idle maintenance
- // after the minimal user inactivity elapses. We schedule an alarm for when
- // this timeout elapses since the device may go to sleep by then.
- scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
- } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
- updateIdleMaintenanceStateTm(false);
- } else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) {
- long now = SystemClock.elapsedRealtime() - 1;
- mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START;
- mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
- updateIdleMaintenanceStateTm(true);
- }
- }
-}
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index bcb6e9e..215d92d 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -30,8 +30,7 @@
private static final String TAG = "MountServiceIdler";
private static ComponentName sIdleService =
- new ComponentName(MountServiceIdler.class.getPackage().getName(),
- MountServiceIdler.class.getName());
+ new ComponentName("android", MountServiceIdler.class.getName());
private static int MOUNT_JOB_ID = 808;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index d26f3fc..7022294 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -41,6 +41,7 @@
import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
+import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.NetworkStats;
import android.net.NetworkUtils;
@@ -145,6 +146,7 @@
public static final int InterfaceClassActivity = 613;
public static final int InterfaceAddressChange = 614;
public static final int InterfaceDnsServerInfo = 615;
+ public static final int RouteChange = 616;
}
static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
@@ -580,6 +582,28 @@
}
}
+ /**
+ * Notify our observers of a route change.
+ */
+ private void notifyRouteChange(String action, RouteInfo route) {
+ final int length = mObservers.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ if (action.equals("updated")) {
+ mObservers.getBroadcastItem(i).routeUpdated(route);
+ } else {
+ mObservers.getBroadcastItem(i).routeRemoved(route);
+ }
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
+ }
+ } finally {
+ mObservers.finishBroadcast();
+ }
+ }
+
//
// Netd Callback handling
//
@@ -722,6 +746,47 @@
}
return true;
// break;
+ case NetdResponseCode.RouteChange:
+ /*
+ * A route has been updated or removed.
+ * Format: "NNN Route <updated|removed> <dst> [via <gateway] [dev <iface>]"
+ */
+ if (!cooked[1].equals("Route") || cooked.length < 6) {
+ throw new IllegalStateException(errorMessage);
+ }
+
+ String via = null;
+ String dev = null;
+ boolean valid = true;
+ for (int i = 4; (i + 1) < cooked.length && valid; i += 2) {
+ if (cooked[i].equals("dev")) {
+ if (dev == null) {
+ dev = cooked[i+1];
+ } else {
+ valid = false; // Duplicate interface.
+ }
+ } else if (cooked[i].equals("via")) {
+ if (via == null) {
+ via = cooked[i+1];
+ } else {
+ valid = false; // Duplicate gateway.
+ }
+ } else {
+ valid = false; // Unknown syntax.
+ }
+ }
+ if (valid) {
+ try {
+ // InetAddress.parseNumericAddress(null) inexplicably returns ::1.
+ InetAddress gateway = null;
+ if (via != null) gateway = InetAddress.parseNumericAddress(via);
+ RouteInfo route = new RouteInfo(new IpPrefix(cooked[3]), gateway, dev);
+ notifyRouteChange(cooked[2], route);
+ return true;
+ } catch (IllegalArgumentException e) {}
+ }
+ throw new IllegalStateException(errorMessage);
+ // break;
default: break;
}
return false;
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 697e1f2..57b56b4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -36,6 +36,9 @@
import android.app.IAppTask;
import android.app.admin.DevicePolicyManager;
import android.appwidget.AppWidgetManager;
+import android.content.DialogInterface.OnClickListener;
+import android.content.res.Resources;
+import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.os.BatteryStats;
import android.os.PersistableBundle;
@@ -171,8 +174,12 @@
import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.format.DateUtils;
import android.text.format.Time;
+import android.text.style.DynamicDrawableSpan;
+import android.text.style.ImageSpan;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
@@ -186,6 +193,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
+import android.widget.TextView;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -570,6 +578,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.
*/
@@ -1158,6 +1172,8 @@
CompatModeDialog mCompatModeDialog;
long mLastMemUsageReportTime = 0;
+ private LockToAppRequestDialog mLockToAppRequest;
+
/**
* Flag whether the current user is a "monkey", i.e. whether
* the UI is driven by a UI automation tool.
@@ -1801,8 +1817,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;
@@ -2176,6 +2233,8 @@
}
};
+ mLockToAppRequest = new LockToAppRequestDialog(mContext, this);
+
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
}
@@ -2194,6 +2253,11 @@
LocalServices.addService(ActivityManagerInternal.class, new LocalService());
}
+ public void initPowerManagement() {
+ mStackSupervisor.initPowerManagement();
+ mBatteryStatsService.initPowerManagement();
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -2764,7 +2828,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;
}
@@ -2819,7 +2883,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
@@ -3657,6 +3721,12 @@
// Keep track of the root activity of the task before we finish it
TaskRecord tr = r.task;
ActivityRecord rootR = tr.getRootActivity();
+ // Do not allow task to finish in Lock Task mode.
+ if (tr == mStackSupervisor.mLockTaskModeTask) {
+ if (rootR == r) {
+ return false;
+ }
+ }
if (mController != null) {
// Find the first activity that is not finishing.
ActivityRecord next = r.task.stack.topRunningActivityLocked(token, 0);
@@ -3801,13 +3871,25 @@
public boolean finishActivityAffinity(IBinder token) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- boolean res = false;
- if (r != null) {
- res = r.task.stack.finishActivityAffinityLocked(r);
+ try {
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+
+ ActivityRecord rootR = r.task.getRootActivity();
+ // Do not allow task to finish in Lock Task mode.
+ if (r.task == mStackSupervisor.mLockTaskModeTask) {
+ if (rootR == r) {
+ Binder.restoreCallingIdentity(origId);
+ return false;
+ }
+ }
+ boolean res = false;
+ if (r != null) {
+ res = r.task.stack.finishActivityAffinityLocked(r);
+ }
+ return res;
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
- return res;
}
}
@@ -6039,7 +6121,11 @@
if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
return false;
}
+ return checkHoldingPermissionsInternalLocked(pm, pi, grantUri, uid, modeFlags, true);
+ }
+ private final boolean checkHoldingPermissionsInternalLocked(IPackageManager pm, ProviderInfo pi,
+ GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {
if (pi.applicationInfo.uid == uid) {
return true;
} else if (!pi.exported) {
@@ -6050,11 +6136,11 @@
boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
try {
// check if target holds top-level <provider> permissions
- if (!readMet && pi.readPermission != null
+ if (!readMet && pi.readPermission != null && considerUidPermissions
&& (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
readMet = true;
}
- if (!writeMet && pi.writePermission != null
+ if (!writeMet && pi.writePermission != null && considerUidPermissions
&& (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
writeMet = true;
}
@@ -6080,7 +6166,8 @@
+ ": match=" + pp.match(path)
+ " check=" + pm.checkUidPermission(pprperm, uid));
if (pprperm != null) {
- if (pm.checkUidPermission(pprperm, uid) == PERMISSION_GRANTED) {
+ if (considerUidPermissions && pm.checkUidPermission(pprperm, uid)
+ == PERMISSION_GRANTED) {
readMet = true;
} else {
allowDefaultRead = false;
@@ -6094,7 +6181,8 @@
+ ": match=" + pp.match(path)
+ " check=" + pm.checkUidPermission(ppwperm, uid));
if (ppwperm != null) {
- if (pm.checkUidPermission(ppwperm, uid) == PERMISSION_GRANTED) {
+ if (considerUidPermissions && pm.checkUidPermission(ppwperm, uid)
+ == PERMISSION_GRANTED) {
writeMet = true;
} else {
allowDefaultWrite = false;
@@ -6291,28 +6379,40 @@
}
}
+ /* There is a special cross user grant if:
+ * - The target is on another user.
+ * - Apps on the current user can access the uri without any uid permissions.
+ * In this case, we grant a uri permission, even if the ContentProvider does not normally
+ * grant uri permissions.
+ */
+ boolean specialCrossUserGrant = UserHandle.getUserId(targetUid) != grantUri.sourceUserId
+ && checkHoldingPermissionsInternalLocked(pm, pi, grantUri, callingUid,
+ modeFlags, false /*without considering the uid permissions*/);
+
// Second... is the provider allowing granting of URI permissions?
- if (!pi.grantUriPermissions) {
- throw new SecurityException("Provider " + pi.packageName
- + "/" + pi.name
- + " does not allow granting of Uri permissions (uri "
- + grantUri + ")");
- }
- if (pi.uriPermissionPatterns != null) {
- final int N = pi.uriPermissionPatterns.length;
- boolean allowed = false;
- for (int i=0; i<N; i++) {
- if (pi.uriPermissionPatterns[i] != null
- && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
- allowed = true;
- break;
- }
- }
- if (!allowed) {
+ if (!specialCrossUserGrant) {
+ if (!pi.grantUriPermissions) {
throw new SecurityException("Provider " + pi.packageName
+ "/" + pi.name
- + " does not allow granting of permission to path of Uri "
- + grantUri);
+ + " does not allow granting of Uri permissions (uri "
+ + grantUri + ")");
+ }
+ if (pi.uriPermissionPatterns != null) {
+ final int N = pi.uriPermissionPatterns.length;
+ boolean allowed = false;
+ for (int i=0; i<N; i++) {
+ if (pi.uriPermissionPatterns[i] != null
+ && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
+ allowed = true;
+ break;
+ }
+ }
+ if (!allowed) {
+ throw new SecurityException("Provider " + pi.packageName
+ + "/" + pi.name
+ + " does not allow granting of permission to path of Uri "
+ + grantUri);
+ }
}
}
@@ -7625,14 +7725,32 @@
}
}
- 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())) {
+ void startLockTaskMode(TaskRecord task) {
+ final String pkg;
+ synchronized (this) {
+ pkg = task.intent.getComponent().getPackageName();
+ }
+ boolean isSystemInitiated = Binder.getCallingUid() == Process.SYSTEM_UID;
+ if (!isSystemInitiated && !isLockTaskAuthorized(pkg)) {
+ final TaskRecord taskRecord = task;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mLockToAppRequest.showLockTaskPrompt(taskRecord);
+ }
+ });
return;
}
long ident = Binder.clearCallingIdentity();
@@ -7641,7 +7759,10 @@
// Since we lost lock on task, make sure it is still there.
task = mStackSupervisor.anyTaskForIdLocked(task.taskId);
if (task != null) {
- mStackSupervisor.setLockTaskModeLocked(task);
+ if ((mFocusedActivity == null) || (task != mFocusedActivity.task)) {
+ throw new IllegalArgumentException("Invalid task, not in foreground");
+ }
+ mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated);
}
}
} finally {
@@ -7651,25 +7772,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) {
@@ -7677,8 +7798,50 @@
}
task = r.task;
}
- if (task != null) {
- startLockTaskMode(task);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ if (task != null) {
+ startLockTaskMode(task);
+ }
+ }
+
+ @Override
+ public void startLockTaskModeOnCurrent() throws RemoteException {
+ checkCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS);
+ ActivityRecord r = null;
+ synchronized (this) {
+ r = mStackSupervisor.topRunningActivityLocked();
+ }
+ startLockTaskMode(r.task);
+ }
+
+ @Override
+ public void stopLockTaskMode() {
+ // Verify that the user matches the package of the intent for the TaskRecord
+ // we are locked to or systtem. This will ensure the same caller for startLockTaskMode
+ // and stopLockTaskMode.
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ try {
+ String pkg =
+ mStackSupervisor.mLockTaskModeTask.intent.getComponent().getPackageName();
+ int uid = mContext.getPackageManager().getPackageUid(pkg,
+ Binder.getCallingUserHandle().getIdentifier());
+ if (uid != callingUid) {
+ throw new SecurityException("Invalid uid, expected " + uid);
+ }
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, "stopLockTaskMode " + e);
+ return;
+ }
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ Log.d(TAG, "stopLockTaskMode");
+ // Stop lock task
+ synchronized (this) {
+ mStackSupervisor.setLockTaskModeLocked(null, false);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -7686,21 +7849,13 @@
}
@Override
- public void stopLockTaskMode() {
- // Check if the calling task is eligible to use lock task
- final int uid = Binder.getCallingUid();
+ public void stopLockTaskModeOnCurrent() throws RemoteException {
+ checkCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS);
+ long ident = Binder.clearCallingIdentity();
try {
- final String name = AppGlobals.getPackageManager().getNameForUid(uid);
- if (!isLockTaskAuthorized(new ComponentName(name, name))) {
- return;
- }
- } catch (RemoteException e) {
- Log.d(TAG, "stopLockTaskMode " + e);
- return;
- }
- // Stop lock task
- synchronized (this) {
- mStackSupervisor.setLockTaskModeLocked(null);
+ stopLockTaskMode();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -7759,7 +7914,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);
}
@@ -12567,6 +12723,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.
@@ -12588,6 +12746,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);
@@ -12655,6 +12814,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());
@@ -15435,6 +15603,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--) {
@@ -16630,7 +16799,7 @@
return true;
}
- mStackSupervisor.setLockTaskModeLocked(null);
+ mStackSupervisor.setLockTaskModeLocked(null, false);
final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
if (userInfo == null) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 11f855e..0825f2e 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager.TaskDescription;
import android.os.PersistableBundle;
import android.os.Trace;
+
import com.android.internal.app.ResolverActivity;
import com.android.internal.util.XmlUtils;
import com.android.server.AttributeCache;
@@ -49,6 +50,7 @@
import android.util.TimeUtils;
import android.view.IApplicationToken;
import android.view.WindowManager;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -58,6 +60,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Objects;
/**
* An entry in the history stack, representing an activity.
@@ -83,6 +86,7 @@
final ActivityManagerService service; // owner
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
+ final ApplicationInfo appInfo; // information about activity's app
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
final int userId; // Which user is this running for?
@@ -103,9 +107,6 @@
static final int RECENTS_ACTIVITY_TYPE = 2;
int mActivityType;
- final String baseDir; // where activity source (resources etc) located
- final String resDir; // where public activity source (public resources etc) located
- final String dataDir; // where activity data should go
CharSequence nonLocalizedLabel; // the label information from the package mgr.
int labelRes; // the label information from the package mgr.
int icon; // resource identifier of activity's icon.
@@ -184,11 +185,13 @@
pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
pw.print(prefix); pw.print("realActivity=");
pw.println(realActivity.flattenToShortString());
- pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
- if (!resDir.equals(baseDir)) {
- pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ if (appInfo != null) {
+ pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
+ if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+ pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
+ }
+ pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
}
- pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
pw.print(" componentSpecified="); pw.print(componentSpecified);
pw.print(" mActivityType="); pw.println(mActivityType);
@@ -418,9 +421,7 @@
taskAffinity = aInfo.taskAffinity;
stateNotNeeded = (aInfo.flags&
ActivityInfo.FLAG_STATE_NOT_NEEDED) != 0;
- baseDir = aInfo.applicationInfo.sourceDir;
- resDir = aInfo.applicationInfo.publicSourceDir;
- dataDir = aInfo.applicationInfo.dataDir;
+ appInfo = aInfo.applicationInfo;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes;
if (nonLocalizedLabel == null && labelRes == 0) {
@@ -488,9 +489,7 @@
realActivity = null;
taskAffinity = null;
stateNotNeeded = false;
- baseDir = null;
- resDir = null;
- dataDir = null;
+ appInfo = null;
processName = null;
packageName = null;
fullscreen = true;
@@ -568,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() {
@@ -580,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);
@@ -1033,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 66e9eb3e..4be208b 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;
@@ -77,6 +79,8 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.service.voice.IVoiceInteractionSession;
import android.util.EventLog;
import android.util.Slog;
@@ -140,9 +144,12 @@
private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
+ private static final String LOCK_TASK_TAG = "Lock-to-App";
+
/** 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.
@@ -227,14 +234,14 @@
* receivers to launch an activity and get that to run before the device
* goes back to sleep.
*/
- final PowerManager.WakeLock mLaunchingActivity;
+ PowerManager.WakeLock mLaunchingActivity;
/**
* Set when the system is going to sleep, until we have
* successfully paused the current activity and released our wake lock.
* At that point the system is allowed to actually sleep.
*/
- final PowerManager.WakeLock mGoingToSleep;
+ PowerManager.WakeLock mGoingToSleep;
/** Stack id of the front stack when user switched, indexed by userId. */
SparseIntArray mUserStackInFront = new SparseIntArray(2);
@@ -251,16 +258,24 @@
/** 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;
+ /**
+ * Notifies the user when entering/exiting lock-task.
+ */
+ private LockTaskNotify mLockTaskNotify;
public ActivityStackSupervisor(ActivityManagerService service) {
mService = service;
+ mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
+ }
+
+ /**
+ * At the time when the constructor runs, the power manager has not yet been
+ * initialized. So we initialize our wakelocks afterwards.
+ */
+ void initPowerManagement() {
PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
- mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
- if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
- throw new IllegalStateException("Calling must be system uid");
- }
mLaunchingActivity =
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
mLaunchingActivity.setReferenceCounted(false);
@@ -281,6 +296,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;
@@ -1158,7 +1186,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;
@@ -2984,13 +3013,16 @@
return list;
}
- void setLockTaskModeLocked(TaskRecord task) {
- final Message lockTaskMsg = Message.obtain();
+ void setLockTaskModeLocked(TaskRecord task, boolean showHomeRecents) {
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)) {
@@ -3000,7 +3032,12 @@
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;
+ lockTaskMsg.arg2 = showHomeRecents ? 1 : 0;
mHandler.sendMessage(lockTaskMsg);
}
@@ -3103,10 +3140,24 @@
case LOCK_TASK_START_MSG: {
// When lock task starts, we disable the status bars.
try {
+ if (mLockTaskNotify == null) {
+ mLockTaskNotify = new LockTaskNotify(mService.mContext);
+ }
+ mLockTaskNotify.show(true);
if (getStatusBarService() != null) {
- getStatusBarService().disable
- (StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK,
- mToken, mService.mContext.getPackageName());
+ int flags =
+ StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK;
+ if (msg.arg2 != 0) {
+ flags ^= StatusBarManager.DISABLE_HOME
+ | StatusBarManager.DISABLE_RECENT;
+ }
+ getStatusBarService().disable(flags, mToken,
+ mService.mContext.getPackageName());
+ }
+ mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+ if (getDevicePolicyManager() != null) {
+ getDevicePolicyManager().notifyLockTaskModeChanged(true,
+ (String)msg.obj, msg.arg1);
}
} catch (RemoteException ex) {
throw new RuntimeException(ex);
@@ -3115,11 +3166,29 @@
case LOCK_TASK_END_MSG: {
// When lock task ends, we enable the status bars.
try {
- if (getStatusBarService() != null) {
- getStatusBarService().disable
- (StatusBarManager.DISABLE_NONE,
- mToken, mService.mContext.getPackageName());
- }
+ if (getStatusBarService() != null) {
+ getStatusBarService().disable(StatusBarManager.DISABLE_NONE, mToken,
+ mService.mContext.getPackageName());
+ }
+ mWindowManager.reenableKeyguard(mToken);
+ if (getDevicePolicyManager() != null) {
+ getDevicePolicyManager().notifyLockTaskModeChanged(false, null,
+ msg.arg1);
+ }
+ if (mLockTaskNotify == null) {
+ mLockTaskNotify = new LockTaskNotify(mService.mContext);
+ }
+ mLockTaskNotify.show(false);
+ try {
+ boolean shouldLockKeyguard = Settings.System.getInt(
+ mService.mContext.getContentResolver(),
+ Settings.System.LOCK_TO_APP_EXIT_LOCKED) != 0;
+ if (shouldLockKeyguard) {
+ mWindowManager.lockNow(null);
+ }
+ } catch (SettingNotFoundException e) {
+ // No setting, don't lock.
+ }
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index eb253eb..f403d08 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -74,12 +74,19 @@
mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout)
* 1000L);
+ }
+
+ /**
+ * At the time when the constructor runs, the power manager has not yet been
+ * initialized. So we initialize the low power observer later.
+ */
+ public void initPowerManagement() {
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mPowerManagerInternal.registerLowPowerModeObserver(this);
mStats.noteLowPowerMode(mPowerManagerInternal.getLowPowerModeEnabled());
(new WakeupReasonThread()).start();
- }
-
+ }
+
public void shutdown() {
Slog.w("BatteryStats", "Writing battery stats before shutdown...");
synchronized (mStats) {
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/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
new file mode 100644
index 0000000..1997f46
--- /dev/null
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -0,0 +1,185 @@
+/*
+ * 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.server.am;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.Message;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.android.internal.R;
+
+/**
+ * Helper to manage showing/hiding a image to notify them that they are entering
+ * or exiting lock-to-app mode.
+ */
+public class LockTaskNotify {
+ private static final String TAG = "LockTaskNotify";
+
+ private static final int SHOW_LENGTH_MS = 1500;
+
+ private final Context mContext;
+ private final H mHandler;
+
+ private ClingWindowView mClingWindow;
+ private WindowManager mWindowManager;
+ private boolean mIsStarting;
+
+ public LockTaskNotify(Context context) {
+ mContext = context;
+ mHandler = new H();
+ mWindowManager = (WindowManager)
+ mContext.getSystemService(Context.WINDOW_SERVICE);
+ }
+
+ public void show(boolean starting) {
+ mIsStarting = starting;
+ mHandler.obtainMessage(H.SHOW).sendToTarget();
+ }
+
+ public WindowManager.LayoutParams getClingWindowLayoutParams() {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ 0
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+ ,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setTitle("LockTaskNotify");
+ lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
+ lp.gravity = Gravity.FILL;
+ return lp;
+ }
+
+ public FrameLayout.LayoutParams getImageLayoutParams() {
+ return new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
+ }
+
+ private void handleShow() {
+ mClingWindow = new ClingWindowView(mContext);
+
+ // we will be hiding the nav bar, so layout as if it's already hidden
+ mClingWindow.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+
+ // show the confirmation
+ WindowManager.LayoutParams lp = getClingWindowLayoutParams();
+ mWindowManager.addView(mClingWindow, lp);
+ }
+
+ private void handleHide() {
+ if (mClingWindow != null) {
+ mWindowManager.removeView(mClingWindow);
+ mClingWindow = null;
+ }
+ }
+
+
+ private class ClingWindowView extends FrameLayout {
+ private View mView;
+
+ private Runnable mUpdateLayoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mView != null && mView.getParent() != null) {
+ mView.setLayoutParams(getImageLayoutParams());
+ }
+ }
+ };
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+ post(mUpdateLayoutRunnable);
+ }
+ }
+ };
+
+ public ClingWindowView(Context context) {
+ super(context);
+ setClickable(true);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mWindowManager.getDefaultDisplay().getMetrics(metrics);
+
+ int id = R.layout.lock_to_app_exit;
+ if (mIsStarting) {
+ id = R.layout.lock_to_app_enter;
+ }
+ mView = View.inflate(getContext(), id, null);
+
+ addView(mView, getImageLayoutParams());
+
+ mContext.registerReceiver(mReceiver,
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(H.HIDE), SHOW_LENGTH_MS);
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent motion) {
+ Slog.v(TAG, "ClingWindowView.onTouchEvent");
+ return true;
+ }
+ }
+
+ private final class H extends Handler {
+ private static final int SHOW = 1;
+ private static final int HIDE = 2;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case SHOW:
+ handleShow();
+ break;
+ case HIDE:
+ handleHide();
+ break;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/LockToAppRequestDialog.java b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
new file mode 100644
index 0000000..6e86dff
--- /dev/null
+++ b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
@@ -0,0 +1,87 @@
+
+package com.android.server.am;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.res.Resources;
+import android.graphics.BitmapFactory;
+import android.provider.Settings;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.DynamicDrawableSpan;
+import android.text.style.ImageSpan;
+import android.util.Slog;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+public class LockToAppRequestDialog implements OnClickListener {
+ private static final String TAG = "ActivityManager";
+
+ final private Context mContext;
+ final private ActivityManagerService mService;
+
+ private AlertDialog mDialog;
+ private TaskRecord mRequestedTask;
+
+ public LockToAppRequestDialog(Context context, ActivityManagerService activityManagerService) {
+ mContext = context;
+ mService = activityManagerService;
+ }
+
+ public void showLockTaskPrompt(TaskRecord task) {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ mRequestedTask = task;
+
+ final Resources r = Resources.getSystem();
+ final String descriptionString = r.getString(R.string.lock_to_app_description);
+ final SpannableString description =
+ new SpannableString(descriptionString.replace('$', ' '));
+ final ImageSpan imageSpan = new ImageSpan(mContext,
+ BitmapFactory.decodeResource(r, R.drawable.ic_recent),
+ DynamicDrawableSpan.ALIGN_BOTTOM);
+ final int index = descriptionString.indexOf('$');
+ if (index >= 0) {
+ description.setSpan(imageSpan, index, index + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ mDialog =
+ new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.lock_to_app_title))
+ .setMessage(description)
+ .setPositiveButton(r.getString(R.string.lock_to_app_positive), this)
+ .setNegativeButton(r.getString(R.string.lock_to_app_negative), this)
+ .create();
+
+ mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ mDialog.show();
+
+ // Make icon fit.
+ final TextView msgTxt = (TextView) mDialog.findViewById(R.id.message);
+ final float width = imageSpan.getDrawable().getIntrinsicWidth();
+ final float height = imageSpan.getDrawable().getIntrinsicHeight();
+ final int lineHeight = msgTxt.getLineHeight();
+ imageSpan.getDrawable().setBounds(0, 0, (int) (lineHeight * width / height), lineHeight);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (DialogInterface.BUTTON_POSITIVE == which) {
+ Slog.d(TAG, "accept lock-to-app request");
+ // Automatically enable if not currently on. (Could be triggered by an app)
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.LOCK_TO_APP_ENABLED, 1);
+
+ // Start lock-to-app.
+ mService.startLockTaskMode(mRequestedTask);
+ } else {
+ Slog.d(TAG, "ignore lock-to-app request");
+ }
+ }
+
+}
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 14f3ef981..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/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index e54c95e..f79c026 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -45,6 +45,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* A running application service.
@@ -70,9 +71,6 @@
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String permission;// permission needed to access service
- final String baseDir; // where activity source (resources etc) located
- final String resDir; // where public activity source (public resources etc) located
- final String dataDir; // where activity data should go
final boolean exported; // from ServiceInfo.exported
final Runnable restarter; // used to schedule retries of starting the service
final long createTime; // when this service was created
@@ -209,11 +207,13 @@
}
long now = SystemClock.uptimeMillis();
long nowReal = SystemClock.elapsedRealtime();
- pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
- if (!resDir.equals(baseDir)) {
- pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ if (appInfo != null) {
+ pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
+ if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+ pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
+ }
+ pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
}
- pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
if (isolatedProc != null) {
pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
@@ -305,9 +305,6 @@
packageName = sInfo.applicationInfo.packageName;
processName = sInfo.processName;
permission = sInfo.permission;
- baseDir = sInfo.applicationInfo.sourceDir;
- resDir = sInfo.applicationInfo.publicSourceDir;
- dataDir = sInfo.applicationInfo.dataDir;
exported = sInfo.exported;
this.restarter = restarter;
createTime = SystemClock.elapsedRealtime();
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..986321d 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);
}
@@ -325,7 +338,11 @@
if (r.isPersistable()) {
mService.notifyTaskPersisterLocked(this, false);
}
- return mActivities.size() == 0;
+ if (mActivities.isEmpty()) {
+ return true;
+ }
+ updateEffectiveIntent();
+ return false;
}
boolean autoRemoveFromRecents() {
@@ -579,12 +596,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 +639,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 +679,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 +698,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 +730,7 @@
int userId = 0;
String lastDescription = null;
long lastTimeOnTop = 0;
+ boolean neverRelinquishIdentity = true;
int taskId = -1;
final int outerDepth = in.getDepth();
@@ -713,6 +761,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 +797,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/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
new file mode 100644
index 0000000..2941574
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -0,0 +1,328 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.fingerprint;
+
+import android.app.Service;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.service.fingerprint.FingerprintManager;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+import android.service.fingerprint.FingerprintUtils;
+import android.service.fingerprint.IFingerprintService;
+import android.service.fingerprint.IFingerprintServiceReceiver;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * A service to manage multiple clients that want to access the fingerprint HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * fingerprint -related events.
+ *
+ * @hide
+ */
+public class FingerprintService extends SystemService {
+ private final String TAG = "FingerprintService";
+ private static final boolean DEBUG = true;
+ private ArrayMap<IBinder, ClientData> mClients = new ArrayMap<IBinder, ClientData>();
+
+ private static final int MSG_NOTIFY = 10;
+
+ Handler mHandler = new Handler() {
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_NOTIFY:
+ handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj);
+ break;
+
+ default:
+ Slog.w(TAG, "Unknown message:" + msg.what);
+ }
+ }
+ };
+ private Context mContext;
+
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_LISTENING = 1;
+ private static final int STATE_ENROLLING = 2;
+ private static final int STATE_REMOVING = 3;
+ private static final long MS_PER_SEC = 1000;
+ public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
+ public static final String ENROLL_FINGERPRINT = "android.permission.ENROLL_FINGERPRINT";
+
+ private static final class ClientData {
+ public IFingerprintServiceReceiver receiver;
+ int state;
+ int userId;
+ public TokenWatcher tokenWatcher;
+ IBinder getToken() { return tokenWatcher.getToken(); }
+ }
+
+ private class TokenWatcher implements IBinder.DeathRecipient {
+ WeakReference<IBinder> token;
+
+ TokenWatcher(IBinder token) {
+ this.token = new WeakReference<IBinder>(token);
+ }
+
+ IBinder getToken() { return token.get(); }
+ public void binderDied() {
+ mClients.remove(token);
+ this.token = null;
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ if (token != null) {
+ if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
+ mClients.remove(token);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+
+ public FingerprintService(Context context) {
+ super(context);
+ mContext = context;
+ nativeInit(this);
+ }
+
+ // TODO: Move these into separate process
+ // JNI methods to communicate from FingerprintManagerService to HAL
+ native int nativeEnroll(int timeout);
+ native int nativeEnrollCancel();
+ native int nativeRemove(int fingerprintId);
+ native int nativeOpenHal();
+ native int nativeCloseHal();
+ native void nativeInit(FingerprintService service);
+
+ // JNI methods for communicating from HAL to clients
+ void notify(int msg, int arg1, int arg2) {
+ mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget();
+ }
+
+ void handleNotify(int msg, int arg1, int arg2) {
+ Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")");
+ for (int i = 0; i < mClients.size(); i++) {
+ ClientData clientData = mClients.valueAt(i);
+ if (clientData == null || clientData.receiver == null) {
+ if (DEBUG) Slog.v(TAG, "clientData at " + i + " is invalid!!");
+ continue;
+ }
+ switch (msg) {
+ case FingerprintManager.FINGERPRINT_ERROR: {
+ final int error = arg1;
+ try {
+ clientData.receiver.onError(error);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ }
+ break;
+ case FingerprintManager.FINGERPRINT_ACQUIRED: {
+ final int acquireInfo = arg1;
+ try {
+ clientData.receiver.onAcquired(acquireInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ break;
+ }
+ case FingerprintManager.FINGERPRINT_PROCESSED: {
+ final int fingerId = arg1;
+ try {
+ clientData.receiver.onProcessed(fingerId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ break;
+ }
+ case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
+ final int fingerId = arg1;
+ final int remaining = arg2;
+ if (clientData.state == STATE_ENROLLING) {
+ // Only send enroll updates to clients that are actually enrolling
+ try {
+ clientData.receiver.onEnrollResult(fingerId, remaining);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ // Update the database with new finger id.
+ // TODO: move to client code (Settings)
+ if (remaining == 0) {
+ FingerprintUtils.addFingerprintIdForUser(fingerId,
+ mContext.getContentResolver(), clientData.userId);
+ clientData.state = STATE_IDLE; // Nothing left to do
+ }
+ } else {
+ if (DEBUG) Slog.w(TAG, "Client not enrolling");
+ break;
+ }
+ break;
+ }
+ case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
+ int fingerId = arg1;
+ if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
+ FingerprintUtils.removeFingerprintIdForUser(fingerId,
+ mContext.getContentResolver(), clientData.userId);
+ if (clientData.receiver != null) {
+ try {
+ clientData.receiver.onRemoved(fingerId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ }
+ clientData.state = STATE_LISTENING;
+ }
+ break;
+ }
+ }
+ }
+
+ void startEnroll(IBinder token, long timeout, int userId) {
+ ClientData clientData = mClients.get(token);
+ if (clientData != null) {
+ if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+ clientData.state = STATE_ENROLLING;
+ nativeEnroll((int) (timeout / MS_PER_SEC));
+ } else {
+ Slog.w(TAG, "enroll(): No listener registered");
+ }
+ }
+
+ void startEnrollCancel(IBinder token, int userId) {
+ ClientData clientData = mClients.get(token);
+ if (clientData != null) {
+ if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+ clientData.state = STATE_LISTENING;
+ nativeEnrollCancel();
+ } else {
+ Slog.w(TAG, "enrollCancel(): No listener registered");
+ }
+ }
+
+ // Remove all fingerprints for the given user.
+ void startRemove(IBinder token, int fingerId, int userId) {
+ ClientData clientData = mClients.get(token);
+ if (clientData != null) {
+ if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+ clientData.state = STATE_REMOVING;
+ // The fingerprint id will be removed when we get confirmation from the HAL
+ int result = nativeRemove(fingerId);
+ if (result != 0) {
+ Slog.w(TAG, "Error removing fingerprint with id = " + fingerId);
+ }
+ } else {
+ Slog.w(TAG, "remove(" + token + "): No listener registered");
+ }
+ }
+
+ void addListener(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
+ if (DEBUG) Slog.v(TAG, "startListening(" + receiver + ")");
+ if (mClients.get(token) == null) {
+ ClientData clientData = new ClientData();
+ clientData.state = STATE_LISTENING;
+ clientData.receiver = receiver;
+ clientData.userId = userId;
+ clientData.tokenWatcher = new TokenWatcher(token);
+ try {
+ token.linkToDeath(clientData.tokenWatcher, 0);
+ mClients.put(token, clientData);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+ }
+ } else {
+ if (DEBUG) Slog.v(TAG, "listener already registered for " + token);
+ }
+ }
+
+ void removeListener(IBinder token, int userId) {
+ if (DEBUG) Slog.v(TAG, "stopListening(" + token + ")");
+ ClientData clientData = mClients.get(token);
+ if (clientData != null) {
+ token.unlinkToDeath(clientData.tokenWatcher, 0);
+ mClients.remove(token);
+ } else {
+ if (DEBUG) Slog.v(TAG, "listener not registered: " + token);
+ }
+ mClients.remove(token);
+ }
+
+ void checkPermission(String permisison) {
+ // TODO
+ }
+
+ private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+ @Override // Binder call
+ public void enroll(IBinder token, long timeout, int userId) {
+ checkPermission(ENROLL_FINGERPRINT);
+ startEnroll(token, timeout, userId);
+ }
+
+ @Override // Binder call
+ public void enrollCancel(IBinder token,int userId) {
+ checkPermission(ENROLL_FINGERPRINT);
+ startEnrollCancel(token, userId);
+ }
+
+ @Override // Binder call
+ public void remove(IBinder token, int fingerprintId, int userId) {
+ checkPermission(ENROLL_FINGERPRINT); // TODO: Maybe have another permission
+ startRemove(token, fingerprintId, userId);
+ }
+
+ @Override // Binder call
+ public void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId)
+ {
+ checkPermission(USE_FINGERPRINT);
+ addListener(token, receiver, userId);
+ }
+
+ @Override // Binder call
+ public void stopListening(IBinder token, int userId) {
+ checkPermission(USE_FINGERPRINT);
+ removeListener(token, userId);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
+ nativeOpenHal();
+ }
+
+}
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index bcd08ebf..74eaf2a 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -26,31 +26,29 @@
/**
* Handles CEC command <Active Source>.
- *
- * <p>Used by feature actions that need to handle the command in their flow.
+ * <p>
+ * Used by feature actions that need to handle the command in their flow.
*/
final class ActiveSourceHandler {
private static final String TAG = "ActiveSourceHandler";
+ private final HdmiCecLocalDevice mSource;
private final HdmiControlService mService;
- private final int mSourceAddress;
- private final int mSourcePath;
- @Nullable private final IHdmiControlCallback mCallback;
+ @Nullable
+ private final IHdmiControlCallback mCallback;
- static ActiveSourceHandler create(HdmiControlService service, int sourceAddress,
- int sourcePath, IHdmiControlCallback callback) {
- if (service == null) {
+ static ActiveSourceHandler create(HdmiCecLocalDevice source,
+ IHdmiControlCallback callback) {
+ if (source == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback);
+ return new ActiveSourceHandler(source, callback);
}
- private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath,
- IHdmiControlCallback callback) {
- mService = service;
- mSourceAddress = sourceAddress;
- mSourcePath = sourcePath;
+ private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
+ mSource = source;
+ mService = mSource.getService();
mCallback = callback;
}
@@ -61,7 +59,7 @@
* @param routingPath routing path of the device to be the active source
*/
void process(int deviceLogicalAddress, int routingPath) {
- if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) {
+ if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) {
invokeCallback(HdmiCec.RESULT_SUCCESS);
return;
}
@@ -69,14 +67,14 @@
if (device == null) {
// "New device action" initiated by <Active Source> does not require
// "Routing change action".
- mService.addAndStartAction(new NewDeviceAction(mService, mSourceAddress,
- deviceLogicalAddress, routingPath, false));
+ mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress,
+ routingPath, false));
}
- if (!mService.isInPresetInstallationMode()) {
- int prevActiveInput = mService.getActiveInput();
- mService.updateActiveDevice(deviceLogicalAddress, routingPath);
- if (prevActiveInput != mService.getActiveInput()) {
+ if (!mSource.isInPresetInstallationMode()) {
+ int prevActiveInput = mSource.getActivePortId();
+ mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
+ if (prevActiveInput != mSource.getActivePortId()) {
// TODO: change port input here.
}
invokeCallback(HdmiCec.RESULT_SUCCESS);
@@ -84,24 +82,33 @@
// TV is in a mode that should keep its current source/input from
// being changed for its operation. Reclaim the active source
// or switch the port back to the one used for the current mode.
- if (mService.getActiveSource() == mSourceAddress) {
+ if (mSource.getActiveSource() == getSourceAddress()) {
HdmiCecMessage activeSource =
- HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath);
+ HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(),
+ getSourcePath());
mService.sendCecCommand(activeSource);
- mService.updateActiveDevice(deviceLogicalAddress, routingPath);
+ mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
invokeCallback(HdmiCec.RESULT_SUCCESS);
} else {
- int activePath = mService.getActivePath();
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
+ int activePath = mSource.getActivePath();
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
routingPath, activePath));
// TODO: Start port select action here
- // PortSelectAction action = new PortSelectAction(mService, mSourceAddress,
- // activePath, mCallback);
+ // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(),
+ // activePath, mCallback);
// mService.addActionAndStart(action);
}
}
}
+ private final int getSourceAddress() {
+ return mSource.getDeviceInfo().getLogicalAddress();
+ }
+
+ private final int getSourcePath() {
+ return mSource.getDeviceInfo().getPhysicalAddress();
+ }
+
private void invokeCallback(int result) {
if (mCallback == null) {
return;
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index f7392e9..02f088f 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -94,12 +94,10 @@
/**
* Constructor.
*
- * @param service an instance of {@link HdmiControlService}.
- * @param sourceAddress a logical address which initiates this action
+ * @param source an instance of {@link HdmiCecLocalDevice}.
*/
- DeviceDiscoveryAction(HdmiControlService service, int sourceAddress,
- DeviceDiscoveryCallback callback) {
- super(service, sourceAddress);
+ DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
+ super(source);
mCallback = Preconditions.checkNotNull(callback);
}
@@ -108,7 +106,7 @@
mDevices.clear();
mState = STATE_WAITING_FOR_DEVICE_POLLING;
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
if (ackedAddress.isEmpty()) {
@@ -121,7 +119,8 @@
allocateDevices(ackedAddress);
startPhysicalAddressStage();
}
- }, HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES, DEVICE_POLLING_RETRY);
+ }, HdmiConstants.POLL_ITERATION_REVERSE_ORDER
+ | HdmiConstants.POLL_STRATEGY_REMOTES_DEVICES, DEVICE_POLLING_RETRY);
return true;
}
@@ -156,7 +155,7 @@
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, address));
+ sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
@@ -179,7 +178,7 @@
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_SET_OSD_NAME)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, address));
+ sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
@@ -203,12 +202,13 @@
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, address));
+ sendCommand(
+ HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
private boolean mayProcessMessageIfCached(int address, int opcode) {
- HdmiCecMessage message = mService.getCecMessageCache().getMessage(address, opcode);
+ HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
if (message != null) {
processCommand(message);
return true;
diff --git a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
index 63c2182..51df473 100644
--- a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
@@ -16,9 +16,10 @@
* limitations under the License.
*/
-import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -40,18 +41,18 @@
private final int mTargetAddress;
private final IHdmiControlCallback mCallback;
- static DevicePowerStatusAction create(HdmiControlService service, int sourceAddress,
+ static DevicePowerStatusAction create(HdmiCecLocalDevice source,
int targetAddress, IHdmiControlCallback callback) {
- if (service == null || callback == null) {
+ if (source == null || callback == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new DevicePowerStatusAction(service, sourceAddress, targetAddress, callback);
+ return new DevicePowerStatusAction(source, targetAddress, callback);
}
- private DevicePowerStatusAction(HdmiControlService service, int sourceAddress,
+ private DevicePowerStatusAction(HdmiCecLocalDevice localDevice,
int targetAddress, IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ super(localDevice);
mTargetAddress = targetAddress;
mCallback = callback;
}
@@ -65,8 +66,8 @@
}
private void queryDevicePowerStatus() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ mTargetAddress));
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index a8696a1..dbe3b80 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -16,10 +16,11 @@
package com.android.server.hdmi;
-import android.hardware.hdmi.IHdmiControlCallback;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiTvClient;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -66,24 +67,20 @@
private final HdmiCecDeviceInfo mTarget;
private final IHdmiControlCallback mCallback;
- private final int mSourcePath;
private int mPowerStatusCounter = 0;
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address of TV initiating this action
- * @param sourcePath physical address of TV
+ * @param source {@link HdmiCecLocalDevice} instance
* @param target target logical device that will be a new active source
* @param callback callback object
*/
- public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath,
+ public DeviceSelectAction(HdmiCecLocalDevice source,
HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ super(source);
mCallback = callback;
- mSourcePath = sourcePath;
mTarget = target;
}
@@ -96,7 +93,7 @@
private void queryDevicePowerStatus() {
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mSourceAddress, mTarget.getLogicalAddress()));
+ getSourceAddress(), mTarget.getLogicalAddress()));
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, TIMEOUT_MS);
}
@@ -118,7 +115,8 @@
case STATE_WAIT_FOR_ACTIVE_SOURCE:
if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
int activePath = HdmiUtils.twoBytesToInt(params);
- ActiveSourceHandler.create(mService, mSourceAddress, mSourcePath, mCallback)
+ ActiveSourceHandler
+ .create(localDevice(), mCallback)
.process(cmd.getSource(), activePath);
finish();
return true;
@@ -174,15 +172,15 @@
private void sendSetStreamPath() {
sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(
- mSourceAddress, mTarget.getPhysicalAddress()));
+ getSourceAddress(), mTarget.getPhysicalAddress()));
mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS);
}
private void sendRemoteKeyCommand(int keyCode) {
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
mTarget.getLogicalAddress(), keyCode));
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
mTarget.getLogicalAddress()));
}
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
index ae272b4..0ec17f6 100644
--- a/services/core/java/com/android/server/hdmi/FeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -22,6 +22,9 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import java.util.List;
/**
* Encapsulates a sequence of CEC/MHL command exchange for a certain feature.
@@ -33,14 +36,13 @@
* of the object. All the actual action classes inherit FeatureAction.
*
* <p>More than one FeatureAction objects can be up and running simultaneously,
- * maintained by {@link HdmiControlService}. Each action is passed a new command
+ * maintained by {@link HdmiCecLocalDevice}. Each action is passed a new command
* arriving from the bus, and either consumes it if the command is what the action expects,
* or yields it to other action.
*
* Declared as package private, accessed by {@link HdmiControlService} only.
*/
abstract class FeatureAction {
-
private static final String TAG = "FeatureAction";
// Timer handler message used for timeout event
@@ -56,19 +58,16 @@
// Internal state indicating the progress of action.
protected int mState = STATE_NONE;
- protected final HdmiControlService mService;
-
- // Logical address of the device for which the feature action is taken. The commands
- // generated in an action all use this field as source address.
- protected final int mSourceAddress;
+ private final HdmiControlService mService;
+ private final HdmiCecLocalDevice mSource;
// Timer that manages timeout events.
protected ActionTimer mActionTimer;
- FeatureAction(HdmiControlService service, int sourceAddress) {
- mService = service;
- mSourceAddress = sourceAddress;
- mActionTimer = createActionTimer(service.getServiceLooper());
+ FeatureAction(HdmiCecLocalDevice source) {
+ mSource = source;
+ mService = mSource.getService();
+ mActionTimer = createActionTimer(mService.getServiceLooper());
}
@VisibleForTesting
@@ -175,6 +174,42 @@
mService.sendCecCommand(cmd, callback);
}
+ protected final void addAndStartAction(FeatureAction action) {
+ mSource.addAndStartAction(action);
+ }
+
+ protected final <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+ return mSource.getActions(clazz);
+ }
+
+ protected final HdmiCecMessageCache getCecMessageCache() {
+ return mSource.getCecMessageCache();
+ }
+
+ /**
+ * Remove the action from the action queue. This is called after the action finishes
+ * its role.
+ *
+ * @param action
+ */
+ protected final void removeAction(FeatureAction action) {
+ mSource.removeAction(action);
+ }
+
+ protected final <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ mSource.removeActionExcept(clazz, null);
+ }
+
+ protected final <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ mSource.removeActionExcept(clazz, exception);
+ }
+
+ protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
+ int retryCount) {
+ mService.pollDevices(callback, pickStrategy, retryCount);
+ }
+
/**
* Clean up action's state.
*
@@ -194,13 +229,23 @@
removeAction(this);
}
- /**
- * Remove the action from the action queue. This is called after the action finishes
- * its role.
- *
- * @param action
- */
- private void removeAction(FeatureAction action) {
- mService.removeAction(action);
+ protected final HdmiCecLocalDevice localDevice() {
+ return mSource;
+ }
+
+ protected final HdmiCecLocalDevicePlayback playback() {
+ return (HdmiCecLocalDevicePlayback) mSource;
+ }
+
+ protected final HdmiCecLocalDeviceTv tv() {
+ return (HdmiCecLocalDeviceTv) mSource;
+ }
+
+ protected final int getSourceAddress() {
+ return mSource.getDeviceInfo().getLogicalAddress();
+ }
+
+ protected final int getSourcePath() {
+ return mSource.getDeviceInfo().getPhysicalAddress();
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index a0c635d..6d05e82 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -17,7 +17,6 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Handler;
@@ -109,10 +108,6 @@
private HdmiControlService mService;
- // Map-like container of all cec devices including local ones.
- // A logical address of device is used as key of container.
- private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
-
// Stores the local CEC devices in the system. Device type is used for key.
private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
@@ -224,90 +219,6 @@
return body;
}
- /**
- * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
- * logical address as new device info's.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
- * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
- * that has the same logical address as new one has.
- */
- HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
- if (oldDeviceInfo != null) {
- removeDeviceInfo(deviceInfo.getLogicalAddress());
- }
- mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
- return oldDeviceInfo;
- }
-
- /**
- * Remove a device info corresponding to the given {@code logicalAddress}.
- * It returns removed {@link HdmiCecDeviceInfo} if exists.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param logicalAddress logical address of device to be removed
- * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
- */
- HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
- if (deviceInfo != null) {
- mDeviceInfos.remove(logicalAddress);
- }
- return deviceInfo;
- }
-
- /**
- * Clear all device info.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- */
- void clearDeviceInfoList() {
- assertRunOnServiceThread();
- mDeviceInfos.clear();
- }
-
- /**
- * Return a list of all {@link HdmiCecDeviceInfo}.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param includeLocalDevice whether to add local device or not
- */
- List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
- assertRunOnServiceThread();
- if (includeLocalDevice) {
- return sparseArrayToList(mDeviceInfos);
- } else {
- ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
- for (int i = 0; i < mDeviceInfos.size(); ++i) {
- HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
- if (mRemoteDeviceAddressPredicate.apply(info.getLogicalAddress())) {
- infoList.add(info);
- }
- }
- return infoList;
- }
- }
-
- /**
- * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param logicalAddress logical address to be retrieved
- * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
- * Returns null if no logical address matched
- */
- HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- return mDeviceInfos.get(logicalAddress);
- }
HdmiPortInfo[] getPortInfos() {
return nativeGetPortInfos(mNativePtr);
@@ -451,33 +362,33 @@
*/
List<HdmiCecLocalDevice> getLocalDeviceList() {
assertRunOnServiceThread();
- return sparseArrayToList(mLocalDevices);
+ return HdmiUtils.sparseArrayToList(mLocalDevices);
}
private List<Integer> pickPollCandidates(int pickStrategy) {
- int strategy = pickStrategy & HdmiControlService.POLL_STRATEGY_MASK;
+ int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK;
Predicate<Integer> pickPredicate = null;
switch (strategy) {
- case HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO:
+ case HdmiConstants.POLL_STRATEGY_SYSTEM_AUDIO:
pickPredicate = mSystemAudioAddressPredicate;
break;
- case HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES:
+ case HdmiConstants.POLL_STRATEGY_REMOTES_DEVICES:
default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
pickPredicate = mRemoteDeviceAddressPredicate;
break;
}
- int iterationStrategy = pickStrategy & HdmiControlService.POLL_ITERATION_STRATEGY_MASK;
+ int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK;
ArrayList<Integer> pollingCandidates = new ArrayList<>();
switch (iterationStrategy) {
- case HdmiControlService.POLL_ITERATION_IN_ORDER:
+ case HdmiConstants.POLL_ITERATION_IN_ORDER:
for (int i = HdmiCec.ADDR_TV; i <= HdmiCec.ADDR_SPECIFIC_USE; ++i) {
if (pickPredicate.apply(i)) {
pollingCandidates.add(i);
}
}
break;
- case HdmiControlService.POLL_ITERATION_REVERSE_ORDER:
+ case HdmiConstants.POLL_ITERATION_REVERSE_ORDER:
default: // The default is reverse order.
for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
if (pickPredicate.apply(i)) {
@@ -489,14 +400,6 @@
return pollingCandidates;
}
- private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
- ArrayList<T> list = new ArrayList<>();
- for (int i = 0; i < array.size(); ++i) {
- list.add(array.valueAt(i));
- }
- return list;
- }
-
private boolean isAllocatedLocalDeviceAddress(int address) {
for (int i = 0; i < mLocalDevices.size(); ++i) {
if (mLocalDevices.valueAt(i).isAddressOf(address)) {
@@ -539,7 +442,7 @@
// new logical address for the device because no device uses
// it as logical address of the device.
if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY)
- == HdmiControlService.SEND_RESULT_SUCCESS) {
+ == HdmiConstants.SEND_RESULT_SUCCESS) {
return true;
}
}
@@ -606,7 +509,7 @@
byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
cecMessage.getDestination(), body);
- if (error != HdmiControlService.SEND_RESULT_SUCCESS) {
+ if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
Slog.w(TAG, "Failed to send " + cecMessage);
}
if (callback != null) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
similarity index 99%
rename from services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java
rename to services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index ebb6f50..7080a56 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -21,7 +21,7 @@
/**
* Helper class to translate android keycode to hdmi cec keycode and vice versa.
*/
-public class HdmiCecKeycodeTranslator {
+public class HdmiCecKeycode {
public static final int UNSUPPORTED_KEYCODE = -1;
public static final int NO_PARAM = -1;
@@ -151,7 +151,7 @@
public static final int UI_SOUND_PRESENTATION_TREBLE_NEUTRAL = 0xC2;
public static final int UI_SOUND_PRESENTATION_TREBLE_STEP_MINUS = 0xC3;
- private HdmiCecKeycodeTranslator() {
+ private HdmiCecKeycode() {
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 7a2a6cc..f86d655a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -19,8 +19,16 @@
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.os.Looper;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
/**
* Class that models a logical CEC device hosted in this system. Handles initialization,
* CEC commands that call for actions customized per device type.
@@ -34,10 +42,37 @@
protected int mPreferredAddress;
protected HdmiCecDeviceInfo mDeviceInfo;
+ // Logical address of the active source.
+ @GuardedBy("mLock")
+ private int mActiveSource;
+
+ // Active routing path. Physical address of the active source but not all the time, such as
+ // when the new active source does not claim itself to be one. Note that we don't keep
+ // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
+ @GuardedBy("mLock")
+ private int mActiveRoutingPath;
+
+ // Set to true while the service is in normal mode. While set to false, no input change is
+ // allowed. Used for situations where input change can confuse users such as channel auto-scan,
+ // system upgrade, etc., a.k.a. "prohibit mode".
+ @GuardedBy("mLock")
+ private boolean mInputChangeEnabled;
+
+ protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
+ protected final Object mLock;
+
+ // A collection of FeatureAction.
+ // Note that access to this collection should happen in service thread.
+ private final LinkedList<FeatureAction> mActions = new LinkedList<>();
+
protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
mService = service;
mDeviceType = deviceType;
mAddress = HdmiCec.ADDR_UNREGISTERED;
+ mLock = service.getServiceLock();
+
+ // TODO: Get control flag from persistent storage
+ mInputChangeEnabled = true;
}
// Factory method that returns HdmiCecLocalDevice of corresponding type.
@@ -69,14 +104,23 @@
* @return true if consumed a message; otherwise, return false.
*/
final boolean dispatchMessage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
int dest = message.getDestination();
if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) {
return false;
}
+ // Cache incoming message. Note that it caches only white-listed one.
+ mCecMessageCache.cacheMessage(message);
return onMessage(message);
}
protected final boolean onMessage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
+ if (dispatchMessageToAction(message)) {
+ return true;
+ }
switch (message.getOpcode()) {
case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
return handleGetMenuLanguage(message);
@@ -90,12 +134,31 @@
return handleGetCecVersion(message);
case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
return handleReportPhysicalAddress(message);
+ case HdmiCec.MESSAGE_INITIATE_ARC:
+ return handleInitiateArc(message);
+ case HdmiCec.MESSAGE_TERMINATE_ARC:
+ return handleTerminateArc(message);
+ case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ return handleSetSystemAudioMode(message);
+ case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+ return handleSystemAudioModeStatus(message);
default:
return false;
}
}
+ private boolean dispatchMessageToAction(HdmiCecMessage message) {
+ for (FeatureAction action : mActions) {
+ if (action.processCommand(message)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
protected boolean handleGivePhysicalAddress() {
+ assertRunOnServiceThread();
+
int physicalAddress = mService.getPhysicalAddress();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, physicalAddress, mDeviceType);
@@ -104,6 +167,8 @@
}
protected boolean handleGiveDeviceVendorId() {
+ assertRunOnServiceThread();
+
int vendorId = mService.getVendorId();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, vendorId);
@@ -112,6 +177,8 @@
}
protected boolean handleGetCecVersion(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
int version = mService.getCecVersion();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
message.getSource(), version);
@@ -120,6 +187,8 @@
}
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
mService.sendCecCommand(
HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
@@ -129,6 +198,8 @@
}
protected boolean handleGiveOsdName(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
// Note that since this method is called after logical address allocation is done,
// mDeviceInfo should not be null.
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
@@ -149,34 +220,235 @@
return false;
}
+ protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleTerminateArc(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleInitiateArc(HdmiCecMessage message) {
+ return false;
+ }
+
final void handleAddressAllocated(int logicalAddress) {
+ assertRunOnServiceThread();
+
mAddress = mPreferredAddress = logicalAddress;
onAddressAllocated(logicalAddress);
}
HdmiCecDeviceInfo getDeviceInfo() {
+ assertRunOnServiceThread();
return mDeviceInfo;
}
void setDeviceInfo(HdmiCecDeviceInfo info) {
+ assertRunOnServiceThread();
mDeviceInfo = info;
}
// Returns true if the logical address is same as the argument.
boolean isAddressOf(int addr) {
+ assertRunOnServiceThread();
return addr == mAddress;
}
// Resets the logical address to unregistered(15), meaning the logical device is invalid.
void clearAddress() {
+ assertRunOnServiceThread();
mAddress = HdmiCec.ADDR_UNREGISTERED;
}
void setPreferredAddress(int addr) {
+ assertRunOnServiceThread();
mPreferredAddress = addr;
}
int getPreferredAddress() {
+ assertRunOnServiceThread();
return mPreferredAddress;
}
+
+ void addAndStartAction(final FeatureAction action) {
+ assertRunOnServiceThread();
+ mActions.add(action);
+ action.start();
+ }
+
+ // See if we have an action of a given type in progress.
+ <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
+ assertRunOnServiceThread();
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Returns all actions matched with given class type.
+ <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+ assertRunOnServiceThread();
+ ArrayList<T> actions = new ArrayList<>();
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ actions.add((T) action);
+ }
+ }
+ return actions;
+ }
+
+ /**
+ * Remove the given {@link FeatureAction} object from the action queue.
+ *
+ * @param action {@link FeatureAction} to remove
+ */
+ void removeAction(final FeatureAction action) {
+ assertRunOnServiceThread();
+ mActions.remove(action);
+ }
+
+ // Remove all actions matched with the given Class type.
+ <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ removeActionExcept(clazz, null);
+ }
+
+ // Remove all actions matched with the given Class type besides |exception|.
+ <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ assertRunOnServiceThread();
+ Iterator<FeatureAction> iter = mActions.iterator();
+ while (iter.hasNext()) {
+ FeatureAction action = iter.next();
+ if (action != exception && action.getClass().equals(clazz)) {
+ action.clear();
+ mActions.remove(action);
+ }
+ }
+ }
+
+ protected void assertRunOnServiceThread() {
+ if (Looper.myLooper() != mService.getServiceLooper()) {
+ throw new IllegalStateException("Should run on service thread.");
+ }
+ }
+
+ /**
+ * Called when a hot-plug event issued.
+ *
+ * @param portId id of port where a hot-plug event happened
+ * @param connected whether to connected or not on the event
+ */
+ void onHotplug(int portId, boolean connected) {
+ }
+
+ final HdmiControlService getService() {
+ return mService;
+ }
+
+ final boolean isConnectedToArcPort(int path) {
+ return mService.isConnectedToArcPort(path);
+ }
+
+ int getActiveSource() {
+ synchronized (mLock) {
+ return mActiveSource;
+ }
+ }
+
+ /**
+ * Returns the active routing path.
+ */
+ int getActivePath() {
+ synchronized (mLock) {
+ return mActiveRoutingPath;
+ }
+ }
+
+ /**
+ * Returns the ID of the active HDMI port. The active port is the one that has the active
+ * routing path connected to it directly or indirectly under the device hierarchy.
+ */
+ int getActivePortId() {
+ synchronized (mLock) {
+ return mService.pathToPortId(mActiveRoutingPath);
+ }
+ }
+
+ /**
+ * Update the active port.
+ *
+ * @param portId the new active port id
+ */
+ void setActivePortId(int portId) {
+ synchronized (mLock) {
+ // We update active routing path instead, since we get the active port id from
+ // the active routing path.
+ mActiveRoutingPath = mService.portIdToPath(portId);
+ }
+ }
+
+ void updateActiveDevice(int logicalAddress, int physicalAddress) {
+ synchronized (mLock) {
+ mActiveSource = logicalAddress;
+ mActiveRoutingPath = physicalAddress;
+ }
+ }
+
+ void setInputChangeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mInputChangeEnabled = enabled;
+ }
+ }
+
+ boolean isInPresetInstallationMode() {
+ synchronized (mLock) {
+ return !mInputChangeEnabled;
+ }
+ }
+
+ /**
+ * Whether the given path is located in the tail of current active path.
+ *
+ * @param path to be tested
+ * @return true if the given path is located in the tail of current active path; otherwise,
+ * false
+ */
+ // TODO: move this to local device tv.
+ boolean isTailOfActivePath(int path) {
+ synchronized (mLock) {
+ // If active routing path is internal source, return false.
+ if (mActiveRoutingPath == 0) {
+ return false;
+ }
+ for (int i = 12; i >= 0; i -= 4) {
+ int curActivePath = (mActiveRoutingPath >> i) & 0xF;
+ if (curActivePath == 0) {
+ return true;
+ } else {
+ int curPath = (path >> i) & 0xF;
+ if (curPath != curActivePath) {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ HdmiCecMessageCache getCecMessageCache() {
+ assertRunOnServiceThread();
+ return mCecMessageCache;
+ }
+
+ int pathToPortId(int newPath) {
+ assertRunOnServiceThread();
+ return mService.pathToPortId(newPath);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d79e283..01345ef 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -17,11 +17,15 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.RemoteException;
+import android.util.Slog;
/**
* Represent a logical device of type Playback residing in Android system.
*/
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
+ private static final String TAG = "HdmiCecLocalDevicePlayback";
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiCec.DEVICE_PLAYBACK);
@@ -32,4 +36,54 @@
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, mService.getPhysicalAddress(), mDeviceType));
}
+
+ void oneTouchPlay(IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (hasAction(OneTouchPlayAction.class)) {
+ Slog.w(TAG, "oneTouchPlay already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+
+ // TODO: Consider the case of multiple TV sets. For now we always direct the command
+ // to the primary one.
+ OneTouchPlayAction action = OneTouchPlayAction.create(this, HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate oneTouchPlay");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
+ }
+
+ void queryDisplayStatus(IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (hasAction(DevicePowerStatusAction.class)) {
+ Slog.w(TAG, "queryDisplayStatus already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+ DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
+ HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate queryDisplayStatus");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
+ }
+
+ private void invokeCallback(IHdmiControlCallback callback, int result) {
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
+
+ @Override
+ void onHotplug(int portId, boolean connected) {
+ // TODO: clear devices connected to the given port id.
+ mCecMessageCache.flushAll();
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 625b256..52c092c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -22,9 +22,12 @@
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -35,12 +38,27 @@
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDeviceTv";
+ // Whether ARC is "enabled" or not.
+ @GuardedBy("mLock")
+ private boolean mArcStatusEnabled = false;
+
+ @GuardedBy("mLock")
+ // Whether SystemAudioMode is "On" or not.
+ private boolean mSystemAudioMode;
+
+ // Map-like container of all cec devices including local ones.
+ // A logical address of device is used as key of container.
+ private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
+
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiCec.DEVICE_TV);
+
+ // TODO: load system audio mode and set it to mSystemAudioMode.
}
@Override
protected void onAddressAllocated(int logicalAddress) {
+ assertRunOnServiceThread();
// TODO: vendor-specific initialization here.
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
@@ -59,17 +77,74 @@
* @param callback callback object to report the result with
*/
void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
- HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress);
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo targetDevice = getDeviceInfo(targetAddress);
if (targetDevice == null) {
invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
return;
}
- mService.removeAction(DeviceSelectAction.class);
- mService.addAndStartAction(new DeviceSelectAction(mService, mAddress,
- mService.getPhysicalAddress(), targetDevice, callback));
+ removeAction(DeviceSelectAction.class);
+ addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
+ }
+
+ /**
+ * Performs the action routing control.
+ *
+ * @param portId new HDMI port to route to
+ * @param callback callback object to report the result with
+ */
+ void portSelect(int portId, IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (isInPresetInstallationMode()) {
+ invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE);
+ return;
+ }
+ // Make sure this call does not stem from <Active Source> message reception, in
+ // which case the two ports will be the same.
+ if (portId == getActivePortId()) {
+ invokeCallback(callback, HdmiCec.RESULT_SUCCESS);
+ return;
+ }
+ setActivePortId(portId);
+
+ // TODO: Return immediately if the operation is triggered by <Text/Image View On>
+ // TODO: Handle invalid port id / active input which should be treated as an
+ // internal tuner.
+
+ removeAction(RoutingControlAction.class);
+
+ int oldPath = mService.portIdToPath(mService.portIdToPath(getActivePortId()));
+ int newPath = mService.portIdToPath(portId);
+ HdmiCecMessage routingChange =
+ HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
+ mService.sendCecCommand(routingChange);
+ addAndStartAction(new RoutingControlAction(this, newPath, callback));
+ }
+
+ /**
+ * Sends key to a target CEC device.
+ *
+ * @param keyCode key code to send. Defined in {@link KeyEvent}.
+ * @param isPressed true if this is keypress event
+ */
+ void sendKeyEvent(int keyCode, boolean isPressed) {
+ assertRunOnServiceThread();
+ List<SendKeyAction> action = getActions(SendKeyAction.class);
+ if (!action.isEmpty()) {
+ action.get(0).processKeyEvent(keyCode, isPressed);
+ } else {
+ if (isPressed) {
+ addAndStartAction(new SendKeyAction(this, getActiveSource(), keyCode));
+ } else {
+ Slog.w(TAG, "Discard key release event");
+ }
+ }
}
private static void invokeCallback(IHdmiControlCallback callback, int result) {
+ if (callback == null) {
+ return;
+ }
try {
callback.onComplete(result);
} catch (RemoteException e) {
@@ -79,6 +154,7 @@
@Override
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
mAddress, Locale.getDefault().getISO3Language());
// TODO: figure out how to handle failed to get language code.
@@ -92,8 +168,9 @@
@Override
protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+ assertRunOnServiceThread();
// Ignore if [Device Discovery Action] is going on.
- if (mService.hasAction(DeviceDiscoveryAction.class)) {
+ if (hasAction(DeviceDiscoveryAction.class)) {
Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
+ "because Device Discovery Action is on-going:" + message);
return true;
@@ -104,16 +181,16 @@
// If it is a new device and connected to the tail of active path,
// it's required to change routing path.
- boolean requireRoutingChange = !mService.isInDeviceList(physicalAddress, logicalAddress)
- && mService.isTailOfActivePath(physicalAddress);
- mService.addAndStartAction(new NewDeviceAction(mService,
- mAddress, message.getSource(), physicalAddress,
+ boolean requireRoutingChange = !isInDeviceList(physicalAddress, logicalAddress)
+ && isTailOfActivePath(physicalAddress);
+ addAndStartAction(new NewDeviceAction(this, message.getSource(), physicalAddress,
requireRoutingChange));
return true;
}
@Override
protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
+ assertRunOnServiceThread();
List<VendorSpecificAction> actions = Collections.emptyList();
// TODO: Call mService.getActions(VendorSpecificAction.class) to get all the actions.
@@ -138,27 +215,321 @@
}
private void launchDeviceDiscovery() {
- mService.clearAllDeviceInfo();
- // TODO: Move the following callback to HdmiLocalDeviceTv.
- DeviceDiscoveryAction action = new DeviceDiscoveryAction(mService, mAddress,
+ assertRunOnServiceThread();
+ clearDeviceInfoList();
+ DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
new DeviceDiscoveryCallback() {
@Override
public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
for (HdmiCecDeviceInfo info : deviceInfos) {
- mService.addCecDevice(info);
+ addCecDevice(info);
}
// Since we removed all devices when it's start and
// device discovery action does not poll local devices,
// we should put device info of local device manually here
for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
- mService.addCecDevice(device.getDeviceInfo());
+ addCecDevice(device.getDeviceInfo());
}
- mService.addAndStartAction(new HotplugDetectionAction(mService,
- mAddress));
+ addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
+
+ // If there is AVR, initiate System Audio Auto initiation action,
+ // which turns on and off system audio according to last system
+ // audio setting.
+ HdmiCecDeviceInfo avrInfo = getAvrDeviceInfo();
+ if (avrInfo != null) {
+ addAndStartAction(new SystemAudioAutoInitiationAction(
+ HdmiCecLocalDeviceTv.this, avrInfo.getLogicalAddress()));
+ }
}
});
- mService.addAndStartAction(action);
+ addAndStartAction(action);
+ }
+
+ // Clear all device info.
+ private void clearDeviceInfoList() {
+ assertRunOnServiceThread();
+ mDeviceInfos.clear();
+ }
+
+ void setSystemAudioMode(boolean on) {
+ synchronized (mLock) {
+ if (on != mSystemAudioMode) {
+ mSystemAudioMode = on;
+ // TODO: Need to set the preference for SystemAudioMode.
+ // TODO: Need to handle the notification of changing the mode and
+ // to identify the notification should be handled in the service or TvSettings.
+ }
+ }
+ }
+
+ boolean getSystemAudioMode() {
+ synchronized (mLock) {
+ assertRunOnServiceThread();
+ return mSystemAudioMode;
+ }
+ }
+
+ /**
+ * Change ARC status into the given {@code enabled} status.
+ *
+ * @return {@code true} if ARC was in "Enabled" status
+ */
+ boolean setArcStatus(boolean enabled) {
+ synchronized (mLock) {
+ boolean oldStatus = mArcStatusEnabled;
+ // 1. Enable/disable ARC circuit.
+ mService.setAudioReturnChannel(enabled);
+
+ // TODO: notify arc mode change to AudioManager.
+
+ // 2. Update arc status;
+ mArcStatusEnabled = enabled;
+ return oldStatus;
+ }
+ }
+
+ /**
+ * Returns whether ARC is enabled or not.
+ */
+ boolean getArcStatus() {
+ synchronized (mLock) {
+ return mArcStatusEnabled;
+ }
+ }
+
+ void setAudioStatus(boolean mute, int volume) {
+ mService.setAudioStatus(mute, volume);
+ }
+
+ @Override
+ protected boolean handleInitiateArc(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // In case where <Initiate Arc> is started by <Request ARC Initiation>
+ // need to clean up RequestArcInitiationAction.
+ removeAction(RequestArcInitiationAction.class);
+ SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
+ message.getSource(), true);
+ addAndStartAction(action);
+ return true;
+ }
+
+ @Override
+ protected boolean handleTerminateArc(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // In case where <Terminate Arc> is started by <Request ARC Termination>
+ // need to clean up RequestArcInitiationAction.
+ // TODO: check conditions of power status by calling is_connected api
+ // to be added soon.
+ removeAction(RequestArcTerminationAction.class);
+ SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
+ message.getSource(), false);
+ addAndStartAction(action);
+ return true;
+ }
+
+ @Override
+ protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ if (!isMessageForSystemAudio(message)) {
+ return false;
+ }
+ SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
+ message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ addAndStartAction(action);
+ return true;
+ }
+
+ @Override
+ protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ if (!isMessageForSystemAudio(message)) {
+ return false;
+ }
+ setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ return true;
+ }
+
+ private boolean isMessageForSystemAudio(HdmiCecMessage message) {
+ if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
+ || message.getDestination() != HdmiCec.ADDR_TV
+ || getAvrDeviceInfo() == null) {
+ Slog.w(TAG, "Skip abnormal CecMessage: " + message);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
+ * logical address as new device info's.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
+ * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
+ * that has the same logical address as new one has.
+ */
+ HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
+ if (oldDeviceInfo != null) {
+ removeDeviceInfo(deviceInfo.getLogicalAddress());
+ }
+ mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
+ return oldDeviceInfo;
+ }
+
+ /**
+ * Remove a device info corresponding to the given {@code logicalAddress}.
+ * It returns removed {@link HdmiCecDeviceInfo} if exists.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param logicalAddress logical address of device to be removed
+ * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
+ */
+ HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
+ if (deviceInfo != null) {
+ mDeviceInfos.remove(logicalAddress);
+ }
+ return deviceInfo;
+ }
+
+ /**
+ * Return a list of all {@link HdmiCecDeviceInfo}.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ */
+ List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includelLocalDevice) {
+ assertRunOnServiceThread();
+ if (includelLocalDevice) {
+ return HdmiUtils.sparseArrayToList(mDeviceInfos);
+ } else {
+
+ ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
+ for (int i = 0; i < mDeviceInfos.size(); ++i) {
+ HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
+ if (!isLocalDeviceAddress(info.getLogicalAddress())) {
+ infoList.add(info);
+ }
+ }
+ return infoList;
+ }
+ }
+
+ private boolean isLocalDeviceAddress(int address) {
+ assertRunOnServiceThread();
+ for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
+ if (device.isAddressOf(address)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param logicalAddress logical address to be retrieved
+ * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
+ * Returns null if no logical address matched
+ */
+ HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
+ return mDeviceInfos.get(logicalAddress);
+ }
+
+ HdmiCecDeviceInfo getAvrDeviceInfo() {
+ assertRunOnServiceThread();
+ return getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+ }
+
+ /**
+ * Called when a device is newly added or a new device is detected.
+ *
+ * @param info device info of a new device.
+ */
+ final void addCecDevice(HdmiCecDeviceInfo info) {
+ assertRunOnServiceThread();
+ addDeviceInfo(info);
+ if (info.getLogicalAddress() == mAddress) {
+ // The addition of TV device itself should not be notified.
+ return;
+ }
+ mService.invokeDeviceEventListeners(info, true);
+ }
+
+ /**
+ * Called when a device is removed or removal of device is detected.
+ *
+ * @param address a logical address of a device to be removed
+ */
+ final void removeCecDevice(int address) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo info = removeDeviceInfo(address);
+ mCecMessageCache.flushMessagesFrom(address);
+ mService.invokeDeviceEventListeners(info, false);
+ }
+
+ /**
+ * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
+ * the given routing path. CEC devices use routing path for its physical address to
+ * describe the hierarchy of the devices in the network.
+ *
+ * @param path routing path or physical address
+ * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
+ */
+ final HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
+ assertRunOnServiceThread();
+ for (HdmiCecDeviceInfo info : getDeviceInfoList(false)) {
+ if (info.getPhysicalAddress() == path) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Whether a device of the specified physical address and logical address exists
+ * in a device info list. However, both are minimal condition and it could
+ * be different device from the original one.
+ *
+ * @param physicalAddress physical address of a device to be searched
+ * @param logicalAddress logical address of a device to be searched
+ * @return true if exist; otherwise false
+ */
+ boolean isInDeviceList(int physicalAddress, int logicalAddress) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo device = getDeviceInfo(logicalAddress);
+ if (device == null) {
+ return false;
+ }
+ return device.getPhysicalAddress() == physicalAddress;
+ }
+
+ @Override
+ void onHotplug(int portNo, boolean connected) {
+ assertRunOnServiceThread();
+ // TODO: delegate onHotplug event to each local device.
+
+ // Tv device will have permanent HotplugDetectionAction.
+ List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
+ if (!hotplugActions.isEmpty()) {
+ // Note that hotplug action is single action running on a machine.
+ // "pollAllDevicesNow" cleans up timer and start poll action immediately.
+ hotplugActions.get(0).pollAllDevicesNow();
+ }
+ }
+
+ boolean canChangeSystemAudio() {
+ // TODO: implement this.
+ // return true if no system audio control sequence is running.
+ return false;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 8dbfd85..361a063 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -377,6 +377,17 @@
return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED);
}
+ /**
+ * Build <Give System Audio Mode Status> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildGiveSystemAudioModeStatus(int src, int dest) {
+ return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
+ }
+
/***** Please ADD new buildXXX() methods above. ******/
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
index b558f2b..5294506 100644
--- a/services/core/java/com/android/server/hdmi/HdmiConstants.java
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java
@@ -80,5 +80,22 @@
static final int INVALID_PORT_ID = -1;
static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+ // Send result codes.
+ static final int SEND_RESULT_SUCCESS = 0;
+ static final int SEND_RESULT_NAK = -1;
+ static final int SEND_RESULT_FAILURE = -2;
+
+ // Strategy for device polling.
+ // Should use "OR(|) operation of POLL_STRATEGY_XXX and POLL_ITERATION_XXX.
+ static final int POLL_STRATEGY_MASK = 0x3; // first and second bit.
+ static final int POLL_STRATEGY_REMOTES_DEVICES = 0x1;
+ static final int POLL_STRATEGY_SYSTEM_AUDIO = 0x2;
+
+ static final int POLL_ITERATION_STRATEGY_MASK = 0x30000; // first and second bit.
+ static final int POLL_ITERATION_IN_ORDER = 0x10000;
+ static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
+
+ static final int UNKNOWN_VOLUME = -1;
+
private HdmiConstants() { /* cannot be instantiated */ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index eb356e9..d323f34 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -43,8 +43,6 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
/**
@@ -57,18 +55,6 @@
// TODO: Rename the permission to HDMI_CONTROL.
private static final String PERMISSION = "android.permission.HDMI_CEC";
- static final int SEND_RESULT_SUCCESS = 0;
- static final int SEND_RESULT_NAK = -1;
- static final int SEND_RESULT_FAILURE = -2;
-
- static final int POLL_STRATEGY_MASK = 0x3; // first and second bit.
- static final int POLL_STRATEGY_REMOTES_DEVICES = 0x1;
- static final int POLL_STRATEGY_SYSTEM_AUDIO = 0x2;
-
- static final int POLL_ITERATION_STRATEGY_MASK = 0x30000; // first and second bit.
- static final int POLL_ITERATION_IN_ORDER = 0x10000;
- static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
-
/**
* Interface to report send result.
*/
@@ -101,10 +87,6 @@
// and sparse call it shares a thread to handle IO operations.
private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
- // A collection of FeatureAction.
- // Note that access to this collection should happen in service thread.
- private final LinkedList<FeatureAction> mActions = new LinkedList<>();
-
// Used to synchronize the access to the service.
private final Object mLock = new Object();
@@ -113,25 +95,27 @@
// 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();
- private final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
-
@Nullable
private HdmiCecController mCecController;
@@ -142,37 +126,10 @@
// from being modified.
private List<HdmiPortInfo> mPortInfo;
- // Logical address of the active source.
- @GuardedBy("mLock")
- private int mActiveSource;
-
- // Active routing path. Physical address of the active source but not all the time, such as
- // when the new active source does not claim itself to be one. Note that we don't keep
- // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
- @GuardedBy("mLock")
- private int mActiveRoutingPath;
-
- // Set to true while the service is in normal mode. While set to false, no input change is
- // allowed. Used for situations where input change can confuse users such as channel auto-scan,
- // system upgrade, etc., a.k.a. "prohibit mode".
- @GuardedBy("mLock")
- private boolean mInputChangeEnabled;
-
- @GuardedBy("mLock")
- // Whether ARC is "enabled" or not.
- // TODO: it may need to hold lock if it's accessed from others.
- private boolean mArcStatusEnabled = false;
-
- @GuardedBy("mLock")
- // Whether SystemAudioMode is "On" or not.
- private boolean mSystemAudioMode;
-
public HdmiControlService(Context context) {
super(context);
mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
- // TODO: Get control flag from persistent storage
- mInputChangeEnabled = true;
}
@Override
@@ -201,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();
@@ -219,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);
}
@@ -233,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);
}
}
@@ -345,44 +302,6 @@
return mHandler.getLooper();
}
- int getActiveSource() {
- synchronized (mLock) {
- return mActiveSource;
- }
- }
-
- /**
- * Returns the active routing path.
- */
- int getActivePath() {
- synchronized (mLock) {
- return mActiveRoutingPath;
- }
- }
-
- /**
- * Returns the ID of the active HDMI port. The active input is the port that has the active
- * routing path connected directly or indirectly under the device hierarchy.
- */
- int getActiveInput() {
- synchronized (mLock) {
- return pathToPortId(mActiveRoutingPath);
- }
- }
-
- void updateActiveDevice(int logicalAddress, int physicalAddress) {
- synchronized (mLock) {
- mActiveSource = logicalAddress;
- mActiveRoutingPath = physicalAddress;
- }
- }
-
- void setInputChangeEnabled(boolean enabled) {
- synchronized (mLock) {
- mInputChangeEnabled = enabled;
- }
- }
-
/**
* Returns physical address of the device.
*/
@@ -399,7 +318,11 @@
HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
- return mCecController.getDeviceInfo(logicalAddress);
+ HdmiCecLocalDeviceTv tv = tv();
+ if (tv == null) {
+ return null;
+ }
+ return tv.getDeviceInfo(logicalAddress);
}
/**
@@ -410,67 +333,6 @@
}
/**
- * Returns a list of {@link HdmiCecDeviceInfo}.
- *
- * @param includeLocalDevice whether to include local devices
- */
- List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
- assertRunOnServiceThread();
- return mCecController.getDeviceInfoList(includeLocalDevice);
- }
-
- /**
- * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
- * the given routing path. CEC devices use routing path for its physical address to
- * describe the hierarchy of the devices in the network.
- *
- * @param path routing path or physical address
- * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
- */
- HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
- assertRunOnServiceThread();
- for (HdmiCecDeviceInfo info : mCecController.getDeviceInfoList(false)) {
- if (info.getPhysicalAddress() == path) {
- return info;
- }
- }
- return null;
- }
-
- /**
- * Add and start a new {@link FeatureAction} to the action queue.
- *
- * @param action {@link FeatureAction} to add and start
- */
- void addAndStartAction(final FeatureAction action) {
- // TODO: may need to check the number of stale actions.
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
- mActions.add(action);
- action.start();
- }
- });
- }
-
- void setSystemAudioMode(boolean on) {
- synchronized (mLock) {
- if (on != mSystemAudioMode) {
- mSystemAudioMode = on;
- // TODO: Need to set the preference for SystemAudioMode.
- // TODO: Need to handle the notification of changing the mode and
- // to identify the notification should be handled in the service or TvSettings.
- }
- }
- }
-
- boolean getSystemAudioMode() {
- synchronized (mLock) {
- return mSystemAudioMode;
- }
- }
-
- /**
* Whether a device of the specified physical address is connected to ARC enabled port.
*/
boolean isConnectedToArcPort(int physicalAddress) {
@@ -483,57 +345,7 @@
return false;
}
- // See if we have an action of a given type in progress.
- <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
- for (FeatureAction action : mActions) {
- if (action.getClass().equals(clazz)) {
- return true;
- }
- }
- return false;
- }
-
- // Returns all actions matched with given class type.
- <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
- ArrayList<T> actions = new ArrayList<>();
- for (FeatureAction action : mActions) {
- if (action.getClass().equals(clazz)) {
- actions.add((T) action);
- }
- }
- return actions;
- }
-
- /**
- * Remove the given {@link FeatureAction} object from the action queue.
- *
- * @param action {@link FeatureAction} to remove
- */
- void removeAction(final FeatureAction action) {
- assertRunOnServiceThread();
- mActions.remove(action);
- }
-
- // Remove all actions matched with the given Class type.
- <T extends FeatureAction> void removeAction(final Class<T> clazz) {
- removeActionExcept(clazz, null);
- }
-
- // Remove all actions matched with the given Class type besides |exception|.
- <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
- final FeatureAction exception) {
- assertRunOnServiceThread();
- Iterator<FeatureAction> iter = mActions.iterator();
- while (iter.hasNext()) {
- FeatureAction action = iter.next();
- if (action != exception && action.getClass().equals(clazz)) {
- action.clear();
- mActions.remove(action);
- }
- }
- }
-
- private void runOnServiceThread(Runnable runnable) {
+ void runOnServiceThread(Runnable runnable) {
mHandler.post(runnable);
}
@@ -548,35 +360,6 @@
}
/**
- * Change ARC status into the given {@code enabled} status.
- *
- * @return {@code true} if ARC was in "Enabled" status
- */
- boolean setArcStatus(boolean enabled) {
- assertRunOnServiceThread();
- synchronized (mLock) {
- boolean oldStatus = mArcStatusEnabled;
- // 1. Enable/disable ARC circuit.
- mCecController.setAudioReturnChannel(enabled);
-
- // TODO: notify arc mode change to AudioManager.
-
- // 2. Update arc status;
- mArcStatusEnabled = enabled;
- return oldStatus;
- }
- }
-
- /**
- * Returns whether ARC is enabled or not.
- */
- boolean getArcStatus() {
- synchronized (mLock) {
- return mArcStatusEnabled;
- }
- }
-
- /**
* Transmit a CEC command to CEC bus.
*
* @param command CEC command to send out
@@ -591,47 +374,17 @@
}
boolean handleCecCommand(HdmiCecMessage message) {
- // Cache incoming message. Note that it caches only white-listed one.
- mCecMessageCache.cacheMessage(message);
-
- // Commands that queries system information replies directly instead
- // of creating FeatureAction because they are state-less.
- // TODO: move the leftover message to local device.
- switch (message.getOpcode()) {
- case HdmiCec.MESSAGE_INITIATE_ARC:
- handleInitiateArc(message);
- return true;
- case HdmiCec.MESSAGE_TERMINATE_ARC:
- handleTerminateArc(message);
- return true;
- case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
- handleSetSystemAudioMode(message);
- return true;
- case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
- handleSystemAudioModeStatus(message);
- return true;
- default:
- if (dispatchMessageToAction(message)) {
- return true;
- }
- break;
- }
-
return dispatchMessageToLocalDevice(message);
}
- private boolean dispatchMessageToAction(HdmiCecMessage message) {
- for (FeatureAction action : mActions) {
- if (action.processCommand(message)) {
- return true;
- }
- }
- return false;
+ void setAudioReturnChannel(boolean enabled) {
+ mCecController.setAudioReturnChannel(enabled);
}
private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
- if (device.dispatchMessage(message)) {
+ if (device.dispatchMessage(message)
+ && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
return true;
}
}
@@ -648,14 +401,9 @@
*/
void onHotplug(int portNo, boolean connected) {
assertRunOnServiceThread();
- // TODO: delegate onHotplug event to each local device.
- // Tv device will have permanent HotplugDetectionAction.
- List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
- if (!hotplugActions.isEmpty()) {
- // Note that hotplug action is single action running on a machine.
- // "pollAllDevicesNow" cleans up timer and start poll action immediately.
- hotplugActions.get(0).pollAllDevicesNow();
+ for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ device.onHotplug(portNo, connected);
}
announceHotplugEvent(portNo, connected);
@@ -675,43 +423,28 @@
}
private int checkPollStrategy(int pickStrategy) {
- int strategy = pickStrategy & POLL_STRATEGY_MASK;
+ int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK;
if (strategy == 0) {
throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
}
- int iterationStrategy = pickStrategy & POLL_ITERATION_STRATEGY_MASK;
+ int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK;
if (iterationStrategy == 0) {
throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
}
return strategy | iterationStrategy;
}
- void clearAllDeviceInfo() {
- assertRunOnServiceThread();
- mCecController.clearDeviceInfoList();
- }
-
List<HdmiCecLocalDevice> getAllLocalDevices() {
assertRunOnServiceThread();
return mCecController.getLocalDeviceList();
}
- /**
- * Whether a device of the specified physical address and logical address exists
- * in a device info list. However, both are minimal condition and it could
- * be different device from the original one.
- *
- * @param physicalAddress physical address of a device to be searched
- * @param logicalAddress logical address of a device to be searched
- * @return true if exist; otherwise false
- */
- boolean isInDeviceList(int physicalAddress, int logicalAddress) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo device = mCecController.getDeviceInfo(logicalAddress);
- if (device == null) {
- return false;
- }
- return device.getPhysicalAddress() == physicalAddress;
+ Object getServiceLock() {
+ return mLock;
+ }
+
+ void setAudioStatus(boolean mute, int volume) {
+ // TODO: Hook up with AudioManager.
}
private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
@@ -721,53 +454,6 @@
getPhysicalAddress(), deviceType, getVendorId(), displayName);
}
- private void handleInitiateArc(HdmiCecMessage message){
- // In case where <Initiate Arc> is started by <Request ARC Initiation>
- // need to clean up RequestArcInitiationAction.
- removeAction(RequestArcInitiationAction.class);
- SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
- message.getDestination(), message.getSource(), true);
- addAndStartAction(action);
- }
-
- private void handleTerminateArc(HdmiCecMessage message) {
- // In case where <Terminate Arc> is started by <Request ARC Termination>
- // need to clean up RequestArcInitiationAction.
- // TODO: check conditions of power status by calling is_connected api
- // to be added soon.
- removeAction(RequestArcTerminationAction.class);
- SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
- message.getDestination(), message.getSource(), false);
- addAndStartAction(action);
- }
-
- private void handleSetSystemAudioMode(HdmiCecMessage message) {
- if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
- return;
- }
- SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
- message.getDestination(), message.getSource(),
- HdmiUtils.parseCommandParamSystemAudioStatus(message));
- addAndStartAction(action);
- }
-
- private void handleSystemAudioModeStatus(HdmiCecMessage message) {
- if (!isMessageForSystemAudio(message)) {
- return;
- }
- setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
- }
-
- private boolean isMessageForSystemAudio(HdmiCecMessage message) {
- if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
- || message.getDestination() != HdmiCec.ADDR_TV
- || getAvrDeviceInfo() == null) {
- Slog.w(TAG, "Skip abnormal CecMessage: " + message);
- return false;
- }
- return true;
- }
-
// Record class that monitors the event of the caller of being killed. Used to clean up
// the listener list and record list accordingly.
private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
@@ -824,10 +510,9 @@
runOnServiceThread(new Runnable() {
@Override
public void run() {
- HdmiCecLocalDeviceTv tv =
- (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+ HdmiCecLocalDeviceTv tv = tv();
if (tv == null) {
- Slog.w(TAG, "Local playback device not available");
+ Slog.w(TAG, "Local tv device not available");
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
@@ -837,6 +522,41 @@
}
@Override
+ public void portSelect(final int portId, final IHdmiControlCallback callback) {
+ enforceAccessPermission();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiCecLocalDeviceTv tv = tv();
+ if (tv == null) {
+ Slog.w(TAG, "Local tv device not available");
+ invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+ return;
+ }
+ tv.portSelect(portId, callback);
+ }
+ });
+ }
+
+ @Override
+ public void sendKeyEvent(final int keyCode, final boolean isPressed) {
+ enforceAccessPermission();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ // TODO: sendKeyEvent is for TV device only for now. Allow other
+ // local devices of different types to use this as well.
+ HdmiCecLocalDeviceTv tv = tv();
+ if (tv == null) {
+ Slog.w(TAG, "Local tv device not available");
+ return;
+ }
+ tv.sendKeyEvent(keyCode, isPressed);
+ }
+ });
+ }
+
+ @Override
public void oneTouchPlay(final IHdmiControlCallback callback) {
enforceAccessPermission();
runOnServiceThread(new Runnable() {
@@ -892,67 +612,32 @@
}
@Override
- public void portSelect(int portId, IHdmiControlCallback callback) {
- // TODO: Implement this
- }
-
- @Override
- public void sendKeyEvent(int keyCode, boolean isPressed) {
- // TODO: Implement this
- }
-
- @Override
public List<HdmiPortInfo> getPortInfo() {
enforceAccessPermission();
return mPortInfo;
}
}
- private void oneTouchPlay(IHdmiControlCallback callback) {
- if (hasAction(OneTouchPlayAction.class)) {
- Slog.w(TAG, "oneTouchPlay already in progress");
- invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
- return;
- }
- HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ private void oneTouchPlay(final IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ HdmiCecLocalDevicePlayback source = playback();
if (source == null) {
Slog.w(TAG, "Local playback device not available");
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
- // TODO: Consider the case of multiple TV sets. For now we always direct the command
- // to the primary one.
- OneTouchPlayAction action = OneTouchPlayAction.create(this,
- source.getDeviceInfo().getLogicalAddress(),
- source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback);
- if (action == null) {
- Slog.w(TAG, "Cannot initiate oneTouchPlay");
- invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
- return;
- }
- addAndStartAction(action);
+ source.oneTouchPlay(callback);
}
- private void queryDisplayStatus(IHdmiControlCallback callback) {
- if (hasAction(DevicePowerStatusAction.class)) {
- Slog.w(TAG, "queryDisplayStatus already in progress");
- invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
- return;
- }
- HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ private void queryDisplayStatus(final IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ HdmiCecLocalDevicePlayback source = playback();
if (source == null) {
Slog.w(TAG, "Local playback device not available");
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
- DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
- source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback);
- if (action == null) {
- Slog.w(TAG, "Cannot initiate queryDisplayStatus");
- invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
- return;
- }
- addAndStartAction(action);
+ source.queryDisplayStatus(callback);
}
private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
@@ -983,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);
}
}
@@ -1003,53 +701,16 @@
}
}
- HdmiCecDeviceInfo getAvrDeviceInfo() {
- return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
- }
-
- void setAudioStatus(boolean mute, int volume) {
- // TODO: Hook up with AudioManager.
- }
-
- boolean isInPresetInstallationMode() {
- synchronized (mLock) {
- return !mInputChangeEnabled;
- }
- }
-
- /**
- * Called when a device is newly added or a new device is detected.
- *
- * @param info device info of a new device.
- */
- void addCecDevice(HdmiCecDeviceInfo info) {
- mCecController.addDeviceInfo(info);
-
- // TODO: announce new device detection.
- }
-
- /**
- * Called when a device is removed or removal of device is detected.
- *
- * @param address a logical address of a device to be removed
- */
- void removeCecDevice(int address) {
- mCecController.removeDeviceInfo(address);
- mCecMessageCache.flushMessagesFrom(address);
-
- // TODO: announce a device removal.
- }
-
- 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);
@@ -1058,39 +719,16 @@
}
}
- HdmiCecMessageCache getCecMessageCache() {
- return mCecMessageCache;
- }
-
private static boolean hasSameTopPort(int path1, int path2) {
return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
== (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
}
- /**
- * Whether the given path is located in the tail of current active path.
- *
- * @param path to be tested
- * @return true if the given path is located in the tail of current active path; otherwise,
- * false
- */
- // TODO: move this to local device tv.
- boolean isTailOfActivePath(int path) {
- // If active routing path is internal source, return false.
- if (mActiveRoutingPath == 0) {
- return false;
- }
- for (int i = 12; i >= 0; i -= 4) {
- int curActivePath = (mActiveRoutingPath >> i) & 0xF;
- if (curActivePath == 0) {
- return true;
- } else {
- int curPath = (path >> i) & 0xF;
- if (curPath != curActivePath) {
- return false;
- }
- }
- }
- return false;
+ private HdmiCecLocalDeviceTv tv() {
+ return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+ }
+
+ private HdmiCecLocalDevicePlayback playback() {
+ return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index b534377..9b7cc8d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -19,6 +19,7 @@
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.util.Slog;
+import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Collections;
@@ -111,4 +112,13 @@
static int threeBytesToInt(byte[] data) {
return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
}
+
+ static <T> List<T> sparseArrayToList(SparseArray<T> array) {
+ ArrayList<T> list = new ArrayList<>();
+ for (int i = 0; i < array.size(); ++i) {
+ list.add(array.valueAt(i));
+ }
+ return list;
+ }
+
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index ae20eda..647cc88 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -52,11 +52,10 @@
/**
* Constructor
*
- * @param service instance of {@link HdmiControlService}
- * @param sourceAddress logical address of a device that initiate this action
+ * @param source {@link HdmiCecLocalDevice} instance
*/
- HotplugDetectionAction(HdmiControlService service, int sourceAddress) {
- super(service, sourceAddress);
+ HotplugDetectionAction(HdmiCecLocalDevice source) {
+ super(source);
}
@Override
@@ -110,7 +109,7 @@
if (mTimeoutCount == 0) {
pollAllDevices();
} else {
- if (mService.getSystemAudioMode()) {
+ if (tv().getSystemAudioMode()) {
pollAudioSystem();
}
}
@@ -121,29 +120,29 @@
private void pollAllDevices() {
Slog.v(TAG, "Poll all devices.");
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
checkHotplug(ackedAddress, false);
}
- }, HdmiControlService.POLL_ITERATION_IN_ORDER
- | HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES, POLL_RETRY_COUNT);
+ }, HdmiConstants.POLL_ITERATION_IN_ORDER
+ | HdmiConstants.POLL_STRATEGY_REMOTES_DEVICES, POLL_RETRY_COUNT);
}
private void pollAudioSystem() {
Slog.v(TAG, "Poll audio system.");
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
checkHotplug(ackedAddress, true);
}
- }, HdmiControlService.POLL_ITERATION_IN_ORDER
- | HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO, POLL_RETRY_COUNT);
+ }, HdmiConstants.POLL_ITERATION_IN_ORDER
+ | HdmiConstants.POLL_STRATEGY_SYSTEM_AUDIO, POLL_RETRY_COUNT);
}
private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
- BitSet currentInfos = infoListToBitSet(mService.getDeviceInfoList(false), audioOnly);
+ BitSet currentInfos = infoListToBitSet(tv().getDeviceInfoList(false), audioOnly);
BitSet polledResult = addressListToBitSet(ackedAddress);
// At first, check removed devices.
@@ -195,7 +194,8 @@
private void addDevice(int addedAddress) {
// Send <Give Physical Address>.
- sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, addedAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(),
+ addedAddress));
}
private void removeDevice(int removedAddress) {
@@ -206,7 +206,7 @@
mayCancelOneTouchRecord(removedAddress);
mayDisableSystemAudioAndARC(removedAddress);
- mService.removeCecDevice(removedAddress);
+ tv().removeCecDevice(removedAddress);
}
private void mayChangeRoutingPath(int address) {
@@ -217,7 +217,7 @@
}
private void mayCancelDeviceSelect(int address) {
- List<DeviceSelectAction> actions = mService.getActions(DeviceSelectAction.class);
+ List<DeviceSelectAction> actions = getActions(DeviceSelectAction.class);
if (actions.isEmpty()) {
return;
}
@@ -225,7 +225,7 @@
// Should ave only one Device Select Action
DeviceSelectAction action = actions.get(0);
if (action.getTargetAddress() == address) {
- mService.removeAction(DeviceSelectAction.class);
+ removeAction(DeviceSelectAction.class);
}
}
@@ -239,11 +239,9 @@
}
// Turn off system audio mode.
- mService.setSystemAudioMode(false);
- if (mService.getArcStatus()) {
- mService.addAndStartAction(
- new RequestArcTerminationAction(mService, mSourceAddress, address));
+ tv().setSystemAudioMode(false);
+ if (tv().getArcStatus()) {
+ addAndStartAction(new RequestArcTerminationAction(localDevice(), address));
}
-
}
}
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 2cae507..4a49f09 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -56,15 +56,14 @@
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param deviceLogicalAddress logical address of the device in interest
* @param devicePhysicalAddress physical address of the device in interest
* @param requireRoutingChange whether to initiate routing change or not
*/
- NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress,
+ NewDeviceAction(HdmiCecLocalDevice source, int deviceLogicalAddress,
int devicePhysicalAddress, boolean requireRoutingChange) {
- super(service, sourceAddress);
+ super(source);
mDeviceLogicalAddress = deviceLogicalAddress;
mDevicePhysicalAddress = devicePhysicalAddress;
mVendorId = HdmiCec.UNKNOWN_VENDOR_ID;
@@ -73,16 +72,16 @@
@Override
public boolean start() {
- if (HdmiCec.getTypeFromAddress(mSourceAddress) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
- if (mService.getAvrDeviceInfo() == null) {
+ if (HdmiCec.getTypeFromAddress(getSourceAddress()) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
+ if (tv().getAvrDeviceInfo() == null) {
// TODO: Start system audio initiation action
}
// If new device is connected through ARC enabled port,
// initiates ARC channel establishment.
- if (mService.isConnectedToArcPort(mDevicePhysicalAddress)) {
- mService.addAndStartAction(new RequestArcInitiationAction(mService, mSourceAddress,
- mDeviceLogicalAddress));
+ if (tv().isConnectedToArcPort(mDevicePhysicalAddress)) {
+ addAndStartAction(new RequestArcInitiationAction(localDevice(),
+ mDeviceLogicalAddress));
}
}
@@ -95,7 +94,7 @@
return true;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(),
mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
return true;
@@ -155,14 +154,14 @@
private void startRoutingChange() {
// Stop existing routing control.
- mService.removeAction(RoutingControlAction.class);
+ removeAction(RoutingControlAction.class);
// Send routing change. The the address is a path of the active port.
int newPath = toTopMostPortPath(mDevicePhysicalAddress);
- sendCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
- mService.getActivePath(), newPath));
- mService.addAndStartAction(new RoutingControlAction(mService, mSourceAddress,
- mService.pathToPortId(newPath), null));
+ sendCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
+ localDevice().getActivePath(), newPath));
+ addAndStartAction(new RoutingControlAction(localDevice(),
+ localDevice().pathToPortId(newPath), null));
}
private static int toTopMostPortPath(int physicalAddress) {
@@ -170,7 +169,7 @@
}
private boolean mayProcessCommandIfCached(int destAddress, int opcode) {
- HdmiCecMessage message = mService.getCecMessageCache().getMessage(destAddress, opcode);
+ HdmiCecMessage message = getCecMessageCache().getMessage(destAddress, opcode);
if (message != null) {
return processCommand(message);
}
@@ -184,7 +183,7 @@
if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(),
mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
}
@@ -193,7 +192,7 @@
if (mDisplayName == null) {
mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress);
}
- mService.addCecDevice(new HdmiCecDeviceInfo(
+ tv().addCecDevice(new HdmiCecDeviceInfo(
mDeviceLogicalAddress, mDevicePhysicalAddress,
HdmiCec.getTypeFromAddress(mDeviceLogicalAddress),
mVendorId, mDisplayName));
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 69fad13..e0a3a8b 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -30,7 +30,7 @@
* <p>Package-private, accessed by {@link HdmiControlService} only.
*/
-public final class OneTouchPlayAction extends FeatureAction {
+final class OneTouchPlayAction extends FeatureAction {
private static final String TAG = "OneTouchPlayAction";
// State in which the action is waiting for <Report Power Status>. In normal situation
@@ -48,34 +48,32 @@
// We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds.
private static final int LOOP_COUNTER_MAX = 10;
- private final int mSourcePath;
private final int mTargetAddress;
private final IHdmiControlCallback mCallback;
private int mPowerStatusCounter = 0;
// Factory method. Ensures arguments are valid.
- static OneTouchPlayAction create(HdmiControlService service, int sourceAddress,
- int sourcePath, int targetAddress, IHdmiControlCallback callback) {
- if (service == null || callback == null) {
+ static OneTouchPlayAction create(HdmiCecLocalDevice source,
+ int targetAddress, IHdmiControlCallback callback) {
+ if (source == null || callback == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new OneTouchPlayAction(service, sourceAddress, sourcePath, targetAddress, callback);
+ return new OneTouchPlayAction(source, targetAddress,
+ callback);
}
- private OneTouchPlayAction(HdmiControlService service, int sourceAddress, int sourcePath,
- int targetAddress, IHdmiControlCallback callback) {
- super(service, sourceAddress);
- mSourcePath = sourcePath;
+ private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
+ IHdmiControlCallback callback) {
+ super(localDevice);
mTargetAddress = targetAddress;
mCallback = callback;
}
@Override
boolean start() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildTextViewOn(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
broadcastActiveSource();
queryDevicePowerStatus();
mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
@@ -84,13 +82,12 @@
}
private void broadcastActiveSource() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath));
+ sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()));
}
private void queryDevicePowerStatus() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ mTargetAddress));
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 08ca306..a2e08f1 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -35,16 +35,14 @@
/**
* @Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address. It should
- * TV type
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type
* @throw IllegalArugmentException if device type of sourceAddress and avrAddress
* is invalid
*/
- RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress);
- HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ RequestArcAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
}
@@ -72,9 +70,9 @@
protected final void disableArcTransmission() {
// Start Set ARC Transmission State action.
- SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(mService,
- mSourceAddress, mAvrAddress, false);
- mService.addAndStartAction(action);
+ SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(localDevice(),
+ mAvrAddress, false);
+ addAndStartAction(action);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index 343aff7..55fb65a 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -31,18 +31,18 @@
*
* For more details look at {@link RequestArcAction#RequestArcAction}.
*/
- RequestArcInitiationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress, avrAddress);
+ RequestArcInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source, avrAddress);
}
@Override
boolean start() {
- HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress,
- mAvrAddress);
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ if (error == HdmiConstants.SEND_RESULT_SUCCESS) {
mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
addTimer(mState, TIMEOUT_MS);
} else {
diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
index d4a35f8..62ca8f6 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
@@ -31,18 +31,18 @@
*
* @see RequestArcAction#RequestArcAction
*/
- RequestArcTerminationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress, avrAddress);
+ RequestArcTerminationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source, avrAddress);
}
@Override
boolean start() {
HdmiCecMessage command =
- HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress);
+ HdmiCecMessageBuilder.buildRequestArcTermination(getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ if (error == HdmiConstants.SEND_RESULT_SUCCESS) {
mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
addTimer(mState, TIMEOUT_MS);
} else {
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 19974ea..0d657b2 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -39,7 +39,7 @@
* <li> Routing at CEC enable time
* </ul>
*/
-public class RoutingControlAction extends FeatureAction {
+final class RoutingControlAction extends FeatureAction {
private static final String TAG = "RoutingControlAction";
// State in which we wait for <Routing Information> to arrive. If timed out, we use the
@@ -63,9 +63,8 @@
// The latest routing path. Updated by each <Routing Information> from CEC switches.
private int mCurrentRoutingPath;
- RoutingControlAction(HdmiControlService service, int sourceAddress, int path,
- IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ RoutingControlAction(HdmiCecLocalDevice localDevice, int path, IHdmiControlCallback callback) {
+ super(localDevice);
mCallback = callback;
mCurrentRoutingPath = path;
}
@@ -92,7 +91,7 @@
}
mCurrentRoutingPath = routingPath;
// Stop possible previous routing change sequence if in progress.
- mService.removeAction(RoutingControlAction.class);
+ removeAction(RoutingControlAction.class);
addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
return true;
} else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
@@ -130,7 +129,8 @@
}
private void sendSetStreamPath() {
- sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(mSourceAddress, mCurrentRoutingPath));
+ sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
+ mCurrentRoutingPath));
}
private static boolean isInActiveRoutingPath(int activePath, int newPath) {
@@ -164,9 +164,9 @@
}
switch (timeoutState) {
case STATE_WAIT_FOR_ROUTING_INFORMATION:
- HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath);
+ HdmiCecDeviceInfo device = tv().getDeviceInfoByPath(mCurrentRoutingPath);
if (device == null) {
- maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+ maybeChangeActiveInput(tv().pathToPortId(mCurrentRoutingPath));
} else {
// TODO: Also check followings and then proceed:
// if routing change was neither triggered by TV at CEC enable time, nor
@@ -184,7 +184,7 @@
case STATE_WAIT_FOR_REPORT_POWER_STATUS:
int tvPowerStatus = getTvPowerStatus();
if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) {
- if (!maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath))) {
+ if (!maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath))) {
sendSetStreamPath();
}
}
@@ -196,7 +196,7 @@
// Called whenever an HDMI input of the TV shall become the active input.
private boolean maybeChangeActiveInput(int path) {
- if (mService.getActiveInput() == mService.pathToPortId(path)) {
+ if (localDevice().getActivePortId() == localDevice().pathToPortId(path)) {
return false;
}
// TODO: Remember the currently active input
@@ -207,7 +207,7 @@
}
private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
- sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, address),
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address),
callback);
}
@@ -216,7 +216,7 @@
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
} else {
- maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+ maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath));
}
}
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index 8e6998f..c3078a2 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -16,10 +16,8 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCecMessage;
-import android.view.KeyEvent;
import android.util.Slog;
-
-import libcore.util.EmptyArray;
+import android.view.KeyEvent;
/**
* Feature action that transmits remote control key command (User Control Press/
@@ -56,13 +54,12 @@
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param targetAddress logical address of the device to send the keys to
* @param keyCode remote control key code as defined in {@link KeyEvent}
*/
- SendKeyAction(HdmiControlService service, int sourceAddress, int targetAddress, int keyCode) {
- super(service, sourceAddress);
+ SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keyCode) {
+ super(source);
mTargetAddress = targetAddress;
mLastKeyCode = keyCode;
}
@@ -112,12 +109,13 @@
if (keyCodeAndParam == null) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress, mTargetAddress,
- keyCodeAndParam));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
+ mTargetAddress, keyCodeAndParam));
}
private void sendKeyUp() {
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
+ mTargetAddress));
}
@Override
@@ -146,6 +144,6 @@
// KeyEvent.KEYCODE_TV_BROADCAST_CABLE.
// The return byte array contains both UI command (keycode) and optional parameter.
private byte[] getCecKeyCodeAndParam(int keyCode) {
- return HdmiCecKeycodeTranslator.androidKeyToCecKey(keyCode);
+ return HdmiCecKeycode.androidKeyToCecKey(keyCode);
}
}
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index d53d88d..cdedd6b 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -39,14 +39,13 @@
/**
* @Constructor
*
- * @param service an instance of {@link HdmiControlService}
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param enabled whether to enable ARC Transmission channel
*/
- SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress,
+ SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress,
boolean enabled) {
- super(service, sourceAddress);
- HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ super(source);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
mEnabled = enabled;
@@ -65,11 +64,11 @@
private void sendReportArcInitiated() {
HdmiCecMessage command =
- HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress);
+ HdmiCecMessageBuilder.buildReportArcInitiated(getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ if (error == HdmiConstants.SEND_RESULT_SUCCESS) {
// Enable ARC status immediately after sending <Report Arc Initiated>.
// If AVR responds with <Feature Abort>, disable ARC status again.
// This is different from spec that says that turns ARC status to
@@ -93,14 +92,14 @@
}
private void setArcStatus(boolean enabled) {
- boolean wasEnabled = mService.setArcStatus(enabled);
+ boolean wasEnabled = tv().setArcStatus(enabled);
Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
// If enabled before and set to "disabled" and send <Report Arc Terminated> to
// av reciever.
if (!enabled && wasEnabled) {
- sendCommand(
- HdmiCecMessageBuilder.buildReportArcTerminated(mSourceAddress, mAvrAddress));
+ sendCommand(HdmiCecMessageBuilder.buildReportArcTerminated(getSourceAddress(),
+ mAvrAddress));
}
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index dde3342..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;
@@ -47,28 +44,27 @@
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address of source device (TV or STB).
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
*/
- SystemAudioAction(HdmiControlService service, int sourceAddress, int avrAddress,
- boolean targetStatus) {
- super(service, sourceAddress);
+ SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus) {
+ super(source);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrLogicalAddress = avrAddress;
mTargetAudioStatus = targetStatus;
}
protected void sendSystemAudioModeRequest() {
- int avrPhysicalAddress = mService.getAvrDeviceInfo().getPhysicalAddress();
- HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(mSourceAddress,
+ int avrPhysicalAddress = tv().getAvrDeviceInfo().getPhysicalAddress();
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ getSourceAddress(),
mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ if (error == HdmiConstants.SEND_RESULT_SUCCESS) {
mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
} else {
@@ -90,40 +86,7 @@
}
protected void setSystemAudioMode(boolean mode) {
- mService.setSystemAudioMode(mode);
- }
-
- protected void sendGiveAudioStatus() {
- HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress,
- mAvrLogicalAddress);
- sendCommand(command, new HdmiControlService.SendMessageCallback() {
- @Override
- public void onSendCompleted(int error) {
- if (error == HdmiControlService.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 = mService.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(
- mSourceAddress, mAvrLogicalAddress, uiCommand));
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
- mSourceAddress, mAvrLogicalAddress));
+ tv().setSystemAudioMode(mode);
}
@Override
@@ -138,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
@@ -146,33 +110,19 @@
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;
- mService.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() {
- mService.removeActionExcept(SystemAudioActionFromTv.class, this);
- mService.removeActionExcept(SystemAudioActionFromAvr.class, this);
+ removeActionExcept(SystemAudioActionFromTv.class, this);
+ removeActionExcept(SystemAudioActionFromAvr.class, this);
}
@Override
@@ -184,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 c5eb44b..b743c64 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -25,16 +25,15 @@
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param tvAddress logical address of TV device
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
*/
- SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress,
+ SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
boolean targetStatus) {
- super(service, tvAddress, avrAddress, targetStatus);
- HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ super(source, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
}
@Override
@@ -45,13 +44,13 @@
}
private void handleSystemAudioActionFromAvr() {
- if (mTargetAudioStatus == mService.getSystemAudioMode()) {
+ if (mTargetAudioStatus == tv().getSystemAudioMode()) {
finish();
return;
}
- if (mService.isInPresetInstallationMode()) {
+ if (tv().isInPresetInstallationMode()) {
sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
- mSourceAddress, mAvrLogicalAddress,
+ getSourceAddress(), mAvrLogicalAddress,
HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED));
mTargetAudioStatus = false;
sendSystemAudioModeRequest();
@@ -60,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/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
index 9994de6..e0c4ff4 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -26,16 +26,15 @@
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param tvAddress logical address of TV device
+ * @param sourceAddress {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of tvAddress is invalid
*/
- SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress,
+ SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
boolean targetStatus) {
- super(service, tvAddress, avrAddress, targetStatus);
- HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ super(sourceAddress, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
new file mode 100644
index 0000000..e4d82ef
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+/**
+ * Action to initiate system audio once AVR is detected on Device discovery action.
+ */
+final class SystemAudioAutoInitiationAction extends FeatureAction {
+ private final int mAvrAddress;
+
+ // State that waits for <System Audio Mode Status> once send
+ // <Give System Audio Mode Status> to AV Receiver.
+ private static final int STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS = 1;
+
+ SystemAudioAutoInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source);
+ mAvrAddress = avrAddress;
+ }
+
+ @Override
+ boolean start() {
+ mState = STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS;
+
+ addTimer(mState, TIMEOUT_MS);
+ sendGiveSystemAudioModeStatus();
+ return true;
+ }
+
+ private void sendGiveSystemAudioModeStatus() {
+ sendCommand(HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(getSourceAddress(),
+ mAvrAddress), new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
+ tv().setSystemAudioMode(false);
+ finish();
+ }
+ }
+ });
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS) {
+ return false;
+ }
+
+ switch (cmd.getOpcode()) {
+ case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+ handleSystemAudioModeStatusMessage();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void handleSystemAudioModeStatusMessage() {
+ // If the last setting is system audio, turn on system audio whatever AVR status is.
+ if (tv().getSystemAudioMode()) {
+ if (canChangeSystemAudio()) {
+ addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true));
+ }
+ } else {
+ // If the last setting is non-system audio, turn off system audio mode
+ // and update system audio status (volume or mute).
+ tv().setSystemAudioMode(false);
+ if (canChangeSystemAudio()) {
+ addAndStartAction(new SystemAudioStatusAction(tv(), mAvrAddress));
+ }
+ }
+ finish();
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
+ switch (mState) {
+ case STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS:
+ handleSystemAudioModeStatusTimeout();
+ break;
+ }
+ }
+
+ private void handleSystemAudioModeStatusTimeout() {
+ if (tv().getSystemAudioMode()) {
+ if (canChangeSystemAudio()) {
+ addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true));
+ }
+ } else {
+ tv().setSystemAudioMode(false);
+ }
+ finish();
+ }
+
+ private boolean canChangeSystemAudio() {
+ return tv().canChangeSystemAudio();
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
new file mode 100644
index 0000000..5f4fc23
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.Slog;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+/**
+ * Action to update audio status (volume or mute) of audio amplifier
+ */
+final class SystemAudioStatusAction extends FeatureAction {
+ private static final String TAG = "SystemAudioStatusAction";
+
+ // State that waits for <ReportAudioStatus>.
+ private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 1;
+
+ private final int mAvrAddress;
+
+ SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source);
+ mAvrAddress = avrAddress;
+ }
+
+ @Override
+ boolean start() {
+ mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
+ addTimer(mState, TIMEOUT_MS);
+ sendGiveAudioStatus();
+ return true;
+ }
+
+ private void sendGiveAudioStatus() {
+ sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(), mAvrAddress),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
+ handleSendGiveAudioStatusFailure();
+ }
+ }
+ });
+ }
+
+ private void handleSendGiveAudioStatusFailure() {
+ // Inform to all application that the audio status (volumn, mute) of
+ // the audio amplifier is unknown.
+ tv().setAudioStatus(false, HdmiConstants.UNKNOWN_VOLUME);
+
+ 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(), mAvrAddress, uiCommand));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+ getSourceAddress(), mAvrAddress));
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAIT_FOR_REPORT_AUDIO_STATUS) {
+ return false;
+ }
+
+ switch (cmd.getOpcode()) {
+ case HdmiCec.MESSAGE_REPORT_AUDIO_STATUS:
+ handleReportAudioStatus(cmd);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void handleReportAudioStatus(HdmiCecMessage cmd) {
+ byte[] params = cmd.getParams();
+ if (params.length > 0) {
+ boolean mute = (params[0] & 0x80) == 0x80;
+ int volume = params[0] & 0x7F;
+ tv().setAudioStatus(mute, volume);
+
+ if ((tv().getSystemAudioMode() && mute) || (!tv().getSystemAudioMode() && !mute)) {
+ // Toggle AVR's mute status to match with the system audio status.
+ sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
+ }
+ finish();
+ } else {
+ Slog.e(TAG, "Invalid <Report Audio Status> message:" + cmd);
+ handleSendGiveAudioStatusFailure();
+ return;
+ }
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
+ handleSendGiveAudioStatusFailure();
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/VendorSpecificAction.java b/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
index 9d45702..c954b50 100644
--- a/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
+++ b/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
@@ -12,8 +12,8 @@
private static final int STATE_1 = 1;
private static final int STATE_2 = 2;
- VendorSpecificAction(HdmiControlService service, int sourceAddress) {
- super(service, sourceAddress);
+ VendorSpecificAction(HdmiCecLocalDevice source) {
+ super(source);
// Modify the constructor if additional arguments are necessary.
}
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/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 4aef2d31..538a252 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -19,19 +19,16 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
-import android.os.BatteryProperty;
-import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.os.BatteryManagerInternal;
import android.os.SystemClock;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.BatteryService;
+import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
@@ -158,14 +155,10 @@
mContext.registerReceiver(this, filter);
// Initialise tracker state.
- BatteryService batteryService = (BatteryService) ServiceManager.getService("battery");
- if (batteryService != null) {
- mBatteryHealthy = !batteryService.getBatteryLevelLow();
- mCharging = batteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
- } else {
- // Unavailable for some reason, we default to false and let ACTION_BATTERY_[OK,LOW]
- // sort it out.
- }
+ BatteryManagerInternal batteryManagerInternal =
+ LocalServices.getService(BatteryManagerInternal.class);
+ mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow();
+ mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
}
boolean isOnStablePower() {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 15a6b25..53337c4 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -203,7 +203,8 @@
@Override
public String toString() {
return String.valueOf(hashCode()).substring(0, 3) + ".."
- + ":[" + job.getService().getPackageName() + ",jId=" + job.getId()
+ + ":[" + job.getService()
+ + ",jId=" + job.getId()
+ ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")"
+ ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging()
+ ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 9ae8aed..1264741 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -17,6 +17,8 @@
package com.android.server.media;
import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.routeprovider.RouteRequest;
@@ -25,7 +27,6 @@
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.MediaController;
-import android.media.session.RemoteVolumeProvider;
import android.media.session.RouteCommand;
import android.media.session.RouteInfo;
import android.media.session.RouteOptions;
@@ -34,10 +35,13 @@
import android.media.session.MediaSessionInfo;
import android.media.session.RouteInterface;
import android.media.session.PlaybackState;
+import android.media.session.ParcelableVolumeInfo;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
+import android.media.VolumeProvider;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -63,6 +67,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.
@@ -105,6 +110,7 @@
// TODO define a RouteState class with relevant info
private int mRouteState;
private long mFlags;
+ private ComponentName mMediaButtonReceiver;
// TransportPerformer fields
@@ -115,9 +121,10 @@
// End TransportPerformer fields
// Volume handling fields
- private int mPlaybackType = MediaSession.VOLUME_TYPE_LOCAL;
+ private AudioManager mAudioManager;
+ private int mVolumeType = MediaSession.VOLUME_TYPE_LOCAL;
private int mAudioStream = AudioManager.STREAM_MUSIC;
- private int mVolumeControlType = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
private int mMaxVolume = 0;
private int mCurrentVolume = 0;
// End volume handling fields
@@ -138,6 +145,7 @@
mSessionCb = new SessionCb(cb);
mService = service;
mHandler = new MessageHandler(handler.getLooper());
+ mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
}
/**
@@ -185,6 +193,10 @@
return mSessionInfo;
}
+ public ComponentName getMediaButtonReceiver() {
+ return mMediaButtonReceiver;
+ }
+
/**
* Get this session's flags.
*
@@ -263,20 +275,42 @@
*
* @param delta The amount to adjust the volume by.
*/
- public void adjustVolumeBy(int delta) {
- if (mVolumeControlType == RemoteVolumeProvider.VOLUME_CONTROL_FIXED) {
- // Nothing to do, the volume cannot be changed
- return;
+ public void adjustVolumeBy(int delta, int flags) {
+ if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+ if (delta == 0) {
+ mAudioManager.adjustStreamVolume(mAudioStream, delta, flags);
+ } else {
+ int direction = 0;
+ int steps = delta;
+ if (delta > 0) {
+ direction = 1;
+ } else if (delta < 0) {
+ direction = -1;
+ steps = -delta;
+ }
+ for (int i = 0; i < steps; i++) {
+ mAudioManager.adjustStreamVolume(mAudioStream, direction, flags);
+ }
+ }
+ } else {
+ if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
+ // Nothing to do, the volume cannot be changed
+ return;
+ }
+ mSessionCb.adjustVolumeBy(delta);
}
- mSessionCb.adjustVolumeBy(delta);
}
- public void setVolumeTo(int value) {
- if (mVolumeControlType != RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
- // Nothing to do. The volume can't be set directly.
- return;
+ public void setVolumeTo(int value, int flags) {
+ if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+ mAudioManager.setStreamVolume(mAudioStream, value, flags);
+ } else {
+ if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
+ // Nothing to do. The volume can't be set directly.
+ return;
+ }
+ mSessionCb.setVolumeTo(value);
}
- mSessionCb.setVolumeTo(value);
}
/**
@@ -350,7 +384,7 @@
* @return The current type of playback.
*/
public int getPlaybackType() {
- return mPlaybackType;
+ return mVolumeType;
}
/**
@@ -506,9 +540,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 +560,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 +579,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 +598,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 +655,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
@@ -661,6 +715,11 @@
}
@Override
+ public void setMediaButtonReceiver(ComponentName mbr) {
+ mMediaButtonReceiver = mbr;
+ }
+
+ @Override
public void setMetadata(MediaMetadata metadata) {
mMetadata = metadata;
mHandler.post(MessageHandler.MSG_UPDATE_METADATA);
@@ -732,7 +791,7 @@
public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
switch(type) {
case MediaSession.VOLUME_TYPE_LOCAL:
- mPlaybackType = type;
+ mVolumeType = type;
int audioStream = arg1;
if (isValidStream(audioStream)) {
mAudioStream = audioStream;
@@ -742,7 +801,7 @@
}
break;
case MediaSession.VOLUME_TYPE_REMOTE:
- mPlaybackType = type;
+ mVolumeType = type;
mVolumeControlType = arg1;
mMaxVolume = arg2;
break;
@@ -929,8 +988,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 +1001,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);
+ }
}
}
@@ -954,6 +1022,35 @@
}
@Override
+ public ParcelableVolumeInfo getVolumeAttributes() {
+ synchronized (mLock) {
+ int type;
+ int max;
+ int current;
+ if (mVolumeType == MediaSession.VOLUME_TYPE_REMOTE) {
+ type = mVolumeControlType;
+ max = mMaxVolume;
+ current = mCurrentVolume;
+ } else {
+ type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ max = mAudioManager.getStreamMaxVolume(mAudioStream);
+ current = mAudioManager.getStreamVolume(mAudioStream);
+ }
+ return new ParcelableVolumeInfo(mVolumeType, mAudioStream, type, max, current);
+ }
+ }
+
+ @Override
+ public void adjustVolumeBy(int delta, int flags) {
+ MediaSessionRecord.this.adjustVolumeBy(delta, flags);
+ }
+
+ @Override
+ public void setVolumeTo(int value, int flags) {
+ MediaSessionRecord.this.setVolumeTo(value, flags);
+ }
+
+ @Override
public void play() throws RemoteException {
mSessionCb.play();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 67065ba..685717f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -23,6 +23,7 @@
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -88,6 +89,7 @@
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
+ private ContentResolver mContentResolver;
private MediaSessionRecord mPrioritySession;
private int mCurrentUserId = -1;
@@ -115,6 +117,7 @@
mKeyguardManager =
(KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
mAudioService = getAudioService();
+ mContentResolver = getContext().getContentResolver();
}
private IAudioService getAudioService() {
@@ -381,8 +384,7 @@
return false;
}
if (compName != null) {
- final String enabledNotifListeners = Settings.Secure.getStringForUser(
- getContext().getContentResolver(),
+ final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
userId);
if (enabledNotifListeners != null) {
@@ -485,6 +487,9 @@
synchronized (mLock) {
List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
int size = records.size();
+ if (size > 0) {
+ persistMediaButtonReceiverLocked(records.get(0));
+ }
ArrayList<MediaSessionToken> tokens = new ArrayList<MediaSessionToken>();
for (int i = 0; i < size; i++) {
tokens.add(new MediaSessionToken(records.get(i).getControllerBinder()));
@@ -504,6 +509,16 @@
}
}
+ private void persistMediaButtonReceiverLocked(MediaSessionRecord record) {
+ ComponentName receiver = record.getMediaButtonReceiver();
+ if (receiver != null) {
+ Settings.System.putStringForUser(mContentResolver,
+ Settings.System.MEDIA_BUTTON_RECEIVER,
+ receiver == null ? "" : receiver.flattenToString(),
+ UserHandle.USER_CURRENT);
+ }
+ }
+
private MediaRouteProviderProxy.RoutesListener mRoutesCallback
= new MediaRouteProviderProxy.RoutesListener() {
@Override
@@ -881,14 +896,6 @@
private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags,
MediaSessionRecord session) {
- int direction = 0;
- int steps = delta;
- if (delta > 0) {
- direction = 1;
- } else if (delta < 0) {
- direction = -1;
- steps = -delta;
- }
if (DEBUG) {
String sessionInfo = session == null ? null : session.getSessionInfo().toString();
Log.d(TAG, "Adjusting session " + sessionInfo + " by " + delta + ". flags=" + flags
@@ -901,6 +908,14 @@
mAudioService.adjustSuggestedStreamVolume(delta, suggestedStream, flags,
getContext().getOpPackageName());
} else {
+ int direction = 0;
+ int steps = delta;
+ if (delta > 0) {
+ direction = 1;
+ } else if (delta < 0) {
+ direction = -1;
+ steps = -delta;
+ }
for (int i = 0; i < steps; i++) {
mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
flags, getContext().getOpPackageName());
@@ -910,26 +925,7 @@
Log.e(TAG, "Error adjusting default volume.", e);
}
} else {
- if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) {
- try {
- if (delta == 0) {
- mAudioService.adjustSuggestedStreamVolume(delta,
- session.getAudioStream(), flags,
- getContext().getOpPackageName());
- } else {
- for (int i = 0; i < steps; i++) {
- mAudioService.adjustSuggestedStreamVolume(direction,
- session.getAudioStream(), flags,
- getContext().getOpPackageName());
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error adjusting volume for stream "
- + session.getAudioStream(), e);
- }
- } else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) {
- session.adjustVolumeBy(delta);
- }
+ session.adjustVolumeBy(delta, flags);
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index f2db791..1d53016 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,12 +16,13 @@
package com.android.server.pm;
-import android.content.BroadcastReceiver;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.util.Log;
import java.util.HashSet;
@@ -30,62 +31,63 @@
/**
* {@hide}
*/
-public class BackgroundDexOptService {
-
+public class BackgroundDexOptService extends JobService {
static final String TAG = "BackgroundDexOptService";
- private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) {
- onIdleStart();
- } else if (Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
- onIdleStop();
- }
- }
- };
-
- final PackageManagerService mPackageManager;
+ static final int BACKGROUND_DEXOPT_JOB = 800;
+ private static ComponentName sDexoptServiceName = new ComponentName(
+ "android",
+ BackgroundDexOptService.class.getName());
final AtomicBoolean mIdleTime = new AtomicBoolean(false);
- public BackgroundDexOptService(Context context) {
- mPackageManager = (PackageManagerService)ServiceManager.getService("package");
-
- IntentFilter idleMaintenanceFilter = new IntentFilter();
- idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START);
- idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_END);
- context.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL,
- idleMaintenanceFilter, null, null);
+ public static void schedule(Context context) {
+ JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
+ .setRequiresDeviceIdle(true)
+ .setRequiresCharging(true)
+ .build();
+ js.schedule(job);
}
- public boolean onIdleStart() {
+ @Override
+ public boolean onStartJob(JobParameters params) {
Log.i(TAG, "onIdleStart");
- if (mPackageManager.isStorageLow()) {
+ final PackageManagerService pm =
+ (PackageManagerService)ServiceManager.getService("package");
+
+ if (pm.isStorageLow()) {
return false;
}
- final HashSet<String> pkgs = mPackageManager.getPackagesThatNeedDexOpt();
+ final HashSet<String> pkgs = pm.getPackagesThatNeedDexOpt();
if (pkgs == null) {
return false;
}
+
+ final JobParameters jobParams = params;
mIdleTime.set(true);
new Thread("BackgroundDexOptService_DexOpter") {
@Override
public void run() {
for (String pkg : pkgs) {
if (!mIdleTime.get()) {
- break;
+ // stopped while still working, so we need to reschedule
+ schedule(BackgroundDexOptService.this);
+ return;
}
- mPackageManager.performDexOpt(pkg, false);
+ pm.performDexOpt(pkg, false);
}
+ // ran to completion, so we abandon our timeslice and do not reschedule
+ jobFinished(jobParams, false);
}
}.start();
return true;
}
- public void onIdleStop() {
+ @Override
+ public boolean onStopJob(JobParameters params) {
Log.i(TAG, "onIdleStop");
mIdleTime.set(false);
+ return false;
}
}
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 1209386..90e263a 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -42,6 +42,7 @@
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.NativeLibraryHelper.ApkHandle;
import com.android.internal.content.PackageHelper;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -146,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;
@@ -1247,7 +1249,7 @@
}
}
- public static final IPackageManager main(Context context, Installer installer,
+ public static final PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
@@ -2242,11 +2244,12 @@
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
return null;
}
+ // App code is gone, so we aren't worried about split paths
pkg = new PackageParser.Package(packageName);
pkg.applicationInfo.packageName = packageName;
pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
- pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
pkg.applicationInfo.sourceDir = ps.codePathString;
+ pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
pkg.applicationInfo.dataDir =
getDataPathForPackage(packageName, 0).getPath();
pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
@@ -3345,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,
@@ -3365,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;
}
@@ -3400,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;
}
@@ -3442,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 {
@@ -3461,7 +3510,7 @@
forwardingResolveInfo.preferredOrder = 0;
forwardingResolveInfo.match = 0;
forwardingResolveInfo.isDefault = true;
- forwardingResolveInfo.filter = cpif;
+ forwardingResolveInfo.filter = filter;
return forwardingResolveInfo;
}
@@ -4081,6 +4130,7 @@
return false;
}
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ // TODO: generate idmap for split APKs
if (mInstaller.idmap(pkg.codePath, opkg.codePath, sharedGid) != 0) {
Slog.e(TAG, "Failed to generate idmap for " + pkg.codePath + " and " + opkg.codePath);
return false;
@@ -4179,6 +4229,7 @@
try {
pp.collectCertificates(pkg, parseFlags);
+ pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
mLastScanError = e.error;
return false;
@@ -4196,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;
@@ -4362,23 +4417,30 @@
}
}
- String codePath = null;
+ final String codePath = pkg.codePath;
+ final String[] splitCodePaths = pkg.splitCodePaths;
+
String resPath = null;
+ String[] splitResPaths = null;
if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
if (ps != null && ps.resourcePathString != null) {
resPath = ps.resourcePathString;
+ splitResPaths = deriveSplitResPaths(pkg.splitCodePaths);
} else {
// Should not happen at all. Just log an error.
Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
}
} else {
resPath = pkg.codePath;
+ splitResPaths = pkg.splitCodePaths;
}
- codePath = pkg.codePath;
// Set application objects path explicitly.
pkg.applicationInfo.sourceDir = codePath;
pkg.applicationInfo.publicSourceDir = resPath;
+ pkg.applicationInfo.splitSourceDirs = splitCodePaths;
+ pkg.applicationInfo.splitPublicSourceDirs = splitResPaths;
+
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
| SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride);
@@ -4626,52 +4688,51 @@
}
}
- boolean performed = false;
- if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
- String path = pkg.codePath;
- try {
- boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
- pkg.packageName,
- instructionSet,
- defer);
- // There are three basic cases here:
- // 1.) we need to dexopt, either because we are forced or it is needed
- // 2.) we are defering a needed dexopt
- // 3.) we are skipping an unneeded dexopt
- if (forceDex || (!defer && isDexOptNeededInternal)) {
- Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
- pkg.packageName, instructionSet);
- // Note that we ran dexopt, since rerunning will
- // probably just result in an error again.
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ final Collection<String> paths = pkg.getAllCodePaths();
+ for (String path : paths) {
+ try {
+ boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
+ pkg.packageName, instructionSet, defer);
+ // There are three basic cases here:
+ // 1.) we need to dexopt, either because we are forced or it is needed
+ // 2.) we are defering a needed dexopt
+ // 3.) we are skipping an unneeded dexopt
+ if (forceDex || (!defer && isDexOptNeededInternal)) {
+ Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
+ pkg.packageName, instructionSet);
+ // Note that we ran dexopt, since rerunning will
+ // probably just result in an error again.
+ pkg.mDexOptNeeded = false;
+ if (ret < 0) {
+ return DEX_OPT_FAILED;
+ }
+ return DEX_OPT_PERFORMED;
+ }
+ if (defer && isDexOptNeededInternal) {
+ if (mDeferredDexOpt == null) {
+ mDeferredDexOpt = new HashSet<PackageParser.Package>();
+ }
+ mDeferredDexOpt.add(pkg);
+ return DEX_OPT_DEFERRED;
+ }
pkg.mDexOptNeeded = false;
- if (ret < 0) {
- return DEX_OPT_FAILED;
- }
- return DEX_OPT_PERFORMED;
+ return DEX_OPT_SKIPPED;
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Apk not found for dexopt: " + path);
+ return DEX_OPT_FAILED;
+ } catch (IOException e) {
+ Slog.w(TAG, "IOException reading apk: " + path, e);
+ return DEX_OPT_FAILED;
+ } catch (StaleDexCacheError e) {
+ Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e);
+ return DEX_OPT_FAILED;
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception when doing dexopt : ", e);
+ return DEX_OPT_FAILED;
}
- if (defer && isDexOptNeededInternal) {
- if (mDeferredDexOpt == null) {
- mDeferredDexOpt = new HashSet<PackageParser.Package>();
- }
- mDeferredDexOpt.add(pkg);
- return DEX_OPT_DEFERRED;
- }
- pkg.mDexOptNeeded = false;
- return DEX_OPT_SKIPPED;
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Apk not found for dexopt: " + path);
- return DEX_OPT_FAILED;
- } catch (IOException e) {
- Slog.w(TAG, "IOException reading apk: " + path, e);
- return DEX_OPT_FAILED;
- } catch (StaleDexCacheError e) {
- Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e);
- return DEX_OPT_FAILED;
- } catch (Exception e) {
- Slog.w(TAG, "Exception when doing dexopt : ", e);
- return DEX_OPT_FAILED;
}
}
return DEX_OPT_SKIPPED;
@@ -4818,7 +4879,7 @@
}
}
if (p != null) {
- usesLibraryFiles.add(p.codePath);
+ usesLibraryFiles.addAll(p.getAllCodePaths());
}
}
@@ -4906,7 +4967,7 @@
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
final File scanFile = new File(pkg.codePath);
- if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
+ if (pkg.applicationInfo.sourceDir == null ||
pkg.applicationInfo.publicSourceDir == null) {
// Bail out. The resource and code paths haven't been set.
Slog.w(TAG, " Code and resource paths haven't been set correctly");
@@ -5355,6 +5416,7 @@
* only for non-system apps and system app upgrades.
*/
if (pkg.applicationInfo.nativeLibraryDir != null) {
+ // TODO: extend to extract native code from split APKs
final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
try {
// Enable gross and lame hacks for apps that are built with old
@@ -5656,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());
@@ -5908,6 +5971,8 @@
a.info.packageName = pkg.applicationInfo.packageName;
a.info.sourceDir = pkg.applicationInfo.sourceDir;
a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
+ a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;
+ a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;
a.info.dataDir = pkg.applicationInfo.dataDir;
a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
mInstrumentation.put(a.getComponentName(), a);
@@ -9021,6 +9086,10 @@
abstract boolean doPostDeleteLI(boolean delete);
abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException;
+ String[] getSplitCodePaths() {
+ return null;
+ }
+
/**
* Called before the source arguments are copied. This is used mostly
* for MoveParams when it needs to read the source file to put it in the
@@ -9109,10 +9178,6 @@
}
}
- String getCodePath() {
- return codeFileName;
- }
-
void createCopyFile() {
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
codeFileName = createTempPackageFile(installDir).getPath();
@@ -9268,10 +9333,6 @@
return status;
}
- String getResourcePath() {
- return resourceFileName;
- }
-
private String getResourcePathFromCodePath() {
final String codePath = getCodePath();
if (isFwdLocked()) {
@@ -9302,6 +9363,16 @@
}
@Override
+ String getCodePath() {
+ return codeFileName;
+ }
+
+ @Override
+ String getResourcePath() {
+ return resourceFileName;
+ }
+
+ @Override
String getNativeLibraryPath() {
if (libraryPath == null) {
libraryPath = getLibraryPathFromCodePath();
@@ -9687,7 +9758,7 @@
return PackageManager.INSTALL_SUCCEEDED;
}
- };
+ }
static String getAsecPackageName(String packageCid) {
int idx = packageCid.lastIndexOf("-");
@@ -9768,6 +9839,20 @@
return codePath.substring(sidx+1, eidx);
}
+ private static String[] deriveSplitResPaths(String[] splitCodePaths) {
+ String[] splitResPaths = null;
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ splitResPaths = new String[splitCodePaths.length];
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ final String splitCodePath = splitCodePaths[i];
+ final String resName = getApkName(splitCodePath) + ".zip";
+ splitResPaths[i] = new File(new File(splitCodePath).getParentFile(),
+ resName).getAbsolutePath();
+ }
+ }
+ return splitResPaths;
+ }
+
class PackageInstalledInfo {
String name;
int uid;
@@ -9824,7 +9909,7 @@
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
- updateSettingsLI(null, newPackage, installerPackageName, null, null, res);
+ updateSettingsLI(newPackage, installerPackageName, null, null, res);
// delete the partially installed application. the data directory will have to be
// restored if it was already existing
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -9914,8 +9999,7 @@
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
- updateSettingsLI(deletedPackage, newPackage, installerPackageName, allUsers,
- perUserInstalled, res);
+ updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);
updatedSettings = true;
}
}
@@ -10041,8 +10125,7 @@
}
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSettingsLI(deletedPackage, newPackage, installerPackageName, allUsers,
- perUserInstalled, res);
+ updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);
updatedSettings = true;
}
}
@@ -10068,21 +10151,13 @@
}
// Utility method used to move dex files during install.
- private int moveDexFilesLI(PackageParser.Package oldPackage, PackageParser.Package newPackage) {
- // TODO: extend to handle splits
- if ((newPackage.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ private int moveDexFilesLI(String oldCodePath, PackageParser.Package newPackage) {
+ // TODO: extend to move split APK dex files
+ if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
final String instructionSet = getAppInstructionSet(newPackage.applicationInfo);
-
- boolean moveSuccess = false;
- if (oldPackage != null
- && (oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
- if (mInstaller.movedex(oldPackage.codePath, newPackage.codePath, instructionSet)
- == 0) {
- moveSuccess = true;
- }
- }
-
- if (!moveSuccess) {
+ int retCode = mInstaller.movedex(oldCodePath, newPackage.codePath,
+ instructionSet);
+ if (retCode != 0) {
/*
* Programs may be lazily run through dexopt, so the
* source may not exist. However, something seems to
@@ -10091,19 +10166,17 @@
* remove the target to make sure there isn't a stale
* file from a previous version of the package.
*/
- if (oldPackage != null) {
- mInstaller.rmdex(oldPackage.codePath, instructionSet);
- }
- mInstaller.rmdex(newPackage.codePath, instructionSet);
newPackage.mDexOptNeeded = true;
+ mInstaller.rmdex(oldCodePath, instructionSet);
+ mInstaller.rmdex(newPackage.codePath, instructionSet);
}
}
return PackageManager.INSTALL_SUCCEEDED;
}
- private void updateSettingsLI(PackageParser.Package oldPackage,
- PackageParser.Package newPackage, String installerPackageName, int[] allUsers,
- boolean[] perUserInstalled, PackageInstalledInfo res) {
+ private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
+ int[] allUsers, boolean[] perUserInstalled,
+ PackageInstalledInfo res) {
String pkgName = newPackage.packageName;
synchronized (mPackages) {
//write settings. the installStatus will be incomplete at this stage.
@@ -10113,12 +10186,6 @@
mSettings.writeLPr();
}
- if ((res.returnCode = moveDexFilesLI(oldPackage, newPackage))
- != PackageManager.INSTALL_SUCCEEDED) {
- // Discontinue if moving dex files failed.
- return;
- }
-
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
synchronized (mPackages) {
@@ -10184,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;
@@ -10206,6 +10273,7 @@
try {
pp.collectCertificates(pkg, parseFlags);
+ pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.returnCode = e.error;
return;
@@ -10315,6 +10383,9 @@
pkg.codePath = args.getCodePath();
pkg.applicationInfo.sourceDir = args.getCodePath();
pkg.applicationInfo.publicSourceDir = args.getResourcePath();
+ pkg.applicationInfo.splitSourceDirs = args.getSplitCodePaths();
+ pkg.applicationInfo.splitPublicSourceDirs = deriveSplitResPaths(
+ pkg.applicationInfo.splitSourceDirs);
pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode, args.user,
@@ -11517,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);
}
}
@@ -11537,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);
}
@@ -12857,15 +12931,12 @@
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- PackageParser.Package oldPackage = new PackageParser.Package(
- pkg.packageName);
- oldPackage.codePath = pkg.codePath;
pkg.codePath = newCodePath;
// Move dex files around
- if (moveDexFilesLI(oldPackage, pkg) != PackageManager.INSTALL_SUCCEEDED) {
+ if (moveDexFilesLI(oldCodePath, pkg) != PackageManager.INSTALL_SUCCEEDED) {
// Moving of dex files failed. Set
// error code and abort move.
- pkg.codePath = oldPackage.codePath;
+ pkg.codePath = oldCodePath;
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
}
}
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/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index fb4b8f0..bd80b54 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -19,10 +19,10 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
-import com.android.server.BatteryService;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
+import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.Watchdog;
@@ -42,6 +42,7 @@
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.net.Uri;
import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -163,13 +164,14 @@
private static final int POWER_HINT_LOW_POWER_MODE = 5;
private final Context mContext;
+ private final ServiceThread mHandlerThread;
+ private final PowerManagerHandler mHandler;
+
private LightsManager mLightsManager;
- private BatteryService mBatteryService;
+ private BatteryManagerInternal mBatteryManagerInternal;
private DisplayManagerInternal mDisplayManagerInternal;
private IBatteryStats mBatteryStats;
private IAppOpsService mAppOps;
- private ServiceThread mHandlerThread;
- private PowerManagerHandler mHandler;
private WindowManagerPolicy mPolicy;
private Notifier mNotifier;
private WirelessChargerDetector mWirelessChargerDetector;
@@ -429,6 +431,11 @@
public PowerManagerService(Context context) {
super(context);
mContext = context;
+ mHandlerThread = new ServiceThread(TAG,
+ Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
+ mHandlerThread.start();
+ mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
@@ -451,39 +458,19 @@
public void onStart() {
publishBinderService(Context.POWER_SERVICE, new BinderService());
publishLocalService(PowerManagerInternal.class, new LocalService());
- }
-
- /**
- * Initialize the power manager.
- * Must be called before any other functions within the power manager are called.
- */
- public void init(LightsManager ls,
- BatteryService bs, IBatteryStats bss,
- IAppOpsService appOps) {
- mLightsManager = ls;
- mBatteryService = bs;
- mBatteryStats = bss;
- mAppOps = appOps;
- mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
- mHandlerThread = new ServiceThread(TAG,
- Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
- mHandlerThread.start();
- mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
}
- void setPolicy(WindowManagerPolicy policy) {
- synchronized (mLock) {
- mPolicy = policy;
- }
- }
-
- public void systemReady() {
+ public void systemReady(IAppOpsService appOps) {
synchronized (mLock) {
mSystemReady = true;
- mDreamManager = LocalServices.getService(DreamManagerInternal.class);
+ mAppOps = appOps;
+ mDreamManager = getLocalService(DreamManagerInternal.class);
+ mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
+ mPolicy = getLocalService(WindowManagerPolicy.class);
+ mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
@@ -494,6 +481,7 @@
// The notifier runs on the system server's main looper so as not to interfere
// with the animations and other critical functions of the power manager.
+ mBatteryStats = BatteryStatsService.getService();
mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
mScreenOnBlocker, mPolicy);
@@ -502,6 +490,8 @@
createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
+
+ mLightsManager = getLocalService(LightsManager.class);
mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
// Initialize display power management.
@@ -1168,10 +1158,10 @@
final boolean wasPowered = mIsPowered;
final int oldPlugType = mPlugType;
final boolean oldLevelLow = mBatteryLevelLow;
- mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
- mPlugType = mBatteryService.getPlugType();
- mBatteryLevel = mBatteryService.getBatteryLevel();
- mBatteryLevelLow = mBatteryService.getBatteryLevelLow();
+ mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+ mPlugType = mBatteryManagerInternal.getPlugType();
+ mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
+ mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
if (DEBUG_SPEW) {
Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
@@ -1254,7 +1244,7 @@
final boolean wasStayOn = mStayOn;
if (mStayOnWhilePluggedInSetting != 0
&& !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
- mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting);
+ mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
} else {
mStayOn = false;
}
@@ -3076,10 +3066,5 @@
mLowPowerModeListeners.add(listener);
}
}
-
- @Override
- public void setPolicy(WindowManagerPolicy policy) {
- PowerManagerService.this.setPolicy(policy);
- }
}
}
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..6798f3f 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 {
@@ -102,11 +106,9 @@
mLogHandler = new LogHandler(IoThread.get().getLooper());
mTvInputHardwareManager = new TvInputHardwareManager(context);
- registerBroadcastReceivers();
synchronized (mLock) {
mUserStates.put(mCurrentUserId, new UserState());
- buildTvInputListLocked(mCurrentUserId);
}
}
@@ -115,6 +117,16 @@
publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
}
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ registerBroadcastReceivers();
+ synchronized (mLock) {
+ buildTvInputListLocked(mCurrentUserId);
+ }
+ }
+ }
+
private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
@Override
@@ -123,6 +135,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 +195,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 +213,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 +400,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 +416,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 +501,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 +639,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 +726,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 +838,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 +988,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 +1155,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/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 6cb6b76..d05d0c7 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -961,8 +961,6 @@
// If the window is not touchable, do not report it but take into account
// the space it takes since the content behind it cannot be touched.
if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- unaccountedSpace.op(boundsInScreen, unaccountedSpace,
- Region.Op.DIFFERENCE);
continue;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 45326f7..f1d0585d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -391,7 +391,7 @@
final WindowStateAnimator winAnimator = unForceHiding.get(i);
winAnimator.setAnimation(a);
winAnimator.mAnimationIsEntrance = true;
- if (startKeyguardExit) {
+ if (startKeyguardExit && mKeyguardGoingAway) {
// Do one time only.
mPolicy.startKeyguardExitAnimation(mCurrentTime + a.getStartOffset(),
a.getDuration());
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2d8a34b..d04d668 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -766,6 +766,8 @@
mDisplaySettings = new DisplaySettings(context);
mDisplaySettings.readSettingsLocked();
+ LocalServices.addService(WindowManagerPolicy.class, mPolicy);
+
mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG));
mFxSession = new SurfaceSession();
@@ -779,7 +781,6 @@
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
- mPowerManagerInternal.setPolicy(mPolicy); // TODO: register as local service instead
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@Override
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 9a5079d..3696e24 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -61,8 +61,7 @@
jobject canvas, jint width, jint height) {
SkBitmap* bitmap = new SkBitmap;
- bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
- bitmap->allocPixels();
+ bitmap->allocN32Pixels(width, height);
bitmap->eraseColor(0);
INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap));
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/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3102cce..0d05c5f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -63,6 +63,7 @@
import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
+import com.android.server.fingerprint.FingerprintService;
import com.android.server.hdmi.HdmiControlService;
import com.android.server.input.InputManagerService;
import com.android.server.job.JobSchedulerService;
@@ -146,8 +147,13 @@
private PowerManagerService mPowerManagerService;
private ActivityManagerService mActivityManagerService;
private DisplayManagerService mDisplayManagerService;
+ private PackageManagerService mPackageManagerService;
+ private PackageManager mPackageManager;
private ContentResolver mContentResolver;
+ private boolean mOnlyCore;
+ private boolean mFirstBoot;
+
/**
* Called to initialize native system services.
*/
@@ -161,6 +167,7 @@
}
public SystemServer() {
+ // Check for factory test mode.
mFactoryTestMode = FactoryTest.getMode();
}
@@ -243,7 +250,7 @@
startBootstrapServices();
startCoreServices();
startOtherServices();
- } catch (RuntimeException ex) {
+ } catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
@@ -287,36 +294,84 @@
mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}
+ /**
+ * Starts the small tangle of critical services that are needed to get
+ * the system off the ground. These services have complex mutual dependencies
+ * which is why we initialize them all in one place here. Unless your service
+ * is also entwined in these dependencies, it should be initialized in one of
+ * the other functions.
+ */
private void startBootstrapServices() {
// Wait for installd to finish starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
// permissions. We need this to complete before we initialize other services.
mInstaller = mSystemServiceManager.startService(Installer.class);
- // Power manager needs to be started early because other services need it.
- // TODO: The conversion to the new pattern is incomplete. We need to switch
- // the power manager's dependencies over then we can use boot phases to arrange
- // initialization order and remove the mPowerManagerService field.
- mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
-
// Activity manager runs the show.
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
- }
- private void startCoreServices() {
+ // Power manager needs to be started early because other services need it.
+ // Native daemons may be watching for it to be registered so it must be ready
+ // to handle incoming binder calls immediately (including being able to verify
+ // the permissions for those calls).
+ mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
+
+ // Now that the power manager has been started, let the activity manager
+ // initialize power management features.
+ mActivityManagerService.initPowerManagement();
+
// Display manager is needed to provide display metrics before package manager
// starts up.
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
+
+ // We need the default display before we can initialize the package manager.
+ mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ // Only run "core" apps if we're encrypting the device.
+ String cryptState = SystemProperties.get("vold.decrypt");
+ if (ENCRYPTING_STATE.equals(cryptState)) {
+ Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
+ mOnlyCore = true;
+ } else if (ENCRYPTED_STATE.equals(cryptState)) {
+ Slog.w(TAG, "Device encrypted - only parsing core apps");
+ mOnlyCore = true;
+ }
+
+ // Start the package manager.
+ Slog.i(TAG, "Package Manager");
+ mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
+ mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
+ mFirstBoot = mPackageManagerService.isFirstBoot();
+ mPackageManager = mSystemContext.getPackageManager();
+
+ // Initialize attribute cache used to cache resources from packages.
+ AttributeCache.init(mSystemContext);
+
+ // Set up the Application instance for the system process and get started.
+ mActivityManagerService.setSystemProcess();
}
+ /**
+ * Starts some essential services that are not tangled up in the bootstrap process.
+ */
+ private void startCoreServices() {
+ // Manages LEDs and display backlight.
+ mSystemServiceManager.startService(LightsService.class);
+
+ // Tracks the battery level. Requires LightService.
+ mSystemServiceManager.startService(BatteryService.class);
+ }
+
+ /**
+ * Starts a miscellaneous grab bag of stuff that has yet to be refactored
+ * and organized.
+ */
private void startOtherServices() {
final Context context = mSystemContext;
AccountManagerService accountManager = null;
ContentService contentService = null;
- LightsManager lights = null;
- BatteryService battery = null;
VibratorService vibrator = null;
IAlarmManager alarm = null;
MountService mountService = null;
@@ -326,7 +381,6 @@
ConnectivityService connectivity = null;
NetworkScoreService networkScore = null;
NsdService serviceDiscovery= null;
- IPackageManager pm = null;
WindowManagerService wm = null;
BluetoothManagerService bluetooth = null;
UsbService usb = null;
@@ -339,8 +393,6 @@
ConsumerIrService consumerIr = null;
AudioService audioService = null;
- boolean onlyCore = false;
- boolean firstBoot = false;
boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
@@ -352,38 +404,12 @@
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
try {
- Slog.i(TAG, "Telephony Registry");
- telephonyRegistry = new TelephonyRegistry(context);
- ServiceManager.addService("telephony.registry", telephonyRegistry);
-
Slog.i(TAG, "Scheduling Policy");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
- AttributeCache.init(context);
-
- // We need the default display before we can initialize the package manager.
- mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
-
- Slog.i(TAG, "Package Manager");
- // Only run "core" apps if we're encrypting the device.
- String cryptState = SystemProperties.get("vold.decrypt");
- if (ENCRYPTING_STATE.equals(cryptState)) {
- Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
- onlyCore = true;
- } else if (ENCRYPTED_STATE.equals(cryptState)) {
- Slog.w(TAG, "Device encrypted - only parsing core apps");
- onlyCore = true;
- }
-
- pm = PackageManagerService.main(context, mInstaller,
- mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
- onlyCore);
- try {
- firstBoot = pm.isFirstBoot();
- } catch (RemoteException e) {
- }
-
- mActivityManagerService.setSystemProcess();
+ Slog.i(TAG, "Telephony Registry");
+ telephonyRegistry = new TelephonyRegistry(context);
+ ServiceManager.addService("telephony.registry", telephonyRegistry);
Slog.i(TAG, "Entropy Mixer");
ServiceManager.addService("entropy", new EntropyMixer(context));
@@ -411,24 +437,10 @@
Slog.i(TAG, "System Content Providers");
mActivityManagerService.installSystemProviders();
- mSystemServiceManager.startService(LightsService.class);
- lights = LocalServices.getService(LightsManager.class);
-
- Slog.i(TAG, "Battery Service");
- battery = new BatteryService(context, lights);
- ServiceManager.addService("battery", battery);
-
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
- // TODO: use boot phase
- // only initialize the power service after we have started the
- // lights service, content providers and the battery service.
- mPowerManagerService.init(lights, battery,
- BatteryStatsService.getService(),
- mActivityManagerService.getAppOpsService());
-
Slog.i(TAG, "Consumer IR Service");
consumerIr = new ConsumerIrService(context);
ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
@@ -447,7 +459,7 @@
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
- !firstBoot, onlyCore);
+ !mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
@@ -521,7 +533,7 @@
}
try {
- pm.performBootDexOpt();
+ mPackageManagerService.performBootDexOpt();
} catch (Throwable e) {
reportWtf("performing boot dexopt", e);
}
@@ -559,13 +571,9 @@
reportWtf("starting LockSettingsService service", e);
}
- try {
- // Always start the Device Policy Manager, so that the API is compatible with
- // API8.
- mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
- } catch (Throwable e) {
- reportWtf("starting DevicePolicyService", e);
- }
+ // Always start the Device Policy Manager, so that the API is compatible with
+ // API8.
+ mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
}
if (!disableSystemUI) {
@@ -636,39 +644,17 @@
reportWtf("starting NetworkPolicy Service", e);
}
- try {
- mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi P2pService", e);
- }
+ mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
- try {
- mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi PasspointService", e);
- }
+ mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
- try {
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi Service", e);
- }
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- try {
- Slog.i(TAG, "Wi-Fi Scanning Service");
- mSystemServiceManager.startService(
+ mSystemServiceManager.startService(
"com.android.server.wifi.WifiScanningService");
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi Scanning Service", e);
- }
-
if (!isEmulator) {
- try {
- mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting Ethernet Service", e);
- }
+ mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
} else {
// Don't start the Ethernet service on the emulator because
// it interferes with qemu's SLIRP emulation, which uses
@@ -712,7 +698,7 @@
* AppWidget Provider. Make sure MountService is completely started
* first before continuing.
*/
- if (mountService != null && !onlyCore) {
+ if (mountService != null && !mOnlyCore) {
mountService.waitForAsecScan();
}
@@ -810,14 +796,11 @@
}
if (!disableNonCoreServices) {
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST) ||
- pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY)) {
- // Manage USB host and device support
- mSystemServiceManager.startService(USB_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- reportWtf("starting UsbService", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
+ || mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_USB_ACCESSORY)) {
+ // Manage USB host and device support
+ mSystemServiceManager.startService(USB_SERVICE_CLASS);
}
try {
@@ -837,20 +820,12 @@
mSystemServiceManager.startService(JobSchedulerService.class);
if (!disableNonCoreServices) {
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
- mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting Backup Service", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
+ mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
}
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
- mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- reportWtf("starting AppWidget Service", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+ mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
}
try {
@@ -860,13 +835,8 @@
reportWtf("starting Recognition Service", e);
}
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
- Slog.i(TAG, "Voice Recognition Service");
- mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- reportWtf("starting Voice Recognition Service", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
+ mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
}
}
@@ -932,45 +902,17 @@
}
}
- try {
- Slog.i(TAG, "IdleMaintenanceService");
- new IdleMaintenanceService(context, battery);
- } catch (Throwable e) {
- reportWtf("starting IdleMaintenanceService", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
+ mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
}
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
- mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- reportWtf("starting Print Service", e);
- }
+ mSystemServiceManager.startService(RestrictionsManagerService.class);
- try {
- mSystemServiceManager.startService(RestrictionsManagerService.class);
- } catch (Throwable e) {
- reportWtf("starting RestrictionsManagerService", e);
- }
+ mSystemServiceManager.startService(MediaSessionService.class);
- try {
- mSystemServiceManager.startService(MediaSessionService.class);
- } catch (Throwable e) {
- reportWtf("starting MediaSessionService", e);
- }
+ mSystemServiceManager.startService(HdmiControlService.class);
- try {
- mSystemServiceManager.startService(HdmiControlService.class);
- } catch (Throwable e) {
- reportWtf("starting HdmiControlService", e);
- }
-
- try {
- Slog.i(TAG, "TvInputManagerService");
- mSystemServiceManager.startService(TvInputManagerService.class);
- } catch (Throwable e) {
- reportWtf("starting TvInputManagerService", e);
- }
+ mSystemServiceManager.startService(TvInputManagerService.class);
if (!disableNonCoreServices) {
try {
@@ -981,27 +923,20 @@
reportWtf("starting MediaRouterService", e);
}
- try {
- Slog.i(TAG, "Trust Manager");
- mSystemServiceManager.startService(TrustManagerService.class);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting TrustManagerService", e);
- }
+ mSystemServiceManager.startService(TrustManagerService.class);
+
+ mSystemServiceManager.startService(FingerprintService.class);
try {
Slog.i(TAG, "BackgroundDexOptService");
- new BackgroundDexOptService(context);
+ BackgroundDexOptService.schedule(context);
} catch (Throwable e) {
reportWtf("starting BackgroundDexOptService", e);
}
+
}
- try {
- Slog.i(TAG, "LauncherAppsService");
- mSystemServiceManager.startService(LauncherAppsService.class);
- } catch (Throwable t) {
- reportWtf("starting LauncherAppsService", t);
- }
+ mSystemServiceManager.startService(LauncherAppsService.class);
}
// Before things start rolling, be sure we have decided whether
@@ -1058,27 +993,26 @@
try {
// TODO: use boot phase
- mPowerManagerService.systemReady();
+ mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
} catch (Throwable e) {
reportWtf("making Power Manager Service ready", e);
}
try {
- pm.systemReady();
+ mPackageManagerService.systemReady();
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
try {
// TODO: use boot phase and communicate these flags some other way
- mDisplayManagerService.systemReady(safeMode, onlyCore);
+ mDisplayManagerService.systemReady(safeMode, mOnlyCore);
} catch (Throwable e) {
reportWtf("making Display Manager Service ready", e);
}
// These are needed to propagate to the runnable below.
final MountService mountServiceF = mountService;
- final BatteryService batteryF = battery;
final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
@@ -1127,11 +1061,6 @@
reportWtf("making Mount Service ready", e);
}
try {
- if (batteryF != null) batteryF.systemReady();
- } catch (Throwable e) {
- reportWtf("making Battery Service ready", e);
- }
- try {
if (networkScoreF != null) networkScoreF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Score Service ready", e);
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index d974509..8a4e123 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -20,15 +20,9 @@
import android.os.Bundle;
import android.telephony.DisconnectCause;
-import android.os.SystemClock;
-
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* A {@link android.app.Service} that provides telephone connections to
diff --git a/telecomm/java/android/telecomm/PhoneApplication.java b/telecomm/java/android/telecomm/PhoneApplication.java
deleted file mode 100644
index 1da54e0..0000000
--- a/telecomm/java/android/telecomm/PhoneApplication.java
+++ /dev/null
@@ -1,183 +0,0 @@
-package android.telecomm;
-
-import android.annotation.SystemApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.telecomm.ITelecommService;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class for managing the primary phone application that will receive incoming calls, and be allowed
- * to make emergency outgoing calls.
- *
- * @hide
- */
-public class PhoneApplication {
- private static final String TAG = PhoneApplication.class.getSimpleName();
- private static final String TELECOMM_SERVICE_NAME = "telecomm";
-
- /**
- * Sets the specified package name as the default phone application. The caller of this method
- * needs to have permission to write to secure settings.
- *
- * @hide
- * */
- @SystemApi
- public static void setDefaultPhoneApplication(String packageName, Context context) {
- // Get old package name
- String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
- Settings.Secure.PHONE_DEFAULT_APPLICATION);
-
- if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
- // No change
- return;
- }
-
- // Only make the change if the new package belongs to a valid phone application
- List<ComponentName> componentNames = getInstalledPhoneApplications(context);
- ComponentName foundComponent = null;
- for (ComponentName componentName : componentNames) {
- if (TextUtils.equals(componentName.getPackageName(), packageName)) {
- foundComponent = componentName;
- break;
- }
- }
-
- if (foundComponent != null) {
- // Update the secure setting.
- Settings.Secure.putString(context.getContentResolver(),
- Settings.Secure.PHONE_DEFAULT_APPLICATION, foundComponent.getPackageName());
- }
- }
-
- /**
- * Returns the installed phone application that will be used to receive incoming calls, and is
- * allowed to make emergency calls.
- *
- * The application will be returned in order of preference:
- * 1) User selected phone application (if still installed)
- * 2) Pre-installed system dialer (if not disabled)
- * 3) Null
- *
- * @hide
- * */
- @SystemApi
- public static ComponentName getDefaultPhoneApplication(Context context) {
- String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
- Settings.Secure.PHONE_DEFAULT_APPLICATION);
-
- final List<ComponentName> componentNames = getInstalledPhoneApplications(context);
- if (!TextUtils.isEmpty(defaultPackageName)) {
- for (ComponentName componentName : componentNames) {
- if (TextUtils.equals(componentName.getPackageName(), defaultPackageName)) {
- return componentName;
- }
- }
- }
-
- // No user-set dialer found, fallback to system dialer
- ComponentName systemDialer = null;
- try {
- systemDialer = getTelecommService().getSystemPhoneApplication();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecommService#getSystemPhoneApplication", e);
- return null;
- }
-
- if (systemDialer == null) {
- // No system dialer configured at build time
- return null;
- }
-
- // Verify that the system dialer has not been disabled.
- return getComponentName(componentNames, systemDialer.getPackageName());
- }
-
- /**
- * Returns a list of installed and available phone applications.
- *
- * In order to appear in the list, a phone application must implement an intent-filter with
- * the DIAL intent for the following schemes:
- *
- * 1) Empty scheme
- * 2) tel Uri scheme
- *
- * @hide
- **/
- @SystemApi
- public static List<ComponentName> getInstalledPhoneApplications(Context context) {
- PackageManager packageManager = context.getPackageManager();
-
- // Get the list of apps registered for the DIAL intent with empty scheme
- Intent intent = new Intent(Intent.ACTION_DIAL);
- List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
-
- List<ComponentName> componentNames = new ArrayList<ComponentName> ();
-
- for (ResolveInfo resolveInfo : resolveInfoList) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
- }
- final ComponentName componentName =
- new ComponentName(activityInfo.packageName, activityInfo.name);
- componentNames.add(componentName);
- }
-
- // TODO: Filter for apps that don't handle DIAL intent with tel scheme
- return componentNames;
- }
-
- /**
- * Returns the {@link ComponentName} for the installed phone application for a given package
- * name.
- *
- * @param context A valid context.
- * @param packageName to retrieve the {@link ComponentName} for.
- *
- * @return The {@link ComponentName} for the installed phone application corresponding to the
- * package name, or null if none is found.
- *
- * @hide
- */
- @SystemApi
- public static ComponentName getPhoneApplicationForPackageName(Context context,
- String packageName) {
- return getComponentName(getInstalledPhoneApplications(context), packageName);
- }
-
- /**
- * Returns the component from a list of application components that corresponds to the package
- * name.
- *
- * @param componentNames A list of component names
- * @param packageName The package name to look for
- * @return The {@link ComponentName} that matches the provided packageName, or null if not
- * found.
- */
- private static ComponentName getComponentName(List<ComponentName> componentNames,
- String packageName) {
- for (ComponentName componentName : componentNames) {
- if (TextUtils.equals(packageName, componentName.getPackageName())) {
- return componentName;
- }
- }
- return null;
- }
-
- private static ITelecommService getTelecommService() {
- return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
- }
-}
diff --git a/telecomm/java/android/telecomm/Subscription.aidl b/telecomm/java/android/telecomm/Subscription.aidl
new file mode 100644
index 0000000..6327fcc
--- /dev/null
+++ b/telecomm/java/android/telecomm/Subscription.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package android.telecomm;
+
+/**
+ * {@hide}
+ */
+parcelable Subscription;
diff --git a/telecomm/java/android/telecomm/Subscription.java b/telecomm/java/android/telecomm/Subscription.java
index f187f4d..964db4a 100644
--- a/telecomm/java/android/telecomm/Subscription.java
+++ b/telecomm/java/android/telecomm/Subscription.java
@@ -16,25 +16,169 @@
package android.telecomm;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Rlog;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import java.util.MissingResourceException;
/**
* Represents a distinct subscription, line of service or call placement method that
- * a {@link ConnectionService} can use to place phone calls.
+ * the system can use to place phone calls.
*/
public class Subscription implements Parcelable {
- public Subscription() {}
+ private static final int NO_DENSITY = -1;
+
+ private static final String LOG_TAG = "Subscription";
+
+ private final ComponentName mComponentName;
+ private final String mId;
+ private final Uri mHandle;
+ private final int mLabelResId;
+ private final int mShortDescriptionResId;
+ private final int mIconResId;
+ private final boolean mIsEnabled;
+ private final boolean mIsSystemDefault;
+
+ public Subscription(
+ ComponentName componentName,
+ String id,
+ Uri handle,
+ int labelResId,
+ int shortDescriptionResId,
+ int iconResId,
+ boolean isEnabled,
+ boolean isSystemDefault) {
+ mComponentName = componentName;
+ mId = id;
+ mHandle = handle;
+ mLabelResId = labelResId;
+ mShortDescriptionResId = shortDescriptionResId;
+ mIconResId = iconResId;
+ mIsSystemDefault = isSystemDefault;
+ mIsEnabled = isEnabled;
+ }
+
+ /**
+ * The {@code ComponentName} of the {@link android.telecomm.ConnectionService} which is
+ * responsible for making phone calls using this {@code Subscription}.
+ *
+ * @return A suitable {@code ComponentName}.
+ */
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
+ * A unique identifier for this {@code Subscription}, generated by and meaningful to the
+ * {@link android.telecomm.ConnectionService} that created it.
+ *
+ * @return A unique identifier for this {@code Subscription}.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * The handle (e.g., a phone number) associated with this {@code Subscription}. This represents
+ * the destination from which outgoing calls using this {@code Subscription} will appear to come
+ * from, if applicable, and the destination to which incoming calls using this
+ * {@code Subscription} may be addressed.
+ *
+ * @return A handle expressed as a {@code Uri}, for example, a phone number.
+ */
+ public Uri getHandle() {
+ return mHandle;
+ }
+
+ /**
+ * A short string label describing this {@code Subscription}.
+ *
+ * @param context The invoking {@code Context}, used for retrieving resources.
+ *
+ * @return A label for this {@code Subscription}.
+ */
+ public String getLabel(Context context) {
+ return getString(context, mLabelResId);
+ }
+
+ /**
+ * A short paragraph describing this {@code Subscription}.
+ *
+ * @param context The invoking {@code Context}, used for retrieving resources.
+ *
+ * @return A description for this {@code Subscription}.
+ */
+ public String getShortDescription(Context context) {
+ return getString(context, mShortDescriptionResId);
+ }
+
+ /**
+ * An icon to represent this {@code Subscription} in a user interface.
+ *
+ * @param context The invoking {@code Context}, used for retrieving resources.
+ *
+ * @return An icon for this {@code Subscription}.
+ */
+ public Drawable getIcon(Context context) {
+ return getIcon(context, mIconResId, NO_DENSITY);
+ }
+
+ /**
+ * An icon to represent this {@code Subscription} in a user interface.
+ *
+ * @param context The invoking {@code Context}, used for retrieving resources.
+ * @param density A display density from {@link DisplayMetrics}.
+ *
+ * @return An icon for this {@code Subscription}.
+ */
+ public Drawable getIcon(Context context, int density) {
+ return getIcon(context, mIconResId, density);
+ }
+
+ /**
+ * Whether this {@code Subscription} is enabled for use.
+ *
+ * @return {@code true} if this {@code Subscription} is enabled.
+ */
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /**
+ * Whether this {@code Subscription} is the system default.
+ *
+ * @return {@code true} if this {@code Subscription} is the system default.
+ */
+ public boolean isSystemDefault() {
+ return mIsSystemDefault;
+ }
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel out, int flags) {}
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mComponentName, flags);
+ out.writeString(mId);
+ out.writeString(mHandle != null ? mHandle.toString() : "");
+ out.writeInt(mLabelResId);
+ out.writeInt(mShortDescriptionResId);
+ out.writeInt(mIconResId);
+ out.writeInt(mIsEnabled ? 1 : 0);
+ out.writeInt(mIsSystemDefault ? 1 : 0);
+ }
- public static final Parcelable.Creator<Subscription> CREATOR
- = new Parcelable.Creator<Subscription>() {
+ public static final Creator<Subscription> CREATOR
+ = new Creator<Subscription>() {
public Subscription createFromParcel(Parcel in) {
return new Subscription(in);
}
@@ -44,5 +188,54 @@
}
};
- private Subscription(Parcel in) {}
+ private Subscription(Parcel in) {
+ mComponentName = in.readParcelable(getClass().getClassLoader());
+ mId = in.readString();
+ String uriString = in.readString();
+ mHandle = uriString.length() > 0 ? Uri.parse(uriString) : null;
+ mLabelResId = in.readInt();
+ mShortDescriptionResId = in.readInt();
+ mIconResId = in.readInt();
+ mIsEnabled = in.readInt() == 1;
+ mIsSystemDefault = in.readInt() == 1;
+ }
+
+ private String getString(Context context, int resId) {
+ Context packageContext;
+ try {
+ packageContext = context.createPackageContext(mComponentName.getPackageName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ if (Rlog.isLoggable(LOG_TAG, Log.WARN)) {
+ Rlog.w(LOG_TAG, "Cannot find package " + mComponentName.getPackageName());
+ }
+ return null;
+ }
+ String result = packageContext.getString(resId);
+ if (result == null && Rlog.isLoggable(LOG_TAG, Log.WARN)) {
+ Rlog.w(LOG_TAG, "Cannot find string " + resId + " in package " +
+ mComponentName.getPackageName());
+ }
+ return result;
+ }
+
+ private Drawable getIcon(Context context, int resId, int density) {
+ Context packageContext;
+ try {
+ packageContext = context.createPackageContext(mComponentName.getPackageName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ if (Rlog.isLoggable(LOG_TAG, Log.WARN)) {
+ Rlog.w(LOG_TAG, "Cannot find package " + mComponentName.getPackageName());
+ }
+ return null;
+ }
+ try {
+ return density == NO_DENSITY ?
+ packageContext.getResources().getDrawable(resId) :
+ packageContext.getResources().getDrawableForDensity(resId, density);
+ } catch (MissingResourceException e) {
+ Rlog.e(LOG_TAG, "Cannot find icon " + resId + " in package " +
+ mComponentName.getPackageName() + ": " + e.toString());
+ return null;
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 4ca878e..0a12c08 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -16,6 +16,7 @@
package android.telecomm;
+import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
@@ -55,19 +56,11 @@
public static final String ACTION_CALL_SERVICE_SELECTOR = CallServiceSelector.class.getName();
/**
- * Activity action: Ask the user to change the default phone application. This will show a
- * dialog that asks the user whether they want to replace the current default phone application
- * with the one defined in {@link #EXTRA_PACKAGE_NAME}.
+ * Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
+ * the speakerphone should be automatically turned on for an outgoing call.
*/
- public static final String ACTION_CHANGE_DEFAULT_PHONE =
- "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
-
- /**
- * The PackageName string passed in as an extra for {@link #ACTION_CHANGE_DEFAULT_PHONE}.
- *
- * @see #ACTION_CHANGE_DEFAULT_PHONE
- */
- public static final String EXTRA_PACKAGE_NAME = "package";
+ public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE =
+ "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
/**
* Extra for {@link #ACTION_INCOMING_CALL} containing the {@link CallServiceDescriptor} that
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 638b86a..c758c6d 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -16,7 +16,7 @@
package com.android.internal.telecomm;
-import android.content.ComponentName;
+import android.telecomm.Subscription;
/**
* Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
@@ -42,7 +42,17 @@
void showCallScreen(boolean showDialpad);
/**
- * Returns the component name of the phone application installed on the system partition.
+ * Gets a list of Subscriptions.
*/
- ComponentName getSystemPhoneApplication();
+ List<Subscription> getSubscriptions();
+
+ /**
+ * Sets the enabled state of a given Subscription.
+ */
+ void setEnabled(in Subscription subscription, boolean enabled);
+
+ /**
+ * Sets a given Subscription as the system default.
+ */
+ void setSystemDefault(in Subscription subscription);
}
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 50bbb1e..124a8ec 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -19,15 +19,12 @@
import android.annotation.SystemApi;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.telephony.Rlog;
+import android.telecomm.Subscription;
import android.util.Log;
import com.android.internal.telecomm.ITelecommService;
@@ -40,7 +37,6 @@
import java.io.FileInputStream;
import java.io.IOException;
-import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -302,6 +298,17 @@
public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
/**
+ * The lookup key used with an {@link android.content.Intent#ACTION_CALL} or
+ * {@link android.content.Intent#ACTION_DIAL} {@code Intent} for a {@link Subscription}
+ * object indicating a preference when making a phone connection.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_SUBSCRIPTION = "subscription";
+
+ /**
* Broadcast intent action indicating that a precise call state
* (cellular) on the device has changed.
*
@@ -3028,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();
@@ -3112,4 +3097,40 @@
}
return false;
}
+
+ /**
+ * Return a list of Subscriptions that can be used to indicate a preference when making
+ * a phone call.
+ *
+ * @see #EXTRA_SUBSCRIPTION
+ * @return A list of {@code Subscription} objects.
+ */
+ public List<Subscription> getSubscriptions() {
+ try {
+ return getTelecommService().getSubscriptions();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getSubscriptions", e);
+ }
+ return null;
+ }
+
+ /** @hide */
+ @SystemApi
+ public void setEnabled(Subscription subscription, boolean enabled) {
+ try {
+ getTelecommService().setEnabled(subscription, enabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setEnabled", e);
+ }
+ }
+
+ /** @hide */
+ @SystemApi
+ public void setSystemDefault(Subscription subscription) {
+ try {
+ getTelecommService().setSystemDefault(subscription);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setSystemDefault", e);
+ }
+ }
}
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/IdleServiceTest/Android.mk b/tests/IdleServiceTest/Android.mk
deleted file mode 100644
index a7879c5..0000000
--- a/tests/IdleServiceTest/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := IdleServiceTest
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/tests/IdleServiceTest/AndroidManifest.xml b/tests/IdleServiceTest/AndroidManifest.xml
deleted file mode 100644
index 16d2324..0000000
--- a/tests/IdleServiceTest/AndroidManifest.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?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.idleservicetest">
-
- <application>
- <service android:name="TestService"
- android:exported="true"
- android:enabled="true"
- android:permission="android.permission.BIND_IDLE_SERVICE" >
- <intent-filter>
- <action android:name="android.service.idle.IdleService" />
- </intent-filter>
- </service>
-
- <service android:name="CrashingTestService"
- android:exported="true"
- android:enabled="true"
- android:permission="android.permission.BIND_IDLE_SERVICE" >
- <intent-filter>
- <action android:name="android.service.idle.IdleService" />
- </intent-filter>
- </service>
-
- <service android:name="TimeoutTestService"
- android:exported="true"
- android:enabled="true"
- android:permission="android.permission.BIND_IDLE_SERVICE" >
- <intent-filter>
- <action android:name="android.service.idle.IdleService" />
- </intent-filter>
- </service>
-
- <!-- UnpermissionedTestService should never run because it does
- not require the necessary permission in its <service> block -->
- <service android:name="UnpermissionedTestService"
- android:exported="true"
- android:enabled="true" >
- <intent-filter>
- <action android:name="android.service.idle.IdleService" />
- </intent-filter>
- </service>
-
- </application>
-</manifest>
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java
deleted file mode 100644
index 022ebcf..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.os.Handler;
-import android.util.Log;
-
-public class CrashingTestService extends IdleService {
- static final String TAG = "CrashingTestService";
-
- String mNull = null;
-
- @Override
- public boolean onIdleStart() {
- Log.i(TAG, "Idle maintenance: onIdleStart()");
-
- Handler h = new Handler();
- Runnable r = new Runnable() {
- @Override
- public void run() {
- Log.i(TAG, "Explicitly crashing");
- if (mNull.equals("")) {
- Log.i(TAG, "won't happen");
- }
- }
- };
- Log.i(TAG, "Posting explicit crash in 15 seconds");
- h.postDelayed(r, 15 * 1000);
- return true;
- }
-
- @Override
- public void onIdleStop() {
- Log.i(TAG, "Idle maintenance: onIdleStop()");
- }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java
deleted file mode 100644
index 7e9805f..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.os.Handler;
-import android.util.Log;
-
-public class TestService extends IdleService {
- static final String TAG = "TestService";
-
- @Override
- public boolean onIdleStart() {
- Log.i(TAG, "Idle maintenance: onIdleStart()");
-
- Handler h = new Handler();
- Runnable r = new Runnable() {
- @Override
- public void run() {
- Log.i(TAG, "Explicitly finishing idle");
- finishIdle();
- }
- };
- Log.i(TAG, "Posting explicit finish in 15 seconds");
- h.postDelayed(r, 15 * 1000);
- return true;
- }
-
- @Override
- public void onIdleStop() {
- Log.i(TAG, "Idle maintenance: onIdleStop()");
- }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java
deleted file mode 100644
index b2ba21b..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.util.Log;
-
-public class TimeoutTestService extends IdleService {
- private static final String TAG = "TimeoutTestService";
-
- @Override
- public boolean onIdleStart() {
- Log.i(TAG, "onIdleStart() but anticipating time-slice timeout");
- return true;
- }
-
- @Override
- public void onIdleStop() {
- Log.i(TAG, "onIdleStop() so we're done");
- }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java
deleted file mode 100644
index b9fe32b..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.util.Log;
-
-// Should never be invoked because its manifest declaration does not
-// require the necessary permission.
-public class UnpermissionedTestService extends IdleService {
- private static final String TAG = "UnpermissionedTestService";
-
- @Override
- public boolean onIdleStart() {
- Log.e(TAG, "onIdleStart() for this service should never be called!");
- return false;
- }
-
- @Override
- public void onIdleStop() {
- Log.e(TAG, "onIdleStop() for this service should never be called!");
- }
-
-}
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/anim/trim_path_animation05.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation05.xml
new file mode 100644
index 0000000..7012f4b
--- /dev/null
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation05.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="sequentially" >
+
+ <objectAnimator
+ android:duration="3000"
+ android:propertyName="pathData"
+ android:valueFrom="@string/triangle"
+ android:valueTo="@string/rectangle"
+ android:valueType="pathType"/>
+
+ <objectAnimator
+ android:duration="3000"
+ android:propertyName="pathData"
+ android:valueFrom="@string/rectangle2"
+ android:valueTo="@string/equal2"
+ android:valueType="pathType"/>
+
+</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..b37b19f 100644
--- a/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
@@ -19,9 +19,13 @@
<target
android:name="pie1"
android:animation="@anim/trim_path_animation01" />
+
<target
android:name="v"
android:animation="@anim/trim_path_animation02" />
+ <target
+ android:name="v"
+ android:animation="@anim/trim_path_animation05" />
<target
android:name="rotationGroup"
@@ -32,5 +36,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
+</animated-vector>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
index e28ec41..a212def 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
@@ -32,27 +32,21 @@
android:name="pie1"
android:fill="#00000000"
android:pathData="M300,70 a230,230 0 1,0 1,0 z"
- android:stroke="#FF00FF00"
+ android:stroke="#FF777777"
android:strokeWidth="70"
android:trimPathEnd=".75"
android:trimPathOffset="0"
android:trimPathStart="0" />
<path
android:name="v"
- android:fill="#FF00FF00"
- android:pathData="M300,70 l 0,-70 70,70 -70,70z" />
+ android:fill="#000000"
+ android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
<group
android:name="translateToCenterGroup"
android:rotation="0.0"
android:translateX="200.0"
android:translateY="200.0" >
- <path
- android:name="twoLines"
- android:pathData="@string/twoLinePathData"
- android:stroke="#FFFF0000"
- android:strokeWidth="20" />
-
<group
android:name="rotationGroup2"
android:pivotX="0.0"
@@ -61,7 +55,7 @@
<path
android:name="twoLines1"
android:pathData="@string/twoLinePathData"
- android:stroke="#FF00FF00"
+ android:stroke="#FFFF0000"
android:strokeWidth="20" />
<group
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/res/values/strings.xml b/tests/VectorDrawableTest/res/values/strings.xml
index b49a1aa..6ae3d7f 100644
--- a/tests/VectorDrawableTest/res/values/strings.xml
+++ b/tests/VectorDrawableTest/res/values/strings.xml
@@ -16,4 +16,11 @@
<resources>
<string name="twoLinePathData" >"M 0,0 v 100 M 0,0 h 100"</string>
+
+ <string name="triangle" > "M300,70 l 0,-70 70,70 0,0 -70,70z"</string>
+ <string name="rectangle" >"M300,70 l 0,-70 70,0 0,140 -70,0 z"</string>
+
+ <string name="rectangle2" >"M300,70 l 0,-70 70,0 0,70z M300,70 l 70,0 0,70 -70,0z"</string>
+ <string name="equal2" > "M300,35 l 0,-35 70,0 0,35z M300,105 l 70,0 0,35 -70,0z"</string>
+
</resources>
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;
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
index aec87976..33db3f5 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
@@ -213,22 +213,22 @@
public String venueName;
/** list of network authentication types */
- public List<NetworkAuthType> networkAuthType;
+ public List<NetworkAuthType> networkAuthTypeList;
/** list of roaming consortium OIs */
- public List<String> roamingConsortium;
+ public List<String> roamingConsortiumList;
/** IP address availability */
public IpAddressType ipAddrTypeAvailability;
- /** NAI realm */
- public List<NaiRealm> naiRealm;
+ /** list of NAI realm */
+ public List<NaiRealm> naiRealmList;
- /** 3GPP cellular network */
- public List<CellularNetwork> cellularNetwork;
+ /** list of 3GPP cellular network */
+ public List<CellularNetwork> cellularNetworkList;
- /** fully qualified domain name (FQDN) */
- public List<String> domainName;
+ /** list of fully qualified domain name (FQDN) */
+ public List<String> domainNameList;
/** HS 2.0 operator friendly name */
public String operatorFriendlyName;
@@ -236,10 +236,10 @@
/** HS 2.0 wan metrics */
public WanMetrics wanMetrics;
- /** HS 2.0 list of IP proto port */
- public List<IpProtoPort> connectionCapability;
+ /** list of HS 2.0 IP proto port */
+ public List<IpProtoPort> connectionCapabilityList;
- /** HS 2.0 list of OSU providers */
+ /** list of HS 2.0 OSU providers */
public List<WifiPasspointOsuProvider> osuProviderList;
/**
@@ -292,15 +292,15 @@
sb.append(" venueName: ").append("(")
.append(venueName.replace("\n", "\\n")).append(")");
- if (networkAuthType != null) {
+ if (networkAuthTypeList != null) {
sb.append(" networkAuthType: ");
- for (NetworkAuthType auth : networkAuthType)
+ for (NetworkAuthType auth : networkAuthTypeList)
sb.append("(").append(auth.toString()).append(")");
}
- if (roamingConsortium != null) {
+ if (roamingConsortiumList != null) {
sb.append(" roamingConsortium: ");
- for (String oi : roamingConsortium)
+ for (String oi : roamingConsortiumList)
sb.append("(").append(oi).append(")");
}
@@ -309,21 +309,21 @@
.append(ipAddrTypeAvailability.toString()).append(")");
}
- if (naiRealm != null) {
+ if (naiRealmList != null) {
sb.append(" naiRealm: ");
- for (NaiRealm realm : naiRealm)
+ for (NaiRealm realm : naiRealmList)
sb.append("(").append(realm.toString()).append(")");
}
- if (cellularNetwork != null) {
+ if (cellularNetworkList != null) {
sb.append(" cellularNetwork: ");
- for (CellularNetwork plmn : cellularNetwork)
+ for (CellularNetwork plmn : cellularNetworkList)
sb.append("(").append(plmn.toString()).append(")");
}
- if (domainName != null) {
+ if (domainNameList != null) {
sb.append(" domainName: ");
- for (String fqdn : domainName)
+ for (String fqdn : domainNameList)
sb.append("(").append(fqdn).append(")");
}
@@ -335,9 +335,9 @@
sb.append(" wanMetrics: ").append("(")
.append(wanMetrics.toString()).append(")");
- if (connectionCapability != null) {
+ if (connectionCapabilityList != null) {
sb.append(" connectionCapability: ");
- for (IpProtoPort ip : connectionCapability)
+ for (IpProtoPort ip : connectionCapabilityList)
sb.append("(").append(ip.toString()).append(")");
}
@@ -356,21 +356,21 @@
out.writeString(bssid);
out.writeString(venueName);
- if (networkAuthType == null) {
+ if (networkAuthTypeList == null) {
out.writeInt(0);
} else {
- out.writeInt(networkAuthType.size());
- for (NetworkAuthType auth : networkAuthType) {
+ out.writeInt(networkAuthTypeList.size());
+ for (NetworkAuthType auth : networkAuthTypeList) {
out.writeInt(auth.type);
out.writeString(auth.redirectUrl);
}
}
- if (roamingConsortium == null) {
+ if (roamingConsortiumList == null) {
out.writeInt(0);
} else {
- out.writeInt(roamingConsortium.size());
- for (String oi : roamingConsortium)
+ out.writeInt(roamingConsortiumList.size());
+ for (String oi : roamingConsortiumList)
out.writeString(oi);
}
@@ -380,32 +380,32 @@
out.writeInt(ipAddrTypeAvailability.availability);
}
- if (naiRealm == null) {
+ if (naiRealmList == null) {
out.writeInt(0);
} else {
- out.writeInt(naiRealm.size());
- for (NaiRealm realm : naiRealm) {
+ out.writeInt(naiRealmList.size());
+ for (NaiRealm realm : naiRealmList) {
out.writeInt(realm.encoding);
out.writeString(realm.realm);
}
}
- if (cellularNetwork == null) {
+ if (cellularNetworkList == null) {
out.writeInt(0);
} else {
- out.writeInt(cellularNetwork.size());
- for (CellularNetwork plmn : cellularNetwork) {
+ out.writeInt(cellularNetworkList.size());
+ for (CellularNetwork plmn : cellularNetworkList) {
out.writeString(plmn.mcc);
out.writeString(plmn.mnc);
}
}
- if (domainName == null) {
+ if (domainNameList == null) {
out.writeInt(0);
} else {
- out.writeInt(domainName.size());
- for (String fqdn : domainName)
+ out.writeInt(domainNameList.size());
+ for (String fqdn : domainNameList)
out.writeString(fqdn);
}
@@ -423,11 +423,11 @@
out.writeInt(wanMetrics.lmd);
}
- if (connectionCapability == null) {
+ if (connectionCapabilityList == null) {
out.writeInt(0);
} else {
- out.writeInt(connectionCapability.size());
- for (IpProtoPort ip : connectionCapability) {
+ out.writeInt(connectionCapabilityList.size());
+ for (IpProtoPort ip : connectionCapabilityList) {
out.writeInt(ip.proto);
out.writeInt(ip.port);
out.writeInt(ip.status);
@@ -462,20 +462,20 @@
n = in.readInt();
if (n > 0) {
- p.networkAuthType = new ArrayList<NetworkAuthType>();
+ p.networkAuthTypeList = new ArrayList<NetworkAuthType>();
for (int i = 0; i < n; i++) {
NetworkAuthType auth = new NetworkAuthType();
auth.type = in.readInt();
auth.redirectUrl = in.readString();
- p.networkAuthType.add(auth);
+ p.networkAuthTypeList.add(auth);
}
}
n = in.readInt();
if (n > 0) {
- p.roamingConsortium = new ArrayList<String>();
+ p.roamingConsortiumList = new ArrayList<String>();
for (int i = 0; i < n; i++)
- p.roamingConsortium.add(in.readString());
+ p.roamingConsortiumList.add(in.readString());
}
n = in.readInt();
@@ -486,31 +486,31 @@
n = in.readInt();
if (n > 0) {
- p.naiRealm = new ArrayList<NaiRealm>();
+ p.naiRealmList = new ArrayList<NaiRealm>();
for (int i = 0; i < n; i++) {
NaiRealm realm = new NaiRealm();
realm.encoding = in.readInt();
realm.realm = in.readString();
- p.naiRealm.add(realm);
+ p.naiRealmList.add(realm);
}
}
n = in.readInt();
if (n > 0) {
- p.cellularNetwork = new ArrayList<CellularNetwork>();
+ p.cellularNetworkList = new ArrayList<CellularNetwork>();
for (int i = 0; i < n; i++) {
CellularNetwork plmn = new CellularNetwork();
plmn.mcc = in.readString();
plmn.mnc = in.readString();
- p.cellularNetwork.add(plmn);
+ p.cellularNetworkList.add(plmn);
}
}
n = in.readInt();
if (n > 0) {
- p.domainName = new ArrayList<String>();
+ p.domainNameList = new ArrayList<String>();
for (int i = 0; i < n; i++)
- p.domainName.add(in.readString());
+ p.domainNameList.add(in.readString());
}
p.operatorFriendlyName = in.readString();
@@ -528,13 +528,13 @@
n = in.readInt();
if (n > 0) {
- p.connectionCapability = new ArrayList<IpProtoPort>();
+ p.connectionCapabilityList = new ArrayList<IpProtoPort>();
for (int i = 0; i < n; i++) {
IpProtoPort ip = new IpProtoPort();
ip.proto = in.readInt();
ip.port = in.readInt();
ip.status = in.readInt();
- p.connectionCapability.add(ip);
+ p.connectionCapabilityList.add(ip);
}
}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index 2f158c2..e7e6767 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -325,7 +325,7 @@
listener = getListener(message.arg2, true);
if (listener != null) {
ParcelableString str = (ParcelableString) message.obj;
- if (str.string == null)
+ if (str == null || str.string == null)
((OsuRemListener) listener).onBrowserDismiss();
else
((OsuRemListener) listener).onBrowserLaunch(str.string);
@@ -485,7 +485,7 @@
*
* @return The list of credentials
*/
- public List<WifiPasspointCredential> getSavedCredentials() {
+ public List<WifiPasspointCredential> getCredentials() {
return null;
}
@@ -529,7 +529,7 @@
Log.d(TAG, "startOsu end");
}
- public void startUserRemediation(Channel c, OsuRemListener listener) {
+ public void startRemediation(Channel c, OsuRemListener listener) {
}
public void connect(WifiPasspointPolicy policy) {