diff options
558 files changed, 16840 insertions, 11457 deletions
diff --git a/Android.mk b/Android.mk index 24796cb57ab5..216d8a2d45f6 100644 --- a/Android.mk +++ b/Android.mk @@ -94,8 +94,6 @@ LOCAL_SRC_FILES += \ 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 \ @@ -610,8 +608,12 @@ framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES 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 \ @@ -705,7 +707,7 @@ $(full_target): $(framework_built) $(gen) $(INTERNAL_PLATFORM_API_FILE): $(full_target) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) -# ==== the private api stubs =================================== +# ==== the system api stubs =================================== include $(CLEAR_VARS) LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES) @@ -717,11 +719,11 @@ LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) -LOCAL_MODULE := private-api-stubs +LOCAL_MODULE := system-api-stubs LOCAL_DROIDDOC_OPTIONS:=\ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ - -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_private_stubs_current_intermediates/src \ + -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_system_stubs_current_intermediates/src \ -showAnnotation android.annotation.SystemApi \ -nodocs diff --git a/CleanSpec.mk b/CleanSpec.mk index 5d927928a203..1968a789da0b 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/framew $(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 6da355fb511f..931ab3b94376 100644 --- a/api/current.txt +++ b/api/current.txt @@ -91,6 +91,7 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ -636,6 +643,8 @@ package android { 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 +894,7 @@ package android { 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 +920,8 @@ package android { 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,6 +932,8 @@ package android { 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 protectionLevel = 16842761; // 0x1010009 @@ -1013,6 +1026,8 @@ package android { 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 +1231,8 @@ package android { 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 @@ -4203,6 +4220,9 @@ package android.app { 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); @@ -5201,7 +5221,7 @@ package android.app.admin { method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName); method public void addUserRestriction(android.content.ComponentName, java.lang.String); method public void clearCrossProfileIntentFilters(android.content.ComponentName); - method public void clearDeviceOwnerApp(); + method public void clearDeviceOwnerApp(java.lang.String); method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String); method public void clearUserRestriction(android.content.ComponentName, java.lang.String); method public android.os.UserHandle createAndInitializeUser(android.content.ComponentName, java.lang.String, java.lang.String, android.content.ComponentName, android.os.Bundle); @@ -5233,7 +5253,7 @@ package android.app.admin { 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(); @@ -5247,7 +5267,7 @@ package android.app.admin { 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); @@ -7045,6 +7065,7 @@ package android.content { field public static final java.lang.String SEARCH_SERVICE = "search"; field public static final java.lang.String SENSOR_SERVICE = "sensor"; field public static final java.lang.String STORAGE_SERVICE = "storage"; + field public static final java.lang.String TELECOMM_SERVICE = "telecomm"; field public static final java.lang.String TELEPHONY_SERVICE = "phone"; field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices"; field public static final java.lang.String TV_INPUT_SERVICE = "tv_input"; @@ -8045,6 +8066,7 @@ package android.content.pm { 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 @@ -8056,13 +8078,14 @@ package android.content.pm { 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_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 @@ -8087,6 +8110,7 @@ package android.content.pm { 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; @@ -8144,6 +8168,8 @@ package android.content.pm { 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; @@ -8212,6 +8238,8 @@ package android.content.pm { 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; } @@ -8241,15 +8269,26 @@ package android.content.pm { } 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); @@ -10211,6 +10250,7 @@ package android.graphics { 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); @@ -10225,6 +10265,7 @@ package android.graphics { 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); @@ -10734,6 +10775,7 @@ package android.graphics { 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); @@ -10743,9 +10785,12 @@ package android.graphics { 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); @@ -15794,7 +15839,9 @@ package android.media.tv { 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"; } @@ -16373,6 +16420,18 @@ package android.net { 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; } @@ -21522,7 +21581,7 @@ package android.os { 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"; @@ -25996,29 +26055,44 @@ package android.service.dreams { 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); } } @@ -27668,8 +27742,17 @@ package android.telecomm { } 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; } @@ -27679,6 +27762,7 @@ package android.telecomm { 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 ';' @@ -27686,6 +27770,11 @@ package android.telecomm { 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 { } } @@ -28127,6 +28216,7 @@ package android.telephony { 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(); @@ -28151,6 +28241,7 @@ package android.telephony { 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 @@ -32486,6 +32577,8 @@ package android.view { 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(); @@ -32763,6 +32856,8 @@ package android.view { 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); @@ -33088,7 +33183,7 @@ package android.view { method public abstract boolean onTouch(android.view.View, android.view.MotionEvent); } - public class ViewAnimationUtils { + public final class ViewAnimationUtils { method public static final android.animation.ValueAnimator createCircularReveal(android.view.View, int, int, float, float); } @@ -35045,12 +35140,15 @@ package android.webkit { 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 { @@ -35192,12 +35290,25 @@ package android.webkit { method public void onRequestFocus(android.webkit.WebView); method public void onShowCustomView(android.view.View, android.webkit.WebChromeClient.CustomViewCallback); method public deprecated void onShowCustomView(android.view.View, int, android.webkit.WebChromeClient.CustomViewCallback); + method public boolean showFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams); } public static abstract interface WebChromeClient.CustomViewCallback { method public abstract void onCustomViewHidden(); } + public static class WebChromeClient.FileChooserParams { + ctor public WebChromeClient.FileChooserParams(); + field public static final int MODE_OPEN_FOLDER = 2; // 0x2 + field public static final int MODE_OPEN_MULTIPLE = 1; // 0x1 + field public static final int MODE_SAVE = 4; // 0x4 + field public java.lang.String acceptTypes; + field public boolean capture; + field public java.lang.String defaultFilename; + field public int mode; + field public java.lang.String title; + } + public class WebHistoryItem implements java.lang.Cloneable { method public android.graphics.Bitmap getFavicon(); method public java.lang.String getOriginalUrl(); @@ -35713,10 +35824,14 @@ package android.widget { 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 { @@ -36131,9 +36246,13 @@ package android.widget { 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(); @@ -36394,10 +36513,14 @@ package android.widget { 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); } @@ -36606,6 +36729,8 @@ package android.widget { 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); @@ -36627,6 +36752,8 @@ package android.widget { 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 { @@ -36999,11 +37126,19 @@ package android.widget { 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(); @@ -37012,13 +37147,21 @@ package android.widget { 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 { @@ -37753,6 +37896,7 @@ package android.widget { method public float getShadowDx(); method public float getShadowDy(); method public float getShadowRadius(); + method public final boolean getShowSoftInputOnFocus(); method public java.lang.CharSequence getText(); method public static int getTextColor(android.content.Context, android.content.res.TypedArray, int); method public final android.content.res.ColorStateList getTextColors(); @@ -37846,6 +37990,7 @@ package android.widget { method public void setScroller(android.widget.Scroller); method public void setSelectAllOnFocus(boolean); method public void setShadowLayer(float, float, float, int); + method public final void setShowSoftInputOnFocus(boolean); method public void setSingleLine(); method public void setSingleLine(boolean); method public final void setSpannableFactory(android.text.Spannable.Factory); @@ -40011,109 +40156,6 @@ package java.lang { field public static final java.lang.Character.UnicodeBlock YI_SYLLABLES; } - public static final class Character.UnicodeScript extends java.lang.Enum { - method public static java.lang.Character.UnicodeScript forName(java.lang.String); - method public static java.lang.Character.UnicodeScript of(int); - method public static java.lang.Character.UnicodeScript valueOf(java.lang.String); - method public static final java.lang.Character.UnicodeScript[] values(); - enum_constant public static final java.lang.Character.UnicodeScript ARABIC; - enum_constant public static final java.lang.Character.UnicodeScript ARMENIAN; - enum_constant public static final java.lang.Character.UnicodeScript AVESTAN; - enum_constant public static final java.lang.Character.UnicodeScript BALINESE; - enum_constant public static final java.lang.Character.UnicodeScript BAMUM; - enum_constant public static final java.lang.Character.UnicodeScript BATAK; - enum_constant public static final java.lang.Character.UnicodeScript BENGALI; - enum_constant public static final java.lang.Character.UnicodeScript BOPOMOFO; - enum_constant public static final java.lang.Character.UnicodeScript BRAHMI; - enum_constant public static final java.lang.Character.UnicodeScript BRAILLE; - enum_constant public static final java.lang.Character.UnicodeScript BUGINESE; - enum_constant public static final java.lang.Character.UnicodeScript BUHID; - enum_constant public static final java.lang.Character.UnicodeScript CANADIAN_ABORIGINAL; - enum_constant public static final java.lang.Character.UnicodeScript CARIAN; - enum_constant public static final java.lang.Character.UnicodeScript CHAM; - enum_constant public static final java.lang.Character.UnicodeScript CHEROKEE; - enum_constant public static final java.lang.Character.UnicodeScript COMMON; - enum_constant public static final java.lang.Character.UnicodeScript COPTIC; - enum_constant public static final java.lang.Character.UnicodeScript CUNEIFORM; - enum_constant public static final java.lang.Character.UnicodeScript CYPRIOT; - enum_constant public static final java.lang.Character.UnicodeScript CYRILLIC; - enum_constant public static final java.lang.Character.UnicodeScript DESERET; - enum_constant public static final java.lang.Character.UnicodeScript DEVANAGARI; - enum_constant public static final java.lang.Character.UnicodeScript EGYPTIAN_HIEROGLYPHS; - enum_constant public static final java.lang.Character.UnicodeScript ETHIOPIC; - enum_constant public static final java.lang.Character.UnicodeScript GEORGIAN; - enum_constant public static final java.lang.Character.UnicodeScript GLAGOLITIC; - enum_constant public static final java.lang.Character.UnicodeScript GOTHIC; - enum_constant public static final java.lang.Character.UnicodeScript GREEK; - enum_constant public static final java.lang.Character.UnicodeScript GUJARATI; - enum_constant public static final java.lang.Character.UnicodeScript GURMUKHI; - enum_constant public static final java.lang.Character.UnicodeScript HAN; - enum_constant public static final java.lang.Character.UnicodeScript HANGUL; - enum_constant public static final java.lang.Character.UnicodeScript HANUNOO; - enum_constant public static final java.lang.Character.UnicodeScript HEBREW; - enum_constant public static final java.lang.Character.UnicodeScript HIRAGANA; - enum_constant public static final java.lang.Character.UnicodeScript IMPERIAL_ARAMAIC; - enum_constant public static final java.lang.Character.UnicodeScript INHERITED; - enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PAHLAVI; - enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PARTHIAN; - enum_constant public static final java.lang.Character.UnicodeScript JAVANESE; - enum_constant public static final java.lang.Character.UnicodeScript KAITHI; - enum_constant public static final java.lang.Character.UnicodeScript KANNADA; - enum_constant public static final java.lang.Character.UnicodeScript KATAKANA; - enum_constant public static final java.lang.Character.UnicodeScript KAYAH_LI; - enum_constant public static final java.lang.Character.UnicodeScript KHAROSHTHI; - enum_constant public static final java.lang.Character.UnicodeScript KHMER; - enum_constant public static final java.lang.Character.UnicodeScript LAO; - enum_constant public static final java.lang.Character.UnicodeScript LATIN; - enum_constant public static final java.lang.Character.UnicodeScript LEPCHA; - enum_constant public static final java.lang.Character.UnicodeScript LIMBU; - enum_constant public static final java.lang.Character.UnicodeScript LINEAR_B; - enum_constant public static final java.lang.Character.UnicodeScript LISU; - enum_constant public static final java.lang.Character.UnicodeScript LYCIAN; - enum_constant public static final java.lang.Character.UnicodeScript LYDIAN; - enum_constant public static final java.lang.Character.UnicodeScript MALAYALAM; - enum_constant public static final java.lang.Character.UnicodeScript MANDAIC; - enum_constant public static final java.lang.Character.UnicodeScript MEETEI_MAYEK; - enum_constant public static final java.lang.Character.UnicodeScript MONGOLIAN; - enum_constant public static final java.lang.Character.UnicodeScript MYANMAR; - enum_constant public static final java.lang.Character.UnicodeScript NEW_TAI_LUE; - enum_constant public static final java.lang.Character.UnicodeScript NKO; - enum_constant public static final java.lang.Character.UnicodeScript OGHAM; - enum_constant public static final java.lang.Character.UnicodeScript OLD_ITALIC; - enum_constant public static final java.lang.Character.UnicodeScript OLD_PERSIAN; - enum_constant public static final java.lang.Character.UnicodeScript OLD_SOUTH_ARABIAN; - enum_constant public static final java.lang.Character.UnicodeScript OLD_TURKIC; - enum_constant public static final java.lang.Character.UnicodeScript OL_CHIKI; - enum_constant public static final java.lang.Character.UnicodeScript ORIYA; - enum_constant public static final java.lang.Character.UnicodeScript OSMANYA; - enum_constant public static final java.lang.Character.UnicodeScript PHAGS_PA; - enum_constant public static final java.lang.Character.UnicodeScript PHOENICIAN; - enum_constant public static final java.lang.Character.UnicodeScript REJANG; - enum_constant public static final java.lang.Character.UnicodeScript RUNIC; - enum_constant public static final java.lang.Character.UnicodeScript SAMARITAN; - enum_constant public static final java.lang.Character.UnicodeScript SAURASHTRA; - enum_constant public static final java.lang.Character.UnicodeScript SHAVIAN; - enum_constant public static final java.lang.Character.UnicodeScript SINHALA; - enum_constant public static final java.lang.Character.UnicodeScript SUNDANESE; - enum_constant public static final java.lang.Character.UnicodeScript SYLOTI_NAGRI; - enum_constant public static final java.lang.Character.UnicodeScript SYRIAC; - enum_constant public static final java.lang.Character.UnicodeScript TAGALOG; - enum_constant public static final java.lang.Character.UnicodeScript TAGBANWA; - enum_constant public static final java.lang.Character.UnicodeScript TAI_LE; - enum_constant public static final java.lang.Character.UnicodeScript TAI_THAM; - enum_constant public static final java.lang.Character.UnicodeScript TAI_VIET; - enum_constant public static final java.lang.Character.UnicodeScript TAMIL; - enum_constant public static final java.lang.Character.UnicodeScript TELUGU; - enum_constant public static final java.lang.Character.UnicodeScript THAANA; - enum_constant public static final java.lang.Character.UnicodeScript THAI; - enum_constant public static final java.lang.Character.UnicodeScript TIBETAN; - enum_constant public static final java.lang.Character.UnicodeScript TIFINAGH; - enum_constant public static final java.lang.Character.UnicodeScript UGARITIC; - enum_constant public static final java.lang.Character.UnicodeScript UNKNOWN; - enum_constant public static final java.lang.Character.UnicodeScript VAI; - enum_constant public static final java.lang.Character.UnicodeScript YI; - } - public final class Class implements java.lang.reflect.AnnotatedElement java.lang.reflect.GenericDeclaration java.io.Serializable java.lang.reflect.Type { method public java.lang.Class<? extends U> asSubclass(java.lang.Class<U>); method public T cast(java.lang.Object); @@ -41954,13 +41996,11 @@ package java.net { method public java.lang.String getValue(); method public int getVersion(); method public boolean hasExpired(); - method public boolean isHttpOnly(); method public static java.util.List<java.net.HttpCookie> parse(java.lang.String); method public void setComment(java.lang.String); method public void setCommentURL(java.lang.String); method public void setDiscard(boolean); method public void setDomain(java.lang.String); - method public void setHttpOnly(boolean); method public void setMaxAge(long); method public void setPath(java.lang.String); method public void setPortlist(java.lang.String); @@ -42351,11 +42391,6 @@ package java.net { method public abstract java.net.SocketImpl createSocketImpl(); } - public abstract interface SocketOption { - method public abstract java.lang.String name(); - method public abstract java.lang.Class<T> type(); - } - public abstract interface SocketOptions { method public abstract java.lang.Object getOption(int) throws java.net.SocketException; method public abstract void setOption(int, java.lang.Object) throws java.net.SocketException; @@ -42386,21 +42421,6 @@ package java.net { ctor public SocketTimeoutException(java.lang.String); } - public final class StandardSocketOptions { - ctor public StandardSocketOptions(); - field public static final java.net.SocketOption IP_MULTICAST_IF; - field public static final java.net.SocketOption IP_MULTICAST_LOOP; - field public static final java.net.SocketOption IP_MULTICAST_TTL; - field public static final java.net.SocketOption IP_TOS; - field public static final java.net.SocketOption SO_BROADCAST; - field public static final java.net.SocketOption SO_KEEPALIVE; - field public static final java.net.SocketOption SO_LINGER; - field public static final java.net.SocketOption SO_RCVBUF; - field public static final java.net.SocketOption SO_REUSEADDR; - field public static final java.net.SocketOption SO_SNDBUF; - field public static final java.net.SocketOption TCP_NODELAY; - } - public final class URI implements java.lang.Comparable java.io.Serializable { ctor public URI(java.lang.String) throws java.net.URISyntaxException; ctor public URI(java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException; @@ -42496,7 +42516,6 @@ package java.net { method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException; method public java.lang.String getContentEncoding(); method public int getContentLength(); - method public long getContentLengthLong(); method public java.lang.String getContentType(); method public long getDate(); method public static boolean getDefaultAllowUserInteraction(); @@ -42511,7 +42530,6 @@ package java.net { method public long getHeaderFieldDate(java.lang.String, long); method public int getHeaderFieldInt(java.lang.String, int); method public java.lang.String getHeaderFieldKey(int); - method public long getHeaderFieldLong(java.lang.String, long); method public java.util.Map<java.lang.String, java.util.List<java.lang.String>> getHeaderFields(); method public long getIfModifiedSince(); method public java.io.InputStream getInputStream() throws java.io.IOException; @@ -42862,10 +42880,6 @@ package java.nio { package java.nio.channels { - public class AlreadyBoundException extends java.lang.IllegalStateException { - ctor public AlreadyBoundException(); - } - public class AlreadyConnectedException extends java.lang.IllegalStateException { ctor public AlreadyConnectedException(); } @@ -42913,32 +42927,25 @@ package java.nio.channels { ctor public ConnectionPendingException(); } - public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel { + public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel { ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider); - method public java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException; method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException; method public abstract java.nio.channels.DatagramChannel disconnect() throws java.io.IOException; - method public java.net.SocketAddress getLocalAddress() throws java.io.IOException; - method public T getOption(java.net.SocketOption<T>) throws java.io.IOException; method public abstract boolean isConnected(); - method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException; - method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException; method public static java.nio.channels.DatagramChannel open() throws java.io.IOException; method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException; method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException; method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException; method public abstract java.net.SocketAddress receive(java.nio.ByteBuffer) throws java.io.IOException; method public abstract int send(java.nio.ByteBuffer, java.net.SocketAddress) throws java.io.IOException; - method public java.nio.channels.DatagramChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException; method public abstract java.net.DatagramSocket socket(); - method public java.util.Set<java.net.SocketOption<?>> supportedOptions(); method public final int validOps(); method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException; method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException; method public final synchronized long write(java.nio.ByteBuffer[]) throws java.io.IOException; } - public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel java.nio.channels.SeekableByteChannel { + public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel { ctor protected FileChannel(); method public abstract void force(boolean) throws java.io.IOException; method public final java.nio.channels.FileLock lock() throws java.io.IOException; @@ -42970,7 +42977,6 @@ package java.nio.channels { public abstract class FileLock implements java.lang.AutoCloseable { ctor protected FileLock(java.nio.channels.FileChannel, long, long, boolean); - method public java.nio.channels.Channel acquiredBy(); method public final java.nio.channels.FileChannel channel(); method public final void close() throws java.io.IOException; method public final boolean isShared(); @@ -43003,32 +43009,6 @@ package java.nio.channels { method public abstract void close() throws java.io.IOException; } - public abstract class MembershipKey { - ctor protected MembershipKey(); - method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException; - method public abstract java.nio.channels.MulticastChannel channel(); - method public abstract void drop(); - method public abstract java.net.InetAddress group(); - method public abstract boolean isValid(); - method public abstract java.net.NetworkInterface networkInterface(); - method public abstract java.net.InetAddress sourceAddress(); - method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress); - } - - public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel { - method public abstract void close() throws java.io.IOException; - method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException; - method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException; - } - - public abstract interface NetworkChannel implements java.lang.AutoCloseable java.nio.channels.Channel java.io.Closeable { - method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException; - method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException; - method public abstract T getOption(java.net.SocketOption<T>) throws java.io.IOException; - method public abstract java.nio.channels.NetworkChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException; - method public abstract java.util.Set<java.net.SocketOption<?>> supportedOptions(); - } - public class NoConnectionPendingException extends java.lang.IllegalStateException { ctor public NoConnectionPendingException(); } @@ -43079,15 +43059,6 @@ package java.nio.channels { method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException; } - public abstract interface SeekableByteChannel implements java.nio.channels.ByteChannel { - method public abstract long position() throws java.io.IOException; - method public abstract java.nio.channels.SeekableByteChannel position(long) throws java.io.IOException; - method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException; - method public abstract long size() throws java.io.IOException; - method public abstract java.nio.channels.SeekableByteChannel truncate(long) throws java.io.IOException; - method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException; - } - public abstract class SelectableChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.Channel { ctor protected SelectableChannel(); method public abstract java.lang.Object blockingLock(); @@ -43136,27 +43107,18 @@ package java.nio.channels { method public abstract java.nio.channels.Selector wakeup(); } - public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.NetworkChannel { + public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel { ctor protected ServerSocketChannel(java.nio.channels.spi.SelectorProvider); method public abstract java.nio.channels.SocketChannel accept() throws java.io.IOException; - method public final java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException; - method public java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException; - method public java.net.SocketAddress getLocalAddress() throws java.io.IOException; - method public T getOption(java.net.SocketOption<T>) throws java.io.IOException; method public static java.nio.channels.ServerSocketChannel open() throws java.io.IOException; - method public java.nio.channels.ServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException; method public abstract java.net.ServerSocket socket(); - method public java.util.Set<java.net.SocketOption<?>> supportedOptions(); method public final int validOps(); } - public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel { + public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel { ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider); - method public java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException; method public abstract boolean connect(java.net.SocketAddress) throws java.io.IOException; method public abstract boolean finishConnect() throws java.io.IOException; - method public java.net.SocketAddress getLocalAddress() throws java.io.IOException; - method public T getOption(java.net.SocketOption<T>) throws java.io.IOException; method public abstract boolean isConnected(); method public abstract boolean isConnectionPending(); method public static java.nio.channels.SocketChannel open() throws java.io.IOException; @@ -43164,9 +43126,7 @@ package java.nio.channels { method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException; method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException; method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException; - method public java.nio.channels.SocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException; method public abstract java.net.Socket socket(); - method public java.util.Set<java.net.SocketOption<?>> supportedOptions(); method public final int validOps(); method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException; method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException; @@ -47126,7 +47086,6 @@ package java.util { method public java.lang.String getDisplayName(java.util.Locale); method public static java.util.Currency getInstance(java.lang.String); method public static java.util.Currency getInstance(java.util.Locale); - method public int getNumericCode(); method public java.lang.String getSymbol(); method public java.lang.String getSymbol(java.util.Locale); } @@ -47411,13 +47370,6 @@ package java.util { method public int getWidth(); } - public class IllformedLocaleException extends java.lang.RuntimeException { - ctor public IllformedLocaleException(); - ctor public IllformedLocaleException(java.lang.String); - ctor public IllformedLocaleException(java.lang.String, int); - method public int getErrorIndex(); - } - public class InputMismatchException extends java.util.NoSuchElementException implements java.io.Serializable { ctor public InputMismatchException(); ctor public InputMismatchException(java.lang.String); @@ -47532,7 +47484,6 @@ package java.util { ctor public Locale(java.lang.String, java.lang.String); ctor public Locale(java.lang.String, java.lang.String, java.lang.String); method public java.lang.Object clone(); - method public static java.util.Locale forLanguageTag(java.lang.String); method public static java.util.Locale[] getAvailableLocales(); method public java.lang.String getCountry(); method public static java.util.Locale getDefault(); @@ -47542,24 +47493,15 @@ package java.util { method public java.lang.String getDisplayLanguage(java.util.Locale); method public final java.lang.String getDisplayName(); method public java.lang.String getDisplayName(java.util.Locale); - method public java.lang.String getDisplayScript(); - method public java.lang.String getDisplayScript(java.util.Locale); method public final java.lang.String getDisplayVariant(); method public java.lang.String getDisplayVariant(java.util.Locale); - method public java.lang.String getExtension(char); - method public java.util.Set<java.lang.Character> getExtensionKeys(); method public java.lang.String getISO3Country(); method public java.lang.String getISO3Language(); method public static java.lang.String[] getISOCountries(); method public static java.lang.String[] getISOLanguages(); method public java.lang.String getLanguage(); - method public java.lang.String getScript(); - method public java.util.Set<java.lang.String> getUnicodeLocaleAttributes(); - method public java.util.Set<java.lang.String> getUnicodeLocaleKeys(); - method public java.lang.String getUnicodeLocaleType(java.lang.String); method public java.lang.String getVariant(); method public static synchronized void setDefault(java.util.Locale); - method public java.lang.String toLanguageTag(); method public final java.lang.String toString(); field public static final java.util.Locale CANADA; field public static final java.util.Locale CANADA_FRENCH; @@ -47577,33 +47519,14 @@ package java.util { field public static final java.util.Locale KOREA; field public static final java.util.Locale KOREAN; field public static final java.util.Locale PRC; - field public static final char PRIVATE_USE_EXTENSION = 120; // 0x0078 'x' field public static final java.util.Locale ROOT; field public static final java.util.Locale SIMPLIFIED_CHINESE; field public static final java.util.Locale TAIWAN; field public static final java.util.Locale TRADITIONAL_CHINESE; field public static final java.util.Locale UK; - field public static final char UNICODE_LOCALE_EXTENSION = 117; // 0x0075 'u' field public static final java.util.Locale US; } - public static final class Locale.Builder { - ctor public Locale.Builder(); - method public java.util.Locale.Builder addUnicodeLocaleAttribute(java.lang.String); - method public java.util.Locale build(); - method public java.util.Locale.Builder clear(); - method public java.util.Locale.Builder clearExtensions(); - method public java.util.Locale.Builder removeUnicodeLocaleAttribute(java.lang.String); - method public java.util.Locale.Builder setExtension(char, java.lang.String); - method public java.util.Locale.Builder setLanguage(java.lang.String); - method public java.util.Locale.Builder setLanguageTag(java.lang.String); - method public java.util.Locale.Builder setLocale(java.util.Locale); - method public java.util.Locale.Builder setRegion(java.lang.String); - method public java.util.Locale.Builder setScript(java.lang.String); - method public java.util.Locale.Builder setUnicodeLocaleKeyword(java.lang.String, java.lang.String); - method public java.util.Locale.Builder setVariant(java.lang.String); - } - public abstract interface Map { method public abstract void clear(); method public abstract boolean containsKey(java.lang.Object); @@ -48290,35 +48213,6 @@ package java.util.concurrent { method public V replace(K, V); } - public class ConcurrentLinkedDeque extends java.util.AbstractCollection implements java.util.Deque java.io.Serializable { - ctor public ConcurrentLinkedDeque(); - ctor public ConcurrentLinkedDeque(java.util.Collection<? extends E>); - method public void addFirst(E); - method public void addLast(E); - method public java.util.Iterator<E> descendingIterator(); - method public E element(); - method public E getFirst(); - method public E getLast(); - method public java.util.Iterator<E> iterator(); - method public boolean offer(E); - method public boolean offerFirst(E); - method public boolean offerLast(E); - method public E peek(); - method public E peekFirst(); - method public E peekLast(); - method public E poll(); - method public E pollFirst(); - method public E pollLast(); - method public E pop(); - method public void push(E); - method public E remove(); - method public E removeFirst(); - method public boolean removeFirstOccurrence(java.lang.Object); - method public E removeLast(); - method public boolean removeLastOccurrence(java.lang.Object); - method public int size(); - } - public class ConcurrentLinkedQueue extends java.util.AbstractQueue implements java.util.Queue java.io.Serializable { ctor public ConcurrentLinkedQueue(); ctor public ConcurrentLinkedQueue(java.util.Collection<? extends E>); @@ -48559,94 +48453,6 @@ package java.util.concurrent { method public static java.util.concurrent.ScheduledExecutorService unconfigurableScheduledExecutorService(java.util.concurrent.ScheduledExecutorService); } - public class ForkJoinPool extends java.util.concurrent.AbstractExecutorService { - ctor public ForkJoinPool(); - ctor public ForkJoinPool(int); - ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean); - method public boolean awaitQuiescence(long, java.util.concurrent.TimeUnit); - method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException; - method protected int drainTasksTo(java.util.Collection<? super java.util.concurrent.ForkJoinTask<?>>); - method public void execute(java.util.concurrent.ForkJoinTask<?>); - method public void execute(java.lang.Runnable); - method public int getActiveThreadCount(); - method public boolean getAsyncMode(); - method public java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory getFactory(); - method public int getParallelism(); - method public int getPoolSize(); - method public int getQueuedSubmissionCount(); - method public long getQueuedTaskCount(); - method public int getRunningThreadCount(); - method public long getStealCount(); - method public java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionHandler(); - method public boolean hasQueuedSubmissions(); - method public T invoke(java.util.concurrent.ForkJoinTask<T>); - method public boolean isQuiescent(); - method public boolean isShutdown(); - method public boolean isTerminated(); - method public boolean isTerminating(); - method public static void managedBlock(java.util.concurrent.ForkJoinPool.ManagedBlocker) throws java.lang.InterruptedException; - method protected java.util.concurrent.ForkJoinTask<?> pollSubmission(); - method public void shutdown(); - method public java.util.List<java.lang.Runnable> shutdownNow(); - method public java.util.concurrent.ForkJoinTask<T> submit(java.util.concurrent.ForkJoinTask<T>); - field public static final java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory defaultForkJoinWorkerThreadFactory; - } - - public static abstract interface ForkJoinPool.ForkJoinWorkerThreadFactory { - method public abstract java.util.concurrent.ForkJoinWorkerThread newThread(java.util.concurrent.ForkJoinPool); - } - - public static abstract interface ForkJoinPool.ManagedBlocker { - method public abstract boolean block() throws java.lang.InterruptedException; - method public abstract boolean isReleasable(); - } - - public abstract class ForkJoinTask implements java.util.concurrent.Future java.io.Serializable { - ctor public ForkJoinTask(); - method public static java.util.concurrent.ForkJoinTask<?> adapt(java.lang.Runnable); - method public static java.util.concurrent.ForkJoinTask<T> adapt(java.lang.Runnable, T); - method public static java.util.concurrent.ForkJoinTask<T> adapt(java.util.concurrent.Callable<? extends T>); - method public boolean cancel(boolean); - method public void complete(V); - method public void completeExceptionally(java.lang.Throwable); - method protected abstract boolean exec(); - method public final java.util.concurrent.ForkJoinTask<V> fork(); - method public final V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException; - method public final V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException; - method public final java.lang.Throwable getException(); - method public static java.util.concurrent.ForkJoinPool getPool(); - method public static int getQueuedTaskCount(); - method public abstract V getRawResult(); - method public static int getSurplusQueuedTaskCount(); - method public static void helpQuiesce(); - method public static boolean inForkJoinPool(); - method public final V invoke(); - method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>, java.util.concurrent.ForkJoinTask<?>); - method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>...); - method public static java.util.Collection<T> invokeAll(java.util.Collection<T>); - method public final boolean isCancelled(); - method public final boolean isCompletedAbnormally(); - method public final boolean isCompletedNormally(); - method public final boolean isDone(); - method public final V join(); - method protected static java.util.concurrent.ForkJoinTask<?> peekNextLocalTask(); - method protected static java.util.concurrent.ForkJoinTask<?> pollNextLocalTask(); - method protected static java.util.concurrent.ForkJoinTask<?> pollTask(); - method public final void quietlyInvoke(); - method public final void quietlyJoin(); - method public void reinitialize(); - method protected abstract void setRawResult(V); - method public boolean tryUnfork(); - } - - public class ForkJoinWorkerThread extends java.lang.Thread { - ctor protected ForkJoinWorkerThread(java.util.concurrent.ForkJoinPool); - method public java.util.concurrent.ForkJoinPool getPool(); - method public int getPoolIndex(); - method protected void onStart(); - method protected void onTermination(java.lang.Throwable); - } - public abstract interface Future { method public abstract boolean cancel(boolean); method public abstract V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException; @@ -48731,52 +48537,6 @@ package java.util.concurrent { method public E take() throws java.lang.InterruptedException; } - public class LinkedTransferQueue extends java.util.AbstractQueue implements java.io.Serializable java.util.concurrent.TransferQueue { - ctor public LinkedTransferQueue(); - ctor public LinkedTransferQueue(java.util.Collection<? extends E>); - method public int drainTo(java.util.Collection<? super E>); - method public int drainTo(java.util.Collection<? super E>, int); - method public int getWaitingConsumerCount(); - method public boolean hasWaitingConsumer(); - method public java.util.Iterator<E> iterator(); - method public boolean offer(E, long, java.util.concurrent.TimeUnit); - method public boolean offer(E); - method public E peek(); - method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException; - method public E poll(); - method public void put(E); - method public int remainingCapacity(); - method public int size(); - method public E take() throws java.lang.InterruptedException; - method public void transfer(E) throws java.lang.InterruptedException; - method public boolean tryTransfer(E); - method public boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException; - } - - public class Phaser { - ctor public Phaser(); - ctor public Phaser(int); - ctor public Phaser(java.util.concurrent.Phaser); - ctor public Phaser(java.util.concurrent.Phaser, int); - method public int arrive(); - method public int arriveAndAwaitAdvance(); - method public int arriveAndDeregister(); - method public int awaitAdvance(int); - method public int awaitAdvanceInterruptibly(int) throws java.lang.InterruptedException; - method public int awaitAdvanceInterruptibly(int, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException; - method public int bulkRegister(int); - method public void forceTermination(); - method public int getArrivedParties(); - method public java.util.concurrent.Phaser getParent(); - method public final int getPhase(); - method public int getRegisteredParties(); - method public java.util.concurrent.Phaser getRoot(); - method public int getUnarrivedParties(); - method public boolean isTerminated(); - method protected boolean onAdvance(int, int); - method public int register(); - } - public class PriorityBlockingQueue extends java.util.AbstractQueue implements java.util.concurrent.BlockingQueue java.io.Serializable { ctor public PriorityBlockingQueue(); ctor public PriorityBlockingQueue(int); @@ -48797,22 +48557,6 @@ package java.util.concurrent { method public E take() throws java.lang.InterruptedException; } - public abstract class RecursiveAction extends java.util.concurrent.ForkJoinTask { - ctor public RecursiveAction(); - method protected abstract void compute(); - method protected final boolean exec(); - method public final java.lang.Void getRawResult(); - method protected final void setRawResult(java.lang.Void); - } - - public abstract class RecursiveTask extends java.util.concurrent.ForkJoinTask { - ctor public RecursiveTask(); - method protected abstract V compute(); - method protected final boolean exec(); - method public final V getRawResult(); - method protected final void setRawResult(V); - } - public class RejectedExecutionException extends java.lang.RuntimeException { ctor public RejectedExecutionException(); ctor public RejectedExecutionException(java.lang.String); @@ -48851,14 +48595,12 @@ package java.util.concurrent { method protected java.util.concurrent.RunnableScheduledFuture<V> decorateTask(java.util.concurrent.Callable<V>, java.util.concurrent.RunnableScheduledFuture<V>); method public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy(); method public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy(); - method public boolean getRemoveOnCancelPolicy(); method public java.util.concurrent.ScheduledFuture<?> schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit); method public java.util.concurrent.ScheduledFuture<V> schedule(java.util.concurrent.Callable<V>, long, java.util.concurrent.TimeUnit); method public java.util.concurrent.ScheduledFuture<?> scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit); method public java.util.concurrent.ScheduledFuture<?> scheduleWithFixedDelay(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit); method public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean); method public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean); - method public void setRemoveOnCancelPolicy(boolean); } public class Semaphore implements java.io.Serializable { @@ -48904,15 +48646,6 @@ package java.util.concurrent { method public abstract java.lang.Thread newThread(java.lang.Runnable); } - public class ThreadLocalRandom extends java.util.Random { - method public static java.util.concurrent.ThreadLocalRandom current(); - method public double nextDouble(double); - method public double nextDouble(double, double); - method public int nextInt(int, int); - method public long nextLong(long); - method public long nextLong(long, long); - } - public class ThreadPoolExecutor extends java.util.concurrent.AbstractExecutorService { ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>); ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>, java.util.concurrent.ThreadFactory); @@ -49000,14 +48733,6 @@ package java.util.concurrent { ctor public TimeoutException(java.lang.String); } - public abstract interface TransferQueue implements java.util.concurrent.BlockingQueue { - method public abstract int getWaitingConsumerCount(); - method public abstract boolean hasWaitingConsumer(); - method public abstract void transfer(E) throws java.lang.InterruptedException; - method public abstract boolean tryTransfer(E); - method public abstract boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException; - } - } package java.util.concurrent.atomic { @@ -49217,7 +48942,6 @@ package java.util.concurrent.locks { method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject); method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject); method public final boolean hasContended(); - method public final boolean hasQueuedPredecessors(); method public final boolean hasQueuedThreads(); method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject); method protected boolean isHeldExclusively(); @@ -49264,7 +48988,6 @@ package java.util.concurrent.locks { method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject); method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject); method public final boolean hasContended(); - method public final boolean hasQueuedPredecessors(); method public final boolean hasQueuedThreads(); method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject); method protected boolean isHeldExclusively(); @@ -50209,10 +49932,8 @@ package java.util.zip { public class ZipFile implements java.io.Closeable { ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException; - ctor public ZipFile(java.io.File, java.nio.charset.Charset) throws java.io.IOException, java.util.zip.ZipException; ctor public ZipFile(java.lang.String) throws java.io.IOException; ctor public ZipFile(java.io.File, int) throws java.io.IOException; - ctor public ZipFile(java.io.File, int, java.nio.charset.Charset) throws java.io.IOException; method public void close() throws java.io.IOException; method public java.util.Enumeration<? extends java.util.zip.ZipEntry> entries(); method public java.lang.String getComment(); @@ -50266,7 +49987,6 @@ package java.util.zip { public class ZipInputStream extends java.util.zip.InflaterInputStream { ctor public ZipInputStream(java.io.InputStream); - ctor public ZipInputStream(java.io.InputStream, java.nio.charset.Charset); method public void closeEntry() throws java.io.IOException; method protected java.util.zip.ZipEntry createZipEntry(java.lang.String); method public java.util.zip.ZipEntry getNextEntry() throws java.io.IOException; @@ -50314,7 +50034,6 @@ package java.util.zip { public class ZipOutputStream extends java.util.zip.DeflaterOutputStream { ctor public ZipOutputStream(java.io.OutputStream); - ctor public ZipOutputStream(java.io.OutputStream, java.nio.charset.Charset); method public void closeEntry() throws java.io.IOException; method public void putNextEntry(java.util.zip.ZipEntry) throws java.io.IOException; method public void setComment(java.lang.String); @@ -51813,7 +51532,6 @@ package javax.net.ssl { method public abstract boolean getEnableSessionCreation(); method public abstract java.lang.String[] getEnabledCipherSuites(); method public abstract java.lang.String[] getEnabledProtocols(); - method public javax.net.ssl.SSLSession getHandshakeSession(); method public abstract javax.net.ssl.SSLEngineResult.HandshakeStatus getHandshakeStatus(); method public abstract boolean getNeedClientAuth(); method public java.lang.String getPeerHost(); @@ -51887,12 +51605,10 @@ package javax.net.ssl { ctor public SSLParameters(java.lang.String[]); ctor public SSLParameters(java.lang.String[], java.lang.String[]); method public java.lang.String[] getCipherSuites(); - method public java.lang.String getEndpointIdentificationAlgorithm(); method public boolean getNeedClientAuth(); method public java.lang.String[] getProtocols(); method public boolean getWantClientAuth(); method public void setCipherSuites(java.lang.String[]); - method public void setEndpointIdentificationAlgorithm(java.lang.String); method public void setNeedClientAuth(boolean); method public void setProtocols(java.lang.String[]); method public void setWantClientAuth(boolean); @@ -51993,7 +51709,6 @@ package javax.net.ssl { method public abstract boolean getEnableSessionCreation(); method public abstract java.lang.String[] getEnabledCipherSuites(); method public abstract java.lang.String[] getEnabledProtocols(); - method public javax.net.ssl.SSLSession getHandshakeSession(); method public abstract boolean getNeedClientAuth(); method public javax.net.ssl.SSLParameters getSSLParameters(); method public abstract javax.net.ssl.SSLSession getSession(); @@ -52049,14 +51764,6 @@ package javax.net.ssl { method public java.lang.String chooseEngineServerAlias(java.lang.String, java.security.Principal[], javax.net.ssl.SSLEngine); } - public abstract class X509ExtendedTrustManager implements javax.net.ssl.X509TrustManager { - ctor public X509ExtendedTrustManager(); - method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException; - method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException; - method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException; - method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException; - } - public abstract interface X509KeyManager implements javax.net.ssl.KeyManager { method public abstract java.lang.String chooseClientAlias(java.lang.String[], java.security.Principal[], java.net.Socket); method public abstract java.lang.String chooseServerAlias(java.lang.String, java.security.Principal[], java.net.Socket); diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 8945526b352b..127b0fc6adf8 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -32,6 +32,7 @@ import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Configuration; import android.graphics.Rect; import android.net.Uri; import android.os.Binder; @@ -43,8 +44,11 @@ import android.os.RemoteException; 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; + import com.android.internal.os.BaseCommand; import dalvik.system.VMRuntime; @@ -123,6 +127,7 @@ public class Am extends BaseCommand { " am stack info <STACK_ID>\n" + " am lock-task <TASK_ID>\n" + " am lock-task stop\n" + + " am get-config\n" + "\n" + "am start: start an Activity. Options are:\n" + " -D: enable debugging\n" + @@ -229,6 +234,9 @@ public class Am extends BaseCommand { "\n" + "am lock-task: bring <TASK_ID> to the front and don't allow other tasks to run\n" + "\n" + + "am get-config: retrieve the configuration and any recent configurations\n" + + " of the device\n" + + "\n" + "<INTENT> specifications include these flags and arguments:\n" + " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + @@ -323,6 +331,8 @@ public class Am extends BaseCommand { runStack(); } else if (op.equals("lock-task")) { runLockTask(); + } else if (op.equals("get-config")) { + runGetConfig(); } else { showError("Error: unknown command '" + op + "'"); } @@ -909,8 +919,7 @@ public class Am extends BaseCommand { } } - if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, - abi)) { + if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) { throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString()); } @@ -1699,4 +1708,19 @@ public class Am extends BaseCommand { } catch (RemoteException e) { } } + + private void runGetConfig() throws Exception { + try { + Configuration config = mAm.getConfiguration(); + if (config == null) { + System.err.println("Activity manager has no configuration"); + return; + } + + System.out.println("config: " + Configuration.resourceQualifierString(config)); + 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 ae35f7b0a8c1..593a1973669a 100644 --- a/cmds/idmap/create.cpp +++ b/cmds/idmap/create.cpp @@ -105,7 +105,7 @@ fail: uint32_t cached_target_crc, cached_overlay_crc; String8 cached_target_path, cached_overlay_path; - if (!ResTable::getIdmapInfo(buf, N, &cached_target_crc, &cached_overlay_crc, + if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc, &cached_target_path, &cached_overlay_path)) { return true; } diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp index 46c0edc03523..90cfa2c610f0 100644 --- a/cmds/idmap/idmap.cpp +++ b/cmds/idmap/idmap.cpp @@ -66,26 +66,32 @@ EXAMPLES \n\ Display an idmap file: \n\ \n\ $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\ - SECTION ENTRY VALUE OFFSET COMMENT \n\ - IDMAP HEADER magic 0x706d6469 0x0 \n\ - base crc 0x484aa77f 0x1 \n\ - overlay crc 0x03c66fa5 0x2 \n\ - base path .......... 0x03-0x42 /system/app/target.apk \n\ - overlay path .......... 0x43-0x82 /vendor/overlay/overlay.apk \n\ - DATA HEADER types count 0x00000003 0x83 \n\ - padding 0x00000000 0x84 \n\ - type offset 0x00000004 0x85 absolute offset 0x87, xml \n\ - type offset 0x00000007 0x86 absolute offset 0x8a, string \n\ - DATA BLOCK entry count 0x00000001 0x87 \n\ - entry offset 0x00000000 0x88 \n\ - entry 0x7f020000 0x89 xml/integer \n\ - DATA BLOCK entry count 0x00000002 0x8a \n\ - entry offset 0x00000000 0x8b \n\ - entry 0x7f030000 0x8c string/str \n\ - entry 0x7f030001 0x8d string/str2 \n\ + SECTION ENTRY VALUE COMMENT \n\ + IDMAP HEADER magic 0x706d6469 \n\ + base crc 0xb65a383f \n\ + overlay crc 0x7b9675e8 \n\ + base path .......... /path/to/target.apk \n\ + overlay path .......... /path/to/overlay.apk \n\ + DATA HEADER target pkg 0x0000007f \n\ + types count 0x00000003 \n\ + DATA BLOCK target type 0x00000002 \n\ + overlay type 0x00000002 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 drawable/drawable \n\ + DATA BLOCK target type 0x00000003 \n\ + overlay type 0x00000003 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 xml/integer \n\ + DATA BLOCK target type 0x00000004 \n\ + overlay type 0x00000004 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 raw/lorem_ipsum \n\ \n\ In this example, the overlay package provides three alternative resource values:\n\ - xml/integer, string/str and string/str2.\n\ + drawable/drawable, xml/integer, and raw/lorem_ipsum \n\ \n\ NOTES \n\ This tool and its expected invocation from installd is modelled on dexopt."; diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp index a59f5d31cab8..b9ac8a59de02 100644 --- a/cmds/idmap/inspect.cpp +++ b/cmds/idmap/inspect.cpp @@ -10,92 +10,108 @@ using namespace android; -#define NEXT(b, i, o) do { if (buf.next(&i, &o) < 0) { return -1; } } while (0) - namespace { - static const uint32_t IDMAP_MAGIC = 0x706d6469; + static const uint32_t IDMAP_MAGIC = 0x504D4449; static const size_t PATH_LENGTH = 256; - static const uint32_t IDMAP_HEADER_SIZE = (3 + 2 * (PATH_LENGTH / sizeof(uint32_t))); void printe(const char *fmt, ...); class IdmapBuffer { private: - char *buf_; + const char* buf_; size_t len_; - mutable size_t pos_; + size_t pos_; public: - IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {} + IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {} ~IdmapBuffer() { if (buf_ != MAP_FAILED) { - munmap(buf_, len_); + munmap(const_cast<char*>(buf_), len_); } } - int init(const char *idmap_path) - { + status_t init(const char *idmap_path) { struct stat st; int fd; if (stat(idmap_path, &st) < 0) { printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno)); - return -1; + return UNKNOWN_ERROR; } len_ = st.st_size; if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) { printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno)); - return -1; + return UNKNOWN_ERROR; } - if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { close(fd); printe("failed to mmap idmap: %s\n", strerror(errno)); - return -1; + return UNKNOWN_ERROR; } close(fd); - return 0; + return NO_ERROR; } - int next(uint32_t *i, uint32_t *offset) const - { + status_t nextUint32(uint32_t* i) { if (!buf_) { printe("failed to read next uint32_t: buffer not initialized\n"); - return -1; + return UNKNOWN_ERROR; } - if (pos_ + 4 > len_) { + + if (pos_ + sizeof(uint32_t) > len_) { printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n", pos_); - return -1; + return UNKNOWN_ERROR; + } + + if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) { + printe("failed to read next uint32_t: not aligned on 4-byte boundary\n"); + return UNKNOWN_ERROR; } - *offset = pos_ / sizeof(uint32_t); - char a = buf_[pos_++]; - char b = buf_[pos_++]; - char c = buf_[pos_++]; - char d = buf_[pos_++]; - *i = (d << 24) | (c << 16) | (b << 8) | a; - return 0; + + *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_)); + pos_ += sizeof(uint32_t); + return NO_ERROR; } - int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const - { + status_t nextUint16(uint16_t* i) { + if (!buf_) { + printe("failed to read next uint16_t: buffer not initialized\n"); + return UNKNOWN_ERROR; + } + + if (pos_ + sizeof(uint16_t) > len_) { + printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n", + pos_); + return UNKNOWN_ERROR; + } + + if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) { + printe("failed to read next uint32_t: not aligned on 2-byte boundary\n"); + return UNKNOWN_ERROR; + } + + *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_)); + pos_ += sizeof(uint16_t); + return NO_ERROR; + } + + status_t nextPath(char *b) { if (!buf_) { printe("failed to read next path: buffer not initialized\n"); - return -1; + return UNKNOWN_ERROR; } if (pos_ + PATH_LENGTH > len_) { printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_); - return -1; + return UNKNOWN_ERROR; } memcpy(b, buf_ + pos_, PATH_LENGTH); - *offset_start = pos_ / sizeof(uint32_t); pos_ += PATH_LENGTH; - *offset_end = pos_ / sizeof(uint32_t) - 1; - return 0; + return NO_ERROR; } }; - void printe(const char *fmt, ...) - { + void printe(const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -104,44 +120,37 @@ namespace { va_end(ap); } - void print_header() - { - printf("SECTION ENTRY VALUE OFFSET COMMENT\n"); + void print_header() { + printf("SECTION ENTRY VALUE COMMENT\n"); } - void print(const char *section, const char *subsection, uint32_t value, uint32_t offset, - const char *fmt, ...) - { + void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printf("%-12s %-12s 0x%08x 0x%-4x ", section, subsection, value, offset); + printf("%-12s %-12s 0x%08x ", section, subsection, value); vprintf(fmt, ap); printf("\n"); va_end(ap); } - void print_path(const char *section, const char *subsection, uint32_t offset_start, - uint32_t offset_end, const char *fmt, ...) - { + void print_path(const char *section, const char *subsection, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start, - offset_end); + printf("%-12s %-12s .......... ", section, subsection); vprintf(fmt, ap); printf("\n"); va_end(ap); } - int resource_metadata(const AssetManager& am, uint32_t res_id, - String8 *package, String8 *type, String8 *name) - { + status_t resource_metadata(const AssetManager& am, uint32_t res_id, + String8 *package, String8 *type, String8 *name) { const ResTable& rt = am.getResources(); struct ResTable::resource_name data; if (!rt.getResourceName(res_id, false, &data)) { printe("failed to get resource name id=0x%08x\n", res_id); - return -1; + return UNKNOWN_ERROR; } if (package) { *package = String8(String16(data.package, data.packageLen)); @@ -152,140 +161,150 @@ namespace { if (name) { *name = String8(String16(data.name, data.nameLen)); } - return 0; - } - - int package_id(const AssetManager& am) - { - return (am.getResources().getBasePackageId(0)) << 24; + return NO_ERROR; } - int parse_idmap_header(const IdmapBuffer& buf, AssetManager& am) - { - uint32_t i, o, e; + status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) { + uint32_t i; char path[PATH_LENGTH]; - NEXT(buf, i, o); + status_t err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + if (i != IDMAP_MAGIC) { printe("not an idmap file: actual magic constant 0x%08x does not match expected magic " "constant 0x%08x\n", i, IDMAP_MAGIC); - return -1; + return UNKNOWN_ERROR; } + print_header(); - print("IDMAP HEADER", "magic", i, o, ""); + print("IDMAP HEADER", "magic", i, ""); + + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "version", i, ""); - NEXT(buf, i, o); - print("", "base crc", i, o, ""); + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "base crc", i, ""); - NEXT(buf, i, o); - print("", "overlay crc", i, o, ""); + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "overlay crc", i, ""); - if (buf.nextPath(path, &o, &e) < 0) { + err = buf.nextPath(path); + if (err != NO_ERROR) { // printe done from IdmapBuffer::nextPath - return -1; + return err; } - print_path("", "base path", o, e, "%s", path); + print_path("", "base path", "%s", path); + if (!am.addAssetPath(String8(path), NULL)) { printe("failed to add '%s' as asset path\n", path); - return -1; + return UNKNOWN_ERROR; } - if (buf.nextPath(path, &o, &e) < 0) { + err = buf.nextPath(path); + if (err != NO_ERROR) { // printe done from IdmapBuffer::nextPath - return -1; + return err; } - print_path("", "overlay path", o, e, "%s", path); + print_path("", "overlay path", "%s", path); - return 0; + return NO_ERROR; } - int parse_data_header(const IdmapBuffer& buf, const AssetManager& am, Vector<uint32_t>& types) - { - uint32_t i, o; - const uint32_t numeric_package = package_id(am); + status_t parse_data(IdmapBuffer& buf, const AssetManager& am) { + const uint32_t packageId = am.getResources().getBasePackageId(0); - NEXT(buf, i, o); - print("DATA HEADER", "types count", i, o, ""); - const uint32_t N = i; + uint16_t data16; + status_t err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), ""); - for (uint32_t j = 0; j < N; ++j) { - NEXT(buf, i, o); - if (i == 0) { - print("", "padding", i, o, ""); - } else { - String8 type; - const uint32_t numeric_type = (j + 1) << 16; - const uint32_t res_id = numeric_package | numeric_type; - if (resource_metadata(am, res_id, NULL, &type, NULL) < 0) { - // printe done from resource_metadata - return -1; - } - print("", "type offset", i, o, "absolute offset 0x%02x, %s", - i + IDMAP_HEADER_SIZE, type.string()); - types.add(numeric_type); - } + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; } + print("", "types count", static_cast<uint32_t>(data16), ""); - return 0; - } + uint32_t typeCount = static_cast<uint32_t>(data16); + while (typeCount > 0) { + typeCount--; + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t targetTypeId = static_cast<uint32_t>(data16); + print("DATA BLOCK", "target type", targetTypeId, ""); - int parse_data_block(const IdmapBuffer& buf, const AssetManager& am, size_t numeric_type) - { - uint32_t i, o, n, id_offset; - const uint32_t numeric_package = package_id(am); - - NEXT(buf, i, o); - print("DATA BLOCK", "entry count", i, o, ""); - n = i; - - NEXT(buf, i, o); - print("", "entry offset", i, o, ""); - id_offset = i; - - for ( ; n > 0; --n) { - String8 type, name; - - NEXT(buf, i, o); - if (i == 0) { - print("", "padding", i, o, ""); - } else { - uint32_t res_id = numeric_package | numeric_type | id_offset; - if (resource_metadata(am, res_id, NULL, &type, &name) < 0) { - // printe done from resource_metadata - return -1; + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + print("", "overlay type", static_cast<uint32_t>(data16), ""); + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t entryCount = static_cast<uint32_t>(data16); + print("", "entry count", entryCount, ""); + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t entryOffset = static_cast<uint32_t>(data16); + print("", "entry offset", entryOffset, ""); + + for (uint32_t i = 0; i < entryCount; i++) { + uint32_t data32; + err = buf.nextUint32(&data32); + if (err != NO_ERROR) { + return err; } - print("", "entry", i, o, "%s/%s", type.string(), name.string()); + + uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i); + String8 type; + String8 name; + err = resource_metadata(am, resID, NULL, &type, &name); + if (err != NO_ERROR) { + return err; + } + print("", "entry", data32, "%s/%s", type.string(), name.string()); } - ++id_offset; } - return 0; + return NO_ERROR; } } -int idmap_inspect(const char *idmap_path) -{ +int idmap_inspect(const char *idmap_path) { IdmapBuffer buf; if (buf.init(idmap_path) < 0) { // printe done from IdmapBuffer::init return EXIT_FAILURE; } AssetManager am; - if (parse_idmap_header(buf, am) < 0) { + if (parse_idmap_header(buf, am) != NO_ERROR) { // printe done from parse_idmap_header return EXIT_FAILURE; } - Vector<uint32_t> types; - if (parse_data_header(buf, am, types) < 0) { + if (parse_data(buf, am) != NO_ERROR) { // printe done from parse_data_header return EXIT_FAILURE; } - const size_t N = types.size(); - for (size_t i = 0; i < N; ++i) { - if (parse_data_block(buf, am, types.itemAt(i)) < 0) { - // printe done from parse_data_block - return EXIT_FAILURE; - } - } return EXIT_SUCCESS; } diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 47047b86a6ec..f85a7dc89876 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.Collections; 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 @@ public final class Pm { 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/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f6883e2f5339..90615d3649f1 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -929,7 +929,8 @@ public class Activity extends ContextThemeWrapper /** * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with - * the attribute {@link android.R.attr#persistable} set true. + * the attribute {@link android.R.attr#persistableMode} set to + * <code>persistAcrossReboots</code>. * * @param savedInstanceState if the activity is being re-initialized after * previously being shut down then this Bundle contains the data it most @@ -1012,8 +1013,9 @@ public class Activity extends ContextThemeWrapper /** * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities - * created with the attribute {@link android.R.attr#persistable}. The {@link - * android.os.PersistableBundle} passed came from the restored PersistableBundle first + * created with the attribute {@link android.R.attr#persistableMode} set to + * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed + * came from the restored PersistableBundle first * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}. * * <p>This method is called between {@link #onStart} and @@ -1111,7 +1113,8 @@ public class Activity extends ContextThemeWrapper /** * This is the same as {@link #onPostCreate(Bundle)} but is called for activities - * created with the attribute {@link android.R.attr#persistable}. + * created with the attribute {@link android.R.attr#persistableMode} set to + * <code>persistAcrossReboots</code>. * * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState} * @param persistentState The data caming from the PersistableBundle first @@ -1352,10 +1355,10 @@ public class Activity extends ContextThemeWrapper /** * 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. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ea4604466a6b..f5514f8c197c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -191,11 +191,13 @@ public final class ActivityThread { /** 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 final class ActivityThread { } public boolean isPersistable() { - return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0; + return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS; } public String toString() { @@ -1585,11 +1587,11 @@ public final class ActivityThread { /** * 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 @@ public final class ActivityThread { + 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 0cccedcd954d..c7030b0a4b0a 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -206,6 +206,9 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { 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, @@ -290,13 +293,17 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { 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 +530,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { * @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 +566,32 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { 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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 586723230ee4..a480219fbf40 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -189,7 +189,9 @@ public class AppOpsManager { /** @hide Retrieve current usage stats via {@link UsageStatsManager}. */ public static final int OP_GET_USAGE_STATS = 43; /** @hide */ - public static final int _NUM_OP = 44; + public static final int OP_MUTE_MICROPHONE = 44; + /** @hide */ + public static final int _NUM_OP = 45; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = @@ -257,6 +259,7 @@ public class AppOpsManager { OP_COARSE_LOCATION, OP_COARSE_LOCATION, OP_GET_USAGE_STATS, + OP_MUTE_MICROPHONE }; /** @@ -308,6 +311,7 @@ public class AppOpsManager { OPSTR_MONITOR_LOCATION, OPSTR_MONITOR_HIGH_POWER_LOCATION, null, + null, }; /** @@ -358,7 +362,8 @@ public class AppOpsManager { "WAKE_LOCK", "MONITOR_LOCATION", "MONITOR_HIGH_POWER_LOCATION", - "GET_USAGE_STATS" + "GET_USAGE_STATS", + "OP_MUTE_MICROPHONE", }; /** @@ -410,6 +415,7 @@ public class AppOpsManager { null, // no permission for generic location monitoring null, // no permission for high power location monitoring android.Manifest.permission.PACKAGE_USAGE_STATS, + null, // no permission for muting/unmuting microphone }; /** @@ -462,6 +468,7 @@ public class AppOpsManager { null, //MONITOR_LOCATION null, //MONITOR_HIGH_POWER_LOCATION null, //GET_USAGE_STATS + UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE }; /** @@ -512,6 +519,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS + AppOpsManager.MODE_ALLOWED, }; /** @@ -566,6 +574,7 @@ public class AppOpsManager { false, false, false, + false, }; private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>(); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 84673d9e238e..de0396e478d7 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -823,8 +823,10 @@ final class ApplicationPackageManager extends PackageManager { 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; diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 89ee145cbaf3..01a388f469f2 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 @@ final class BackStackState implements Parcelable { 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 @@ final class BackStackState implements Parcelable { 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 @@ final class BackStackState implements Parcelable { 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 @@ final class BackStackState implements Parcelable { 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 class BackStackState implements Parcelable { 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 @@ final class BackStackState implements Parcelable { 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 @@ final class BackStackState implements Parcelable { 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements bumpBackStackNesting(1); + TransitionState state = beginTransition(mSharedElementSourceNames, + mSharedElementTargetNames); + Op op = mHead; while (op != null) { switch (op.cmd) { @@ -616,14 +752,17 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements pw.flush(); } + if (state == null) { + state = beginTransition(mSharedElementTargetNames, mSharedElementSourceNames); + } else { + setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames); + } + bumpBackStackNesting(-1); Op op = mTail; @@ -709,7 +1027,8 @@ final class BackStackRecord extends FragmentTransaction implements mManager.removeFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); - } break; + } + break; case OP_REPLACE: { Fragment f = op.fragment; if (f != null) { @@ -719,42 +1038,48 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 @@ final class BackStackRecord extends FragmentTransaction implements 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 52d458566faf..425a14018a3a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -115,9 +115,9 @@ import android.os.storage.IMountService; 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; import android.util.AndroidRuntimeException; @@ -142,6 +142,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsService; import com.android.internal.appwidget.IAppWidgetService.Stub; import com.android.internal.os.IDropBoxManagerService; +import com.android.internal.telecomm.ITelecommService; import java.io.File; import java.io.FileInputStream; @@ -464,11 +465,6 @@ class ContextImpl extends Context { 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()); @@ -517,6 +513,9 @@ class ContextImpl extends Context { 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()); }}); @@ -554,6 +553,13 @@ class ContextImpl extends Context { return new TelephonyManager(ctx.getOuterContext()); }}); + registerService(TELECOMM_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(TELECOMM_SERVICE); + return new TelecommManager(ctx.getOuterContext(), + ITelecommService.Stub.asInterface(b)); + }}); + registerService(UI_MODE_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new UiModeManager(); @@ -681,6 +687,7 @@ class ContextImpl extends Context { return new MediaSessionManager(ctx); } }); + registerService(TRUST_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(TRUST_SERVICE); @@ -688,6 +695,14 @@ class ContextImpl extends Context { } }); + 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); @@ -2178,10 +2193,10 @@ class ContextImpl extends Context { || 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 f54cb877690a..365cc8e8e0cd 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -106,7 +106,16 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { 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 +124,15 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { @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) { @@ -207,24 +221,6 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } } - 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 +238,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { setSharedElementState(sharedElementState, sharedElementSnapshots); requestLayoutForSharedElements(); - boolean startEnterTransition = allowOverlappingTransitions(); + boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning; boolean startSharedElementTransition = true; Transition transition = beginTransition(startEnterTransition, startSharedElementTransition); @@ -258,6 +254,29 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { 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 +301,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { 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 +312,17 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } } 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; } @@ -388,11 +413,21 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { 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 8d5b8317ac85..9f3dbdcf21cf 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -19,6 +19,7 @@ import android.animation.Animator; 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.os.Message; 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 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { 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 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { break; case MSG_SHARED_ELEMENT_DESTINATION: mExitSharedElementBundle = resultData; - if (mExitTransitionStarted) { + sharedElementExitBack(); + break; + } + } + + private void sharedElementExitBack() { + if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) { + startTransition(new Runnable() { + public void run() { startSharedElementExit(); } - break; + }); } } private void startSharedElementExit() { - if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) { - Transition transition = getSharedElementExitTransition(); - TransitionManager.beginDelayedTransition(getDecor(), transition); - ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle, - mSharedElementNames); - setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots); + 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 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } } }, 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 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { 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 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { 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 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { Transition viewsTransition = getExitTransition(); Transition transition = mergeTransitions(sharedElementTransition, viewsTransition); - mExitTransitionStarted = true; if (transition != null) { TransitionManager.beginDelayedTransition(getDecor(), transition); } @@ -269,15 +319,31 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } 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 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } 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 76f9d97455cf..b8f1962d0b80 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1494,7 +1494,7 @@ final class FragmentManagerImpl extends FragmentManager { return false; } final BackStackRecord bss = mBackStack.remove(last); - bss.popFromBackStack(true); + bss.popFromBackStack(true, null); reportBackStackChanged(); } else { int index = -1; @@ -1538,9 +1538,10 @@ final class FragmentManagerImpl extends FragmentManager { 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 6e9989968896..7479ecdafde3 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 class FragmentTransaction { 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/LoadedApk.java b/core/java/android/app/LoadedApk.java index 3ae8bfcb4f3f..065e88db320d 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.ref.WeakReference; 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 @@ public final class LoadedApk { 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 final class LoadedApk { 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 @@ public final class LoadedApk { mPackageName = "android"; mAppDir = null; mResDir = null; + mSplitAppDirs = null; + mSplitResDirs = null; mOverlayDirs = null; mSharedLibraries = null; mDataDir = null; @@ -214,53 +221,6 @@ public final class LoadedApk { 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 @@ public final class LoadedApk { } 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 @@ public final class LoadedApk { * 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 @@ public final class LoadedApk { return mResDir; } + public String[] getSplitAppDirs() { + return mSplitAppDirs; + } + + public String[] getSplitResDirs() { + return mSplitResDirs; + } + public String[] getOverlayDirs() { return mOverlayDirs; } @@ -487,7 +482,7 @@ public final class LoadedApk { 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 276f936605a6..b94fd41b0a5f 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2867,16 +2867,16 @@ public class Notification implements Parcelable /** * 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 @@ public class Notification implements Parcelable /** * 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 @@ public class Notification implements Parcelable /** * 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 @@ public class Notification implements Parcelable * .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 a67faa09be86..3c1311537848 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -149,9 +149,9 @@ public class ResourcesManager { * @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 @@ public class ResourcesManager { 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/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e80c7616204f..af653a380025 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -18,6 +18,7 @@ package android.app.admin; 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; @@ -1839,11 +1840,13 @@ public class DevicePolicyManager { * This function should be used cautiously as once it is called it cannot * be undone. The device owner can only be set as a part of device setup * before setup completes. + * + * @param packageName The package name of the device owner. */ - public void clearDeviceOwnerApp() { + public void clearDeviceOwnerApp(String packageName) { if (mService != null) { try { - mService.clearDeviceOwner(mContext.getPackageName()); + mService.clearDeviceOwner(packageName); } catch (RemoteException re) { Log.w(TAG, "Failed to clear device owner"); } @@ -2340,15 +2343,20 @@ public class DevicePolicyManager { } /** - * 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 +2364,13 @@ public class DevicePolicyManager { } /** - * 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 +2381,12 @@ public class DevicePolicyManager { /** * 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 ee7e74b9cc16..e7b77d82da30 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -142,9 +142,9 @@ interface IDevicePolicyManager { 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); diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index 56a55fb4ae28..706ef043dccb 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -228,19 +228,35 @@ public class BackupTransport { } /** - * 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 +348,11 @@ public class BackupTransport { // 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,10 +364,14 @@ public class BackupTransport { * {@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. + * 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 much data to be delivered to the app. A negative + * 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. @@ -380,6 +379,24 @@ public class BackupTransport { 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 +467,7 @@ public class BackupTransport { } @Override - public String nextRestorePackage() throws RemoteException { + public RestoreDescription nextRestorePackage() throws RemoteException { return BackupTransport.this.nextRestorePackage(); } @@ -478,5 +495,15 @@ public class BackupTransport { 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/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java b/core/java/android/app/backup/RestoreDescription.aidl index b2ba21b542b2..9cbea786fda9 100644 --- a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java +++ b/core/java/android/app/backup/RestoreDescription.aidl @@ -14,23 +14,6 @@ * limitations under the License. */ -package com.android.idleservicetest; +package android.app.backup; -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"); - } - -} +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 000000000000..0fb43557252a --- /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 582dede9574c..000000000000 --- 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 54abccdc1b4f..000000000000 --- 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 2331b81aba1b..000000000000 --- 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 1c9bf9dad835..000000000000 --- 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/Context.java b/core/java/android/content/Context.java index ccf84516df1d..fd19b401101c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2035,6 +2035,7 @@ public abstract class Context { AUDIO_SERVICE, MEDIA_ROUTER_SERVICE, TELEPHONY_SERVICE, + TELECOMM_SERVICE, CLIPBOARD_SERVICE, INPUT_METHOD_SERVICE, TEXT_SERVICES_MANAGER_SERVICE, @@ -2163,6 +2164,8 @@ public abstract class Context { * @see android.media.MediaRouter * @see #TELEPHONY_SERVICE * @see android.telephony.TelephonyManager + * @see #TELECOMM_SERVICE + * @see android.telecomm.TelecommManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager * @see #UI_MODE_SERVICE @@ -2494,6 +2497,16 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a + * {@link android.telecomm.TelecommManager} to manage telecomm-related features + * of the device. + * + * @see #getSystemService + * @see android.telecomm.TelecommManager + */ + public static final String TELECOMM_SERVICE = "telecomm"; + + /** + * Use with {@link #getSystemService} to retrieve a * {@link android.text.ClipboardManager} for accessing and modifying * the contents of the global clipboard. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 6e53a6fb5969..3dfa78b4804c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -7375,6 +7375,7 @@ public class Intent implements Parcelable, Cloneable { 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 791e5aa46c7f..abc8cde2c9eb 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -104,6 +104,28 @@ public class ActivityInfo extends ComponentInfo 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,12 +252,6 @@ public class ActivityInfo extends ComponentInfo */ 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} - */ - public static final int FLAG_PERSISTABLE = 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. * {@link android.R.attr#autoRemoveFromRecents} @@ -641,13 +657,23 @@ public class ActivityInfo extends ComponentInfo 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 +714,7 @@ public class ActivityInfo extends ComponentInfo dest.writeInt(softInputMode); dest.writeInt(uiOptions); dest.writeString(parentActivityName); + dest.writeInt(persistableMode); } public static final Parcelable.Creator<ActivityInfo> CREATOR @@ -713,5 +740,6 @@ public class ActivityInfo extends ComponentInfo 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 6b44a115cd42..06f4019df06d 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -23,8 +23,12 @@ import android.os.Parcel; 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 @@ -398,17 +402,30 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 +529,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { + " 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 +611,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 +655,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 +698,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java index a977e41c3d69..dab0caffd57a 100644 --- a/core/java/android/content/pm/InstrumentationInfo.java +++ b/core/java/android/content/pm/InstrumentationInfo.java @@ -30,17 +30,32 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { * "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 69fa408c5871..6c10bb8fce7a 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -57,6 +57,65 @@ public class LauncherApps { 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 @@ public class LauncherApps { 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 class LauncherApps { 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 @@ public class LauncherApps { for (OnAppsChangedListener listener : mListeners) { listener.onPackageRemoved(user, packageName); } + for (OnAppsChangedCallback callback : mCallbacks) { + callback.onPackageRemoved(packageName, user); + } } } @@ -321,6 +420,9 @@ public class LauncherApps { for (OnAppsChangedListener listener : mListeners) { listener.onPackageChanged(user, packageName); } + for (OnAppsChangedCallback callback : mCallbacks) { + callback.onPackageChanged(packageName, user); + } } } @@ -333,6 +435,9 @@ public class LauncherApps { for (OnAppsChangedListener listener : mListeners) { listener.onPackageAdded(user, packageName); } + for (OnAppsChangedCallback callback : mCallbacks) { + callback.onPackageAdded(packageName, user); + } } } @@ -346,6 +451,9 @@ public class LauncherApps { for (OnAppsChangedListener listener : mListeners) { listener.onPackagesAvailable(user, packageNames, replacing); } + for (OnAppsChangedCallback callback : mCallbacks) { + callback.onPackagesAvailable(packageNames, user, replacing); + } } } @@ -359,7 +467,10 @@ public class LauncherApps { 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 550c1f1eb2b9..8d9b8d9e0ade 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.content.pm.PackageParser.PackageParserException; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; @@ -2873,16 +2874,17 @@ public abstract class PackageManager { DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); final File sourceFile = new File(archiveFilePath); - PackageParser.Package pkg = packageParser.parsePackage( - sourceFile, archiveFilePath, metrics, 0); - if (pkg == null) { + try { + PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, + 0); + if ((flags & GET_SIGNATURES) != 0) { + packageParser.collectCertificates(pkg, 0); + } + PackageUserState state = new PackageUserState(); + return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state); + } catch (PackageParserException e) { return null; } - if ((flags & GET_SIGNATURES) != 0) { - packageParser.collectCertificates(pkg, 0); - } - PackageUserState state = new PackageUserState(); - return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state); } /** diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 4cac7fdaaecc..618c2bd646ff 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -18,6 +18,7 @@ package android.content.pm; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import android.content.ComponentName; import android.content.Intent; @@ -31,6 +32,7 @@ import android.os.Build; import android.os.Bundle; import android.os.PatternMatcher; import android.os.UserHandle; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Base64; import android.util.DisplayMetrics; @@ -81,6 +83,8 @@ public class PackageParser { private static final boolean DEBUG_PARSER = false; private static final boolean DEBUG_BACKUP = false; + // TODO: switch outError users to PackageParserException + /** File name in an APK for the Android manifest. */ private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"; @@ -207,10 +211,10 @@ public class PackageParser { } } - /* Light weight package info. - * @hide + /** + * Lightweight parsed details about a single APK file. */ - public static class PackageLite { + public static class ApkLite { public final String packageName; public final String splitName; public final int versionCode; @@ -218,7 +222,7 @@ public class PackageParser { public final VerifierInfo[] verifiers; public final Signature[] signatures; - public PackageLite(String packageName, String splitName, int versionCode, + public ApkLite(String packageName, String splitName, int versionCode, int installLocation, List<VerifierInfo> verifiers, Signature[] signatures) { this.packageName = packageName; this.splitName = splitName; @@ -247,6 +251,10 @@ public class PackageParser { mArchiveSourcePath = archiveSourcePath; } + public PackageParser(File archiveSource) { + this(archiveSource.getAbsolutePath()); + } + public void setSeparateProcesses(String[] procs) { mSeparateProcesses = procs; } @@ -255,6 +263,10 @@ public class PackageParser { mOnlyCoreApps = onlyCoreApps; } + private static final boolean isPackageFilename(File file) { + return isPackageFilename(file.getName()); + } + private static final boolean isPackageFilename(String name) { return name.endsWith(".apk"); } @@ -497,26 +509,84 @@ public class PackageParser { public final static int PARSE_IS_PRIVILEGED = 1<<7; public final static int PARSE_GET_SIGNATURES = 1<<8; - public int getParseError() { - return mParseError; + /** + * Parse all APK files under the given directory as a single package. + */ + public Package parseSplitPackage(File apkDir, DisplayMetrics metrics, int flags, + boolean trustedOverlay) 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; + 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); + } + + if (TextUtils.isEmpty(lite.splitName)) { + baseFile = file; + break; + } + } + } + + if (baseFile == null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Missing base APK in " + apkDir); + } + + final Package pkg = parseBaseApk(baseFile, metrics, flags, trustedOverlay); + 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); + } + + return pkg; } - public Package parsePackage(File sourceFile, String destCodePath, - DisplayMetrics metrics, int flags) { - return parsePackage(sourceFile, destCodePath, metrics, flags, false); + public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags) + throws PackageParserException { + return parseMonolithicPackage(apkFile, metrics, flags, false); } - public Package parsePackage(File sourceFile, String destCodePath, - DisplayMetrics metrics, int flags, boolean trustedOverlay) { + public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags, + boolean trustedOverlay) throws PackageParserException { + final Package pkg = parseBaseApk(apkFile, metrics, flags, trustedOverlay); + if (pkg != null) { + return pkg; + } else { + throw new PackageParserException(mParseError, "Failed to parse " + apkFile); + } + } + + private Package parseBaseApk(File apkFile, DisplayMetrics metrics, int flags, + boolean trustedOverlay) { mParseError = PackageManager.INSTALL_SUCCEEDED; - mArchiveSourcePath = sourceFile.getPath(); - if (!sourceFile.isFile()) { + mArchiveSourcePath = apkFile.getAbsolutePath(); + if (!apkFile.isFile()) { Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath); mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } - if (!isPackageFilename(sourceFile.getName()) + if (!isPackageFilename(apkFile.getName()) && (flags&PARSE_MUST_BE_APK) != 0) { if ((flags&PARSE_IS_SYSTEM) == 0) { // We expect to have non-.apk files in the system dir, @@ -560,13 +630,12 @@ public class PackageParser { Exception errorException = null; try { // XXXX todo: need to figure out correct configuration. - pkg = parsePackage(res, parser, flags, trustedOverlay, errorText); + pkg = parseBaseApk(res, parser, flags, trustedOverlay, errorText); } catch (Exception e) { errorException = e; mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; } - if (pkg == null) { // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED // just means to skip this app so don't make a fuss about it. @@ -590,22 +659,25 @@ public class PackageParser { parser.close(); assmgr.close(); - // Set code and resource paths - pkg.mPath = destCodePath; - pkg.mScanPath = mArchiveSourcePath; - //pkg.applicationInfo.sourceDir = destCodePath; - //pkg.applicationInfo.publicSourceDir = destRes; + pkg.codePath = mArchiveSourcePath; pkg.mSignatures = null; return pkg; } + private void parseSplitApk(Package pkg, File apkFile, DisplayMetrics metrics, int flags, + boolean trustedOverlay) throws PackageParserException { + // TODO: expand split APK parsing + pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths, + apkFile.getAbsolutePath()); + } + /** * Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the * APK. If it successfully scanned the package and found the * {@code AndroidManifest.xml}, {@code true} is returned. */ - public boolean collectManifestDigest(Package pkg) { + public void collectManifestDigest(Package pkg) throws PackageParserException { try { final StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath); try { @@ -616,13 +688,19 @@ public class PackageParser { } finally { jarFile.close(); } - return true; } catch (IOException e) { - return false; + throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Failed to collect manifest digest"); + } + } + + public void collectCertificates(Package pkg, int flags) throws PackageParserException { + if (!collectCertificatesInternal(pkg, flags)) { + throw new PackageParserException(mParseError, "Failed to collect certificates"); } } - public boolean collectCertificates(Package pkg, int flags) { + private boolean collectCertificatesInternal(Package pkg, int flags) { pkg.mSignatures = null; WeakReference<byte[]> readBufferRef; @@ -808,7 +886,7 @@ public class PackageParser { * @param flags Special parse flags * @return PackageLite object with package information or null on failure. */ - public static PackageLite parsePackageLite(String packageFilePath, int flags) { + public static ApkLite parseApkLite(String packageFilePath, int flags) { AssetManager assmgr = null; final XmlResourceParser parser; final Resources res; @@ -844,9 +922,9 @@ public class PackageParser { final AttributeSet attrs = parser; final String errors[] = new String[1]; - PackageLite packageLite = null; + ApkLite packageLite = null; try { - packageLite = parsePackageLite(res, parser, attrs, flags, signatures, errors); + packageLite = parseApkLite(res, parser, attrs, flags, signatures, errors); } catch (PackageParserException e) { Slog.w(TAG, packageFilePath, e); } catch (IOException e) { @@ -930,7 +1008,7 @@ public class PackageParser { (splitName != null) ? splitName.intern() : splitName); } - private static PackageLite parsePackageLite(Resources res, XmlPullParser parser, + private static ApkLite parseApkLite(Resources res, XmlPullParser parser, AttributeSet attrs, int flags, Signature[] signatures, String[] outError) throws IOException, XmlPullParserException, PackageParserException { final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags); @@ -972,7 +1050,7 @@ public class PackageParser { } } - return new PackageLite(packageSplit.first, packageSplit.second, versionCode, + return new ApkLite(packageSplit.first, packageSplit.second, versionCode, installLocation, verifiers, signatures); } @@ -988,9 +1066,8 @@ public class PackageParser { return new Signature(sig); } - private Package parsePackage( - Resources res, XmlResourceParser parser, int flags, boolean trustedOverlay, - String[] outError) throws XmlPullParserException, IOException { + private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags, + boolean trustedOverlay, String[] outError) throws XmlPullParserException, IOException { AttributeSet attrs = parser; mParseInstrumentationArgs = null; @@ -1019,7 +1096,13 @@ public class PackageParser { } } - final Package pkg = new Package(pkgName, splitName); + if (!TextUtils.isEmpty(splitName)) { + outError[0] = "Expected base APK, but found split " + splitName; + mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; + return null; + } + + final Package pkg = new Package(pkgName); boolean foundApp = false; TypedArray sa = res.obtainAttributes(attrs, @@ -2524,10 +2607,9 @@ public class PackageParser { 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, @@ -3580,10 +3662,17 @@ public class PackageParser { return true; } + /** + * Representation of a full package parsed from APK files on disk. A package + * consists of a single base APK, and zero or more split APKs. + */ public final static class Package { public String packageName; - public String splitName; + + // TODO: work towards making these paths invariant + public String codePath; + public String[] splitCodePaths; // For now we only support one application per package. public final ApplicationInfo applicationInfo = new ApplicationInfo(); @@ -3615,9 +3704,6 @@ public class PackageParser { // We store the application meta-data independently to avoid multiple unwanted references public Bundle mAppMetaData = null; - // If this is a 3rd party app, this is the path of the zip file. - public String mPath; - // The version code declared for this package. public int mVersionCode; @@ -3637,10 +3723,6 @@ public class PackageParser { // preferred up order. public int mPreferredOrder = 0; - // For use by the package manager to keep track of the path to the - // file an app came from. - public String mScanPath; - // For use by package manager to keep track of where it needs to do dexopt. public boolean mDexOptNeeded = true; @@ -3700,9 +3782,8 @@ public class PackageParser { public Set<PublicKey> mSigningKeys; public Map<String, Set<PublicKey>> mKeySetMapping; - public Package(String packageName, String splitName) { + public Package(String packageName) { this.packageName = packageName; - this.splitName = splitName; applicationInfo.packageName = packageName; applicationInfo.uid = -1; } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index a07fc9708b7f..a83bd4aeb231 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -17,11 +17,14 @@ package android.content.res; import android.content.pm.ActivityInfo; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.view.View; +import java.text.Format; +import java.util.ArrayList; import java.util.Locale; /** @@ -1306,4 +1309,240 @@ public final class Configuration implements Parcelable, Comparable<Configuration private static int getScreenLayoutNoDirection(int screenLayout) { return screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK; } + + /** + * + * @hide + */ + public static String localeToResourceQualifier(Locale locale) { + StringBuilder sb = new StringBuilder(); + boolean l = (locale.getLanguage().length() != 0); + boolean c = (locale.getCountry().length() != 0); + boolean s = (locale.getScript().length() != 0); + boolean v = (locale.getVariant().length() != 0); + + if (l) { + sb.append(locale.getLanguage()); + if (c) { + sb.append("-r").append(locale.getCountry()); + if (s) { + sb.append("-s").append(locale.getScript()); + if (v) { + sb.append("-v").append(locale.getVariant()); + } + } + } + } + return sb.toString(); + } + + + /** + * Returns a string representation of the configuration that can be parsed + * by build tools (like AAPT). + * + * + * + * @hide + */ + public static String resourceQualifierString(Configuration config) { + ArrayList<String> parts = new ArrayList<String>(); + + if (config.mcc != 0) { + parts.add(config.mcc + "mcc"); + if (config.mnc != 0) { + parts.add(config.mnc + "mnc"); + } + } + + if (!config.locale.getLanguage().isEmpty()) { + parts.add(localeToResourceQualifier(config.locale)); + } + + switch (config.screenLayout & Configuration.SCREENLAYOUT_LAYOUTDIR_MASK) { + case Configuration.SCREENLAYOUT_LAYOUTDIR_LTR: + parts.add("ldltr"); + break; + case Configuration.SCREENLAYOUT_LAYOUTDIR_RTL: + parts.add("ldrtl"); + break; + default: + break; + } + + if (config.smallestScreenWidthDp != 0) { + parts.add("sw" + config.smallestScreenWidthDp + "dp"); + } + + if (config.screenWidthDp != 0) { + parts.add("w" + config.screenWidthDp + "dp"); + } + + if (config.screenHeightDp != 0) { + parts.add("h" + config.screenHeightDp + "dp"); + } + + switch (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) { + case Configuration.SCREENLAYOUT_SIZE_SMALL: + parts.add("small"); + break; + case Configuration.SCREENLAYOUT_SIZE_NORMAL: + parts.add("normal"); + break; + case Configuration.SCREENLAYOUT_SIZE_LARGE: + parts.add("large"); + break; + case Configuration.SCREENLAYOUT_SIZE_XLARGE: + parts.add("xlarge"); + break; + default: + break; + } + + switch (config.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) { + case Configuration.SCREENLAYOUT_LONG_YES: + parts.add("long"); + break; + case Configuration.SCREENLAYOUT_LONG_NO: + parts.add("notlong"); + break; + default: + break; + } + + switch (config.orientation) { + case Configuration.ORIENTATION_LANDSCAPE: + parts.add("land"); + break; + case Configuration.ORIENTATION_PORTRAIT: + parts.add("port"); + break; + default: + break; + } + + switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) { + case Configuration.UI_MODE_TYPE_APPLIANCE: + parts.add("appliance"); + break; + case Configuration.UI_MODE_TYPE_DESK: + parts.add("desk"); + break; + case Configuration.UI_MODE_TYPE_TELEVISION: + parts.add("television"); + break; + case Configuration.UI_MODE_TYPE_CAR: + parts.add("car"); + break; + case Configuration.UI_MODE_TYPE_WATCH: + parts.add("watch"); + break; + default: + break; + } + + switch (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) { + case Configuration.UI_MODE_NIGHT_YES: + parts.add("night"); + break; + case Configuration.UI_MODE_NIGHT_NO: + parts.add("notnight"); + break; + default: + break; + } + + switch (config.densityDpi) { + case 0: + break; + case 120: + parts.add("ldpi"); + break; + case 160: + parts.add("mdpi"); + break; + case 213: + parts.add("tvdpi"); + break; + case 240: + parts.add("hdpi"); + break; + case 320: + parts.add("xhdpi"); + break; + default: + parts.add(config.densityDpi + "dpi"); + break; + } + + switch (config.touchscreen) { + case Configuration.TOUCHSCREEN_NOTOUCH: + parts.add("notouch"); + break; + case Configuration.TOUCHSCREEN_FINGER: + parts.add("finger"); + break; + default: + break; + } + + switch (config.keyboardHidden) { + case Configuration.KEYBOARDHIDDEN_NO: + parts.add("keysexposed"); + break; + case Configuration.KEYBOARDHIDDEN_YES: + parts.add("keyshidden"); + break; + case Configuration.KEYBOARDHIDDEN_SOFT: + parts.add("keyssoft"); + break; + default: + break; + } + + switch (config.keyboard) { + case Configuration.KEYBOARD_NOKEYS: + parts.add("nokeys"); + break; + case Configuration.KEYBOARD_QWERTY: + parts.add("qwerty"); + break; + case Configuration.KEYBOARD_12KEY: + parts.add("12key"); + break; + default: + break; + } + + switch (config.navigationHidden) { + case Configuration.NAVIGATIONHIDDEN_NO: + parts.add("navexposed"); + break; + case Configuration.NAVIGATIONHIDDEN_YES: + parts.add("navhidden"); + break; + default: + break; + } + + switch (config.navigation) { + case Configuration.NAVIGATION_NONAV: + parts.add("nonav"); + break; + case Configuration.NAVIGATION_DPAD: + parts.add("dpad"); + break; + case Configuration.NAVIGATION_TRACKBALL: + parts.add("trackball"); + break; + case Configuration.NAVIGATION_WHEEL: + parts.add("wheel"); + break; + default: + break; + } + + parts.add("v" + Build.VERSION.SDK_INT); + return TextUtils.join("-", parts); + } } diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl index 4c50dda1f096..31896f55b09f 100644 --- a/core/java/android/hardware/ICameraService.aidl +++ b/core/java/android/hardware/ICameraService.aidl @@ -69,4 +69,9 @@ interface ICameraService * 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); } diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 09015622d2a0..73188ff0898b 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -19,8 +19,10 @@ package android.hardware.camera2; 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 @@ public final class CameraManager { 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 @@ public final class CameraManager { 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 @@ public final class CameraManager { } } - 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); } /** @@ -456,6 +495,53 @@ public final class CameraManager { } } + /** + * 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/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java index 22ff9c63114f..ab7e8449230c 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java @@ -34,6 +34,7 @@ import android.util.Log; * <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 @@ public class CameraDeviceState { 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/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java index 3fd2309912ec..038255762040 100644 --- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java @@ -148,6 +148,12 @@ public class GLThreadManager { 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 f9cf90596973..c34a34de021e 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -48,7 +48,6 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class LegacyCameraDevice implements AutoCloseable { public static final String DEBUG_PROP = "HAL1ShimLogging"; - private final String TAG; private final int mCameraId; @@ -56,10 +55,11 @@ public class LegacyCameraDevice implements AutoCloseable { private final CameraDeviceState mDeviceState = new CameraDeviceState(); 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,11 +81,18 @@ public class LegacyCameraDevice implements AutoCloseable { 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() { + try { + mDeviceCallbacks.onCameraError(errorCode, extras); + } catch (RemoteException e) { + throw new IllegalStateException( + "Received remote exception during onCameraError callback: ", e); + } + } + }); + } @@ -98,36 +105,55 @@ public class LegacyCameraDevice implements AutoCloseable { 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() { + 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() { + 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() { + 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 +187,8 @@ public class LegacyCameraDevice implements AutoCloseable { 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); @@ -244,6 +272,22 @@ public class LegacyCameraDevice implements AutoCloseable { 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 000000000000..8bb066fd09a3 --- /dev/null +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.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.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.impl.CameraMetadataNative; +import android.hardware.camera2.params.StreamConfiguration; +import android.hardware.camera2.params.StreamConfigurationDuration; +import android.util.Log; + +import java.util.ArrayList; +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; + + /** + * 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) { + mapStreamConfigs(m, p); + + // TODO: map other fields + } + + private static void mapStreamConfigs(CameraMetadataNative m, Camera.Parameters p) { + // TODO: set non-empty durations + m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[] {} ); + m.set(SCALER_AVAILABLE_STALL_DURATIONS, new StreamConfigurationDuration[] {} ); + + ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>(); + /* + * 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)); + } + } + appendStreamConfig(availableStreamConfigs, + HAL_PIXEL_FORMAT_BLOB, p.getSupportedPictureSizes()); + m.set(SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + availableStreamConfigs.toArray(new StreamConfiguration[0])); + } + + 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); + } + } +} diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index c4669f516b1b..7b522ffa36ce 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -20,6 +20,7 @@ import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; import android.hardware.camera2.utils.LongParcelable; import android.hardware.camera2.impl.CameraMetadataNative; import android.os.ConditionVariable; @@ -28,12 +29,15 @@ import android.os.Message; 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,6 +68,7 @@ public class RequestThreadManager { 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 RequestHolder mInFlightPreview; @@ -74,6 +79,8 @@ public class RequestThreadManager { private GLThreadManager mGLThreadManager; private SurfaceTexture mPreviewTexture; + private Size mIntermediateBufferSize; + private final RequestQueue mRequestQueue = new RequestQueue(); private SurfaceTexture mDummyTexture; private Surface mDummySurface; @@ -93,6 +100,31 @@ public class RequestThreadManager { } } + + /** + * 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. */ @@ -230,7 +262,13 @@ public class RequestThreadManager { 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 +286,7 @@ public class RequestThreadManager { startPreview(); } + private void configureOutputs(Collection<Surface> outputs) throws IOException { stopPreview(); if (mGLThreadManager != null) { @@ -261,6 +300,7 @@ public class RequestThreadManager { mInFlightPreview = null; mInFlightJpeg = null; + for (Surface s : outputs) { int format = LegacyCameraDevice.nativeDetectSurfaceType(s); switch (format) { @@ -273,6 +313,52 @@ public class RequestThreadManager { } } + 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); + + Camera.Parameters params = mCamera.getParameters(); + + // Find largest jpeg dimension - assume to have the same aspect ratio as sensor. + List<Size> supportedJpegSizes = convertSizeList(params.getSupportedPictureSizes()); + Size largestJpegDimen = findLargestByArea(supportedJpegSizes); + + List<Size> supportedPreviewSizes = convertSizeList(params.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"); + } + } + + + // TODO: Detect and optimize single-output paths here to skip stream teeing. if (mGLThreadManager == null) { mGLThreadManager = new GLThreadManager(mCameraId); @@ -282,7 +368,28 @@ public class RequestThreadManager { 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 @@ -376,8 +483,10 @@ public class RequestThreadManager { // TODO: err handling throw new IOError(e); } - // TODO: Set fields in result. - mDeviceState.setCaptureResult(holder, new CameraMetadataNative()); + Camera.Parameters params = mCamera.getParameters(); + CameraMetadataNative result = convertResultMetadata(params, + holder.getRequest()); + mDeviceState.setCaptureResult(holder, result); } break; case MSG_CLEANUP: @@ -397,6 +506,15 @@ public class RequestThreadManager { } }; + private CameraMetadataNative convertResultMetadata(Camera.Parameters params, + CaptureRequest request) { + CameraMetadataNative result = new CameraMetadataNative(); + result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength()); + + // TODO: Remaining result metadata tags conversions. + return result; + } + /** * Create a new RequestThreadManager. * @@ -437,6 +555,12 @@ public class RequestThreadManager { 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())); + } } /** diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java index 2f0f6bc4582e..49f419f2dcf0 100644 --- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java +++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java @@ -431,6 +431,11 @@ public class SurfaceTextureRenderer { 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)) { diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java index 40cda08cf7f7..898c74612ca6 100644 --- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java +++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java @@ -47,6 +47,8 @@ public class CameraBinderDecorator { * - 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/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java index 8ad94632fbd7..d86dd5e7b207 100644 --- a/core/java/android/hardware/hdmi/HdmiCec.java +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -185,6 +185,7 @@ public final class HdmiCec { 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/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index a0bf5527e228..559a469e5269 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -40,6 +40,6 @@ interface IHdmiControlService { void addDeviceEventListener(IHdmiDeviceEventListener listener); void deviceSelect(int logicalAddress, IHdmiControlCallback callback); void portSelect(int portId, IHdmiControlCallback callback); - void sendKeyEvent(int keyCode); + void sendKeyEvent(int keyCode, boolean isPressed); List<HdmiPortInfo> getPortInfo(); } diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl index dd9c39f275d0..b7af37474307 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 @@ interface INetworkManagementEventObserver { * @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 a14d13f17650..f1fa3eb74826 100644 --- a/core/java/android/net/IpPrefix.java +++ b/core/java/android/net/IpPrefix.java @@ -18,6 +18,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; +import android.util.Pair; import java.net.InetAddress; import java.net.UnknownHostException; @@ -46,9 +47,18 @@ public final class IpPrefix implements Parcelable { 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 @@ public final class IpPrefix implements Parcelable { * @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 @@ public final class IpPrefix implements Parcelable { } /** - * Returns the prefix length of this {@code IpAddress}. + * Returns the prefix length of this {@code IpPrefix}. * * @return the prefix length. */ @@ -138,6 +170,20 @@ public final class IpPrefix implements Parcelable { } /** + * 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 524607822db8..f9a25f9ed265 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -18,6 +18,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; +import android.util.Pair; import java.net.Inet4Address; import java.net.InetAddress; @@ -166,23 +167,9 @@ public class LinkAddress implements Parcelable { * @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 8eefa0f55fab..e7184eddc449 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -31,6 +31,7 @@ import java.util.Collection; 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 @@ public final class LinkProperties implements Parcelable { } /** - * 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 @@ public final class LinkProperties implements Parcelable { "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 535bbe2415ef..81426704cc0f 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -699,15 +699,15 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { break; } - try { - if (enable) { - return mPhoneService.enableApnType(apnType); - } else { - return mPhoneService.disableApnType(apnType); - } - } catch (RemoteException e) { - if (retry == 0) getPhoneService(true); - } +// try { +// if (enable) { +// return mPhoneService.enableApnType(apnType); +// } else { +// return mPhoneService.disableApnType(apnType); +// } +// } catch (RemoteException e) { +// if (retry == 0) getPhoneService(true); +// } } loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\""); diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index b02f88ec8137..15c0a71aa21e 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -24,6 +24,8 @@ import java.util.Collection; import java.util.Locale; import android.util.Log; +import android.util.Pair; + /** * Native methods for managing network interfaces. @@ -218,24 +220,17 @@ public class NetworkUtils { } /** - * 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 @@ public class NetworkUtils { 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 @@ public class NetworkUtils { } /** + * 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 000000000000..92dd1416d923 --- /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/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java new file mode 100644 index 000000000000..f3a95b90d5c1 --- /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 69b828f82ac8..08a15eb7dfa1 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 class PowerManagerInternal { 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 468cfe0229c9..757f38e065de 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -226,14 +226,14 @@ public class UserManager { 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 diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java index e32890deadd8..67f64090db16 100644 --- a/core/java/android/preference/SeekBarPreference.java +++ b/core/java/android/preference/SeekBarPreference.java @@ -40,11 +40,19 @@ public class SeekBarPreference extends Preference Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - final TypedArray a = context.obtainStyledAttributes( + TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.ProgressBar, defStyleAttr, defStyleRes); setMax(a.getInt(com.android.internal.R.styleable.ProgressBar_max, mMax)); a.recycle(); - setLayoutResource(com.android.internal.R.layout.preference_widget_seekbar); + + a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.SeekBarPreference, defStyleAttr, defStyleRes); + final int layoutResId = a.getResourceId( + com.android.internal.R.styleable.SeekBarPreference_layout, + com.android.internal.R.layout.preference_widget_seekbar); + a.recycle(); + + setLayoutResource(layoutResId); } public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) { @@ -52,7 +60,7 @@ public class SeekBarPreference extends Preference } public SeekBarPreference(Context context, AttributeSet attrs) { - this(context, attrs, 0); + this(context, attrs, com.android.internal.R.attr.seekBarPreferenceStyle); } public SeekBarPreference(Context context) { diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index ba66e6542e95..1e7d7f1d4830 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -7963,8 +7963,7 @@ public final class ContactsContract { 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 2a5e9fd904ef..06c05eedc8ab 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4537,6 +4537,12 @@ public final class Settings { 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 2fcec52d2e65..5fd597ba0ec4 100644 --- a/core/java/android/service/fingerprint/FingerprintManager.java +++ b/core/java/android/service/fingerprint/FingerprintManager.java @@ -22,12 +22,14 @@ import android.content.ContentResolver; 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 @@ import android.util.Log; 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 @@ public class FingerprintManager { 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 class FingerprintManager { } }; - 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 class FingerprintManager { */ 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 @@ public class FingerprintManager { * @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 @@ public class FingerprintManager { 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 @@ public class FingerprintManager { * 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 34f16558acca..e5193f572b80 100644 --- a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java +++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java @@ -30,18 +30,32 @@ public class FingerprintManagerReceiver { 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 c7fa7cdadd08..000000000000 --- 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 81a2aac5867b..f4b5526928c9 100644 --- a/core/java/android/service/fingerprint/FingerprintUtils.java +++ b/core/java/android/service/fingerprint/FingerprintUtils.java @@ -18,10 +18,12 @@ package android.service.fingerprint; 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 @@ class FingerprintUtils { 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 e92c20cd0339..43d5e9a48d13 100644 --- a/core/java/android/service/fingerprint/IFingerprintService.aidl +++ b/core/java/android/service/fingerprint/IFingerprintService.aidl @@ -22,17 +22,20 @@ import android.service.fingerprint.IFingerprintServiceReceiver; * 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 4826b59b650c..af4128f1122d 100644 --- a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl @@ -24,7 +24,8 @@ import android.os.UserHandle; */ 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/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index a6cddae80688..8fa45e699d03 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -80,6 +80,11 @@ public class TrustAgentService extends Service { private ITrustAgentServiceCallback mCallback; + private Runnable mPendingGrantTrustTask; + + // Lock used to access mPendingGrantTrustTask and mCallback. + private final Object mLock = new Object(); + private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { @@ -127,12 +132,24 @@ public class TrustAgentService extends Service { * @param initiatedByUser indicates that the user has explicitly initiated an action that proves * the user is about to use the device. */ - public final void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser) { - if (mCallback != null) { - try { - mCallback.grantTrust(message.toString(), durationMs, initiatedByUser); - } catch (RemoteException e) { - onError("calling enableTrust()"); + public final void grantTrust( + final CharSequence message, final long durationMs, final boolean initiatedByUser) { + synchronized (mLock) { + if (mCallback != null) { + try { + mCallback.grantTrust(message.toString(), durationMs, initiatedByUser); + } catch (RemoteException e) { + onError("calling enableTrust()"); + } + } else { + // Remember trust has been granted so we can effectively grant it once the service + // is bound. + mPendingGrantTrustTask = new Runnable() { + @Override + public void run() { + grantTrust(message, durationMs, initiatedByUser); + } + }; } } } @@ -141,11 +158,16 @@ public class TrustAgentService extends Service { * Call to revoke trust on the device. */ public final void revokeTrust() { - if (mCallback != null) { - try { - mCallback.revokeTrust(); - } catch (RemoteException e) { - onError("calling revokeTrust()"); + synchronized (mLock) { + if (mPendingGrantTrustTask != null) { + mPendingGrantTrustTask = null; + } + if (mCallback != null) { + try { + mCallback.revokeTrust(); + } catch (RemoteException e) { + onError("calling revokeTrust()"); + } } } } @@ -164,7 +186,13 @@ public class TrustAgentService extends Service { } public void setCallback(ITrustAgentServiceCallback callback) { - mCallback = callback; + synchronized (mLock) { + mCallback = callback; + if (mPendingGrantTrustTask != null) { + mPendingGrantTrustTask.run(); + mPendingGrantTrustTask = null; + } + } } } diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java index d426d1247c7b..8674c66c90d2 100644 --- a/core/java/android/text/GraphicsOperations.java +++ b/core/java/android/text/GraphicsOperations.java @@ -38,7 +38,7 @@ extends CharSequence * {@hide} */ void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd, - float x, float y, int flags, Paint p); + float x, float y, boolean isRtl, Paint p); /** * Just like {@link Paint#measureText}. @@ -55,12 +55,12 @@ extends CharSequence * @hide */ float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, - int flags, float[] advances, int advancesIndex, Paint paint); + boolean isRtl, float[] advances, int advancesIndex, Paint paint); /** * Just like {@link Paint#getTextRunCursor}. * @hide */ - int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset, + int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset, int cursorOpt, Paint p); } diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index f8e3c83b5879..2415b11d2480 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -159,18 +159,17 @@ class MeasuredText { mPos = p + len; if (mEasy) { - int flags = mDir == Layout.DIR_LEFT_TO_RIGHT - ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL; - return paint.getTextRunAdvances(mChars, p, len, p, len, flags, mWidths, p); + boolean isRtl = mDir != Layout.DIR_LEFT_TO_RIGHT; + return paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, mWidths, p); } float totalAdvance = 0; int level = mLevels[p]; for (int q = p, i = p + 1, e = p + len;; ++i) { if (i == e || mLevels[i] != level) { - int flags = (level & 0x1) == 0 ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL; + boolean isRtl = (level & 0x1) != 0; totalAdvance += - paint.getTextRunAdvances(mChars, q, i - q, q, i - q, flags, mWidths, q); + paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, mWidths, q); if (i == e) { break; } diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 1d9aa053ced8..6b984d60a0dc 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -503,7 +503,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable // Span watchers need to be called after text watchers, which may update the layout sendToSpanWatchers(start, end, newLen - origLen); - return this; + return this; } private static boolean hasNonExclusiveExclusiveSpanAt(CharSequence text, int offset) { @@ -745,7 +745,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } } - return 0; + return 0; } /** @@ -1117,20 +1117,20 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * {@hide} */ public void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd, - float x, float y, int flags, Paint p) { + float x, float y, boolean isRtl, Paint p) { checkRange("drawTextRun", start, end); int contextLen = contextEnd - contextStart; int len = end - start; if (contextEnd <= mGapStart) { - c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, flags, p); + c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, isRtl, p); } else if (contextStart >= mGapStart) { c.drawTextRun(mText, start + mGapLength, len, contextStart + mGapLength, - contextLen, x, y, flags, p); + contextLen, x, y, isRtl, p); } else { char[] buf = TextUtils.obtain(contextLen); getChars(contextStart, contextEnd, buf, 0); - c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, flags, p); + c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, isRtl, p); TextUtils.recycle(buf); } } @@ -1187,7 +1187,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * Don't call this yourself -- exists for Paint to use internally. * {@hide} */ - public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags, + public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, boolean isRtl, float[] advances, int advancesPos, Paint p) { float ret; @@ -1197,15 +1197,15 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (end <= mGapStart) { ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen, - flags, advances, advancesPos); + isRtl, advances, advancesPos); } else if (start >= mGapStart) { ret = p.getTextRunAdvances(mText, start + mGapLength, len, - contextStart + mGapLength, contextLen, flags, advances, advancesPos); + contextStart + mGapLength, contextLen, isRtl, advances, advancesPos); } else { char[] buf = TextUtils.obtain(contextLen); getChars(contextStart, contextEnd, buf, 0); ret = p.getTextRunAdvances(buf, start - contextStart, len, - 0, contextLen, flags, advances, advancesPos); + 0, contextLen, isRtl, advances, advancesPos); TextUtils.recycle(buf); } @@ -1228,7 +1228,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * * @param contextStart the start index of the context * @param contextEnd the (non-inclusive) end index of the context - * @param flags either DIRECTION_RTL or DIRECTION_LTR + * @param dir either DIRECTION_RTL or DIRECTION_LTR * @param offset the cursor position to move from * @param cursorOpt how to move the cursor, one of CURSOR_AFTER, * CURSOR_AT_OR_AFTER, CURSOR_BEFORE, @@ -1238,7 +1238,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * @deprecated This is an internal method, refrain from using it in your code */ @Deprecated - public int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset, + public int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset, int cursorOpt, Paint p) { int ret; @@ -1246,15 +1246,15 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable int contextLen = contextEnd - contextStart; if (contextEnd <= mGapStart) { ret = p.getTextRunCursor(mText, contextStart, contextLen, - flags, offset, cursorOpt); + dir, offset, cursorOpt); } else if (contextStart >= mGapStart) { ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen, - flags, offset + mGapLength, cursorOpt) - mGapLength; + dir, offset + mGapLength, cursorOpt) - mGapLength; } else { char[] buf = TextUtils.obtain(contextLen); getChars(contextStart, contextEnd, buf, 0); ret = p.getTextRunCursor(buf, 0, contextLen, - flags, offset - contextStart, cursorOpt) + contextStart; + dir, offset - contextStart, cursorOpt) + contextStart; TextUtils.recycle(buf); } diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index d892f19b4b35..c19cf32c45d4 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -664,14 +664,14 @@ class TextLine { } } - int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; + int dir = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE; if (mCharsValid) { return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart, - flags, offset, cursorOpt); + dir, offset, cursorOpt); } else { return wp.getTextRunCursor(mText, mStart + spanStart, - mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart; + mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart; } } @@ -738,15 +738,14 @@ class TextLine { int contextLen = contextEnd - contextStart; if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) { - int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; if (mCharsValid) { ret = wp.getTextRunAdvances(mChars, start, runLen, - contextStart, contextLen, flags, null, 0); + contextStart, contextLen, runIsRtl, null, 0); } else { int delta = mStart; ret = wp.getTextRunAdvances(mText, delta + start, delta + end, delta + contextStart, delta + contextEnd, - flags, null, 0); + runIsRtl, null, 0); } } @@ -977,16 +976,15 @@ class TextLine { private void drawTextRun(Canvas c, TextPaint wp, int start, int end, int contextStart, int contextEnd, boolean runIsRtl, float x, int y) { - int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR; if (mCharsValid) { int count = end - start; int contextCount = contextEnd - contextStart; c.drawTextRun(mChars, start, count, contextStart, contextCount, - x, y, flags, wp); + x, y, runIsRtl, wp); } else { int delta = mStart; c.drawTextRun(mText, delta + start, delta + end, - delta + contextStart, delta + contextEnd, x, y, flags, wp); + delta + contextStart, delta + contextEnd, x, y, runIsRtl, wp); } } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 0a4f641006f9..508769d0ff29 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -206,6 +206,10 @@ public abstract class Transition implements Cloneable { // 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 @@ -1347,6 +1351,21 @@ public abstract class Transition implements Cloneable { } 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 @@ public abstract class Transition implements Cloneable { 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(); } } @@ -1866,6 +1887,20 @@ public abstract class Transition implements Cloneable { 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(""); diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java index 698b5638795a..c04be4f57aac 100644 --- a/core/java/android/transition/TransitionSet.java +++ b/core/java/android/transition/TransitionSet.java @@ -168,30 +168,106 @@ public class TransitionSet extends Transition { @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/Visibility.java b/core/java/android/transition/Visibility.java index 0f7638b2e337..c6c833772784 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -18,6 +18,9 @@ package android.transition; 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 @@ public abstract class Visibility extends Transition { 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; + } } } } @@ -378,6 +389,26 @@ public abstract class Visibility extends Transition { 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; + } + /** * The default implementation of this method returns a null Animator. Subclasses should * override this method to make targets disappear with the desired transition. The diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index d7a913d6c380..181f77e2c886 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -599,6 +599,42 @@ public final class Display { } /** + * 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. + * @hide + */ + 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}). + * @hide + */ + 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 b0fe0faf72f6..98696c7e54ec 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -180,6 +180,20 @@ public final class DisplayInfo implements Parcelable { 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 @@ public final class DisplayInfo implements Parcelable { && 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 @@ public final class DisplayInfo implements Parcelable { 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 @@ public final class DisplayInfo implements Parcelable { 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 @@ public final class DisplayInfo implements Parcelable { 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 @@ public final class DisplayInfo implements Parcelable { 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 50560971828b..68e2146c821b 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -51,10 +51,10 @@ class GLES20Canvas extends HardwareCanvas { private int mWidth; private int mHeight; - + private float[] mPoint; private float[] mLine; - + private Rect mClipBounds; private RectF mPathBounds; @@ -167,7 +167,7 @@ class GLES20Canvas extends HardwareCanvas { nSetViewport(mRenderer, width, height); } - + private static native void nSetViewport(long renderer, int width, int height); @Override @@ -208,22 +208,22 @@ class GLES20Canvas extends HardwareCanvas { /** * Must match Caches::FlushMode values - * - * @see #flushCaches(int) + * + * @see #flushCaches(int) */ static final int FLUSH_CACHES_LAYERS = 0; - + /** * Must match Caches::FlushMode values - * - * @see #flushCaches(int) + * + * @see #flushCaches(int) */ static final int FLUSH_CACHES_MODERATE = 1; /** * Must match Caches::FlushMode values - * - * @see #flushCaches(int) + * + * @see #flushCaches(int) */ static final int FLUSH_CACHES_FULL = 2; @@ -245,7 +245,7 @@ class GLES20Canvas extends HardwareCanvas { /////////////////////////////////////////////////////////////////////////// // Hardware layer /////////////////////////////////////////////////////////////////////////// - + void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) { layer.setLayerPaint(paint); nDrawLayer(mRenderer, layer.getLayer(), x, y); @@ -298,7 +298,7 @@ class GLES20Canvas extends HardwareCanvas { public boolean clipRect(float left, float top, float right, float bottom) { return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } - + private static native boolean nClipRect(long renderer, float left, float top, float right, float bottom, int op); @@ -311,14 +311,14 @@ class GLES20Canvas extends HardwareCanvas { public boolean clipRect(int left, int top, int right, int bottom) { return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } - + private static native boolean nClipRect(long renderer, int left, int top, int right, int bottom, int op); @Override public boolean clipRect(Rect rect) { return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, - Region.Op.INTERSECT.nativeInt); + Region.Op.INTERSECT.nativeInt); } @Override @@ -360,7 +360,7 @@ class GLES20Canvas extends HardwareCanvas { public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) { return nQuickReject(mRenderer, left, top, right, bottom); } - + private static native boolean nQuickReject(long renderer, float left, float top, float right, float bottom); @@ -385,7 +385,7 @@ class GLES20Canvas extends HardwareCanvas { public void translate(float dx, float dy) { if (dx != 0.0f || dy != 0.0f) nTranslate(mRenderer, dx, dy); } - + private static native void nTranslate(long renderer, float dx, float dy); @Override @@ -399,21 +399,24 @@ class GLES20Canvas extends HardwareCanvas { public void rotate(float degrees) { nRotate(mRenderer, degrees); } - + private static native void nRotate(long renderer, float degrees); @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); } - + private static native void nScale(long renderer, float sx, float sy); @Override public void setMatrix(Matrix matrix) { nSetMatrix(mRenderer, matrix == null ? 0 : matrix.native_instance); } - + private static native void nSetMatrix(long renderer, long matrix); @SuppressWarnings("deprecation") @@ -421,16 +424,16 @@ class GLES20Canvas extends HardwareCanvas { public void getMatrix(Matrix matrix) { nGetMatrix(mRenderer, matrix.native_instance); } - + private static native void nGetMatrix(long renderer, long matrix); @Override public void concat(Matrix matrix) { if (matrix != null) nConcatMatrix(mRenderer, matrix.native_instance); } - + private static native void nConcatMatrix(long renderer, long matrix); - + /////////////////////////////////////////////////////////////////////////// // State management /////////////////////////////////////////////////////////////////////////// @@ -439,14 +442,14 @@ class GLES20Canvas extends HardwareCanvas { public int save() { return nSave(mRenderer, Canvas.CLIP_SAVE_FLAG | Canvas.MATRIX_SAVE_FLAG); } - + @Override public int save(int saveFlags) { return nSave(mRenderer, saveFlags); } private static native int nSave(long renderer, int flags); - + @Override public int saveLayer(RectF bounds, Paint paint, int saveFlags) { if (bounds != null) { @@ -494,12 +497,12 @@ class GLES20Canvas extends HardwareCanvas { private static native int nSaveLayerAlpha(long renderer, float left, float top, float right, float bottom, int alpha, int saveFlags); - + @Override public void restore() { nRestore(mRenderer); } - + private static native void nRestore(long renderer); @Override @@ -508,12 +511,12 @@ class GLES20Canvas extends HardwareCanvas { } private static native void nRestoreToCount(long renderer, int saveCount); - + @Override public int getSaveCount() { return nGetSaveCount(mRenderer); } - + private static native int nGetSaveCount(long renderer); /////////////////////////////////////////////////////////////////////////// @@ -544,9 +547,9 @@ class GLES20Canvas extends HardwareCanvas { /////////////////////////////////////////////////////////////////////////// @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); } @@ -739,7 +742,7 @@ class GLES20Canvas extends HardwareCanvas { public void drawColor(int color, PorterDuff.Mode mode) { nDrawColor(mRenderer, color, mode.nativeInt); } - + private static native void nDrawColor(long renderer, int color, int mode); @Override @@ -771,8 +774,8 @@ class GLES20Canvas extends HardwareCanvas { } @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 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawPicture(Picture picture) { - if (picture.createdFromStream) { - return; - } - picture.endRecording(); // TODO: Implement rendering } @Override public void drawPicture(Picture picture, Rect dst) { - if (picture.createdFromStream) { - return; - } - save(); translate(dst.left, dst.top); if (picture.getWidth() > 0 && picture.getHeight() > 0) { @@ -826,10 +821,6 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawPicture(Picture picture, RectF dst) { - if (picture.createdFromStream) { - return; - } - save(); translate(dst.left, dst.top); if (picture.getWidth() > 0 && picture.getHeight() > 0) { @@ -930,7 +921,7 @@ class GLES20Canvas extends HardwareCanvas { nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } - + private static native void nDrawText(long renderer, char[] text, int index, int count, float x, float y, int bidiFlags, long paint, long typeface); @@ -978,68 +969,66 @@ class GLES20Canvas extends HardwareCanvas { } nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count, - long path, float hOffset, float vOffset, int bidiFlags, long nativePaint); + long path, float hOffset, float vOffset, int bidiFlags, long nativePaint, + long typeface); @Override public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) { if (text.length() == 0) return; nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } private static native void nDrawTextOnPath(long renderer, String text, int start, int end, - long path, float hOffset, float vOffset, int bidiFlags, long nativePaint); + long path, float hOffset, float vOffset, int bidiFlags, long nativePaint, + long typeface); @Override public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, - float x, float y, int dir, Paint paint) { + float x, float y, boolean isRtl, Paint paint) { if ((index | count | text.length - index - count) < 0) { throw new IndexOutOfBoundsException(); } - if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) { - throw new IllegalArgumentException("Unknown direction: " + dir); - } - nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir, + nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface); } private static native void nDrawTextRun(long renderer, char[] text, int index, int count, - int contextIndex, int contextCount, float x, float y, int dir, long nativePaint, long nativeTypeface); + int contextIndex, int contextCount, float x, float y, boolean isRtl, long nativePaint, long nativeTypeface); @Override public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, - float x, float y, int dir, Paint paint) { + float x, float y, boolean isRtl, Paint paint) { if ((start | end | end - start | text.length() - end) < 0) { throw new IndexOutOfBoundsException(); } - int flags = dir == 0 ? 0 : 1; if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { nDrawTextRun(mRenderer, text.toString(), start, end, contextStart, - contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface); + contextEnd, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawTextRun(this, start, end, - contextStart, contextEnd, x, y, flags, paint); + contextStart, contextEnd, x, y, isRtl, paint); } else { int contextLen = contextEnd - contextStart; int len = end - start; char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, contextStart, contextEnd, buf, 0); nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen, - x, y, flags, paint.mNativePaint, paint.mNativeTypeface); + x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface); TemporaryBuffer.recycle(buf); } } private static native void nDrawTextRun(long renderer, String text, int start, int end, - int contextStart, int contextEnd, float x, float y, int flags, long nativePaint, long nativeTypeface); + int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint, long nativeTypeface); @Override public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset, diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 3de81443d818..c0e42a393380 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -16,6 +16,7 @@ package android.view; +import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.SurfaceTexture; @@ -452,10 +453,10 @@ public abstract class HardwareRenderer { * * @return A hardware renderer backed by OpenGL. */ - static HardwareRenderer create(boolean translucent) { + static HardwareRenderer create(Context context, boolean translucent) { HardwareRenderer renderer = null; if (GLES20Canvas.isAvailable()) { - renderer = new ThreadedRenderer(translucent); + renderer = new ThreadedRenderer(context, translucent); } return renderer; } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 79f19b583bc5..94d8f703c53b 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -458,6 +458,8 @@ public class SurfaceControl { public float xDpi; public float yDpi; public boolean secure; + public long appVsyncOffsetNanos; + public long presentationDeadlineNanos; public PhysicalDisplayInfo() { } @@ -479,7 +481,9 @@ public class SurfaceControl { && 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 @@ public class SurfaceControl { xDpi = other.xDpi; yDpi = other.yDpi; secure = other.secure; + appVsyncOffsetNanos = other.appVsyncOffsetNanos; + presentationDeadlineNanos = other.presentationDeadlineNanos; } // For debugging purposes @@ -502,7 +508,8 @@ public class SurfaceControl { 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/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 2bfbd650c050..166edc247176 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -16,21 +16,29 @@ package android.view; +import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.graphics.drawable.Drawable; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.Trace; import android.util.Log; +import android.util.LongSparseArray; import android.util.TimeUtils; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; /** * Hardware renderer that proxies the rendering to a render thread. Most calls @@ -71,8 +79,8 @@ public class ThreadedRenderer extends HardwareRenderer { private Choreographer mChoreographer; private boolean mProfilingEnabled; - ThreadedRenderer(boolean translucent) { - AtlasInitializer.sInstance.init(); + ThreadedRenderer(Context context, boolean translucent) { + AtlasInitializer.sInstance.init(context); long rootNodePtr = nCreateRootRenderNode(); mRootNode = RenderNode.adopt(rootNodePtr); @@ -334,7 +342,7 @@ public class ThreadedRenderer extends HardwareRenderer { private AtlasInitializer() {} - synchronized void init() { + synchronized void init(Context context) { if (mInitialized) return; IBinder binder = ServiceManager.getService("assetatlas"); if (binder == null) return; @@ -346,6 +354,8 @@ public class ThreadedRenderer extends HardwareRenderer { if (buffer != null) { long[] map = atlas.getMap(); if (map != null) { + // TODO Remove after fixing b/15425820 + validateMap(context, map); nSetAtlas(buffer, map); mInitialized = true; } @@ -361,6 +371,30 @@ public class ThreadedRenderer extends HardwareRenderer { Log.w(LOG_TAG, "Could not acquire atlas", e); } } + + private static void validateMap(Context context, long[] map) { + Log.d("Atlas", "Validating map..."); + HashSet<Long> preloadedPointers = new HashSet<Long>(); + + // We only care about drawables that hold bitmaps + final Resources resources = context.getResources(); + final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables(); + + final int count = drawables.size(); + for (int i = 0; i < count; i++) { + final Bitmap bitmap = drawables.valueAt(i).getBitmap(); + if (bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888) { + preloadedPointers.add(bitmap.mNativeBitmap); + } + } + + for (int i = 0; i < map.length; i += 4) { + if (!preloadedPointers.contains(map[i])) { + Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i])); + map[i] = 0; + } + } + } } static native void setupShadersDiskCache(String cacheFile); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 41037a52b85b..9156216dd619 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -25,6 +25,7 @@ import android.annotation.NonNull; 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; @@ -44,6 +45,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.Shader; +import android.graphics.PorterDuff.Mode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManagerGlobal; @@ -3187,6 +3189,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @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 +4027,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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; } } @@ -6116,6 +6131,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ protected boolean fitSystemWindows(Rect insets) { if ((mPrivateFlags3 & PFLAG3_APPLYING_INSETS) == 0) { + if (insets == null) { + // Null insets by definition have already been consumed. + // This call cannot apply insets since there are none to apply, + // so return false. + return false; + } // If we're not in the process of dispatching the newer apply insets call, // that means we're not in the compatibility path. Dispatch into the newer // apply insets path and take things from there. @@ -13426,8 +13447,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Destroy any previous software drawing cache if needed if (mLayerType == LAYER_TYPE_SOFTWARE) { destroyDrawingCache(); - invalidateParentCaches(); - invalidate(true); } mLayerType = layerType; @@ -13435,13 +13454,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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); } /** @@ -15697,7 +15713,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } - Drawable d= null; + Drawable d = null; if (resid != 0) { d = mContext.getDrawable(resid); } @@ -15773,8 +15789,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // 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; } @@ -15785,6 +15802,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, background.setVisible(getVisibility() == VISIBLE, false); mBackground = background; + applyBackgroundTint(); + if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) { mPrivateFlags &= ~PFLAG_SKIP_DRAW; mPrivateFlags |= PFLAG_ONLY_DRAWS_BACKGROUND; @@ -15840,6 +15859,88 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * 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/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java index 3854f34faeb2..29e865f3b36b 100644 --- a/core/java/android/view/ViewAnimationUtils.java +++ b/core/java/android/view/ViewAnimationUtils.java @@ -23,7 +23,7 @@ import android.animation.ValueAnimator; * Defines common utilities for working with View's animations. * */ -public class ViewAnimationUtils { +public final class ViewAnimationUtils { private ViewAnimationUtils() {} /** * Returns a ValueAnimator which can animate a clipping circle. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4cd1b25c56bd..82e5ddd0c22f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -656,7 +656,7 @@ public final class ViewRootImpl implements ViewParent, } 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); @@ -700,7 +700,7 @@ public final class ViewRootImpl implements ViewParent, } final boolean translucent = attrs.format != PixelFormat.OPAQUE; - mAttachInfo.mHardwareRenderer = HardwareRenderer.create(translucent); + mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent); if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString()); mAttachInfo.mHardwareAccelerated = diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index abed08292041..321d9d3d9d24 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 @@ public class CookieManager { * 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 @@ public class CookieManager { } /** + * 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 @@ public class CookieManager { /** * 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 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 @@ public class CookieManager { /** * 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/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index ec396aa1f375..470d4134c0b7 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -408,7 +408,6 @@ public class WebChromeClient { * @return true if filePathCallback will be invoked, false to use default handling. * * @see FileChooserParams - * @hide For API approval */ public boolean showFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { @@ -416,11 +415,8 @@ public class WebChromeClient { } /** - * Parameters used in the {@link #showFileChooser(WebView,ValueCallback<String[]>,FileChooserParams)} - * method. - * + * Parameters used in the {@link #showFileChooser} method. * This is intended to be used as a read-only data struct by the application. - * @hide For API approval */ public static class FileChooserParams { // Flags for mode diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 08931fe49196..eb3f88234157 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -16,11 +16,15 @@ 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.PorterDuff.Mode; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -31,10 +35,16 @@ import android.view.ViewConfiguration; 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 +93,15 @@ public abstract class AbsSeekBar extends ProgressBar { 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 +127,7 @@ public abstract class AbsSeekBar extends ProgressBar { * @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 +137,7 @@ public abstract class AbsSeekBar extends ProgressBar { } else { needUpdate = false; } + if (thumb != null) { thumb.setCallback(this); if (canResolveLayoutDirection()) { @@ -136,8 +156,12 @@ public abstract class AbsSeekBar extends ProgressBar { requestLayout(); } } + mThumb = thumb; + + applyThumbTint(); invalidate(); + if (needUpdate) { updateThumbAndTrackPos(getWidth(), getHeight()); if (thumb != null && thumb.isStateful()) { @@ -160,6 +184,88 @@ public abstract class AbsSeekBar extends ProgressBar { } /** + * 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() { diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index c934ad731161..747d2b17bff3 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -16,11 +16,15 @@ 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.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; @@ -48,7 +52,12 @@ public abstract class CompoundButton extends Button implements Checkable { 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 +83,22 @@ public abstract class CompoundButton extends Button implements Checkable { 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 +191,11 @@ public abstract class CompoundButton extends Button implements Checkable { } /** - * 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 +212,114 @@ public abstract class CompoundButton extends Button implements Checkable { } /** - * 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()); + + if (d != null) { + d.setCallback(this); + d.setLayoutDirection(getLayoutDirection()); + if (d.isStateful()) { + d.setState(getDrawableState()); + } + d.setVisible(getVisibility() == VISIBLE, false); + setMinHeight(d.getIntrinsicHeight()); + applyButtonTint(); + } } + } - refreshDrawableState(); + /** + * 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 diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 01a6b8aff4b8..5a1492979e08 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -18,11 +18,15 @@ package android.widget; 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.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; @@ -33,6 +37,8 @@ import android.view.accessibility.AccessibilityEvent; 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 +68,9 @@ public class FrameLayout extends ViewGroup { @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 +128,15 @@ public class FrameLayout extends ViewGroup { 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); @@ -231,32 +249,34 @@ public class FrameLayout extends ViewGroup { * 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 +301,89 @@ public class FrameLayout extends ViewGroup { 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/ImageView.java b/core/java/android/widget/ImageView.java index a40b85eccef0..399e08719c12 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; @@ -30,6 +32,7 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Xfermode; +import android.graphics.PorterDuff.Mode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -44,6 +47,8 @@ import android.view.accessibility.AccessibilityEvent; 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 +80,18 @@ public class ImageView extends View { 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 +164,21 @@ public class ImageView extends View { 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 +449,88 @@ public class ImageView extends View { } /** + * 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 +805,20 @@ public class ImageView extends View { 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 { @@ -1177,6 +1276,7 @@ public class ImageView extends View { public void setColorFilter(ColorFilter cf) { if (mColorFilter != cf) { mColorFilter = cf; + mHasColorFilter = true; mColorMod = true; applyColorMod(); invalidate(); @@ -1231,7 +1331,9 @@ public class ImageView extends View { // 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/ProgressBar.java b/core/java/android/widget/ProgressBar.java index af32f1ca2013..62a8becf6372 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 @@ public class ProgressBar extends View { 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 @@ public class ProgressBar extends View { 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 @@ public class ProgressBar extends View { 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 @@ public class ProgressBar extends View { 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 @@ public class ProgressBar extends View { * @see #setIndeterminate(boolean) */ public void setIndeterminateDrawable(Drawable d) { - if (d != null) { - d.setCallback(this); - } - mIndeterminateDrawable = d; - if (mIndeterminateDrawable != null && canResolveLayoutDirection()) { - mIndeterminateDrawable.setLayoutDirection(getLayoutDirection()); + 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(); + } } - 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 @@ public class ProgressBar extends View { * @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 @@ public class ProgressBar extends View { } } + 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; diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index 9601d4a703df..9914800ec6ca 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -25,6 +25,7 @@ import android.content.res.TypedArray; 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 @@ public class Spinner extends AbsSpinner implements OnClickListener { * {@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 @@ public class Spinner extends AbsSpinner implements OnClickListener { 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/TextView.java b/core/java/android/widget/TextView.java index 22f5e23e5168..0f51e8bb52ed 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2727,7 +2727,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Sets whether the soft input method will be made visible when this * TextView gets focused. The default is true. - * @hide */ @android.view.RemotableViewMethod public final void setShowSoftInputOnFocus(boolean show) { @@ -2738,7 +2737,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns whether the soft input method will be made visible when this * TextView gets focused. The default is true. - * @hide */ public final boolean getShowSoftInputOnFocus() { // When there is no Editor, return default true value @@ -9062,11 +9060,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void drawTextRun(Canvas c, int start, int end, - int contextStart, int contextEnd, float x, float y, int flags, Paint p) { + int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint p) { int count = end - start; int contextCount = contextEnd - contextStart; c.drawTextRun(mChars, start + mStart, count, contextStart + mStart, - contextCount, x, y, flags, p); + contextCount, x, y, isRtl, p); } public float measureText(int start, int end, Paint p) { @@ -9078,20 +9076,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public float getTextRunAdvances(int start, int end, int contextStart, - int contextEnd, int flags, float[] advances, int advancesIndex, + int contextEnd, boolean isRtl, float[] advances, int advancesIndex, Paint p) { int count = end - start; int contextCount = contextEnd - contextStart; return p.getTextRunAdvances(mChars, start + mStart, count, - contextStart + mStart, contextCount, flags, advances, + contextStart + mStart, contextCount, isRtl, advances, advancesIndex); } - public int getTextRunCursor(int contextStart, int contextEnd, int flags, + public int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset, int cursorOpt, Paint p) { int contextCount = contextEnd - contextStart; return p.getTextRunCursor(mChars, contextStart + mStart, - contextCount, flags, offset + mStart, cursorOpt); + contextCount, dir, offset + mStart, cursorOpt); } } diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 2b625522e886..8ee0a1b51e84 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.content.res.Resources; import android.graphics.Canvas; import android.media.AudioManager; +import android.media.ClosedCaptionRenderer; import android.media.MediaFormat; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; @@ -316,6 +317,7 @@ public class VideoView extends SurfaceView context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer); controller.registerRenderer(new WebVttRenderer(context)); controller.registerRenderer(new TtmlRenderer(context)); + controller.registerRenderer(new ClosedCaptionRenderer(context)); mMediaPlayer.setSubtitleAnchor(controller, this); if (mAudioSession != 0) { diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 960fa49e2154..643225d7f031 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 @@ interface IBackupTransport { 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 @@ interface IBackupTransport { */ 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 c9d621dcd2d8..ff0ee65c911d 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -19,6 +19,7 @@ package com.android.internal.backup; 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 @@ public class LocalTransport extends BackupTransport { 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 @@ -361,30 +368,55 @@ public class LocalTransport extends BackupTransport { mRestorePackages = packages; mRestorePackage = -1; mRestoreToken = token; - mRestoreDataDir = new File(mDataDir, Long.toString(token)); + mRestoreSetDir = new File(mDataDir, Long.toString(token)); + mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR); + mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR); return BackupTransport.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; + 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; } 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 @@ -463,8 +495,8 @@ public class LocalTransport extends BackupTransport { // Full restore handling public int prepareFullRestore(long token, String[] targetPackages) { - mRestoreDataDir = new File(mDataDir, Long.toString(token)); - mFullRestoreSetDir = new File(mRestoreDataDir, "_full"); + mRestoreSetDir = new File(mDataDir, Long.toString(token)); + mFullRestoreSetDir = new File(mRestoreSetDir, FULL_DATA_DIR); mFullRestorePackages = new HashSet<String>(); if (mFullRestoreSetDir.exists()) { List<String> pkgs = Arrays.asList(mFullRestoreSetDir.list()); diff --git a/core/java/com/android/internal/policy/IFaceLockInterface.aidl b/core/java/com/android/internal/policy/IFaceLockInterface.aidl index 017801bbf886..bc1f0020f495 100644 --- a/core/java/com/android/internal/policy/IFaceLockInterface.aidl +++ b/core/java/com/android/internal/policy/IFaceLockInterface.aidl @@ -23,6 +23,7 @@ interface IFaceLockInterface { 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/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java index 87a50a9940af..fda6479e2d56 100644 --- a/core/java/com/android/server/SystemServiceManager.java +++ b/core/java/com/android/server/SystemServiceManager.java @@ -50,8 +50,19 @@ public class SystemServiceManager { * @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 430dd631dbc6..3d9fb5c872f7 100644 --- a/core/java/com/android/server/net/BaseNetworkObserver.java +++ b/core/java/com/android/server/net/BaseNetworkObserver.java @@ -18,6 +18,7 @@ package com.android.server.net; import android.net.INetworkManagementEventObserver; import android.net.LinkAddress; +import android.net.RouteInfo; /** * Base {@link INetworkManagementEventObserver} that provides no-op @@ -70,4 +71,14 @@ public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub { 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 000000000000..7dd8dd860219 --- /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 15dfed1bc76b..cb00062b3923 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -89,7 +89,7 @@ LOCAL_SRC_FILES:= \ android_util_Process.cpp \ android_util_StringBlock.cpp \ android_util_XmlBlock.cpp \ - android/graphics/AndroidPicture.cpp \ + android_graphics_Picture.cpp \ android/graphics/AutoDecodeCancel.cpp \ android/graphics/Bitmap.cpp \ android/graphics/BitmapFactory.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f2b9bac92a9f..a7a1faad41cb 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -159,6 +159,7 @@ extern int register_android_net_TrafficStats(JNIEnv* env); 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 @@ static const RegJNIRec gRegJNI[] = { 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 5977ab263a99..000000000000 --- a/core/jni/android/graphics/AndroidPicture.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "AndroidPicture.h" -#include "SkCanvas.h" -#include "SkStream.h" - -AndroidPicture::AndroidPicture(const AndroidPicture* src) { - if (NULL != src) { - mWidth = src->width(); - mHeight = src->height(); - if (NULL != src->mPicture.get()) { - mPicture.reset(SkRef(src->mPicture.get())); - } if (NULL != src->mRecorder.get()) { - mPicture.reset(src->makePartialCopy()); - } - } else { - mWidth = 0; - mHeight = 0; - } -} - -SkCanvas* AndroidPicture::beginRecording(int width, int height) { - mPicture.reset(NULL); - mRecorder.reset(new SkPictureRecorder); - mWidth = width; - mHeight = height; - return mRecorder->beginRecording(width, height, NULL, 0); -} - -void AndroidPicture::endRecording() { - if (NULL != mRecorder.get()) { - mPicture.reset(mRecorder->endRecording()); - mRecorder.reset(NULL); - } -} - -int AndroidPicture::width() const { - if (NULL != mPicture.get()) { - SkASSERT(mPicture->width() == mWidth); - SkASSERT(mPicture->height() == mHeight); - } - - return mWidth; -} - -int AndroidPicture::height() const { - if (NULL != mPicture.get()) { - SkASSERT(mPicture->width() == mWidth); - SkASSERT(mPicture->height() == mHeight); - } - - return mHeight; -} - -AndroidPicture* AndroidPicture::CreateFromStream(SkStream* stream) { - AndroidPicture* newPict = new AndroidPicture; - - newPict->mPicture.reset(SkPicture::CreateFromStream(stream)); - if (NULL != newPict->mPicture.get()) { - newPict->mWidth = newPict->mPicture->width(); - newPict->mHeight = newPict->mPicture->height(); - } - - return newPict; -} - -void AndroidPicture::serialize(SkWStream* stream) const { - if (NULL != mRecorder.get()) { - SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy()); - tempPict->serialize(stream); - } else if (NULL != mPicture.get()) { - mPicture->serialize(stream); - } else { - SkPicture empty; - empty.serialize(stream); - } -} - -void AndroidPicture::draw(SkCanvas* canvas) { - if (NULL != mRecorder.get()) { - this->endRecording(); - SkASSERT(NULL != mPicture.get()); - } - if (NULL != mPicture.get()) { - // TODO: remove this const_cast once pictures are immutable - const_cast<SkPicture*>(mPicture.get())->draw(canvas); - } -} - -SkPicture* AndroidPicture::makePartialCopy() const { - SkASSERT(NULL != mRecorder.get()); - - SkPictureRecorder reRecorder; - - SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0); - mRecorder->partialReplay(canvas); - return reRecorder.endRecording(); -} diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 6de3b9ec2f2b..69e149e94974 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -569,12 +569,11 @@ public: 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 @@ public: 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); } @@ -882,26 +880,26 @@ public: static void drawText___CIIFFIPaintTypeface(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index, jint count, - jfloat x, jfloat y, jint flags, + jfloat x, jfloat y, jint bidiFlags, jlong paintHandle, jlong typefaceHandle) { SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); jchar* textArray = env->GetCharArrayElements(text, NULL); - drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint, typeface); + drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, bidiFlags, paint, typeface); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } static void drawText__StringIIFFIPaintTypeface(JNIEnv* env, jobject, jlong canvasHandle, jstring text, jint start, jint end, - jfloat x, jfloat y, jint flags, + jfloat x, jfloat y, jint bidiFlags, jlong paintHandle, jlong typefaceHandle) { SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); const jchar* textArray = env->GetStringChars(text, NULL); - drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint, typeface); + drawTextWithGlyphs(canvas, textArray, start, end, x, y, bidiFlags, paint, typeface); env->ReleaseStringChars(text, textArray); } @@ -951,24 +949,25 @@ public: static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray, int start, int end, - jfloat x, jfloat y, int flags, SkPaint* paint, TypefaceImpl* typeface) { + jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) { jint count = end - start; - drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, flags, paint, typeface); + drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, bidiFlags, paint, + typeface); } static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray, int start, int count, int contextCount, - jfloat x, jfloat y, int flags, SkPaint* paint, TypefaceImpl* typeface) { + jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) { #ifdef USE_MINIKIN Layout layout; - std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface); + std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface); layout.doLayout(textArray, start, count, contextCount, css); drawGlyphsToSkia(canvas, paint, layout, x, y); #else sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - textArray, start, count, contextCount, flags); + textArray, start, count, contextCount, bidiFlags); if (value == NULL) { return; } @@ -979,7 +978,8 @@ public: x -= value->getTotalAdvance(); } paint->setTextAlign(SkPaint::kLeft_Align); - doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), x, y, flags, paint); + doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), + x, y, paint); doDrawTextDecorations(canvas, x, y, value->getTotalAdvance(), paint); paint->setTextAlign(align); #endif @@ -990,42 +990,37 @@ public: #define kStdUnderline_Offset (1.0f / 9.0f) #define kStdUnderline_Thickness (1.0f / 18.0f) -static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length, SkPaint* paint) { - uint32_t flags; - SkDrawFilter* drawFilter = canvas->getDrawFilter(); - if (drawFilter) { - SkPaint paintCopy(*paint); - drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); - flags = paintCopy.getFlags(); - } else { - flags = paint->getFlags(); - } - if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { - SkScalar left = x; - SkScalar right = x + length; - float textSize = paint->getTextSize(); - float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); - if (flags & SkPaint::kUnderlineText_Flag) { - SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; - SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; - canvas->drawRectCoords(left, top, right, bottom, *paint); + static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length, + SkPaint* paint) { + uint32_t flags; + SkDrawFilter* drawFilter = canvas->getDrawFilter(); + if (drawFilter) { + SkPaint paintCopy(*paint); + drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); + flags = paintCopy.getFlags(); + } else { + flags = paint->getFlags(); } - if (flags & SkPaint::kStrikeThruText_Flag) { - SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; - SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; - canvas->drawRectCoords(left, top, right, bottom, *paint); + if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { + SkScalar left = x; + SkScalar right = x + length; + float textSize = paint->getTextSize(); + float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); + if (flags & SkPaint::kUnderlineText_Flag) { + SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; + SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; + canvas->drawRectCoords(left, top, right, bottom, *paint); + } + if (flags & SkPaint::kStrikeThruText_Flag) { + SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; + SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; + canvas->drawRectCoords(left, top, right, bottom, *paint); + } } } -} - - static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count, - jfloat x, jfloat y, int flags, SkPaint* paint) { - // Beware: this needs Glyph encoding (already done on the Paint constructor) - canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint); - } static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray, - int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { + int index, int count, jfloat x, jfloat y, SkPaint* paint) { SkPoint* posPtr = new SkPoint[count]; for (int indx = 0; indx < count; indx++) { posPtr[indx].fX = x + posArray[indx * 2]; @@ -1035,33 +1030,35 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l delete[] posPtr; } - static void drawTextRun___CIIIIFFIPaintTypeface( + static void drawTextRun___CIIIIFFZPaintTypeface( JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, - jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) { + jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) { SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); + int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR; jchar* chars = env->GetCharArrayElements(text, NULL); drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex, - count, contextCount, x, y, dirFlags, paint, typeface); + count, contextCount, x, y, bidiFlags, paint, typeface); env->ReleaseCharArrayElements(text, chars, JNI_ABORT); } - static void drawTextRun__StringIIIIFFIPaintTypeface( + static void drawTextRun__StringIIIIFFZPaintTypeface( JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start, jint end, jint contextStart, jint contextEnd, - jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) { + jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) { SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); + int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR; jint count = end - start; jint contextCount = contextEnd - contextStart; const jchar* chars = env->GetStringChars(text, NULL); drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart, - count, contextCount, x, y, dirFlags, paint, typeface); + count, contextCount, x, y, bidiFlags, paint, typeface); env->ReleaseStringChars(text, chars); } @@ -1126,29 +1123,82 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l 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_); } @@ -1242,11 +1292,9 @@ static JNINativeMethod gCanvasMethods[] = { {"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}, @@ -1268,17 +1316,17 @@ static JNINativeMethod gCanvasMethods[] = { (void*) SkCanvasGlue::drawText___CIIFFIPaintTypeface}, {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) SkCanvasGlue::drawText__StringIIFFIPaintTypeface}, - {"native_drawTextRun","(J[CIIIIFFIJJ)V", - (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaintTypeface}, - {"native_drawTextRun","(JLjava/lang/String;IIIIFFIJJ)V", - (void*) SkCanvasGlue::drawTextRun__StringIIIIFFIPaintTypeface}, + {"native_drawTextRun","(J[CIIIIFFZJJ)V", + (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 a4337cccbcb5..2bd7a288836a 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -3,7 +3,6 @@ #include "jni.h" #include "JNIHelp.h" #include "GraphicsJNI.h" -#include "AndroidPicture.h" #include "SkCanvas.h" #include "SkDevice.h" @@ -346,17 +345,6 @@ android::TypefaceImpl* GraphicsJNI::getNativeTypeface(JNIEnv* env, jobject paint return p; } -AndroidPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) -{ - SkASSERT(env); - SkASSERT(picture); - SkASSERT(env->IsInstanceOf(picture, gPicture_class)); - jlong pictureHandle = env->GetLongField(picture, gPicture_nativeInstanceID); - AndroidPicture* p = reinterpret_cast<AndroidPicture*>(pictureHandle); - SkASSERT(p); - return p; -} - SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) { SkASSERT(env); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 2e2f920eb338..ad174f7e7a78 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -14,7 +14,6 @@ class SkBitmapRegionDecoder; class SkCanvas; class SkPaint; -class AndroidPicture; class GraphicsJNI { public: @@ -50,7 +49,6 @@ public: static SkPaint* getNativePaint(JNIEnv*, jobject paint); static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint); static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap); - static AndroidPicture* getNativePicture(JNIEnv*, jobject picture); static SkRegion* getNativeRegion(JNIEnv*, jobject region); // Given the 'native' long held by the Rasterizer.java object, return a diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp index a9360ea1ecbb..fc92d53193c0 100644 --- a/core/jni/android/graphics/MinikinUtils.cpp +++ b/core/jni/android/graphics/MinikinUtils.cpp @@ -19,6 +19,7 @@ #include <string> #include "SkPaint.h" +#include "SkPathMeasure.h" #include "minikin/Layout.h" #include "TypefaceImpl.h" #include "MinikinSkia.h" @@ -76,4 +77,20 @@ float MinikinUtils::xOffsetForTextAlign(SkPaint* paint, const Layout& layout) { return 0; } +float MinikinUtils::hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path) { + float align = 0; + switch (paint->getTextAlign()) { + case SkPaint::kCenter_Align: + align = -0.5f; + break; + case SkPaint::kRight_Align: + align = -1; + break; + default: + return 0; + } + SkPathMeasure measure(path, false); + return align * (layout.getAdvance() - measure.getLength()); +} + } diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h index a96c6b19eca9..b2662a15cebf 100644 --- a/core/jni/android/graphics/MinikinUtils.h +++ b/core/jni/android/graphics/MinikinUtils.h @@ -36,6 +36,7 @@ public: static float xOffsetForTextAlign(SkPaint* paint, const Layout& layout); + static float hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path); // f is a functor of type void f(size_t start, size_t end); template <typename F> static void forFontRun(const Layout& layout, SkPaint* paint, F& f) { diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 8b11d311894d..d07b154291fb 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -707,7 +707,7 @@ public: } static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, TypefaceImpl* typeface, const jchar *text, - jint start, jint count, jint contextCount, jint flags, + jint start, jint count, jint contextCount, jboolean isRtl, jfloatArray advances, jint advancesIndex) { NPE_CHECK_RETURN_ZERO(env, paint); NPE_CHECK_RETURN_ZERO(env, text); @@ -729,14 +729,16 @@ public: jfloat* advancesArray = new jfloat[count]; jfloat totalAdvance = 0; + int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR; + #ifdef USE_MINIKIN Layout layout; - std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface); + std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface); layout.doLayout(text, start, count, contextCount, css); layout.getAdvances(advancesArray); totalAdvance = layout.getAdvance(); #else - TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags, + TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, bidiFlags, advancesArray, &totalAdvance); #endif @@ -747,28 +749,28 @@ public: return totalAdvance; } - static jfloat getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle, + static jfloat getTextRunAdvances___CIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, - jint flags, jfloatArray advances, jint advancesIndex) { + jboolean isRtl, jfloatArray advances, jint advancesIndex) { SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); jchar* textArray = env->GetCharArrayElements(text, NULL); jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextIndex, - index - contextIndex, count, contextCount, flags, advances, advancesIndex); + index - contextIndex, count, contextCount, isRtl, advances, advancesIndex); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); return result; } - static jfloat getTextRunAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle, + static jfloat getTextRunAdvances__StringIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, - jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags, + jstring text, jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl, jfloatArray advances, jint advancesIndex) { SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); const jchar* textArray = env->GetStringChars(text, NULL); jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextStart, - start - contextStart, end - start, contextEnd - contextStart, flags, + start - contextStart, end - start, contextEnd - contextStart, isRtl, advances, advancesIndex); env->ReleaseStringChars(text, textArray); return result; @@ -819,21 +821,21 @@ public: } static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text, - jint contextStart, jint contextCount, jint flags, jint offset, jint cursorOpt) { + jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) { SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); jchar* textArray = env->GetCharArrayElements(text, NULL); - jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, flags, + jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, dir, offset, cursorOpt); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); return result; } static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text, - jint contextStart, jint contextEnd, jint flags, jint offset, jint cursorOpt) { + jint contextStart, jint contextEnd, jint dir, jint offset, jint cursorOpt) { SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); const jchar* textArray = env->GetStringChars(text, NULL); jint result = doTextRunCursor(env, paint, textArray, contextStart, - contextEnd - contextStart, flags, offset, cursorOpt); + contextEnd - contextStart, dir, offset, cursorOpt); env->ReleaseStringChars(text, textArray); return result; } @@ -934,32 +936,62 @@ public: 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; @@ -974,28 +1006,28 @@ public: 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; } @@ -1106,14 +1138,14 @@ static JNINativeMethod methods[] = { {"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[CIIIII[FI)F", - (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI}, - {"native_getTextRunAdvances","(JJLjava/lang/String;IIIII[FI)F", - (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI}, + {"native_getTextRunAdvances","(JJ[CIIIIZ[FI)F", + (void*) SkPaintGlue::getTextRunAdvances___CIIIIZ_FI}, + {"native_getTextRunAdvances","(JJLjava/lang/String;IIIIZ[FI)F", + (void*) SkPaintGlue::getTextRunAdvances__StringIIIIZ_FI}, {"native_getTextGlyphs","(JLjava/lang/String;IIIII[C)I", diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp index 420a17fccf62..6ef1d2c10597 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: #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 @@ public: 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 @@ static JNINativeMethod methods[] = { {"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 0683f7386de8..bc0c25f3af1a 100644 --- a/core/jni/android/graphics/Picture.cpp +++ b/core/jni/android/graphics/Picture.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,123 +14,104 @@ * limitations under the License. */ -#include "jni.h" -#include "GraphicsJNI.h" -#include <android_runtime/AndroidRuntime.h> -#include "AndroidPicture.h" +#include "Picture.h" #include "SkCanvas.h" #include "SkStream.h" -#include "SkTemplates.h" -#include "CreateJavaOutputStreamAdaptor.h" namespace android { -class SkPictureGlue { -public: - static jlong newPicture(JNIEnv* env, jobject, jlong srcHandle) { - const AndroidPicture* src = reinterpret_cast<AndroidPicture*>(srcHandle); - return reinterpret_cast<jlong>(new AndroidPicture(src)); - } - - static jlong deserialize(JNIEnv* env, jobject, jobject jstream, - jbyteArray jstorage) { - AndroidPicture* picture = NULL; - SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage); - if (strm) { - picture = AndroidPicture::CreateFromStream(strm); - delete strm; +Picture::Picture(const Picture* src) { + if (NULL != src) { + mWidth = src->width(); + mHeight = src->height(); + if (NULL != src->mPicture.get()) { + mPicture.reset(SkRef(src->mPicture.get())); + } if (NULL != src->mRecorder.get()) { + mPicture.reset(src->makePartialCopy()); } - return reinterpret_cast<jlong>(picture); + } else { + mWidth = 0; + mHeight = 0; } +} - static void killPicture(JNIEnv* env, jobject, jlong pictureHandle) { - AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); - SkASSERT(picture); - delete picture; - } +SkCanvas* Picture::beginRecording(int width, int height) { + mPicture.reset(NULL); + mRecorder.reset(new SkPictureRecorder); + mWidth = width; + mHeight = height; + return mRecorder->beginRecording(width, height, NULL, 0); +} - static void draw(JNIEnv* env, jobject, jlong canvasHandle, - jlong pictureHandle) { - SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); - AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); - SkASSERT(canvas); - SkASSERT(picture); - picture->draw(canvas); +void Picture::endRecording() { + if (NULL != mRecorder.get()) { + mPicture.reset(mRecorder->endRecording()); + mRecorder.reset(NULL); } +} - static jboolean serialize(JNIEnv* env, jobject, jlong pictureHandle, - jobject jstream, jbyteArray jstorage) { - AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); - SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); - - if (NULL != strm) { - picture->serialize(strm); - delete strm; - return JNI_TRUE; - } - return JNI_FALSE; +int Picture::width() const { + if (NULL != mPicture.get()) { + SkASSERT(mPicture->width() == mWidth); + SkASSERT(mPicture->height() == mHeight); } - static jint getWidth(JNIEnv* env, jobject jpic) { - NPE_CHECK_RETURN_ZERO(env, jpic); - AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic); - int width = pict->width(); - return static_cast<jint>(width); - } + return mWidth; +} - static jint getHeight(JNIEnv* env, jobject jpic) { - NPE_CHECK_RETURN_ZERO(env, jpic); - AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic); - int height = pict->height(); - return static_cast<jint>(height); +int Picture::height() const { + if (NULL != mPicture.get()) { + SkASSERT(mPicture->width() == mWidth); + SkASSERT(mPicture->height() == mHeight); } - static jlong beginRecording(JNIEnv* env, jobject, jlong pictHandle, - jint w, jint h) { - AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle); - // beginRecording does not ref its return value, it just returns it. - SkCanvas* canvas = pict->beginRecording(w, h); - // the java side will wrap this guy in a Canvas.java, which will call - // unref in its finalizer, so we have to ref it here, so that both that - // Canvas.java and our picture can both be owners - canvas->ref(); - return reinterpret_cast<jlong>(canvas); - } + return mHeight; +} - static void endRecording(JNIEnv* env, jobject, jlong pictHandle) { - AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle); - pict->endRecording(); - } -}; +Picture* Picture::CreateFromStream(SkStream* stream) { + Picture* newPict = new Picture; -static JNINativeMethod gPictureMethods[] = { - {"getWidth", "()I", (void*) SkPictureGlue::getWidth}, - {"getHeight", "()I", (void*) SkPictureGlue::getHeight}, - {"nativeConstructor", "(J)J", (void*) SkPictureGlue::newPicture}, - {"nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", (void*)SkPictureGlue::deserialize}, - {"nativeBeginRecording", "(JII)J", (void*) SkPictureGlue::beginRecording}, - {"nativeEndRecording", "(J)V", (void*) SkPictureGlue::endRecording}, - {"nativeDraw", "(JJ)V", (void*) SkPictureGlue::draw}, - {"nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", (void*)SkPictureGlue::serialize}, - {"nativeDestructor","(J)V", (void*) SkPictureGlue::killPicture} -}; + newPict->mPicture.reset(SkPicture::CreateFromStream(stream)); + if (NULL != newPict->mPicture.get()) { + newPict->mWidth = newPict->mPicture->width(); + newPict->mHeight = newPict->mPicture->height(); + } -#include <android_runtime/AndroidRuntime.h> + return newPict; +} -#define REG(env, name, array) \ - result = android::AndroidRuntime::registerNativeMethods(env, name, array, \ - SK_ARRAY_COUNT(array)); \ - if (result < 0) return result +void Picture::serialize(SkWStream* stream) const { + if (NULL != mRecorder.get()) { + SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy()); + tempPict->serialize(stream); + } else if (NULL != mPicture.get()) { + mPicture->serialize(stream); + } else { + SkPicture empty; + empty.serialize(stream); + } +} -int register_android_graphics_Picture(JNIEnv* env) { - int result; +void Picture::draw(SkCanvas* canvas) { + if (NULL != mRecorder.get()) { + this->endRecording(); + SkASSERT(NULL != mPicture.get()); + } + if (NULL != mPicture.get()) { + // TODO: remove this const_cast once pictures are immutable + const_cast<SkPicture*>(mPicture.get())->draw(canvas); + } +} - REG(env, "android/graphics/Picture", gPictureMethods); +SkPicture* Picture::makePartialCopy() const { + SkASSERT(NULL != mRecorder.get()); - return result; -} + SkPictureRecorder reRecorder; + SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0); + mRecorder->partialReplay(canvas); + return reRecorder.endRecording(); } - +}; // namespace android diff --git a/core/jni/android/graphics/AndroidPicture.h b/core/jni/android/graphics/Picture.h index f434941c4381..abb04035d6fe 100644 --- a/core/jni/android/graphics/AndroidPicture.h +++ b/core/jni/android/graphics/Picture.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_PICTURE_H -#define ANDROID_PICTURE_H +#ifndef ANDROID_GRAPHICS_PICTURE_H +#define ANDROID_GRAPHICS_PICTURE_H #include "SkPicture.h" #include "SkPictureRecorder.h" @@ -28,13 +28,15 @@ class SkPictureRecorder; class SkStream; class SkWStream; +namespace android { + // Skia's SkPicture class has been split into an SkPictureRecorder // and an SkPicture. AndroidPicture recreates the functionality // of the old SkPicture interface by flip-flopping between the two // new classes. -class AndroidPicture { +class Picture { public: - explicit AndroidPicture(const AndroidPicture* src = NULL); + explicit Picture(const Picture* src = NULL); SkCanvas* beginRecording(int width, int height); @@ -44,7 +46,7 @@ public: int height() const; - static AndroidPicture* CreateFromStream(SkStream* stream); + static Picture* CreateFromStream(SkStream* stream); void serialize(SkWStream* stream) const; @@ -60,4 +62,6 @@ private: // resulting picture will have balanced saves and restores. SkPicture* makePartialCopy() const; }; -#endif // ANDROID_PICTURE_H + +}; // namespace android +#endif // ANDROID_GRAPHICS_PICTURE_H diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp new file mode 100644 index 000000000000..f827907477f2 --- /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_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 86207f0a3ea2..87ee6184563d 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -174,21 +174,21 @@ static int read_memtrack_memory(struct memtrack_proc* p, int pid, 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 @@ static void read_mapinfo(FILE *fp, stats_t* stats) 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 @@ static void read_mapinfo(FILE *fp, stats_t* stats) 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 @@ static void read_mapinfo(FILE *fp, stats_t* stats) 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 f8a1fd9a4893..ad36843c764b 100644 --- a/core/jni/android_server_FingerprintManager.cpp +++ b/core/jni/android_server_FingerprintManager.cpp @@ -20,54 +20,195 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> +#include <hardware/hardware.h> +#include <hardware/fingerprint.h> #include <utils/Log.h> +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find static method" methodName); + +#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + 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; -} gFingerprintManagerClassInfo; + 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) { - return -1; // TODO + 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) { - return -1; // TODO + 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 } }; -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); - -#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ - var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find static method" methodName); - -#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ - var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find method" methodName); - -#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ - 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)); +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 9fa5ec93af35..de00e594ccda 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -641,16 +641,16 @@ static void renderTextLayout(OpenGLRenderer* renderer, Layout* layout, #endif static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, - jfloat x, jfloat y, int flags, SkPaint* paint, TypefaceImpl* typeface) { + jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) { #ifdef USE_MINIKIN Layout layout; - std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface); + std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface); layout.doLayout(text, 0, count, count, css); x += xOffsetForTextAlign(paint, layout.getAdvance()); renderTextLayout(renderer, &layout, x, y, paint); #else sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, 0, count, count, flags); + text, 0, count, count, bidiFlags); if (value == NULL) { return; } @@ -669,10 +669,50 @@ static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, #endif } +#ifdef USE_MINIKIN +class RenderTextOnPathFunctor { +public: + RenderTextOnPathFunctor(const Layout& layout, OpenGLRenderer* renderer, float hOffset, + float vOffset, SkPaint* paint, SkPath* path) + : layout(layout), renderer(renderer), hOffset(hOffset), vOffset(vOffset), + paint(paint), path(path) { + } + void operator()(size_t start, size_t end) { + uint16_t glyphs[1]; + for (size_t i = start; i < end; i++) { + glyphs[0] = layout.getGlyphId(i); + float x = hOffset + layout.getX(i); + float y = vOffset + layout.getY(i); + renderer->drawTextOnPath((const char*) glyphs, sizeof(glyphs), 1, path, x, y, paint); + } + } +private: + const Layout& layout; + OpenGLRenderer* renderer; + float hOffset; + float vOffset; + SkPaint* paint; + SkPath* path; +}; +#endif + static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count, - SkPath* path, jfloat hOffset, jfloat vOffset, int flags, 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, flags); + text, 0, count, count, bidiFlags); if (value == NULL) { return; } @@ -681,20 +721,21 @@ static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int co int bytesCount = glyphsCount * sizeof(jchar); renderer->drawTextOnPath((const char*) glyphs, bytesCount, glyphsCount, path, hOffset, vOffset, paint); +#endif } static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, jint start, jint count, jint contextCount, jfloat x, jfloat y, - int flags, SkPaint* paint, TypefaceImpl* typeface) { + int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) { #ifdef USE_MINIKIN Layout layout; - std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface); + std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface); layout.doLayout(text, start, count, contextCount, css); x += xOffsetForTextAlign(paint, layout.getAdvance()); renderTextLayout(renderer, &layout, x, y, paint); #else sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, start, count, contextCount, flags); + text, start, count, contextCount, bidiFlags); if (value == NULL) { return; } @@ -715,71 +756,76 @@ static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz, jlong rendererPtr, jcharArray text, jint index, jint count, - jfloat x, jfloat y, jint flags, jlong paintPtr, jlong typefacePtr) { + jfloat x, jfloat y, jint bidiFlags, jlong paintPtr, jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); jchar* textArray = env->GetCharArrayElements(text, NULL); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); - renderText(renderer, textArray + index, count, x, y, flags, paint, typeface); + renderText(renderer, textArray + index, count, x, y, bidiFlags, paint, typeface); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject clazz, jlong rendererPtr, jstring text, jint start, jint end, - jfloat x, jfloat y, jint flags, jlong paintPtr, jlong typefacePtr) { + jfloat x, jfloat y, jint bidiFlags, jlong paintPtr, jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); const jchar* textArray = env->GetStringChars(text, NULL); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); - renderText(renderer, textArray + start, end - start, x, y, flags, paint, typeface); + renderText(renderer, textArray + start, end - start, x, y, bidiFlags, paint, typeface); env->ReleaseStringChars(text, textArray); } 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 flags, 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, flags, 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 flags, 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, flags, paint); + hOffset, vOffset, bidiFlags, paint, typeface); env->ReleaseStringChars(text, textArray); } static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject clazz, jlong rendererPtr, jcharArray text, jint index, jint count, - jint contextIndex, jint contextCount, jfloat x, jfloat y, jint dirFlags, + jint contextIndex, jint contextCount, jfloat x, jfloat y, jboolean isRtl, jlong paintPtr, jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); jchar* textArray = env->GetCharArrayElements(text, NULL); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); + int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR; renderTextRun(renderer, textArray + contextIndex, index - contextIndex, - count, contextCount, x, y, dirFlags, paint, typeface); + count, contextCount, x, y, bidiFlags, paint, typeface); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject clazz, jlong rendererPtr, jstring text, jint start, jint end, - jint contextStart, int contextEnd, jfloat x, jfloat y, jint dirFlags, + jint contextStart, int contextEnd, jfloat x, jfloat y, jboolean isRtl, jlong paintPtr, jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); const jchar* textArray = env->GetStringChars(text, NULL); @@ -788,15 +834,16 @@ static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject clazz, SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); + int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR; renderTextRun(renderer, textArray + contextStart, start - contextStart, - count, contextCount, x, y, dirFlags, paint, typeface); + count, contextCount, x, y, bidiFlags, paint, typeface); env->ReleaseStringChars(text, textArray); } static void renderPosText(OpenGLRenderer* renderer, const jchar* text, int count, - const jfloat* positions, jint dirFlags, SkPaint* paint) { + const jfloat* positions, jint bidiFlags, SkPaint* paint) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, - text, 0, count, count, dirFlags); + text, 0, count, count, bidiFlags); if (value == NULL) { return; } @@ -984,12 +1031,12 @@ static JNINativeMethod gMethods[] = { { "nDrawText", "(JLjava/lang/String;IIFFIJJ)V", (void*) android_view_GLES20Canvas_drawText }, - { "nDrawTextOnPath", "(J[CIIJFFIJ)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath }, - { "nDrawTextOnPath", "(JLjava/lang/String;IIJFFIJ)V", + { "nDrawTextOnPath", "(J[CIIJFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath }, + { "nDrawTextOnPath", "(JLjava/lang/String;IIJFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextOnPath }, - { "nDrawTextRun", "(J[CIIIIFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextRunArray }, - { "nDrawTextRun", "(JLjava/lang/String;IIIIFFIJJ)V", + { "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 }, diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index c0d522191147..cfc8eb89fceb 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -61,6 +61,8 @@ static struct { jfieldID xDpi; jfieldID yDpi; jfieldID secure; + jfieldID appVsyncOffsetNanos; + jfieldID presentationDeadlineNanos; } gPhysicalDisplayInfoClassInfo; static struct { @@ -392,6 +394,10 @@ static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, 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 @@ int register_android_view_SurfaceControl(JNIEnv* env) 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/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index c58bf04f1f4e..0cdddbae6c47 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -14,46 +14,40 @@ * limitations under the License. */ -#include "android_runtime/AndroidRuntime.h" +#define LOG_TAG "Zygote" // sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc #include <sys/mount.h> #include <linux/fs.h> #include <grp.h> +#include <fcntl.h> #include <paths.h> #include <signal.h> #include <stdlib.h> +#include <unistd.h> +#include <sys/capability.h> +#include <sys/personality.h> +#include <sys/prctl.h> #include <sys/resource.h> -#include <sys/types.h> #include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> #include <sys/wait.h> -#include <unistd.h> -#include <fcntl.h> -#include "cutils/fs.h" -#include "cutils/multiuser.h" -#include "cutils/sched_policy.h" -#include "utils/String8.h" + +#include <cutils/fs.h> +#include <cutils/multiuser.h> +#include <cutils/sched_policy.h> +#include <utils/String8.h> +#include <selinux/android.h> + +#include "android_runtime/AndroidRuntime.h" #include "JNIHelp.h" #include "ScopedLocalRef.h" #include "ScopedPrimitiveArray.h" #include "ScopedUtfChars.h" -#if defined(HAVE_PRCTL) -#include <sys/prctl.h> -#endif - -#include <selinux/android.h> - -#if defined(__linux__) -#include <sys/personality.h> -#include <sys/utsname.h> -#if defined(HAVE_ANDROID_OS) -#include <sys/capability.h> -#endif -#endif - namespace { using android::String8; @@ -97,11 +91,9 @@ static void SigChldHandler(int /*signal_number*/) { if (WTERMSIG(status) != SIGKILL) { ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status)); } -#ifdef WCOREDUMP if (WCOREDUMP(status)) { ALOGI("Process %d dumped core.", pid); } -#endif /* ifdef WCOREDUMP */ } // If the just-crashed process is the system_server, bring down zygote @@ -199,8 +191,6 @@ static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) { } } -#if defined(HAVE_ANDROID_OS) - // The debug malloc library needs to know whether it's the zygote or a child. extern "C" int gMallocLeakZygoteChild; @@ -254,17 +244,6 @@ static void SetSchedulerPolicy(JNIEnv* env) { } } -#else - -static int gMallocLeakZygoteChild = 0; - -static void EnableKeepCapabilities(JNIEnv*) {} -static void DropCapabilitiesBoundingSet(JNIEnv*) {} -static void SetCapabilities(JNIEnv*, int64_t, int64_t) {} -static void SetSchedulerPolicy(JNIEnv*) {} - -#endif - // Create a private mount namespace and bind mount appropriate emulated // storage for the given user. static bool MountEmulatedStorage(uid_t uid, jint mount_mode) { @@ -337,7 +316,6 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode) { return true; } -#if defined(__linux__) static bool NeedsNoRandomizeWorkaround() { #if !defined(__arm__) return false; @@ -357,7 +335,6 @@ static bool NeedsNoRandomizeWorkaround() { return (major < 3) || ((major == 3) && (minor < 4)); #endif } -#endif // Utility to close down the Zygote socket file descriptors while // the child is still running as root with Zygote's privileges. Each @@ -474,7 +451,6 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra RuntimeAbort(env); } -#if defined(__linux__) if (NeedsNoRandomizeWorkaround()) { // Work around ARM kernel ASLR lossage (http://b/5817320). int old_personality = personality(0xffffffff); @@ -483,58 +459,49 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra ALOGW("personality(%d) failed", new_personality); } } -#endif SetCapabilities(env, permittedCapabilities, effectiveCapabilities); SetSchedulerPolicy(env); -#if defined(HAVE_ANDROID_OS) - { // NOLINT(whitespace/braces) - const char* se_info_c_str = NULL; - ScopedUtfChars* se_info = NULL; - if (java_se_info != NULL) { - se_info = new ScopedUtfChars(env, java_se_info); - se_info_c_str = se_info->c_str(); - if (se_info_c_str == NULL) { - ALOGE("se_info_c_str == NULL"); - RuntimeAbort(env); - } - } - const char* se_name_c_str = NULL; - ScopedUtfChars* se_name = NULL; - if (java_se_name != NULL) { - se_name = new ScopedUtfChars(env, java_se_name); - se_name_c_str = se_name->c_str(); - if (se_name_c_str == NULL) { - ALOGE("se_name_c_str == NULL"); - RuntimeAbort(env); - } - } - rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str); - if (rc == -1) { - ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid, - is_system_server, se_info_c_str, se_name_c_str); - RuntimeAbort(env); - } - - // Make it easier to debug audit logs by setting the main thread's name to the - // nice name rather than "app_process". - if (se_info_c_str == NULL && is_system_server) { - se_name_c_str = "system_server"; - } - if (se_info_c_str != NULL) { - SetThreadName(se_name_c_str); - } + const char* se_info_c_str = NULL; + ScopedUtfChars* se_info = NULL; + if (java_se_info != NULL) { + se_info = new ScopedUtfChars(env, java_se_info); + se_info_c_str = se_info->c_str(); + if (se_info_c_str == NULL) { + ALOGE("se_info_c_str == NULL"); + RuntimeAbort(env); + } + } + const char* se_name_c_str = NULL; + ScopedUtfChars* se_name = NULL; + if (java_se_name != NULL) { + se_name = new ScopedUtfChars(env, java_se_name); + se_name_c_str = se_name->c_str(); + if (se_name_c_str == NULL) { + ALOGE("se_name_c_str == NULL"); + RuntimeAbort(env); + } + } + rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str); + if (rc == -1) { + ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid, + is_system_server, se_info_c_str, se_name_c_str); + RuntimeAbort(env); + } - delete se_info; - delete se_name; + // Make it easier to debug audit logs by setting the main thread's name to the + // nice name rather than "app_process". + if (se_info_c_str == NULL && is_system_server) { + se_name_c_str = "system_server"; } -#else - UNUSED(is_system_server); - UNUSED(java_se_info); - UNUSED(java_se_name); -#endif + if (se_info_c_str != NULL) { + SetThreadName(se_name_c_str); + } + + delete se_info; + delete se_name; UnsetSigChldHandler(); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index edca10148144..8b5dff014e19 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 --> <!-- =============================================== --> @@ -2928,6 +2935,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/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png Binary files differindex 17acfc52914a..c5c0e97d8d49 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png Binary files differindex 17acfc52914a..c5c0e97d8d49 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png Binary files differindex 9b8ca22720c2..3b3122548377 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png Binary files differindex 9b8ca22720c2..3b3122548377 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png Binary files differindex bc20f6c38dcb..b65009ed2db6 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png Binary files differindex bc20f6c38dcb..b65009ed2db6 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png Binary files differindex 571819b9fcd3..a2dfcaede942 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png Binary files differindex 571819b9fcd3..c3fda0e4fa50 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png Binary files differindex 1f83b5a500b8..bae60a7bd94f 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png Binary files differindex 1f83b5a500b8..bae60a7bd94f 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png Binary files differindex 733cf4594180..a9653b0a3282 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png Binary files differindex 733cf4594180..a9653b0a3282 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png Binary files differindex 2265de4679f0..394cb5ec41a8 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png Binary files differindex 2265de4679f0..394cb5ec41a8 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png Binary files differindex f3ada58e8869..aa23c6ec4ad7 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png Binary files differindex f3ada58e8869..028b3b8d6d6a 100644 --- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/ic_corp_badge.png b/core/res/res/drawable-hdpi/ic_corp_badge.png Binary files differnew file mode 100644 index 000000000000..c79ce92f98b0 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_corp_badge.png diff --git a/core/res/res/drawable-hdpi/ic_corp_icon_badge.png b/core/res/res/drawable-hdpi/ic_corp_icon_badge.png Binary files differnew file mode 100644 index 000000000000..0059e091cd1d --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_corp_icon_badge.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png Binary files differindex 3fdd3bc5df97..4e6d076a1169 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png Binary files differindex 3fdd3bc5df97..4e6d076a1169 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png Binary files differindex eaa02b332c4d..ca61cb2caf9e 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png Binary files differindex eaa02b332c4d..ca61cb2caf9e 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png Binary files differindex 28c8b944fc5f..b5999be2d6ff 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png Binary files differindex 28c8b944fc5f..b5999be2d6ff 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png Binary files differindex 6090cce2d4fb..8392ac3e8189 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png Binary files differindex 6090cce2d4fb..522bafd3741d 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png Binary files differindex 3f2e982edda4..ebb2f8bbd518 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png Binary files differindex 3f2e982edda4..ebb2f8bbd518 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png Binary files differindex 14b958b5a1b9..3fa20ca5ee2e 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png Binary files differindex 14b958b5a1b9..3fa20ca5ee2e 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png Binary files differindex 4db22d4de82c..6cc59ed0ba2f 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png Binary files differindex 4db22d4de82c..6cc59ed0ba2f 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png Binary files differindex a11e1c770bf1..a1fcd08436af 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png Binary files differindex a11e1c770bf1..c6c02248b5bf 100644 --- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/ic_corp_badge.png b/core/res/res/drawable-mdpi/ic_corp_badge.png Binary files differnew file mode 100644 index 000000000000..c1447fe9fb5e --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_corp_badge.png diff --git a/core/res/res/drawable-mdpi/ic_corp_icon_badge.png b/core/res/res/drawable-mdpi/ic_corp_icon_badge.png Binary files differnew file mode 100644 index 000000000000..5ff8c5d27733 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_corp_icon_badge.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png Binary files differindex c08deab0b9fe..1e455302206a 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png Binary files differindex c08deab0b9fe..1e455302206a 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png Binary files differindex 8b1a55c30fc1..2c63c5dd23cb 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png Binary files differindex 8b1a55c30fc1..2c63c5dd23cb 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png Binary files differindex 77cd1fad4ba9..dd5e26e5ba8e 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png Binary files differindex 77cd1fad4ba9..dd5e26e5ba8e 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png Binary files differindex e0e354010e2b..aa9b3c551ef7 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png Binary files differindex e0e354010e2b..367c25ac1f2c 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png Binary files differindex 324e490b780c..df28ad0a7b6b 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png Binary files differindex 324e490b780c..df28ad0a7b6b 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png Binary files differindex e126cc64456b..3a2783143554 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png Binary files differindex e126cc64456b..3a2783143554 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png Binary files differindex 4c1f1b97c233..d68bdf42a2eb 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png Binary files differindex 4c1f1b97c233..d68bdf42a2eb 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png Binary files differindex 219d37b50b61..da03ec999e5c 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png Binary files differindex 219d37b50b61..482b2495251e 100644 --- a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/ic_corp_badge.png b/core/res/res/drawable-xhdpi/ic_corp_badge.png Binary files differnew file mode 100644 index 000000000000..2d3d748f772b --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_corp_badge.png diff --git a/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png Binary files differnew file mode 100644 index 000000000000..dc5716d439d6 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png diff --git a/core/res/res/drawable-xxhdpi/ic_corp_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_badge.png Binary files differnew file mode 100644 index 000000000000..430e63b415fe --- /dev/null +++ b/core/res/res/drawable-xxhdpi/ic_corp_badge.png diff --git a/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png Binary files differnew file mode 100644 index 000000000000..cc00dd885b05 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml deleted file mode 100644 index 532571245d55..000000000000 --- a/core/res/res/drawable/ic_corp_badge.xml +++ /dev/null @@ -1,34 +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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="19.0dp" - android:height="19.0dp"/> - - <viewport - android:viewportWidth="19.0" - android:viewportHeight="19.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: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:fill="#FFFFFF"/> - <path - android:pathData="M4.75,4.75 h9.5 v9.5 h-9.5z" - 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 deleted file mode 100644 index 7bfab4c55a0d..000000000000 --- a/core/res/res/drawable/ic_corp_icon_badge.xml +++ /dev/null @@ -1,40 +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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64.0dp" - android:height="64.0dp"/> - - <viewport - android:viewportWidth="64.0" - android:viewportHeight="64.0"/> - - <path - android:fill="#FF000000" - android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/> - <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"/> - <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:fill="#FFFFFF"/> - <path - android:pathData="M42.0,42.0 h14.0 v14.0 h-14.0z" - android:fill="#00000000"/> -</vector> diff --git a/core/res/res/drawable/view_accessibility_focused.xml b/core/res/res/drawable/view_accessibility_focused.xml index 0da9a81bd8fb..68e3f1ecb97f 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/preference_widget_seekbar.xml b/core/res/res/layout/preference_widget_seekbar.xml index c42796587683..05daa1a42011 100644 --- a/core/res/res/layout/preference_widget_seekbar.xml +++ b/core/res/res/layout/preference_widget_seekbar.xml @@ -14,9 +14,7 @@ limitations under the License. --> -<!-- Layout for a Preference in a PreferenceActivity. The - Preference is able to place a specific widget for its particular - type in the "widget_frame" layout. --> +<!-- Layout used by SeekBarPreference for the seekbar widget style. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/core/res/res/layout/preference_widget_seekbar_material.xml b/core/res/res/layout/preference_widget_seekbar_material.xml new file mode 100644 index 000000000000..f70a4726aa9b --- /dev/null +++ b/core/res/res/layout/preference_widget_seekbar_material.xml @@ -0,0 +1,86 @@ +<?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. +--> + +<!-- Layout used by SeekBarPreference for the seekbar widget style. --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:gravity="center_vertical" + android:paddingStart="?attr/listPreferredItemPaddingStart" + android:paddingEnd="?attr/listPreferredItemPaddingEnd"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center" + android:minWidth="@dimen/preference_icon_minWidth" + android:orientation="horizontal"> + <ImageView + android:id="@+android:id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:minWidth="48dp" + /> + </LinearLayout> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dip" + android:layout_marginEnd="8dip" + android:layout_marginTop="6dip" + android:layout_marginBottom="6dip" + android:layout_weight="1"> + + <TextView android:id="@+android:id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceMedium" + android:ellipsize="marquee" + android:fadingEdge="horizontal" /> + + <TextView android:id="@+android:id/summary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@android:id/title" + android:layout_alignStart="@android:id/title" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:attr/textColorSecondary" + android:maxLines="4" /> + + <!-- Preference should place its actual preference widget here. --> + <LinearLayout android:id="@+android:id/widget_frame" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_below="@android:id/summary" + android:layout_alignStart="@android:id/title" + android:minWidth="@dimen/preference_widget_width" + android:gravity="center" + android:orientation="vertical" /> + + <SeekBar android:id="@+android:id/seekbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@android:id/summary" + android:layout_toEndOf="@android:id/widget_frame" + android:layout_alignParentEnd="true" /> + + </RelativeLayout> + +</LinearLayout> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 21fd37d8de07..72b09093d3bc 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1362,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Laat \'n program toe om vir veranderinge in vertrouenstaat te luister."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Voorsien \'n vertroude agent."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Laat \'n program toe om \'n vertroude agent te voorsien."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 abeadfaccf30..da891368585b 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1362,6 +1362,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 aa477745488d..626858fd360a 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1362,6 +1362,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 a8ceac6326a6..ca71ce35bb01 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"</string> <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Услуги, които ви струват пари"</string> <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Извършват неща, които могат да ви струват пари."</string> <string name="permgrouplab_messages" msgid="7521249148445456662">"Вашите съобщения"</string> @@ -1363,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Разрешава на приложението да следи за промени в състоянието на надеждност."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Предоставяне на trust agent."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Разрешава на приложението да предоставя trust agent."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 f6aaf73d9f4f..e1eed49aae1b 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1362,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permet que una aplicació escolti els canvis en l\'estat de confiança."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Proporcionar un agent de confiança"</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permet que una aplicació proporcioni un agent de confiança."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 d18c10718277..cf06dc1aed5e 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1362,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Umožňuje aplikaci naslouchat změnám ve stavu důvěryhodnosti."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Poskytování zástupce důvěryhodnosti"</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Umožňuje aplikaci poskytnout zástupce důvěryhodnosti."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 869545180dac..0aa2a36939e5 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1362,6 +1362,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 bffbd1e5d470..d557d373a5fa 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1362,6 +1362,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 59e9085cfb01..8205c6061579 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1362,6 +1362,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 c7d614925165..a0c35c82a12b 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1362,6 +1362,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 c7d614925165..a0c35c82a12b 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -1362,6 +1362,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 8db11143feee..b6a7667c96b6 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1362,6 +1362,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 5ad1cd34a19e..1dcbf62a44a0 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1362,6 +1362,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 7a4659b52be2..60d036e13aa4 100644 --- a/core/res/res/values-et-rEE/strings.xml +++ b/core/res/res/values-et-rEE/strings.xml @@ -1362,6 +1362,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 e9628ec2feee..b4727bbe081f 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1362,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"به یک برنامه کاربردی برای گوش دادن به تغییرات در trust اجازه میدهد."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"یک عامل مورد اعتماد فراهم میآورد."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"به برنامه امکان میدهد یک عامل مورد اعتماد فراهم آورد."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 09f1f4532b3a..02c6da4d444c 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1363,6 +1363,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Antaa sovelluksen seurata luottamuksen tilamuutoksia."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Luotettavan tahon tarjoaminen"</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Antaa sovelluksen tarjota luotettavan tahon."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 5982bcadfe64..b1292b32a000 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> @@ -1363,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permet à une application de détecter les modifications de l\'état de confiance."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fournir un agent de confiance."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permet à une application de fournir un agent de confiance."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 89ef90c1a27e..f511df0aad02 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> @@ -1363,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permettre à une application de détecter les modifications de l\'état de confiance."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fournir un agent de confiance"</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permettre à une application de fournir un agent de confiance"</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 056f63fc6ed7..470ec4110266 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1362,6 +1362,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 8dbe7918e327..7dc8e191a5ba 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> @@ -1363,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Omogućuje aplikaciji praćenje promjena pouzdanog stanja."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Pružanje agenta za pouzdanost."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Omogućuje aplikaciji pružanje agenta za pouzdanost."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 cbe36be3ff89..2582737d54a4 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1362,6 +1362,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 17779b7baea9..f0bee2b310b4 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> @@ -1365,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Ծրագրին թույլ է տալիս լսել վստահության կարգավիճակի փոփոխությունները:"</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Տրամադրել վստահելի գործակալ:"</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ծրագրին թույլ է տալիս տրամադրել վստահելի գործակալ:"</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 2fb9f8a21fad..9dca8a5771d2 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> @@ -1363,6 +1362,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 8a9ff5842469..0ea6f6ee15ab 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1362,6 +1362,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 8de99714aaa0..19fb05aea8b4 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1362,6 +1362,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 3fde38ab86df..6ba99bfbdc20 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> @@ -1363,6 +1362,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 01a7194e4f42..879d00fc9ba9 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> @@ -1365,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"საშუალებას აძლევს აპლიკაციას მოუსმინოს ცვლილებებს სანდო მდგომარეობაში."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"სანდო აგენტის წარმოდგენა."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"საშუალებას აძლევს აპლიკაციას წარმოადგინოს სანდო აგენტი."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 62b2e8a782e9..daf004c5006e 100644 --- a/core/res/res/values-km-rKH/strings.xml +++ b/core/res/res/values-km-rKH/strings.xml @@ -1362,6 +1362,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-ko/strings.xml b/core/res/res/values-ko/strings.xml index 88327ca626d1..43d1b25d0956 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> @@ -1363,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"애플리케이션이 Trust 상태에서의 변경사항을 수신할 수 있도록 허용합니다."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Trust Agent 제공"</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"애플리케이션이 Trust Agent를 제공할 수 있도록 허용합니다."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 44f5e0de96f9..e386a247facf 100644 --- a/core/res/res/values-lo-rLA/strings.xml +++ b/core/res/res/values-lo-rLA/strings.xml @@ -1362,6 +1362,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-lt/strings.xml b/core/res/res/values-lt/strings.xml index 09fbdfbb3afe..be7e571f7460 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1362,6 +1362,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 2cb57357a288..b421da49366b 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1362,6 +1362,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 b030060fd70a..803128fa1ba3 100644 --- a/core/res/res/values-mn-rMN/strings.xml +++ b/core/res/res/values-mn-rMN/strings.xml @@ -1362,6 +1362,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 9ad8f37b4419..1d1a75b83121 100644 --- a/core/res/res/values-ms-rMY/strings.xml +++ b/core/res/res/values-ms-rMY/strings.xml @@ -401,10 +401,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 menguruskan frasa kunci untuk pengesahan sebutan laluan. 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> @@ -1365,6 +1363,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Membenarkan aplikasi mendengar perubahan dalam keadaan amanah."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Sediakan ejen amanah."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Membenarkan aplikasi menyediakan ejen amanah."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 1ae5c5e552f3..31fa11d8a0ea 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> @@ -1363,6 +1362,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 e42d71485e17..a655f36c535d 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1362,6 +1362,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 64609df33110..c30ad74c1a5b 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1362,6 +1362,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 059a85f53181..2e4e42364523 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1362,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que uma aplicação registe alterações no trust state."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fornecer um agente fidedigno."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que uma aplicação forneça um agente fidedigno."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 ec805f51c0b7..3df7ecb7bab1 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> @@ -1365,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que o aplicativo detecte alterações no estado de confiança."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fornecer um agente de confiança."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que um aplicativo forneça um agente de confiança."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 ba25b523ac7e..ff26d8fe3e44 100644 --- a/core/res/res/values-rm/strings.xml +++ b/core/res/res/values-rm/strings.xml @@ -2190,6 +2190,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 07c32fbcac51..110b24380964 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -401,10 +401,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> @@ -1365,6 +1363,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite unei aplicații să detecteze modificările în starea de încredere."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Indicați un agent de încredere."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite unei aplicații să indice un agent de încredere."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 a7ca57b94a35..12d1895f180a 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> @@ -1363,6 +1362,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 5631559459ab..ab596c5fa096 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1362,6 +1362,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 a51e909cc0a0..cc06fd326cd2 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> @@ -1363,6 +1362,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 4b2aa24a9f4d..099e6ef970e2 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 за посао"</string> <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Услуге које се плаћају"</string> <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Покреће радње које могу да се плаћају."</string> <string name="permgrouplab_messages" msgid="7521249148445456662">"Поруке"</string> @@ -1363,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Дозвољава апликацији да прати промене Trust стања."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Обезбеђивање поузданог агента."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Дозвољава апликацији да обезбеди поузданог агента."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 f9cd57a3687f..46bb5636aee4 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1362,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Tillåter att en app lyssnar efter ändringar i den betrodda agentens status."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Tillhandahåll en betrodd agent."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Tillåter att en app tillhandahåller en betrodd agent."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 9573e151d968..595c80784ca0 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1362,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Huruhusu programu kusikiliza mabadiliko katika hali ya kuaminiwa."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Toa wakala wa uaminifu."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Huruhusu programu kutoa wakala wa uaminifu."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 68d5442b42b6..feabf4ff9d7b 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1362,6 +1362,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 5c926bac6b9e..13b629e6b268 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> @@ -1363,6 +1362,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 3583fb0d6eb9..0c19b03c5008 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> @@ -1363,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Bir uygulamanın, güven durumundaki değişiklikleri dinlemesine izin verir."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Güven aracısı sağlama."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Bir uygulamanın güven aracısı sağlamasına izin verir."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 fdb40b5fcfdf..0d56975bba93 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> @@ -1362,6 +1362,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 022efcc584c9..dd13b4556483 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> @@ -1363,6 +1362,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"Cho phép ứng dụng quan sát các thay đổi ở trạng thái đáng tin cậy."</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Cung cấp tác nhân đáng tin cậy."</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Cho phép ứng dụng cung cấp tác nhân đáng tin cậy."</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 f2090a257b01..3a073661b2a7 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -401,10 +401,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> @@ -1365,6 +1363,10 @@ <string name="permdesc_trust_listener" msgid="8233895334214716864">"允许应用检测信任状态的变化。"</string> <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"提供信任的代理。"</string> <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"允许应用提供信任的代理。"</string> + <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) --> + <skip /> + <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) --> + <skip /> <string name="permlab_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 3ebf4166d7b5..8e290b52be7b 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> @@ -1363,6 +1362,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 85e47acaf6bf..6ab2b48ad0f1 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> @@ -1363,6 +1362,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 639d400b914c..07238c38d374 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1362,6 +1362,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 52138968cab6..8d8f69e87df8 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -834,6 +834,8 @@ <attr name="preferenceFragmentPaddingSide" format="dimension" /> <!-- Default style for switch preferences. --> <attr name="switchPreferenceStyle" format="reference" /> + <!-- Default style for seekbar preferences. --> + <attr name="seekBarPreferenceStyle" format="reference" /> <!-- ============================ --> <!-- Text selection handle styles --> @@ -2430,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. --> @@ -3019,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. --> @@ -3104,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. --> @@ -3209,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. --> @@ -3399,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"> @@ -3408,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"> @@ -6607,6 +6795,10 @@ <attr name="disableDependentsState" /> </declare-styleable> + <declare-styleable name="SeekBarPreference"> + <attr name="layout" /> + </declare-styleable> + <!-- Use <code>tts-engine</code> as the root tag of the XML resource that describes a text to speech engine implemented as a subclass of {@link android.speech.tts.TextToSpeechService}. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 814d8fc0c800..a8735cb21f5a 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -873,17 +873,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. - - <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" /> + <!-- 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 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 @@ -1623,7 +1639,7 @@ <!-- @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" /> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 9f6c7ad81089..f3045144718c 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/public.xml b/core/res/res/values/public.xml index 202c1271ec20..c5ce2292bde5 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,22 @@ <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-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6e1b5ef62c47..e31dbaf08009 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 diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 5bd612287291..d6be31339b2e 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -980,6 +980,10 @@ please see styles_device_defaults.xml. <item name="android:switchTextOff">@android:string/capital_off</item> </style> + <style name="Preference.SeekBarPreference"> + <item name="android:layout">@android:layout/preference_widget_seekbar</item> + </style> + <style name="Preference.PreferenceScreen"> </style> @@ -1035,6 +1039,10 @@ please see styles_device_defaults.xml. <item name="android:switchTextOff">@android:string/capital_off</item> </style> + <style name="Preference.Holo.SeekBarPreference"> + <item name="android:layout">@android:layout/preference_widget_seekbar</item> + </style> + <style name="Preference.Holo.PreferenceScreen"> </style> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 84dbc7921b16..84d38ce1d835 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -254,6 +254,7 @@ easier. <style name="Preference.DeviceDefault.PreferenceScreen" parent="Preference.Material.PreferenceScreen"/> <style name="Preference.DeviceDefault.RingtonePreference" parent="Preference.Material.RingtonePreference"/> <style name="Preference.DeviceDefault.SwitchPreference" parent="Preference.Material.SwitchPreference"/> + <style name="Preference.DeviceDefault.SeekBarPreference" parent="Preference.Material.SeekBarPreference"/> <!-- AlertDialog Styles --> <style name="AlertDialog.DeviceDefault" parent="AlertDialog.Material"/> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 0c95149e8b16..25307b9ba198 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -64,6 +64,10 @@ please see styles_device_defaults.xml. <item name="switchTextOff">@string/capital_off</item> </style> + <style name="Preference.Material.SeekBarPreference"> + <item name="layout">@android:layout/preference_widget_seekbar_material</item> + </style> + <style name="Preference.Material.PreferenceScreen"/> <style name="Preference.Material.DialogPreference"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d69f60acb090..5f4553b49eac 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1893,5 +1893,6 @@ <java-symbol type="color" name="timepicker_default_selector_color_material" /> <java-symbol type="color" name="timepicker_default_numbers_background_color_material" /> <java-symbol type="style" name="TextAppearance.Material.TimePicker.TimeLabel" /> + <java-symbol type="attr" name="seekBarPreferenceStyle" /> </resources> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index cb5cb0cd2d87..5eec19733988 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -321,6 +321,7 @@ please see themes_device_defaults.xml. <item name="preferenceInformationStyle">@android:style/Preference.Information</item> <item name="checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item> <item name="switchPreferenceStyle">@android:style/Preference.SwitchPreference</item> + <item name="seekBarPreferenceStyle">@android:style/Preference.SeekBarPreference</item> <item name="yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item> <item name="dialogPreferenceStyle">@android:style/Preference.DialogPreference</item> <item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item> @@ -1203,6 +1204,7 @@ please see themes_device_defaults.xml. <item name="preferenceInformationStyle">@android:style/Preference.Holo.Information</item> <item name="checkBoxPreferenceStyle">@android:style/Preference.Holo.CheckBoxPreference</item> <item name="switchPreferenceStyle">@android:style/Preference.Holo.SwitchPreference</item> + <item name="seekBarPreferenceStyle">@android:style/Preference.Holo.SeekBarPreference</item> <item name="yesNoPreferenceStyle">@android:style/Preference.Holo.DialogPreference.YesNoPreference</item> <item name="dialogPreferenceStyle">@android:style/Preference.Holo.DialogPreference</item> <item name="editTextPreferenceStyle">@android:style/Preference.Holo.DialogPreference.EditTextPreference</item> @@ -1543,6 +1545,7 @@ please see themes_device_defaults.xml. <item name="preferenceInformationStyle">@android:style/Preference.Holo.Information</item> <item name="checkBoxPreferenceStyle">@android:style/Preference.Holo.CheckBoxPreference</item> <item name="switchPreferenceStyle">@android:style/Preference.Holo.SwitchPreference</item> + <item name="seekBarPreferenceStyle">@android:style/Preference.Holo.SeekBarPreference</item> <item name="yesNoPreferenceStyle">@android:style/Preference.Holo.DialogPreference.YesNoPreference</item> <item name="dialogPreferenceStyle">@android:style/Preference.Holo.DialogPreference</item> <item name="editTextPreferenceStyle">@android:style/Preference.Holo.DialogPreference.EditTextPreference</item> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 27c8754ad31c..8e83e481014d 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -160,6 +160,7 @@ easier. <item name="preferenceInformationStyle">@style/Preference.DeviceDefault.Information</item> <item name="checkBoxPreferenceStyle">@style/Preference.DeviceDefault.CheckBoxPreference</item> <item name="switchPreferenceStyle">@style/Preference.DeviceDefault.SwitchPreference</item> + <item name="seekBarPreferenceStyle">@style/Preference.DeviceDefault.SeekBarPreference</item> <item name="yesNoPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference.YesNoPreference</item> <item name="dialogPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference</item> <item name="editTextPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference.EditTextPreference</item> @@ -421,6 +422,7 @@ easier. <item name="preferenceInformationStyle">@style/Preference.DeviceDefault.Information</item> <item name="checkBoxPreferenceStyle">@style/Preference.DeviceDefault.CheckBoxPreference</item> <item name="switchPreferenceStyle">@style/Preference.DeviceDefault.SwitchPreference</item> + <item name="seekBarPreferenceStyle">@style/Preference.DeviceDefault.SeekBarPreference</item> <item name="yesNoPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference.YesNoPreference</item> <item name="dialogPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference</item> <item name="editTextPreferenceStyle">@style/Preference.DeviceDefault.DialogPreference.EditTextPreference</item> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index 769c8a17c4ec..77e430764ec2 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -282,6 +282,7 @@ please see themes_device_defaults.xml. <item name="preferenceInformationStyle">@style/Preference.Material.Information</item> <item name="checkBoxPreferenceStyle">@style/Preference.Material.CheckBoxPreference</item> <item name="switchPreferenceStyle">@style/Preference.Material.SwitchPreference</item> + <item name="seekBarPreferenceStyle">@style/Preference.Material.SeekBarPreference</item> <item name="yesNoPreferenceStyle">@style/Preference.Material.DialogPreference.YesNoPreference</item> <item name="dialogPreferenceStyle">@style/Preference.Material.DialogPreference</item> <item name="editTextPreferenceStyle">@style/Preference.Material.DialogPreference.EditTextPreference</item> @@ -634,6 +635,7 @@ please see themes_device_defaults.xml. <item name="preferenceInformationStyle">@style/Preference.Material.Information</item> <item name="checkBoxPreferenceStyle">@style/Preference.Material.CheckBoxPreference</item> <item name="switchPreferenceStyle">@style/Preference.Material.SwitchPreference</item> + <item name="seekBarPreferenceStyle">@style/Preference.Material.SeekBarPreference</item> <item name="yesNoPreferenceStyle">@style/Preference.Material.DialogPreference.YesNoPreference</item> <item name="dialogPreferenceStyle">@style/Preference.Material.DialogPreference</item> <item name="editTextPreferenceStyle">@style/Preference.Material.DialogPreference.EditTextPreference</item> diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 07a6a10896fb..7251e7c3fa77 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -29,6 +29,7 @@ import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageParser.PackageParserException; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.net.Uri; @@ -60,7 +61,6 @@ import android.util.Log; import java.io.File; import java.io.IOException; import java.io.InputStream; - import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -303,14 +303,13 @@ public class PackageManagerTests extends AndroidTestCase { return Uri.fromFile(outFile); } - private PackageParser.Package parsePackage(Uri packageURI) { + private PackageParser.Package parsePackage(Uri packageURI) throws PackageParserException { final String archiveFilePath = packageURI.getPath(); PackageParser packageParser = new PackageParser(archiveFilePath); File sourceFile = new File(archiveFilePath); DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); - PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath, - metrics, 0); + PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, 0); packageParser = null; return pkg; } @@ -579,18 +578,18 @@ public class PackageManagerTests extends AndroidTestCase { PackageParser.Package pkg; - InstallParams(String outFileName, int rawResId) { + InstallParams(String outFileName, int rawResId) throws PackageParserException { this.pkg = getParsedPackage(outFileName, rawResId); - this.packageURI = Uri.fromFile(new File(pkg.mScanPath)); + this.packageURI = Uri.fromFile(new File(pkg.codePath)); } InstallParams(PackageParser.Package pkg) { - this.packageURI = Uri.fromFile(new File(pkg.mScanPath)); + this.packageURI = Uri.fromFile(new File(pkg.codePath)); this.pkg = pkg; } long getApkSize() { - File file = new File(pkg.mScanPath); + File file = new File(pkg.codePath); return file.length(); } } @@ -691,7 +690,8 @@ public class PackageManagerTests extends AndroidTestCase { } } - private PackageParser.Package getParsedPackage(String outFileName, int rawResId) { + private PackageParser.Package getParsedPackage(String outFileName, int rawResId) + throws PackageParserException { PackageManager pm = mContext.getPackageManager(); File filesDir = mContext.getFilesDir(); File outFile = new File(filesDir, outFileName); @@ -1343,7 +1343,7 @@ public class PackageManagerTests extends AndroidTestCase { assertUninstalled(info); } } finally { - File outFile = new File(ip.pkg.mScanPath); + File outFile = new File(ip.pkg.codePath); if (outFile != null && outFile.exists()) { outFile.delete(); } 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 000000000000..cf278fb81986 --- /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 c80d0bfd8d0c..af6a32be88b3 100644 --- a/core/tests/coretests/src/android/net/RouteInfoTest.java +++ b/core/tests/coretests/src/android/net/RouteInfoTest.java @@ -19,7 +19,7 @@ package android.net; 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 @@ public class RouteInfoTest extends TestCase { 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 @@ public class RouteInfoTest extends TestCase { // 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 @@ public class RouteInfoTest extends TestCase { 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 58b7db99c92f..42de9eab2347 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 @@ public abstract class OverlayBaseTest extends AndroidTestCase { } } - 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 abstract class OverlayBaseTest extends AndroidTestCase { */ 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/Android.mk b/data/fonts/Android.mk index abb960c75ffe..6437e0773f22 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -42,12 +42,21 @@ $(eval $(call create-font-symlink,DroidSerif-Bold.ttf,NotoSerif-Bold.ttf)) $(eval $(call create-font-symlink,DroidSerif-Italic.ttf,NotoSerif-Italic.ttf)) $(eval $(call create-font-symlink,DroidSerif-BoldItalic.ttf,NotoSerif-BoldItalic.ttf)) +extra_font_files := \ + DroidSans.ttf \ + DroidSans-Bold.ttf + ################################ # On space-constrained devices, we include a subset of fonts: ifeq ($(SMALLER_FONT_FOOTPRINT),true) + droidsans_fallback_src := DroidSansFallback.ttf -extra_font_files := DroidSans.ttf DroidSans-Bold.ttf -else + +else # !SMALLER_FONT_FOOTPRINT + +droidsans_fallback_src := DroidSansFallbackFull.ttf + +ifneq ($(EXTENDED_FONT_FOOTPRINT),true) include $(CLEAR_VARS) LOCAL_MODULE := MTLmr3m.ttf LOCAL_SRC_FILES := $(LOCAL_MODULE) @@ -55,12 +64,9 @@ LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts include $(BUILD_PREBUILT) +extra_font_files += MTLmr3m.ttf +endif # !EXTENDED_FONT_FOOTPRINT -droidsans_fallback_src := DroidSansFallbackFull.ttf -extra_font_files := \ - DroidSans.ttf \ - DroidSans-Bold.ttf \ - MTLmr3m.ttf endif # SMALLER_FONT_FOOTPRINT ################################ diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml index c2d5afef55bc..1eaae657e7eb 100644 --- a/data/fonts/fallback_fonts.xml +++ b/data/fonts/fallback_fonts.xml @@ -219,6 +219,26 @@ </family> <family> <fileset> + <file lang="zh-CN">NotoSansHans-Regular.otf</file> + </fileset> + </family> + <family> + <fileset> + <file lang="zh-TW">NotoSansHant-Regular.otf</file> + </fileset> + </family> + <family> + <fileset> + <file lang="ja">NotoSansJP-Regular.otf</file> + </fileset> + </family> + <family> + <fileset> + <file lang="ko">NotoSansKR-Regular.otf</file> + </fileset> + </family> + <family> + <fileset> <file>NanumGothic.ttf</file> </fileset> </family> diff --git a/docs/html/google/auth/api-client.jd b/docs/html/google/auth/api-client.jd index 402a95f980d1..5331d1ed9ab0 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 6ff00c076138..b4028bd265fc 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 94f6715a9469..a73f688ec04f 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 744e191595fe..4332953ab307 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 @@ page.title=Set Up Google Play Services SDK <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 @@ page.title=Set Up Google Play Services SDK </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 @@ The download includes the client library and code samples.</p> 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> - -<p>To install the Google Play services SDK for development:</p> -<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">Add Google Play Services to Your Project</h2> +<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> -<h2 id="Setup">Set Up a Project that Uses Google Play Services</h2> -<p><b>Using Android Studio:</b></p> +<div class="select-ide studio"> <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 @@ element: <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 @@ element: 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 @@ about the error and provides an action 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/index.jd b/docs/html/index.jd index 606baf80e309..de2980ba9f10 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" /> diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd index 40618a34fb35..2fa029fb9029 100644 --- a/docs/html/preview/api-overview.jd +++ b/docs/html/preview/api-overview.jd @@ -15,7 +15,9 @@ sdk.platform.apiLevel=20 <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="#BehaviorMediaControl">If your app uses RemoteControlClient...</a></li> <li><a href="#BehaviorFullscreen">If your app uses fullScreenIntent...</a></li> <li><a href="#BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</a></li> </ol> @@ -23,9 +25,10 @@ sdk.platform.apiLevel=20 <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 +44,7 @@ sdk.platform.apiLevel=20 </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,7 +58,7 @@ sdk.platform.apiLevel=20 <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> @@ -71,7 +74,7 @@ sdk.platform.apiLevel=20 </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,130 @@ Differences Report »</a> </li> </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, -<a href="{@docRoot}preview/setup-sdk.html">give the L Developer Preview a -try</a> and send us your feedback!</p> +<p>The L Developer Preview is intended for <strong>developer early adopters</strong> and +<strong>testers</strong>. If you are interested in influencing the direction of the +Android framework, <a href="{@docRoot}preview/setup-sdk.html">give the L +Developer Preview a try</a> and send us your feedback!</p> -<p class="caution"><strong>Caution:</strong>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> - -<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> +notifications look right with the new color scheme:</p> + +<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> <h3 id="BehaviorMediaControl">If your app uses RemoteControlClient...</h3> -<p>Lockscreens in L will not show transport controls for your +<p>Lockscreens in the L Developer Preview do not show transport controls for your {@link android.media.RemoteControlClient}. Instead, your app can provide media playback control from the lockscreen through a media notification. This 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>Call {@code +Notification.Builder.setVisibility(Notification.VISIBILITY_PUBLIC)} to mark a +notification as safe to display on the lockscreen (even when the lockscreen is +secured with a PIN, pattern, or password). For more information, see +<a href="#LockscreenNotifications">Lockscreen Notifications</a>.</p> <h3 id="BehaviorFullscreen">If your app uses fullScreenIntent...</h3> <p>Notifications now appear in a small floating window if all these conditions -are met: 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 +are met:</p> + +<ul> + <li>The user’s activity is in fullscreen mode,</li> + <li>The screen is on, and</li> + <li>The device is unlocked</li> +</ul> + +<p>If your app implements fullscreen activities, make sure that these heads-up notifications are presented correctly.</p> <h3 id="BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</h3> -<p>With the introduction of the new 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 +230,15 @@ information.</p> <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 +246,9 @@ which feel natural and delightful to users. This support includes:</p> <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 +259,57 @@ content to be shown over a secure lockscreen.</p> 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> +on a secure lockscreen, create a public notification object and put a reference +to it in the private notification's {@code publicVersion} field.</li> <li>{@code VISIBILITY_PUBLIC}. Shows the notification’s full content. This is the system default if visibility is left unspecified.</li> <li>{@code VISIBILITY_SECRET}. Shows only the most minimal information, excluding even the notification’s icon.</li> </ul> +<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 +324,35 @@ people as being more important.</li> <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 +372,20 @@ the new features included in this release, see <a href="https://developer.chrome <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 +416,20 @@ ES 3.1. Key new functionality provided in OpenGL ES 3.1 includes:</p> </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 +439,17 @@ capture request. Now when the system completes the image capture request, your {@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 +458,8 @@ format as provided by {@code MediaCodec}. <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,14 +468,16 @@ media keys, and other events to the same ongoing 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> @@ -381,46 +489,58 @@ 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>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 +549,19 @@ BLE device.</p> 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>Conversely, if you want to scan for Bluetooth LE devices nearby, call -{@code android.bluetooth.le.BluetoothLeScanner.startScan()} and pass in an +<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> 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 +569,12 @@ flexible use of NFC:</p> <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 +588,31 @@ activity is in the foreground. <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 want to defer until the unit is + plugged in.</li> + <li>The app has a task that requires network access (or requires a wifi + 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> +</ul> <p>For example, you can add code like this to run your task on an unmetered network:</p> @@ -513,23 +648,33 @@ statistical data about battery usage on a device, organized by unique user ID </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/battery_historian@2x.png 2x" + alt="" width="440" height="240" + id="figure1" /> +<p class="img-caption"> + <strong>Figure 1.</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 +693,70 @@ $ historian.par [-p powerfile] bugreport.txt > out.html </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/managed_apps_launcher@2x.png 2x" + alt="" width="360" height="572" id="figure2" /> + <p class="img-caption"> + <strong>Figure 2.</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> <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 +additional parameters if you only want to convert a portion of the document into a bitmap image (for example, to implement <a href="http://en.wikipedia.org/wiki/Tiled_rendering">tile rendering</a> in order to zoom in on the document).</p> <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 +766,45 @@ and {@code android.app.UiAutomation.getWindowContentFrameStats()} methods to 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}">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 Binary files differnew file mode 100644 index 000000000000..5b0db74cb87e --- /dev/null +++ b/docs/html/preview/images/battery_historian.png diff --git a/docs/html/preview/images/battery_historian@2x.png b/docs/html/preview/images/battery_historian@2x.png Binary files differnew file mode 100644 index 000000000000..dbb5d5eb5ff8 --- /dev/null +++ b/docs/html/preview/images/battery_historian@2x.png diff --git a/docs/html/preview/images/managed_apps_launcher.png b/docs/html/preview/images/managed_apps_launcher.png Binary files differnew file mode 100644 index 000000000000..983d90460353 --- /dev/null +++ b/docs/html/preview/images/managed_apps_launcher.png diff --git a/docs/html/preview/images/managed_apps_launcher@2.png b/docs/html/preview/images/managed_apps_launcher@2.png Binary files differnew file mode 100644 index 000000000000..d298fd2deb28 --- /dev/null +++ b/docs/html/preview/images/managed_apps_launcher@2.png diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd index 84a2d85a6d83..e44e9f30a0d1 100644 --- a/docs/html/preview/index.jd +++ b/docs/html/preview/index.jd @@ -3,7 +3,8 @@ page.viewport_width=970 fullpage=true no_footer_links=true page.type=about - +page.metaDescription=Test and build your apps against the next version of Android to ensure they're ready when the platform officially launches. +page.image={@docRoot}preview/images/hero.jpg @jd:body <style> @@ -22,19 +23,6 @@ page.type=about } </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"> @@ -93,10 +81,10 @@ page.type=about <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. + material design, the new Google-wide standard. </p> <p class="landing-small"> - <a href="/preview/quantum/index.html">Learn about</a> + <a href="/preview/material/index.html">Learn about material</a> </p> </div> <div class="col-4"> @@ -108,7 +96,7 @@ page.type=about </p> <p class="landing-small"> - <a href="/preview/api-overview.html#art">Learn more</a> + <a href="/preview/api-overview.html#ART">Learn about ART</a> </p> </div> <div class="col-4"> @@ -119,7 +107,7 @@ page.type=about how they look, and automatic syncing to non-handheld devices. </p> <p class="landing-small"> - <a href="/preview/api-overview#graphics">Learn more</a> + <a href="/preview/api-overview.html#UI">Learn more</a> </p> </div> <div class="col-4"> @@ -130,7 +118,7 @@ page.type=about to give you more control over resource usage. </p> <p class="landing-small"> - <a href="/preview/api-overview#multimedia.html">Learn more</a> + <a href="/preview/api-overview.html#Power">Learn more</a> </p> </div> </div> @@ -174,13 +162,13 @@ page.type=about <img class="landing-social-image" src="{@docRoot}preview/images/bugs.png" alt=""> </a> <div class="landing-social-copy"> - <p>Submit Bugs</p> + <p>Issue Tracker</p> <p class="landing-small"> Let us know when you encounter problems, so we can fix them and make the platform better for you and your users. </p><p class="landing-small"> <a target="_blank" href="http://submit-bugs!"> - Submit Bugs</a> + Report Issues</a> </p> <p></p> </div> @@ -190,13 +178,13 @@ page.type=about <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>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> + Discuss on Google+</a> </p> </div> </div> @@ -205,14 +193,14 @@ page.type=about <img class="landing-social-image" src="{@docRoot}preview/images/updates.png" alt=""> </a> <div class="landing-social-copy"> - <p>Get Updates</p> + <p>Support and Updates</p> <p class="landing-small"> Updates to the L Developer Preview are delivered - in the Android SDK Manager. Check back here + in the Android SDK Manager. Check back periodically for news about the changes. </p> <p class="landing-small"> - <a target="_blank" href="{@docRoot}preview/release-notes.html">See Release Notes</a> + <a target="_blank" href="{@docRoot}preview/support.html">Get Support</a> </p> </div> </div> @@ -236,8 +224,6 @@ page.type=about License</a>. </div> </div> - - </div> <!-- end landing-body-content --> <script> diff --git a/docs/html/preview/material/animations.jd b/docs/html/preview/material/animations.jd index 8297c65d2d94..cee782a0289b 100644 --- a/docs/html/preview/material/animations.jd +++ b/docs/html/preview/material/animations.jd @@ -2,9 +2,22 @@ page.title=Animations @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 @@ APIs that let you customize these animations and create new ones:</p> </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 @@ it as the background of your view. You can define a <code>RippleDrawable</code> 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 @@ anim.start(); </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 @@ transitions of shared elements between activities.</p> <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 @@ object:</p> <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 @@ imgContainerView.setOnClickListener(new View.OnClickListener() { <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 @@ ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, </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 @@ mAnimator.start(); </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 @@ to define an <code>AnimatedStateListDrawable</code> as an XML resource:</p> </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 b5555ad2f552..ce04e9e8f8b3 100644 --- a/docs/html/preview/material/compatibility.jd +++ b/docs/html/preview/material/compatibility.jd @@ -2,22 +2,34 @@ page.title=Compatibility @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 @@ theme on devices running earlier versions of Android:</p> 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 @@ and your alternative layout files for earlier versions of Android inside <code>r 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 c527550484e0..9c0e55d6f0d6 100644 --- a/docs/html/preview/material/get-started.jd +++ b/docs/html/preview/material/get-started.jd @@ -2,135 +2,145 @@ page.title=Get Started @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>To apply the material theme in your app, specify a style that inherits from +<code>android:theme.Material</code>:</p> -<p>The new Material theme provides:</p> +<pre> +<!-- res/values/styles.xml --> +<resources> + <!-- your app's theme inherits from the Material theme --> + <style name="AppTheme" parent="android:Theme.Material"> + <!-- theme customizations --> + </style> +</resources> +</pre> -<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 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>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> +<h2 id="layouts">Design Your Layouts</h2> -<p>The Material theme is defined as:</p> +<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><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> +<li>Baseline grids</li> +<li>Keylines</li> +<li>Spacing</li> +<li>Touch target size</li> +<li>Layout structure</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>You still define layouts inside XML files using the standard tools from the Android framework. +For details on the material design guidelines, see the <a href="">material design +specification</a>.</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> +<h2 id="depth">Specify Depth in Your Views</h2> -<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> +<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> -<h3 style="margin-top:25px;">Customize the Status Bar</h3> +<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>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>For more details, see <a href="{@docRoot}preview/material/views-shadows.html">Views and +Shadows</a>.</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 id="widgets">Use the New UI Widgets</h2> -<h2 style="margin-top:35px">Material Design</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> -<p>In addition to applying the Material theme, you also have to:</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> -<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>For more information, see <a href="{@docRoot}preview/material/ui-widgets.html">UI Widgets</a>.</p> -<p>The Android L Developer Preview provides new attributes to make it easy to customize the -Material theme:</p> -<pre> -<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> - </style> -</resources> -</pre> +<h2 id="apis">Use the APIs to Customize Your Animations</h2> -<p>Ensure that you follow material design guidelines when choosing colors for your app.</p> +<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> -<p>Design your layouts according to the material design specification. In particular, pay -attention to:</p> +<pre> +// inside your activity +getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); -<ul> - <li>Baseline grids</li> - <li>Keylines</li> - <li>Spacing</li> - <li>Touch target size</li> - <li>Layout structure</li> -</ul> +// set an exit transition +getWindow().setExitTransition(new Explode()); +</pre> -<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 +<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 468e4bd9d6dd..b7abcb4db0de 100644 --- a/docs/html/preview/material/index.jd +++ b/docs/html/preview/material/index.jd @@ -19,9 +19,9 @@ design apps:</p> </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 @@ their color palette, and default animations for touch feedback and activity tran </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 @@ their color palette, and default animations for touch feedback and activity tran <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 @@ their color palette, and default animations for touch feedback and activity tran </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 @@ touch feedback in UI controls, view state changes, and activity transitions.</p> <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 000000000000..b954960b8979 --- /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 5c12a1af9692..f18bff9e52b9 100644 --- a/docs/html/preview/material/ui-widgets.jd +++ b/docs/html/preview/material/ui-widgets.jd @@ -2,13 +2,22 @@ page.title=UI Widgets @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 @@ performance by avoiding the creation of unnecessary views or performing expensiv <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 @@ public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { </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 Binary files differdeleted file mode 100644 index cd5a6a2aa3be..000000000000 --- a/docs/html/preview/material/videos/Dial.mp4 +++ /dev/null diff --git a/docs/html/preview/material/videos/Dial.ogv b/docs/html/preview/material/videos/Dial.ogv Binary files differdeleted file mode 100644 index b7b29d089a63..000000000000 --- a/docs/html/preview/material/videos/Dial.ogv +++ /dev/null diff --git a/docs/html/preview/material/videos/Dial.webm b/docs/html/preview/material/videos/Dial.webm Binary files differdeleted file mode 100644 index e30d2a563738..000000000000 --- a/docs/html/preview/material/videos/Dial.webm +++ /dev/null diff --git a/docs/html/preview/material/views-shadows.jd b/docs/html/preview/material/views-shadows.jd index 52fe83cce37d..c5884d65d2df 100644 --- a/docs/html/preview/material/views-shadows.jd +++ b/docs/html/preview/material/views-shadows.jd @@ -2,14 +2,24 @@ page.title=Views and Shadows @jd:body - -<p>In material design apps, depth has meaning. You should assign higher elevation values to more +<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 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 @@ static component, and the translation is used for animations:</p> <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 @@ myView.setOutline(outline); <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 8f6f8c198f0d..5920eccdbef0 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 000000000000..67404b682947 --- /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 065468503411..86e48458916e 100644 --- a/docs/html/preview/setup-devices.jd +++ b/docs/html/preview/setup-devices.jd @@ -26,12 +26,6 @@ Android system image is not subject to the same testing as the factory OS and 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 000000000000..23ce6ff32473 --- /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 000000000000..602439ff0804 --- /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 062968e4f19f..000000000000 --- 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 69e3e4e4f78e..000000000000 --- 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 b9de3a4bf051..000000000000 --- 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 Binary files differdeleted file mode 100644 index cd96164ca3bb..000000000000 --- a/docs/html/preview/tv/images/atv.png +++ /dev/null diff --git a/docs/html/preview/tv/images/browsefragment.png b/docs/html/preview/tv/images/browsefragment.png Binary files differdeleted file mode 100644 index 8998b136853c..000000000000 --- a/docs/html/preview/tv/images/browsefragment.png +++ /dev/null diff --git a/docs/html/preview/tv/images/detailsfragment.png b/docs/html/preview/tv/images/detailsfragment.png Binary files differdeleted file mode 100644 index 014ab231151e..000000000000 --- a/docs/html/preview/tv/images/detailsfragment.png +++ /dev/null diff --git a/docs/html/preview/tv/images/home-recommendations.png b/docs/html/preview/tv/images/home-recommendations.png Binary files differdeleted file mode 100644 index 2c188270a938..000000000000 --- a/docs/html/preview/tv/images/home-recommendations.png +++ /dev/null diff --git a/docs/html/preview/tv/index.jd b/docs/html/preview/tv/index.jd deleted file mode 100644 index da4098569480..000000000000 --- 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 f3b51bbd2eda..000000000000 --- 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 7f726bd97885..000000000000 --- 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 9d878b0679d1..000000000000 --- 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 7da3b5d56a4a..000000000000 --- 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 b37225431349..000000000000 --- 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 9513bc637a76..000000000000 --- 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 5655152c65d6..000000000000 --- 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 3041e588fa5d..000000000000 --- 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 52ac2e5ad8fe..000000000000 --- 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 c38c9277b8c6..e6c0118cca5c 100644 --- a/docs/html/sdk/installing/adding-packages.jd +++ b/docs/html/sdk/installing/adding-packages.jd @@ -14,7 +14,7 @@ ol.large > li { background:#eee; } ol.large > li:nth-child(odd) { -} +} ol.large > li:before { display:inline; left:-40px; @@ -74,9 +74,14 @@ style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li> </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 @@ style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li> <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 @@ style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li> <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 @@ style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li> <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 @@ style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li> <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 @@ style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li> <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 @@ style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li> </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 @@ style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li> </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 @@ the guide to <strong><a href="{@docRoot}training/basics/firstapp/index.html" <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 b6929bd31215..3671726f4a7a 100644 --- a/docs/html/sdk/installing/index.jd +++ b/docs/html/sdk/installing/index.jd @@ -108,13 +108,13 @@ style="float:right;font-size:13px"><a href='' onclick='showAll();return false;' 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 @@ the {@code sdk/} directory. For example:</p> <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 af6bd756a5ac..8ac61638b0a4 100644 --- a/docs/html/sdk/installing/studio.jd +++ b/docs/html/sdk/installing/studio.jd @@ -339,18 +339,12 @@ td.no { </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 @@ for possible resolutions to known issues: <a href="http://tools.android.com/know 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/tv/images/hero.jpg b/docs/html/tv/images/hero.jpg Binary files differindex c42a43617a9d..e95116726653 100644 --- a/docs/html/tv/images/hero.jpg +++ b/docs/html/tv/images/hero.jpg diff --git a/docs/html/tv/index.jd b/docs/html/tv/index.jd index e1cae8cb65c4..3e7652c733b7 100644 --- a/docs/html/tv/index.jd +++ b/docs/html/tv/index.jd @@ -4,7 +4,6 @@ fullpage=true no_footer_links=true page.type=about - @jd:body <style> @@ -14,17 +13,9 @@ page.type=about } </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 @@ page.type=about 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 @@ page.type=about </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 @@ page.type=about </div> </div> - <div class="landing-body"> <div class="landing-breakout cols"> @@ -119,13 +110,13 @@ page.type=about </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 @@ page.type=about 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 @@ page.type=about <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 @@ page.type=about 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 @@ page.type=about <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 @@ page.type=about <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-secondary"> 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-secondary"> Request ADT-1 Developer Kit </a> + </div> </div> </div> @@ -232,31 +222,33 @@ page.type=about </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 026698c95339..0d9325d1d971 100644 --- a/docs/html/wear/index.jd +++ b/docs/html/wear/index.jd @@ -51,7 +51,7 @@ page.type=about </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 @@ page.type=about </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 @@ page.type=about <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 @@ page.type=about </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 @@ page.type=about </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 @@ page.type=about <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 @@ page.type=about <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 @@ page.type=about </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 @@ page.type=about </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-secondary" style="margin-top: 20px;"> Get Started </a> </div> @@ -277,7 +225,7 @@ page.type=about </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 @@ page.type=about </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 @@ page.type=about 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 13789ca1c4e0..13421aa1dea0 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -65,11 +65,11 @@ public class Canvas { /** * Used to determine when compatibility scaling is in effect. - * + * * @hide */ protected int mScreenDensity = Bitmap.DENSITY_NONE; - + // Used by native code @SuppressWarnings("UnusedDeclaration") private int mSurfaceFormat; @@ -79,7 +79,7 @@ public class Canvas { * @hide */ public static final int DIRECTION_LTR = 0; - + /** * Flag for drawTextRun indicating right-to-left run direction. * @hide @@ -136,7 +136,7 @@ public class Canvas { /** * Construct a canvas with the specified bitmap to draw into. The bitmap * must be mutable. - * + * * <p>The initial target density of the canvas is the same as the given * bitmap's density. * @@ -177,10 +177,10 @@ public class Canvas { /** * Indicates whether this Canvas uses hardware acceleration. - * + * * Note that this method does not define what type of hardware acceleration * may or may not be used. - * + * * @return True if drawing operations are hardware accelerated, * false otherwise. */ @@ -189,7 +189,7 @@ public class Canvas { } /** - * Specify a bitmap for the canvas to draw into. All canvas state such as + * Specify a bitmap for the canvas to draw into. All canvas state such as * layers, filters, and the save/restore stack are reset with the exception * of the current matrix and clip stack. Additionally, as a side-effect * the canvas' target density is updated to match that of the bitmap. @@ -279,7 +279,7 @@ public class Canvas { * to determine the scaling factor when drawing a bitmap into it. * * @see #setDensity(int) - * @see Bitmap#getDensity() + * @see Bitmap#getDensity() */ public int getDensity() { return mDensity; @@ -295,7 +295,7 @@ public class Canvas { * {@link Bitmap#DENSITY_NONE} to disable bitmap scaling. * * @see #getDensity() - * @see Bitmap#setDensity(int) + * @see Bitmap#setDensity(int) */ public void setDensity(int density) { if (mBitmap != null) { @@ -313,19 +313,19 @@ public class Canvas { * Returns the maximum allowed width for bitmaps drawn with this canvas. * Attempting to draw with a bitmap wider than this value will result * in an error. - * - * @see #getMaximumBitmapHeight() + * + * @see #getMaximumBitmapHeight() */ public int getMaximumBitmapWidth() { return MAXMIMUM_BITMAP_SIZE; } - + /** * Returns the maximum allowed height for bitmaps drawn with this canvas. * Attempting to draw with a bitmap taller than this value will result * in an error. - * - * @see #getMaximumBitmapWidth() + * + * @see #getMaximumBitmapWidth() */ public int getMaximumBitmapHeight() { return MAXMIMUM_BITMAP_SIZE; @@ -357,8 +357,8 @@ public class Canvas { /** clip against the layer's bounds */ public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10; /** restore everything when restore() is called */ - public static final int ALL_SAVE_FLAG = 0x1F; - + public static final int ALL_SAVE_FLAG = 0x1F; + /** * Saves the current matrix and clip onto a private stack. Subsequent * calls to translate,scale,rotate,skew,concat or clipRect,clipPath @@ -371,7 +371,7 @@ public class Canvas { public int save() { return native_save(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG); } - + /** * Based on saveFlags, can save the current matrix and clip onto a private * stack. Subsequent calls to translate,scale,rotate,skew,concat or @@ -589,25 +589,25 @@ public class Canvas { public void concat(@Nullable Matrix matrix) { if (matrix != null) native_concat(mNativeCanvasWrapper, matrix.native_instance); } - + /** * Completely replace the current matrix with the specified matrix. If the * matrix parameter is null, then the current matrix is reset to identity. - * + * * <strong>Note:</strong> it is recommended to use {@link #concat(Matrix)}, * {@link #scale(float, float)}, {@link #translate(float, float)} and * {@link #rotate(float)} instead of this method. * * @param matrix The matrix to replace the current matrix with. If it is * null, set the current matrix to identity. - * - * @see #concat(Matrix) + * + * @see #concat(Matrix) */ public void setMatrix(@Nullable Matrix matrix) { native_setMatrix(mNativeCanvasWrapper, matrix == null ? 0 : matrix.native_instance); } - + /** * Return, in ctm, the current transformation matrix. This does not alter * the matrix in the canvas, but just returns a copy of it. @@ -628,7 +628,7 @@ public class Canvas { getMatrix(m); return m; } - + /** * Modify the current clip with the specified rectangle. * @@ -677,7 +677,7 @@ public class Canvas { return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, Region.Op.INTERSECT.nativeInt); } - + /** * Modify the current clip with the specified rectangle, which is * expressed in local coordinates. @@ -744,7 +744,7 @@ public class Canvas { public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) { return native_clipPath(mNativeCanvasWrapper, path.ni(), op.nativeInt); } - + /** * Intersect the current clip with the specified path. * @@ -754,7 +754,7 @@ public class Canvas { public boolean clipPath(@NonNull Path path) { return clipPath(path, Region.Op.INTERSECT); } - + /** * Modify the current clip with the specified region. Note that unlike * clipRect() and clipPath() which transform their arguments by the @@ -789,11 +789,11 @@ public class Canvas { public boolean clipRegion(@NonNull Region region) { return clipRegion(region, Region.Op.INTERSECT); } - + public @Nullable DrawFilter getDrawFilter() { return mDrawFilter; } - + public void setDrawFilter(@Nullable DrawFilter filter) { long nativeFilter = 0; if (filter != null) { @@ -814,7 +814,7 @@ public class Canvas { * Antialiased: Treat edges by rounding-out, since they may be antialiased */ AA(1); - + EdgeType(int nativeInt) { this.nativeInt = nativeInt; } @@ -900,7 +900,7 @@ public class Canvas { public boolean getClipBounds(@Nullable Rect bounds) { return native_getClipBounds(mNativeCanvasWrapper, bounds); } - + /** * Retrieve the bounds of the current clip (in local coordinates). * @@ -911,7 +911,7 @@ public class Canvas { getClipBounds(r); return r; } - + /** * Fill the entire canvas' bitmap (restricted to the current clip) with the * specified RGB color, using srcover porterduff mode. @@ -968,7 +968,7 @@ public class Canvas { public void drawPaint(@NonNull Paint paint) { native_drawPaint(mNativeCanvasWrapper, paint.mNativePaint); } - + /** * Draw a series of points. Each point is centered at the coordinate * specified by pts[], and its diameter is specified by the paint's stroke @@ -1065,7 +1065,7 @@ public class Canvas { public void drawRect(@NonNull Rect r, @NonNull Paint paint) { drawRect(r.left, r.top, r.right, r.bottom, paint); } - + /** * Draw the specified Rect using the specified paint. The rectangle will @@ -1091,7 +1091,15 @@ public class Canvas { 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); } /** @@ -1111,15 +1119,15 @@ public class Canvas { /** * <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> * @@ -1133,10 +1141,34 @@ public class Canvas { */ 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); } @@ -1197,7 +1229,7 @@ public class Canvas { * @param patch The ninepatch object to render * @param dst The destination rectangle. * @param paint The paint to draw the bitmap with. may be null - * + * * @hide */ public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) { @@ -1220,7 +1252,7 @@ public class Canvas { /** * Draw the specified bitmap, with its top/left corner at (x,y), using * the specified paint, transformed by the current matrix. - * + * * <p>Note: if the paint contains a maskfilter that generates a mask which * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. @@ -1230,7 +1262,7 @@ public class Canvas { * <p>If the bitmap and canvas have different densities, this function * will take care of automatically scaling the bitmap to draw at the * same density as the canvas. - * + * * @param bitmap The bitmap to be drawn * @param left The position of the left side of the bitmap being drawn * @param top The position of the top side of the bitmap being drawn @@ -1246,7 +1278,7 @@ public class Canvas { * Draw the specified bitmap, scaling/translating automatically to fill * the destination rectangle. If the source rectangle is not null, it * specifies the subset of the bitmap to draw. - * + * * <p>Note: if the paint contains a maskfilter that generates a mask which * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. @@ -1257,7 +1289,7 @@ public class Canvas { * This is because the source and destination rectangle coordinate * spaces are in their respective densities, so must already have the * appropriate scaling factor applied. - * + * * @param bitmap The bitmap to be drawn * @param src May be null. The subset of the bitmap to be drawn * @param dst The rectangle that the bitmap will be scaled/translated @@ -1278,7 +1310,7 @@ public class Canvas { * Draw the specified bitmap, scaling/translating automatically to fill * the destination rectangle. If the source rectangle is not null, it * specifies the subset of the bitmap to draw. - * + * * <p>Note: if the paint contains a maskfilter that generates a mask which * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. @@ -1289,7 +1321,7 @@ public class Canvas { * This is because the source and destination rectangle coordinate * spaces are in their respective densities, so must already have the * appropriate scaling factor applied. - * + * * @param bitmap The bitmap to be drawn * @param src May be null. The subset of the bitmap to be drawn * @param dst The rectangle that the bitmap will be scaled/translated @@ -1305,7 +1337,7 @@ public class Canvas { native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst, paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity); } - + /** * Treat the specified array of colors as a bitmap, and draw it. This gives * the same result as first creating a bitmap from the array, and then @@ -1394,7 +1426,7 @@ public class Canvas { throw new ArrayIndexOutOfBoundsException(); } } - + /** * Draw the bitmap through the mesh, where mesh vertices are evenly * distributed across the bitmap. There are meshWidth+1 vertices across, and @@ -1446,7 +1478,7 @@ public class Canvas { TRIANGLES(0), TRIANGLE_STRIP(1), TRIANGLE_FAN(2); - + VertexMode(int nativeInt) { this.nativeInt = nativeInt; } @@ -1456,7 +1488,7 @@ public class Canvas { */ public final int nativeInt; } - + /** * Draw the array of vertices, interpreted as triangles (based on mode). The * verts array is required, and specifies the x,y pairs for each vertex. If @@ -1485,7 +1517,7 @@ public class Canvas { * @param indices If not null, array of indices to reference into the * vertex (texs, colors) array. * @param indexCount number of entries in the indices array (if not null). - * @param paint Specifies the shader to use if the texs array is non-null. + * @param paint Specifies the shader to use if the texs array is non-null. */ public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts, int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors, @@ -1595,7 +1627,7 @@ public class Canvas { * bidi on the provided text, but renders it as a uniform right-to-left or * left-to-right run, as indicated by dir. Alignment of the text is as * determined by the Paint's TextAlign value. - * + * * @param text the text to render * @param index the start of the text to render * @param count the count of chars to render @@ -1606,13 +1638,12 @@ public class Canvas { * + count. * @param x the x position at which to draw the text * @param y the y position at which to draw the text - * @param dir the run direction, either {@link #DIRECTION_LTR} or - * {@link #DIRECTION_RTL}. + * @param isRtl whether the run is in RTL direction * @param paint the paint * @hide */ public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex, - int contextCount, float x, float y, int dir, @NonNull Paint paint) { + int contextCount, float x, float y, boolean isRtl, @NonNull Paint paint) { if (text == null) { throw new NullPointerException("text is null"); @@ -1623,12 +1654,9 @@ public class Canvas { if ((index | count | text.length - index - count) < 0) { throw new IndexOutOfBoundsException(); } - if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) { - throw new IllegalArgumentException("unknown dir: " + dir); - } native_drawTextRun(mNativeCanvasWrapper, text, index, count, - contextIndex, contextCount, x, y, dir, paint.mNativePaint, paint.mNativeTypeface); + contextIndex, contextCount, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface); } /** @@ -1644,12 +1672,12 @@ public class Canvas { * position can be used for shaping context. * @param x the x position at which to draw the text * @param y the y position at which to draw the text - * @param dir the run direction, either 0 for LTR or 1 for RTL. + * @param isRtl whether the run is in RTL direction * @param paint the paint * @hide */ public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart, - int contextEnd, float x, float y, int dir, @NonNull Paint paint) { + int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) { if (text == null) { throw new NullPointerException("text is null"); @@ -1661,22 +1689,20 @@ public class Canvas { throw new IndexOutOfBoundsException(); } - int flags = dir == 0 ? 0 : 1; - if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { native_drawTextRun(mNativeCanvasWrapper, text.toString(), start, end, - contextStart, contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface); + contextStart, contextEnd, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawTextRun(this, start, end, - contextStart, contextEnd, x, y, flags, paint); + contextStart, contextEnd, x, y, isRtl, paint); } else { int contextLen = contextEnd - contextStart; int len = end - start; char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, contextStart, contextEnd, buf, 0); native_drawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len, - 0, contextLen, x, y, flags, paint.mNativePaint, paint.mNativeTypeface); + 0, contextLen, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface); TemporaryBuffer.recycle(buf); } } @@ -1684,7 +1710,7 @@ public class Canvas { /** * Draw the text in the array, with each character's origin specified by * the pos array. - * + * * This method does not support glyph composition and decomposition and * should therefore not be used to render complex scripts. * @@ -1708,7 +1734,7 @@ public class Canvas { /** * Draw the text in the array, with each character's origin specified by * the pos array. - * + * * This method does not support glyph composition and decomposition and * should therefore not be used to render complex scripts. * @@ -1744,7 +1770,7 @@ public class Canvas { } native_drawTextOnPath(mNativeCanvasWrapper, text, index, count, path.ni(), hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } /** @@ -1764,7 +1790,7 @@ public class Canvas { float vOffset, @NonNull Paint paint) { if (text.length() > 0) { native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } } @@ -1776,7 +1802,7 @@ public class Canvas { * <p> * <strong>Note:</strong> This forces the picture to internally call * {@link Picture#endRecording} in order to prepare for playback. - * + * * @param picture The picture to be drawn */ public void drawPicture(@NonNull Picture picture) { @@ -1785,7 +1811,7 @@ public class Canvas { picture.draw(this); restoreToCount(restoreCount); } - + /** * Draw the picture, stretched to fit into the dst rectangle. */ @@ -1798,7 +1824,7 @@ public class Canvas { drawPicture(picture); restore(); } - + /** * Draw the picture, stretched to fit into the dst rectangle. */ @@ -1914,14 +1940,14 @@ public class Canvas { 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,11 +2003,11 @@ public class Canvas { private static native void native_drawTextRun(long nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, - float x, float y, int flags, long nativePaint, long nativeTypeface); + float x, float y, boolean isRtl, long nativePaint, long nativeTypeface); private static native void native_drawTextRun(long nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, - float x, float y, int flags, long nativePaint, long nativeTypeface); + float x, float y, boolean isRtl, long nativePaint, long nativeTypeface); private static native void native_drawPosText(long nativeCanvas, char[] text, int index, @@ -1995,11 +2021,11 @@ public class Canvas { int count, long nativePath, float hOffset, float vOffset, int bidiFlags, - long nativePaint); + long nativePaint, long nativeTypeface); private static native void native_drawTextOnPath(long nativeCanvas, String text, long nativePath, float hOffset, float vOffset, - int flags, long nativePaint); + int flags, long nativePaint, long nativeTypeface); private static native void finalizer(long nativeCanvas); } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 4268a244fd2a..17ce0269867e 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -56,7 +56,7 @@ public class Paint { * @hide */ public int mBidiFlags = BIDI_DEFAULT_LTR; - + static final Style[] sStyleArray = { Style.FILL, Style.STROKE, Style.FILL_AND_STROKE }; @@ -202,14 +202,14 @@ public class Paint { /** * Bidi flag to set LTR paragraph direction. - * + * * @hide */ public static final int BIDI_LTR = 0x0; /** * Bidi flag to set RTL paragraph direction. - * + * * @hide */ public static final int BIDI_RTL = 0x1; @@ -217,7 +217,7 @@ public class Paint { /** * Bidi flag to detect paragraph direction via heuristics, defaulting to * LTR. - * + * * @hide */ public static final int BIDI_DEFAULT_LTR = 0x2; @@ -225,21 +225,21 @@ public class Paint { /** * Bidi flag to detect paragraph direction via heuristics, defaulting to * RTL. - * + * * @hide */ public static final int BIDI_DEFAULT_RTL = 0x3; /** * Bidi flag to override direction to all LTR (ignore bidi). - * + * * @hide */ public static final int BIDI_FORCE_LTR = 0x4; /** * Bidi flag to override direction to all RTL (ignore bidi). - * + * * @hide */ public static final int BIDI_FORCE_RTL = 0x5; @@ -331,7 +331,7 @@ public class Paint { * either FILL or STROKE. */ FILL_AND_STROKE (2); - + Style(int nativeInt) { this.nativeInt = nativeInt; } @@ -357,7 +357,7 @@ public class Paint { * of the path. */ SQUARE (2); - + private Cap(int nativeInt) { this.nativeInt = nativeInt; } @@ -381,7 +381,7 @@ public class Paint { * The outer edges of a join meet with a straight line */ BEVEL (2); - + private Join(int nativeInt) { this.nativeInt = nativeInt; } @@ -405,7 +405,7 @@ public class Paint { * The text is drawn to the left of the x,y origin */ RIGHT (2); - + private Align(int nativeInt) { this.nativeInt = nativeInt; } @@ -418,7 +418,7 @@ public class Paint { public Paint() { this(0); } - + /** * Create a new paint with the specified flags. Use setFlags() to change * these after the paint is created. @@ -475,7 +475,7 @@ public class Paint { setTextLocale(Locale.getDefault()); setElegantTextHeight(false); } - + /** * Copy the fields from src into this paint. This is equivalent to calling * get() on all of the src fields, and calling the corresponding set() @@ -529,7 +529,7 @@ public class Paint { /** * Return the bidi flags on the paint. - * + * * @return the bidi flags on the paint * @hide */ @@ -552,7 +552,7 @@ public class Paint { /** * Return the paint's flags. Use the Flag enum to test flag values. - * + * * @return the paint's flags (see enums ending in _Flag for bit masks) */ public native int getFlags(); @@ -587,7 +587,7 @@ public class Paint { public final boolean isAntiAlias() { return (getFlags() & ANTI_ALIAS_FLAG) != 0; } - + /** * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit * AntiAliasing smooths out the edges of what is being drawn, but is has @@ -597,7 +597,7 @@ public class Paint { * @param aa true to set the antialias bit in the flags, false to clear it */ public native void setAntiAlias(boolean aa); - + /** * Helper for getFlags(), returning true if DITHER_FLAG bit is set * Dithering affects how colors that are higher precision than the device @@ -611,7 +611,7 @@ public class Paint { public final boolean isDither() { return (getFlags() & DITHER_FLAG) != 0; } - + /** * Helper for setFlags(), setting or clearing the DITHER_FLAG bit * Dithering affects how colors that are higher precision than the device @@ -623,7 +623,7 @@ public class Paint { * @param dither true to set the dithering bit in flags, false to clear it */ public native void setDither(boolean dither); - + /** * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set * @@ -649,7 +649,7 @@ public class Paint { public final boolean isSubpixelText() { return (getFlags() & SUBPIXEL_TEXT_FLAG) != 0; } - + /** * Helper for setFlags(), setting or clearing the SUBPIXEL_TEXT_FLAG bit * @@ -657,7 +657,7 @@ public class Paint { * flags, false to clear it. */ public native void setSubpixelText(boolean subpixelText); - + /** * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set * @@ -708,7 +708,7 @@ public class Paint { * flags, false to clear it. */ public native void setFakeBoldText(boolean fakeBoldText); - + /** * Whether or not the bitmap filter is activated. * Filtering affects the sampling of bitmaps when they are transformed. @@ -720,13 +720,13 @@ public class Paint { public final boolean isFilterBitmap() { return (getFlags() & FILTER_BITMAP_FLAG) != 0; } - + /** * Helper for setFlags(), setting or clearing the FILTER_BITMAP_FLAG bit. * Filtering affects the sampling of bitmaps when they are transformed. * Filtering does not affect how the colors in the bitmap are converted into * device pixels. That is dependent on dithering and xfermodes. - * + * * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's * flags, false to clear it. */ @@ -773,7 +773,7 @@ public class Paint { * @param color The new color (including alpha) to set in the paint. */ public native void setColor(int color); - + /** * Helper to getColor() that just returns the color's alpha value. This is * the same as calling getColor() >>> 24. It always returns a value between @@ -1285,7 +1285,7 @@ public class Paint { */ public static class FontMetrics { /** - * The maximum distance above the baseline for the tallest glyph in + * The maximum distance above the baseline for the tallest glyph in * the font at a given text size. */ public float top; @@ -1298,7 +1298,7 @@ public class Paint { */ public float descent; /** - * The maximum distance below the baseline for the lowest glyph in + * The maximum distance below the baseline for the lowest glyph in * the font at a given text size. */ public float bottom; @@ -1307,7 +1307,7 @@ public class Paint { */ public float leading; } - + /** * Return the font's recommended interline spacing, given the Paint's * settings for typeface, textSize, etc. If metrics is not null, return the @@ -1318,7 +1318,7 @@ public class Paint { * @return the font's recommended interline spacing. */ public native float getFontMetrics(FontMetrics metrics); - + /** * Allocates a new FontMetrics object, and then calls getFontMetrics(fm) * with it, returning the object. @@ -1328,7 +1328,7 @@ public class Paint { getFontMetrics(fm); return fm; } - + /** * Convenience method for callers that want to have FontMetrics values as * integers. @@ -1339,7 +1339,7 @@ public class Paint { public int descent; public int bottom; public int leading; - + @Override public String toString() { return "FontMetricsInt: top=" + top + " ascent=" + ascent + " descent=" + descent + " bottom=" + bottom + @@ -1364,7 +1364,7 @@ public class Paint { getFontMetricsInt(fm); return fm; } - + /** * Return the recommend line spacing based on the current typeface and * text size. @@ -1407,7 +1407,7 @@ public class Paint { } private native float native_measureText(char[] text, int index, int count, int bidiFlags); - + /** * Return the width of the text. * @@ -1439,7 +1439,7 @@ public class Paint { } private native float native_measureText(String text, int start, int end, int bidiFlags); - + /** * Return the width of the text. * @@ -1466,7 +1466,7 @@ public class Paint { } private native float native_measureText(String text, int bidiFlags); - + /** * Return the width of the text. * @@ -1503,7 +1503,7 @@ public class Paint { TemporaryBuffer.recycle(buf); return result; } - + /** * Measure the text, stopping early if the measured width exceeds maxWidth. * Return the number of chars that were measured, and if measuredWidth is @@ -1532,20 +1532,22 @@ public class Paint { 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 @@ public class Paint { 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); /** @@ -1738,7 +1742,7 @@ public class Paint { if (end - start > widths.length) { throw new ArrayIndexOutOfBoundsException(); } - + if (text.length() == 0 || start == end) { return 0; } @@ -1755,7 +1759,7 @@ public class Paint { } return res; } - + /** * Return the advance widths for the characters in the string. * @@ -1816,15 +1820,12 @@ public class Paint { * @hide */ public float getTextRunAdvances(char[] chars, int index, int count, - int contextIndex, int contextCount, int flags, float[] advances, + int contextIndex, int contextCount, boolean isRtl, float[] advances, int advancesIndex) { if (chars == null) { throw new IllegalArgumentException("text cannot be null"); } - if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) { - throw new IllegalArgumentException("unknown flags value: " + flags); - } if ((index | count | contextIndex | contextCount | advancesIndex | (index - contextIndex) | (contextCount - count) | ((contextIndex + contextCount) - (index + count)) @@ -1839,13 +1840,13 @@ public class Paint { } if (!mHasCompatScaling) { return native_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count, - contextIndex, contextCount, flags, advances, advancesIndex); + contextIndex, contextCount, isRtl, advances, advancesIndex); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); float res = native_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count, - contextIndex, contextCount, flags, advances, advancesIndex); + contextIndex, contextCount, isRtl, advances, advancesIndex); setTextSize(oldSize); if (advances != null) { @@ -1864,7 +1865,7 @@ public class Paint { * @hide */ public float getTextRunAdvances(CharSequence text, int start, int end, - int contextStart, int contextEnd, int flags, float[] advances, + int contextStart, int contextEnd, boolean isRtl, float[] advances, int advancesIndex) { if (text == null) { @@ -1880,16 +1881,16 @@ public class Paint { if (text instanceof String) { return getTextRunAdvances((String) text, start, end, - contextStart, contextEnd, flags, advances, advancesIndex); + contextStart, contextEnd, isRtl, advances, advancesIndex); } if (text instanceof SpannedString || text instanceof SpannableString) { return getTextRunAdvances(text.toString(), start, end, - contextStart, contextEnd, flags, advances, advancesIndex); + contextStart, contextEnd, isRtl, advances, advancesIndex); } if (text instanceof GraphicsOperations) { return ((GraphicsOperations) text).getTextRunAdvances(start, end, - contextStart, contextEnd, flags, advances, advancesIndex, this); + contextStart, contextEnd, isRtl, advances, advancesIndex, this); } if (text.length() == 0 || end == start) { return 0f; @@ -1900,7 +1901,7 @@ public class Paint { char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, contextStart, contextEnd, buf, 0); float result = getTextRunAdvances(buf, start - contextStart, len, - 0, contextLen, flags, advances, advancesIndex); + 0, contextLen, isRtl, advances, advancesIndex); TemporaryBuffer.recycle(buf); return result; } @@ -1937,8 +1938,7 @@ public class Paint { * must be <= start * @param contextEnd the index past the last character to use for shaping context, * must be >= end - * @param flags the flags to control the advances, either {@link #DIRECTION_LTR} - * or {@link #DIRECTION_RTL} + * @param isRtl whether the run is in RTL direction * @param advances array to receive the advances, must have room for all advances, * can be null if only total advance is needed * @param advancesIndex the position in advances at which to put the @@ -1948,14 +1948,11 @@ public class Paint { * @hide */ public float getTextRunAdvances(String text, int start, int end, int contextStart, - int contextEnd, int flags, float[] advances, int advancesIndex) { + int contextEnd, boolean isRtl, float[] advances, int advancesIndex) { if (text == null) { throw new IllegalArgumentException("text cannot be null"); } - if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) { - throw new IllegalArgumentException("unknown flags value: " + flags); - } if ((start | end | contextStart | contextEnd | advancesIndex | (end - start) | (start - contextStart) | (contextEnd - end) | (text.length() - contextEnd) @@ -1970,13 +1967,13 @@ public class Paint { if (!mHasCompatScaling) { return native_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end, - contextStart, contextEnd, flags, advances, advancesIndex); + contextStart, contextEnd, isRtl, advances, advancesIndex); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); float totalAdvance = native_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end, - contextStart, contextEnd, flags, advances, advancesIndex); + contextStart, contextEnd, isRtl, advances, advancesIndex); setTextSize(oldSize); if (advances != null) { @@ -2005,7 +2002,7 @@ public class Paint { * @param text the text * @param contextStart the start of the context * @param contextLength the length of the context - * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR} + * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR} * @param offset the cursor position to move from * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER}, * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE}, @@ -2014,7 +2011,7 @@ public class Paint { * @hide */ public int getTextRunCursor(char[] text, int contextStart, int contextLength, - int flags, int offset, int cursorOpt) { + int dir, int offset, int cursorOpt) { int contextEnd = contextStart + contextLength; if (((contextStart | contextEnd | offset | (contextEnd - contextStart) | (offset - contextStart) | (contextEnd - offset) @@ -2024,7 +2021,7 @@ public class Paint { } return native_getTextRunCursor(mNativePaint, text, - contextStart, contextLength, flags, offset, cursorOpt); + contextStart, contextLength, dir, offset, cursorOpt); } /** @@ -2045,7 +2042,7 @@ public class Paint { * @param text the text * @param contextStart the start of the context * @param contextEnd the end of the context - * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR} + * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR} * @param offset the cursor position to move from * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER}, * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE}, @@ -2054,22 +2051,22 @@ public class Paint { * @hide */ public int getTextRunCursor(CharSequence text, int contextStart, - int contextEnd, int flags, int offset, int cursorOpt) { + int contextEnd, int dir, int offset, int cursorOpt) { if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { return getTextRunCursor(text.toString(), contextStart, contextEnd, - flags, offset, cursorOpt); + dir, offset, cursorOpt); } if (text instanceof GraphicsOperations) { return ((GraphicsOperations) text).getTextRunCursor( - contextStart, contextEnd, flags, offset, cursorOpt, this); + contextStart, contextEnd, dir, offset, cursorOpt, this); } int contextLen = contextEnd - contextStart; char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, contextStart, contextEnd, buf, 0); - int result = getTextRunCursor(buf, 0, contextLen, flags, offset - contextStart, cursorOpt); + int result = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt); TemporaryBuffer.recycle(buf); return result; } @@ -2092,7 +2089,7 @@ public class Paint { * @param text the text * @param contextStart the start of the context * @param contextEnd the end of the context - * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR} + * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR} * @param offset the cursor position to move from * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER}, * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE}, @@ -2101,7 +2098,7 @@ public class Paint { * @hide */ public int getTextRunCursor(String text, int contextStart, int contextEnd, - int flags, int offset, int cursorOpt) { + int dir, int offset, int cursorOpt) { if (((contextStart | contextEnd | offset | (contextEnd - contextStart) | (offset - contextStart) | (contextEnd - offset) | (text.length() - contextEnd) | cursorOpt) < 0) @@ -2110,7 +2107,7 @@ public class Paint { } return native_getTextRunCursor(mNativePaint, text, - contextStart, contextEnd, flags, offset, cursorOpt); + contextStart, contextEnd, dir, offset, cursorOpt); } /** @@ -2156,7 +2153,7 @@ public class Paint { native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y, path.ni()); } - + /** * Return in bounds (allocated by the caller) the smallest rectangle that * encloses all of the characters, with an implied origin at (0,0). @@ -2176,7 +2173,7 @@ public class Paint { } nativeGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds); } - + /** * Return in bounds (allocated by the caller) the smallest rectangle that * encloses all of the characters, with an implied origin at (0,0). @@ -2197,7 +2194,7 @@ public class Paint { nativeGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, bounds); } - + @Override protected void finalize() throws Throwable { try { @@ -2252,15 +2249,15 @@ public class Paint { private static native float native_getTextRunAdvances(long native_object, long native_typeface, char[] text, int index, int count, int contextIndex, int contextCount, - int flags, float[] advances, int advancesIndex); + boolean isRtl, float[] advances, int advancesIndex); private static native float native_getTextRunAdvances(long native_object, long native_typeface, String text, int start, int end, int contextStart, int contextEnd, - int flags, float[] advances, int advancesIndex); + boolean isRtl, float[] advances, int advancesIndex); private native int native_getTextRunCursor(long native_object, char[] text, - int contextStart, int contextLength, int flags, int offset, int cursorOpt); + int contextStart, int contextLength, int dir, int offset, int cursorOpt); private native int native_getTextRunCursor(long native_object, String text, - int contextStart, int contextEnd, int flags, int offset, int cursorOpt); + int contextStart, int contextEnd, int dir, int offset, int cursorOpt); private static native void native_getTextPath(long native_object, long native_typeface, int bidiFlags, char[] text, int index, int count, float x, float y, long path); diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java index c600f470bd92..c40a66da174f 100644 --- a/graphics/java/android/graphics/Path.java +++ b/graphics/java/android/graphics/Path.java @@ -62,7 +62,7 @@ public class Path { } 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 @@ public class Path { * 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 @@ public class Path { * 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 class Path { */ 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 @@ public class Path { * @param sweepAngle Sweep angle (in degrees) measured clockwise */ public void arcTo(RectF oval, float startAngle, float sweepAngle) { + 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, oval, startAngle, sweepAngle, 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 @@ public class Path { 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 @@ public class Path { * @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 @@ public class Path { * @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"); - } + 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, rect, rx, ry, dir.nativeInt); + 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 @@ public class Path { 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 @@ public class Path { 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 a0211653c4c1..5aa7c6a8df52 100644 --- a/graphics/java/android/graphics/Picture.java +++ b/graphics/java/android/graphics/Picture.java @@ -31,18 +31,13 @@ public class Picture { private Canvas mRecordingCanvas; private final long mNativePicture; - /** - * @hide - */ - public final boolean createdFromStream; - private static final int WORKING_STREAM_STORAGE = 16 * 1024; /** * Creates an empty picture that is ready to record. */ public Picture() { - this(nativeConstructor(0), false); + this(nativeConstructor(0)); } /** @@ -51,7 +46,23 @@ public class Picture { * changes will not be reflected in this picture. */ public Picture(Picture src) { - this(nativeConstructor(src != null ? src.mNativePicture : 0), false); + this(nativeConstructor(src != null ? src.mNativePicture : 0)); + } + + private Picture(long nativePicture) { + if (nativePicture == 0) { + throw new RuntimeException(); + } + mNativePicture = nativePicture; + } + + @Override + protected void finalize() throws Throwable { + try { + nativeDestructor(mNativePicture); + } finally { + super.finalize(); + } } /** @@ -85,13 +96,17 @@ public class Picture { * Get the width of the picture as passed to beginRecording. This * does not reflect (per se) the content of the picture. */ - public native int getWidth(); + public int getWidth() { + return nativeGetWidth(mNativePicture); + } /** * Get the height of the picture as passed to beginRecording. This * does not reflect (per se) the content of the picture. */ - public native int getHeight(); + public int getHeight() { + return nativeGetHeight(mNativePicture); + } /** * Draw this picture on the canvas. @@ -130,7 +145,7 @@ public class Picture { */ @Deprecated public static Picture createFromStream(InputStream stream) { - return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]), true); + return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE])); } /** @@ -159,32 +174,12 @@ public class Picture { } } - protected void finalize() throws Throwable { - try { - nativeDestructor(mNativePicture); - } finally { - super.finalize(); - } - } - - final long ni() { - return mNativePicture; - } - - private Picture(long nativePicture, boolean fromStream) { - if (nativePicture == 0) { - throw new RuntimeException(); - } - mNativePicture = nativePicture; - createdFromStream = fromStream; - } - // return empty picture if src is 0, or a copy of the native src private static native long nativeConstructor(long nativeSrcOr0); - private static native long nativeCreateFromStream(InputStream stream, - byte[] storage); - private static native long nativeBeginRecording(long nativeCanvas, - int w, int h); + private static native long nativeCreateFromStream(InputStream stream, byte[] storage); + private static native int nativeGetWidth(long nativePicture); + private static native int nativeGetHeight(long nativePicture); + private static native long nativeBeginRecording(long nativeCanvas, int w, int h); private static native void nativeEndRecording(long nativeCanvas); private static native void nativeDraw(long nativeCanvas, long nativePicture); private static native boolean nativeWriteToStream(long nativePicture, @@ -201,18 +196,15 @@ public class Picture { @Override public void setBitmap(Bitmap bitmap) { - throw new RuntimeException( - "Cannot call setBitmap on a picture canvas"); + throw new RuntimeException("Cannot call setBitmap on a picture canvas"); } @Override public void drawPicture(Picture picture) { if (mPicture == picture) { - throw new RuntimeException( - "Cannot draw a picture into its recording canvas"); + throw new RuntimeException("Cannot draw a picture into its recording canvas"); } super.drawPicture(picture); } } } - diff --git a/graphics/java/android/graphics/drawable/Animatable.java b/graphics/java/android/graphics/drawable/Animatable.java index 9dc62c36d618..4edfad4fd7dd 100644 --- a/graphics/java/android/graphics/drawable/Animatable.java +++ b/graphics/java/android/graphics/drawable/Animatable.java @@ -32,7 +32,7 @@ public interface Animatable { /** * Indicates whether the animation is running. - * + * * @return True if the animation is running, false otherwise. */ boolean isRunning(); diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java index e8024f7864f0..0fd442382dd6 100644 --- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java @@ -19,6 +19,8 @@ package android.graphics.drawable; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.ColorFilter; +import android.graphics.PorterDuff.Mode; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.Resources.Theme; @@ -88,6 +90,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac canvas.restoreToCount(saveCount); } + @Override public void start() { if (!mRunning) { mRunning = true; @@ -95,11 +98,13 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } } + @Override public void stop() { mRunning = false; unscheduleSelf(this); } + @Override public boolean isRunning() { return mRunning; } @@ -108,10 +113,11 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac unscheduleSelf(this); scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration); } - + + @Override public void run() { // TODO: This should be computed in draw(Canvas), based on the amount - // of time since the last frame drawn + // of time since the last frame drawn mCurrentDegrees += mIncrement; if (mCurrentDegrees > (360.0f - mIncrement)) { mCurrentDegrees = 0.0f; @@ -119,7 +125,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac invalidateSelf(); nextFrame(); } - + @Override public boolean setVisible(boolean visible, boolean restart) { mState.mDrawable.setVisible(visible, restart); @@ -133,8 +139,8 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac unscheduleSelf(this); } return changed; - } - + } + /** * Returns the drawable rotated by this RotateDrawable. */ @@ -148,7 +154,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac | mState.mChangingConfigurations | mState.mDrawable.getChangingConfigurations(); } - + @Override public void setAlpha(int alpha) { mState.mDrawable.setAlpha(alpha); @@ -165,10 +171,16 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } @Override + public void setTint(ColorStateList tint, Mode tintMode) { + mState.mDrawable.setTint(tint, tintMode); + } + + @Override public int getOpacity() { return mState.mDrawable.getOpacity(); } + @Override public void invalidateDrawable(Drawable who) { final Callback callback = getCallback(); if (callback != null) { @@ -176,6 +188,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } } + @Override public void scheduleDrawable(Drawable who, Runnable what, long when) { final Callback callback = getCallback(); if (callback != null) { @@ -183,6 +196,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } } + @Override public void unscheduleDrawable(Drawable who, Runnable what) { final Callback callback = getCallback(); if (callback != null) { @@ -194,7 +208,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac public boolean getPadding(Rect padding) { return mState.mDrawable.getPadding(padding); } - + @Override public boolean isStateful() { return mState.mDrawable.isStateful(); @@ -206,6 +220,16 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } @Override + protected boolean onLevelChange(int level) { + return mState.mDrawable.setLevel(level); + } + + @Override + protected boolean onStateChange(int[] state) { + return mState.mDrawable.setState(state); + } + + @Override public int getIntrinsicWidth() { return mState.mDrawable.getIntrinsicWidth(); } @@ -231,11 +255,11 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable); super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible); - + TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX); final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION; final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); - + tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY); final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION; final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); @@ -250,7 +274,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } a.recycle(); - + int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && @@ -306,7 +330,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac Drawable mDrawable; int mChangingConfigurations; - + boolean mPivotXRel; float mPivotX; boolean mPivotYRel; @@ -315,7 +339,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac int mFramesCount; private boolean mCanConstantState; - private boolean mCheckedConstantState; + private boolean mCheckedConstantState; public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner, Resources res) { @@ -341,12 +365,12 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac public Drawable newDrawable() { return new AnimatedRotateDrawable(this, null); } - + @Override public Drawable newDrawable(Resources res) { return new AnimatedRotateDrawable(this, res); } - + @Override public int getChangingConfigurations() { return mChangingConfigurations; diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java index 1ee022490e7b..074076151d56 100644 --- a/graphics/java/android/graphics/drawable/AnimationDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java @@ -28,7 +28,6 @@ import android.os.SystemClock; import android.util.AttributeSet; /** - * * An object used to create frame-by-frame animations, defined by a series of Drawable objects, * which can be used as a View object's background. * <p> @@ -114,6 +113,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An * @see #isRunning() * @see #stop() */ + @Override public void start() { if (!isRunning()) { run(); @@ -127,6 +127,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An * @see #isRunning() * @see #start() */ + @Override public void stop() { if (isRunning()) { unscheduleSelf(this); @@ -138,6 +139,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An * * @return true if the animation is running, false otherwise */ + @Override public boolean isRunning() { return mAnimating; } @@ -148,6 +150,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An * * @see #start() */ + @Override public void run() { nextFrame(false); } @@ -165,41 +168,41 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An public int getNumberOfFrames() { return mAnimationState.getChildCount(); } - + /** * @return The Drawable at the specified frame index */ public Drawable getFrame(int index) { return mAnimationState.getChild(index); } - + /** - * @return The duration in milliseconds of the frame at the + * @return The duration in milliseconds of the frame at the * specified index */ public int getDuration(int i) { return mAnimationState.mDurations[i]; } - + /** * @return True of the animation will play once, false otherwise */ public boolean isOneShot() { return mAnimationState.mOneShot; } - + /** * Sets whether the animation should play once or repeat. - * + * * @param oneShot Pass true if the animation should only play once */ public void setOneShot(boolean oneShot) { mAnimationState.mOneShot = oneShot; } - + /** * Add a frame to the animation - * + * * @param frame The frame to add * @param duration How long in milliseconds the frame should appear */ @@ -209,7 +212,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An setFrame(0, true, false); } } - + private void nextFrame(boolean unschedule) { int next = mCurFrame+1; final int N = mAnimationState.getChildCount(); @@ -239,21 +242,21 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { - + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.AnimationDrawable); super.inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.AnimationDrawable_visible); - + mAnimationState.setVariablePadding(a.getBoolean( com.android.internal.R.styleable.AnimationDrawable_variablePadding, false)); - + mAnimationState.mOneShot = a.getBoolean( com.android.internal.R.styleable.AnimationDrawable_oneshot, false); - + a.recycle(); - + int type; final int innerDepth = parser.getDepth()+1; @@ -267,7 +270,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An if (depth > innerDepth || !parser.getName().equals("item")) { continue; } - + a = r.obtainAttributes(attrs, com.android.internal.R.styleable.AnimationDrawableItem); int duration = a.getInt( com.android.internal.R.styleable.AnimationDrawableItem_duration, -1); @@ -278,9 +281,9 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An } int drawableRes = a.getResourceId( com.android.internal.R.styleable.AnimationDrawableItem_drawable, 0); - + a.recycle(); - + Drawable dr; if (drawableRes != 0) { dr = r.getDrawable(drawableRes); @@ -295,7 +298,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An } dr = Drawable.createFromXmlInner(r, parser, attrs, theme); } - + mAnimationState.addFrame(dr, duration); if (dr != null) { dr.setCallback(this); @@ -342,7 +345,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An } public void addFrame(Drawable dr, int dur) { - // Do not combine the following. The array index must be evaluated before + // Do not combine the following. The array index must be evaluated before // the array is accessed because super.addChild(dr) has a side effect on mDurations. int pos = super.addChild(dr); mDurations[pos] = dur; diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index ef6c0859f666..0a394d56d322 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -31,6 +31,7 @@ import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuff.Mode; +import android.graphics.drawable.ColorDrawable.ColorState; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.Shader; @@ -618,9 +619,11 @@ public class BitmapDrawable extends Drawable { @Override public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) { - mBitmapState.mTint = tint; - mBitmapState.mTintMode = tintMode; - computeTintFilter(); + final BitmapState state = mBitmapState; + state.mTint = tint; + state.mTintMode = tintMode; + + mTintFilter = updateTintFilter(mTintFilter, tint, tintMode); invalidateSelf(); } @@ -638,21 +641,6 @@ public class BitmapDrawable extends Drawable { return mBitmapState.mTintMode; } - private void computeTintFilter() { - final BitmapState state = mBitmapState; - if (state.mTint != null && state.mTintMode != null) { - final int color = state.mTint.getColorForState(getState(), 0); - if (mTintFilter != null) { - mTintFilter.setColor(color); - mTintFilter.setMode(state.mTintMode); - } else { - mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); - } - } else { - mTintFilter = null; - } - } - /** * @hide Candidate for future API inclusion */ @@ -679,17 +667,11 @@ public class BitmapDrawable extends Drawable { @Override protected boolean onStateChange(int[] stateSet) { - final ColorStateList tint = mBitmapState.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 BitmapState state = mBitmapState; + if (state.mTint != null && state.mTintMode != null) { + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); + return true; } - return false; } @@ -946,7 +928,7 @@ public class BitmapDrawable extends Drawable { mTargetDensity = state.mTargetDensity; } - computeTintFilter(); + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); computeBitmapSize(); } } diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java index 3ac99727b53e..174de3ae7c54 100644 --- a/graphics/java/android/graphics/drawable/ClipDrawable.java +++ b/graphics/java/android/graphics/drawable/ClipDrawable.java @@ -19,10 +19,12 @@ package android.graphics.drawable; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.Resources.Theme; import android.graphics.*; +import android.graphics.PorterDuff.Mode; import android.view.Gravity; import android.util.AttributeSet; @@ -52,7 +54,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { public static final int HORIZONTAL = 1; public static final int VERTICAL = 2; - + ClipDrawable() { this(null, null); } @@ -111,6 +113,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { // overrides from Drawable.Callback + @Override public void invalidateDrawable(Drawable who) { final Callback callback = getCallback(); if (callback != null) { @@ -118,6 +121,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { } } + @Override public void scheduleDrawable(Drawable who, Runnable what, long when) { final Callback callback = getCallback(); if (callback != null) { @@ -125,6 +129,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { } } + @Override public void unscheduleDrawable(Drawable who, Runnable what) { final Callback callback = getCallback(); if (callback != null) { @@ -169,6 +174,11 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { } @Override + public void setTint(ColorStateList tint, Mode tintMode) { + mClipState.mDrawable.setTint(tint, tintMode); + } + + @Override public int getOpacity() { return mClipState.mDrawable.getOpacity(); } @@ -197,7 +207,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { @Override public void draw(Canvas canvas) { - + if (mClipState.mDrawable.getLevel() == 0) { return; } diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java index df5ca33b9111..37161826e0ed 100644 --- a/graphics/java/android/graphics/drawable/ColorDrawable.java +++ b/graphics/java/android/graphics/drawable/ColorDrawable.java @@ -17,6 +17,8 @@ package android.graphics.drawable; import android.graphics.*; +import android.graphics.PorterDuff.Mode; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; @@ -39,9 +41,13 @@ import java.io.IOException; * @attr ref android.R.styleable#ColorDrawable_color */ public class ColorDrawable extends Drawable { + private final Paint mPaint = new Paint(); + @ViewDebug.ExportedProperty(deepExport = true, prefix = "state_") private ColorState mColorState; - private final Paint mPaint = new Paint(); + private ColorStateList mTint; + private PorterDuffColorFilter mTintFilter; + private boolean mMutated; /** @@ -84,9 +90,17 @@ public class ColorDrawable extends Drawable { @Override public void draw(Canvas canvas) { - if ((mColorState.mUseColor >>> 24) != 0) { + final ColorFilter colorFilter = mPaint.getColorFilter(); + if ((mColorState.mUseColor >>> 24) != 0 || colorFilter != null || mTintFilter != null) { + if (colorFilter == null) { + mPaint.setColorFilter(mTintFilter); + } + mPaint.setColor(mColorState.mUseColor); canvas.drawRect(getBounds(), mPaint); + + // Restore original color filter. + mPaint.setColorFilter(colorFilter); } } @@ -141,16 +155,51 @@ public class ColorDrawable extends Drawable { } /** - * Setting a color filter on a ColorDrawable has no effect. + * Sets the color filter applied to this color. + * <p> + * Only supported on version {@link android.os.Build.VERSION_CODES#L} and + * above. Calling this method has no effect on earlier versions. * - * @param colorFilter Ignore. + * @see android.graphics.drawable.Drawable#setColorFilter(ColorFilter) */ @Override public void setColorFilter(ColorFilter colorFilter) { + mPaint.setColorFilter(colorFilter); + } + + @Override + public void setTint(ColorStateList tint, Mode tintMode) { + final ColorState state = mColorState; + if (state.mTint != tint || state.mTintMode != tintMode) { + state.mTint = tint; + state.mTintMode = tintMode; + + mTintFilter = updateTintFilter(mTintFilter, tint, tintMode); + invalidateSelf(); + } + } + + @Override + protected boolean onStateChange(int[] stateSet) { + final ColorState state = mColorState; + if (state.mTint != null && state.mTintMode != null) { + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); + return true; + } + return false; + } + + @Override + public boolean isStateful() { + return mTint != null && mTint.isStateful(); } @Override public int getOpacity() { + if (mTintFilter != null || mPaint.getColorFilter() != null) { + return PixelFormat.TRANSLUCENT; + } + switch (mColorState.mUseColor >>> 24) { case 255: return PixelFormat.OPAQUE; @@ -165,8 +214,7 @@ public class ColorDrawable extends Drawable { throws XmlPullParserException, IOException { super.inflate(r, parser, attrs, theme); - final TypedArray a = obtainAttributes( - r, theme, attrs, R.styleable.ColorDrawable); + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable); inflateStateFromTypedArray(a); a.recycle(); } @@ -225,21 +273,25 @@ public class ColorDrawable extends Drawable { } final static class ColorState extends ConstantState { + int[] mThemeAttrs; int mBaseColor; // base color, independent of setAlpha() @ViewDebug.ExportedProperty int mUseColor; // basecolor modulated by setAlpha() int mChangingConfigurations; - int[] mThemeAttrs; + ColorStateList mTint; + Mode mTintMode; ColorState() { // Empty constructor. } ColorState(ColorState state) { + mThemeAttrs = state.mThemeAttrs; mBaseColor = state.mBaseColor; mUseColor = state.mUseColor; mChangingConfigurations = state.mChangingConfigurations; - mThemeAttrs = state.mThemeAttrs; + mTint = state.mTint; + mTintMode = state.mTintMode; } @Override @@ -276,6 +328,6 @@ public class ColorDrawable extends Drawable { mColorState = state; } - // No local properties to initialize. + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); } } diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 6a7757b912e7..40b55a7f0afe 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -17,13 +17,6 @@ package android.graphics.drawable; import android.annotation.NonNull; -import android.graphics.Insets; -import android.graphics.Xfermode; -import android.os.Trace; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; @@ -31,15 +24,19 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; import android.graphics.PixelFormat; import android.graphics.PorterDuff; +import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.Region; -import android.graphics.PorterDuff.Mode; +import android.graphics.Xfermode; +import android.os.Trace; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.StateSet; @@ -47,6 +44,9 @@ import android.util.TypedValue; import android.util.Xml; import android.view.View; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -1226,6 +1226,26 @@ public abstract class Drawable { } /** + * Ensures the tint filter is consistent with the current tint color and + * mode. + */ + PorterDuffColorFilter updateTintFilter(PorterDuffColorFilter tintFilter, ColorStateList tint, + PorterDuff.Mode tintMode) { + if (tint == null || tintMode == null) { + return null; + } + + final int color = tint.getColorForState(getState(), Color.TRANSPARENT); + if (tintFilter == null) { + return new PorterDuffColorFilter(color, tintMode); + } + + tintFilter.setColor(color); + tintFilter.setMode(tintMode); + return tintFilter; + } + + /** * Obtains styled attributes from the theme, if available, or unstyled * resources if the theme is null. */ @@ -1240,8 +1260,10 @@ public abstract class Drawable { /** * 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 2aef39fc3e52..8be6eb745c09 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -16,6 +16,7 @@ package android.graphics.drawable; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.graphics.Canvas; @@ -23,6 +24,7 @@ import android.graphics.ColorFilter; import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.graphics.PorterDuff.Mode; import android.os.SystemClock; import android.util.LayoutDirection; import android.util.SparseArray; @@ -151,7 +153,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @Override public void setColorFilter(ColorFilter cf) { - mDrawableContainerState.mHasColorFilter = true; + mDrawableContainerState.mHasColorFilter = (cf != null); if (mDrawableContainerState.mColorFilter != cf) { mDrawableContainerState.mColorFilter = cf; @@ -162,6 +164,20 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } } + @Override + public void setTint(ColorStateList tint, Mode tintMode) { + mDrawableContainerState.mHasTint = (tint != null && tintMode != null); + + if (mDrawableContainerState.mTint != tint || mDrawableContainerState.mTintMode != tintMode) { + mDrawableContainerState.mTint = tint; + mDrawableContainerState.mTintMode = tintMode; + + if (mCurrDrawable != null) { + mCurrDrawable.mutate().setTint(tint, tintMode); + } + } + } + /** * Change the global fade duration when a new drawable is entering * the scene. @@ -396,6 +412,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } if (mDrawableContainerState.mHasColorFilter) { d.setColorFilter(mDrawableContainerState.mColorFilter); + } else if (mDrawableContainerState.mHasTint) { + d.setTint(mDrawableContainerState.mTint, mDrawableContainerState.mTintMode); } d.setVisible(isVisible(), true); d.setDither(mDrawableContainerState.mDither); @@ -566,6 +584,10 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { ColorFilter mColorFilter; boolean mHasColorFilter; + ColorStateList mTint; + Mode mTintMode; + boolean mHasTint; + DrawableContainerState(DrawableContainerState orig, DrawableContainer owner, Resources res) { mOwner = owner; @@ -588,6 +610,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mAutoMirrored = orig.mAutoMirrored; mColorFilter = orig.mColorFilter; mHasColorFilter = orig.mHasColorFilter; + mTint = orig.mTint; + mTintMode = orig.mTintMode; + mHasTint = orig.mHasTint; // Cloning the following values may require creating futures. mConstantPadding = orig.getConstantPadding(); @@ -741,7 +766,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { final int N = mNumChildren; final Drawable[] drawables = mDrawables; for (int i = 0; i < N; i++) { - final Drawable d = drawables[i]; + final Drawable d = drawables[i]; if (d != null) { if (d.canApplyTheme()) { return true; diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java index 9e0ab86e18c5..220e81c5c7d1 100644 --- a/graphics/java/android/graphics/drawable/InsetDrawable.java +++ b/graphics/java/android/graphics/drawable/InsetDrawable.java @@ -19,10 +19,12 @@ package android.graphics.drawable; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.Resources.Theme; import android.graphics.*; +import android.graphics.PorterDuff.Mode; import android.util.AttributeSet; import android.util.Log; @@ -44,8 +46,7 @@ import java.io.IOException; * @attr ref android.R.styleable#InsetDrawable_insetTop * @attr ref android.R.styleable#InsetDrawable_insetBottom */ -public class InsetDrawable extends Drawable implements Drawable.Callback -{ +public class InsetDrawable extends Drawable implements Drawable.Callback { // Most of this is copied from ScaleDrawable. private InsetState mInsetState; private final Rect mTmpRect = new Rect(); @@ -62,13 +63,13 @@ public class InsetDrawable extends Drawable implements Drawable.Callback public InsetDrawable(Drawable drawable, int insetLeft, int insetTop, int insetRight, int insetBottom) { this(null, null); - + mInsetState.mDrawable = drawable; mInsetState.mInsetLeft = insetLeft; mInsetState.mInsetTop = insetTop; mInsetState.mInsetRight = insetRight; mInsetState.mInsetBottom = insetBottom; - + if (drawable != null) { drawable.setCallback(this); } @@ -78,7 +79,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { int type; - + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.InsetDrawable); @@ -168,7 +169,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback | mInsetState.mChangingConfigurations | mInsetState.mDrawable.getChangingConfigurations(); } - + @Override public boolean getPadding(Rect padding) { boolean pad = mInsetState.mDrawable.getPadding(padding); @@ -178,7 +179,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback padding.top += mInsetState.mInsetTop; padding.bottom += mInsetState.mInsetBottom; - if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight | + if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight | mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0) { return true; } else { @@ -217,6 +218,11 @@ public class InsetDrawable extends Drawable implements Drawable.Callback mInsetState.mDrawable.setColorFilter(cf); } + @Override + public void setTint(ColorStateList tint, Mode tintMode) { + mInsetState.mDrawable.setTint(tint, tintMode); + } + /** {@hide} */ @Override public void setLayoutDirection(int layoutDirection) { @@ -227,7 +233,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback public int getOpacity() { return mInsetState.mDrawable.getOpacity(); } - + @Override public boolean isStateful() { return mInsetState.mDrawable.isStateful(); @@ -239,7 +245,12 @@ public class InsetDrawable extends Drawable implements Drawable.Callback onBoundsChange(getBounds()); return changed; } - + + @Override + protected boolean onLevelChange(int level) { + return mInsetState.mDrawable.setLevel(level); + } + @Override protected void onBoundsChange(Rect bounds) { final Rect r = mTmpRect; @@ -321,12 +332,12 @@ public class InsetDrawable extends Drawable implements Drawable.Callback public Drawable newDrawable() { return new InsetDrawable(this, null); } - + @Override public Drawable newDrawable(Resources res) { return new InsetDrawable(this, res); } - + @Override public int getChangingConfigurations() { return mChangingConfigurations; diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 27f0a9d7c80c..5cea7c926304 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -16,12 +16,14 @@ package android.graphics.drawable; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; +import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; @@ -630,6 +632,15 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { } } + @Override + public void setTint(ColorStateList tint, Mode tintMode) { + final ChildDrawable[] array = mLayerState.mChildren; + final int N = mLayerState.mNum; + for (int i = 0; i < N; i++) { + array[i].mDrawable.setTint(tint, tintMode); + } + } + /** * Sets the opacity of this drawable directly, instead of collecting the * states from the layers diff --git a/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java b/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java index 9e56f67f4b23..c484094f0ff5 100644 --- a/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java +++ b/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java @@ -27,8 +27,10 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PorterDuffColorFilter; import android.graphics.Paint.Cap; import android.graphics.Paint.Style; +import android.graphics.PorterDuff.Mode; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; @@ -67,6 +69,7 @@ class MaterialProgressDrawable extends Drawable implements Animatable { private final Ring mRing; private MaterialProgressState mState; + private PorterDuffColorFilter mTintFilter; /** Canvas rotation in degrees. */ private float mRotation; @@ -106,6 +109,8 @@ class MaterialProgressDrawable extends Drawable implements Animatable { float insets = minEdge / 2.0f - state.mInnerRadius; ring.setInsets(insets); } + + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); } @Override @@ -118,15 +123,21 @@ class MaterialProgressDrawable extends Drawable implements Animatable { } @Override - protected boolean onStateChange(int[] state) { - boolean changed = super.onStateChange(state); + protected boolean onStateChange(int[] stateSet) { + boolean changed = super.onStateChange(stateSet); - final int color = mState.mColor.getColorForState(state, Color.TRANSPARENT); + final MaterialProgressState state = mState; + final int color = state.mColor.getColorForState(stateSet, Color.TRANSPARENT); if (color != mRing.getColor()) { mRing.setColor(color); changed = true; } + if (state.mTint != null && state.mTintMode != null) { + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); + changed = true; + } + return changed; } @@ -223,11 +234,24 @@ class MaterialProgressDrawable extends Drawable implements Animatable { return mRing.getColorFilter(); } + @Override + public void setTint(ColorStateList tint, Mode tintMode) { + if (mState.mTint != tint || mState.mTintMode != tintMode) { + mState.mTint = tint; + mState.mTintMode = tintMode; + + mTintFilter = updateTintFilter(mTintFilter, tint, tintMode); + invalidateSelf(); + } + } + + @SuppressWarnings("unused") private void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } + @SuppressWarnings("unused") private float getRotation() { return mRotation; } @@ -331,6 +355,8 @@ class MaterialProgressDrawable extends Drawable implements Animatable { private int mWidth = -1; private int mHeight = -1; private ColorStateList mColor = ColorStateList.valueOf(Color.TRANSPARENT); + private ColorStateList mTint = null; + private Mode mTintMode = null; public MaterialProgressState(MaterialProgressState orig) { if (orig != null) { @@ -340,6 +366,8 @@ class MaterialProgressDrawable extends Drawable implements Animatable { mWidth = orig.mWidth; mHeight = orig.mHeight; mColor = orig.mColor; + mTint = orig.mTint; + mTintMode = orig.mTintMode; } } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index fea68ee1a4db..28335eabe082 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -327,25 +327,12 @@ public class NinePatchDrawable extends Drawable { @Override public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) { - mNinePatchState.mTint = tint; - mNinePatchState.mTintMode = tintMode; - computeTintFilter(); - invalidateSelf(); - } - - private void computeTintFilter() { final NinePatchState state = mNinePatchState; - if (state.mTint != null && state.mTintMode != null) { - final int color = state.mTint.getColorForState(getState(), 0); - if (mTintFilter != null) { - mTintFilter.setColor(color); - mTintFilter.setMode(state.mTintMode); - } else { - mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); - } - } else { - mTintFilter = null; - } + state.mTint = tint; + state.mTintMode = tintMode; + + mTintFilter = updateTintFilter(mTintFilter, tint, tintMode); + invalidateSelf(); } @Override @@ -549,15 +536,10 @@ public class NinePatchDrawable extends Drawable { @Override protected boolean onStateChange(int[] stateSet) { - final ColorStateList tint = mNinePatchState.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 NinePatchState state = mNinePatchState; + if (state.mTint != null && state.mTintMode != null) { + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); + return true; } return false; @@ -689,7 +671,7 @@ public class NinePatchDrawable extends Drawable { mPadding = new Rect(state.mPadding); } - computeTintFilter(); + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); setNinePatch(state.mNinePatch); } } diff --git a/graphics/java/android/graphics/drawable/PaintDrawable.java b/graphics/java/android/graphics/drawable/PaintDrawable.java index c71cda169737..a82e7b950edf 100644 --- a/graphics/java/android/graphics/drawable/PaintDrawable.java +++ b/graphics/java/android/graphics/drawable/PaintDrawable.java @@ -35,7 +35,7 @@ public class PaintDrawable extends ShapeDrawable { public PaintDrawable(int color) { getPaint().setColor(color); } - + /** * Specify radius for the corners of the rectangle. If this is > 0, then the * drawable is drawn in a round-rectangle, rather than a rectangle. @@ -51,7 +51,7 @@ public class PaintDrawable extends ShapeDrawable { } setCornerRadii(radii); } - + /** * Specify radii for each of the 4 corners. For each corner, the array * contains 2 values, [X_radius, Y_radius]. The corners are ordered @@ -78,9 +78,9 @@ public class PaintDrawable extends ShapeDrawable { int radius = a.getDimensionPixelSize( com.android.internal.R.styleable.DrawableCorners_radius, 0); setCornerRadius(radius); - + // now check of they have any per-corner radii - + int topLeftRadius = a.getDimensionPixelSize( com.android.internal.R.styleable.DrawableCorners_topLeftRadius, radius); int topRightRadius = a.getDimensionPixelSize( diff --git a/graphics/java/android/graphics/drawable/PictureDrawable.java b/graphics/java/android/graphics/drawable/PictureDrawable.java index cb2d8f67a4ba..6dcda1f56590 100644 --- a/graphics/java/android/graphics/drawable/PictureDrawable.java +++ b/graphics/java/android/graphics/drawable/PictureDrawable.java @@ -25,7 +25,7 @@ import android.graphics.Rect; /** * Drawable subclass that wraps a Picture, allowing the picture to be used - * whereever a Drawable is supported. + * wherever a Drawable is supported. */ public class PictureDrawable extends Drawable { @@ -40,7 +40,7 @@ public class PictureDrawable extends Drawable { public PictureDrawable(Picture picture) { mPicture = picture; } - + /** * Return the picture associated with the drawable. May be null. * @@ -49,7 +49,7 @@ public class PictureDrawable extends Drawable { public Picture getPicture() { return mPicture; } - + /** * Associate a picture with this drawable. The picture may be null. * @@ -58,7 +58,7 @@ public class PictureDrawable extends Drawable { public void setPicture(Picture picture) { mPicture = picture; } - + @Override public void draw(Canvas canvas) { if (mPicture != null) { @@ -86,16 +86,16 @@ public class PictureDrawable extends Drawable { // not sure, so be safe return PixelFormat.TRANSLUCENT; } - + @Override public void setFilterBitmap(boolean filter) {} - + @Override public void setDither(boolean dither) {} - + @Override public void setColorFilter(ColorFilter colorFilter) {} - + @Override public void setAlpha(int alpha) {} } diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java index 06aeb983b2e3..8f8fa98c8acd 100644 --- a/graphics/java/android/graphics/drawable/RotateDrawable.java +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -22,6 +22,8 @@ import org.xmlpull.v1.XmlPullParserException; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Rect; +import android.graphics.PorterDuff.Mode; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.Resources.Theme; @@ -137,6 +139,11 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { } @Override + public void setTint(ColorStateList tint, Mode tintMode) { + mState.mDrawable.setTint(tint, tintMode); + } + + @Override public int getOpacity() { return mState.mDrawable.getOpacity(); } diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java index f090c11bd13e..46c92fe82dfb 100644 --- a/graphics/java/android/graphics/drawable/ScaleDrawable.java +++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java @@ -19,10 +19,12 @@ package android.graphics.drawable; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.Resources.Theme; import android.graphics.*; +import android.graphics.PorterDuff.Mode; import android.view.Gravity; import android.util.AttributeSet; @@ -188,6 +190,11 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { } @Override + public void setTint(ColorStateList tint, Mode tintMode) { + mScaleState.mDrawable.setTint(tint, tintMode); + } + + @Override public int getOpacity() { return mScaleState.mDrawable.getOpacity(); } diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java index 024f77cf2e7f..beb300d5789d 100644 --- a/graphics/java/android/graphics/drawable/ShapeDrawable.java +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -24,6 +24,7 @@ import android.graphics.ColorFilter; 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; @@ -32,6 +33,7 @@ import android.graphics.drawable.shapes.Shape; import android.content.res.Resources.Theme; import android.util.AttributeSet; +import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -74,7 +76,7 @@ public class ShapeDrawable extends Drawable { * ShapeDrawable constructor. */ public ShapeDrawable() { - this((ShapeState) null); + this(new ShapeState(null), null, null); } /** @@ -83,20 +85,11 @@ public class ShapeDrawable extends Drawable { * @param s the Shape that this ShapeDrawable should be */ public ShapeDrawable(Shape s) { - this((ShapeState) null); + this(new ShapeState(null), null, null); mShapeState.mShape = s; } - private ShapeDrawable(ShapeState state) { - mShapeState = new ShapeState(state); - - if (state != null && state.mTint != null) { - final int color = state.mTint.getColorForState(getState(), 0); - mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); - } - } - /** * Returns the Shape of this ShapeDrawable. */ @@ -292,31 +285,13 @@ public class ShapeDrawable extends Drawable { } @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 @@ -357,17 +332,11 @@ public class ShapeDrawable extends Drawable { @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; } @@ -408,20 +377,8 @@ public class ShapeDrawable extends Drawable { throws XmlPullParserException, IOException { super.inflate(r, parser, attrs, theme); - TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ShapeDrawable); - - int color = mShapeState.mPaint.getColor(); - color = a.getColor(com.android.internal.R.styleable.ShapeDrawable_color, color); - mShapeState.mPaint.setColor(color); - - boolean dither = a.getBoolean(com.android.internal.R.styleable.ShapeDrawable_dither, false); - mShapeState.mPaint.setDither(dither); - - setIntrinsicWidth((int) - a.getDimension(com.android.internal.R.styleable.ShapeDrawable_width, 0f)); - setIntrinsicHeight((int) - a.getDimension(com.android.internal.R.styleable.ShapeDrawable_height, 0f)); - + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ShapeDrawable); + updateStateFromTypedArray(a); a.recycle(); int type; @@ -441,6 +398,38 @@ public class ShapeDrawable extends Drawable { } } + @Override + public void applyTheme(Theme t) { + super.applyTheme(t); + + final ShapeState state = mShapeState; + if (state == null || state.mThemeAttrs == null) { + return; + } + + final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ShapeDrawable); + updateStateFromTypedArray(a); + a.recycle(); + } + + private void updateStateFromTypedArray(TypedArray a) { + final ShapeState state = mShapeState; + final Paint paint = state.mPaint; + + int color = paint.getColor(); + color = a.getColor(R.styleable.ShapeDrawable_color, color); + paint.setColor(color); + + boolean dither = paint.isDither(); + dither = a.getBoolean(R.styleable.ShapeDrawable_dither, dither); + paint.setDither(dither); + + setIntrinsicWidth((int) a.getDimension( + R.styleable.ShapeDrawable_width, state.mIntrinsicWidth)); + setIntrinsicHeight((int) a.getDimension( + R.styleable.ShapeDrawable_height, state.mIntrinsicHeight)); + } + private void updateShape() { if (mShapeState.mShape != null) { final Rect r = getBounds(); @@ -498,6 +487,7 @@ public class ShapeDrawable extends Drawable { * Defines the intrinsic properties of this ShapeDrawable's Shape. */ final static class ShapeState extends ConstantState { + int[] mThemeAttrs; int mChangingConfigurations; Paint mPaint; Shape mShape; @@ -511,6 +501,7 @@ public class ShapeDrawable extends Drawable { ShapeState(ShapeState orig) { if (orig != null) { + mThemeAttrs = orig.mThemeAttrs; mPaint = orig.mPaint; mShape = orig.mShape; mTint = orig.mTint; @@ -526,13 +517,23 @@ public class ShapeDrawable extends Drawable { } @Override + public boolean canApplyTheme() { + return mThemeAttrs != null; + } + + @Override public Drawable newDrawable() { - return new ShapeDrawable(this); + return new ShapeDrawable(this, null, null); } @Override public Drawable newDrawable(Resources res) { - return new ShapeDrawable(this); + return new ShapeDrawable(this, res, null); + } + + @Override + public Drawable newDrawable(Resources res, Theme theme) { + return new ShapeDrawable(this, res, theme); } @Override @@ -541,6 +542,17 @@ public class ShapeDrawable extends Drawable { } } + private ShapeDrawable(ShapeState state, Resources res, Theme theme) { + if (theme != null && state.canApplyTheme()) { + mShapeState = new ShapeState(state); + applyTheme(theme); + } else { + mShapeState = state; + } + + mTintFilter = updateTintFilter(mTintFilter, mShapeState.mTint, mShapeState.mTintMode); + } + /** * Base class defines a factory object that is called each time the drawable * is resized (has a new width or height). Its resize() method returns a diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java index b2fac9bef74c..f359fdd0b475 100644 --- a/graphics/java/android/graphics/drawable/StateListDrawable.java +++ b/graphics/java/android/graphics/drawable/StateListDrawable.java @@ -235,10 +235,10 @@ public class StateListDrawable extends DrawableContainer { public Drawable getStateDrawable(int index) { return mStateListState.getChild(index); } - + /** * Gets the index of the drawable with the provided state set. - * + * * @param stateSet the state set to look up * @return the index of the provided state set, or -1 if not found * @hide pending API council @@ -248,7 +248,7 @@ public class StateListDrawable extends DrawableContainer { public int getStateDrawableIndex(int[] stateSet) { return mStateListState.indexOfStateSet(stateSet); } - + @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java index 622e90ba4c04..4380ca4e8145 100644 --- a/graphics/java/android/graphics/drawable/TransitionDrawable.java +++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java @@ -42,20 +42,20 @@ import android.os.SystemClock; public class TransitionDrawable extends LayerDrawable implements Drawable.Callback { /** - * A transition is about to start. + * A transition is about to start. */ private static final int TRANSITION_STARTING = 0; - + /** * The transition has started and the animation is in progress */ private static final int TRANSITION_RUNNING = 1; - + /** * No transition will be applied */ private static final int TRANSITION_NONE = 2; - + /** * The current state of the transition. One of {@link #TRANSITION_STARTING}, * {@link #TRANSITION_RUNNING} and {@link #TRANSITION_NONE} @@ -101,10 +101,10 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba LayerState createConstantState(LayerState state, Resources res) { return new TransitionState((TransitionState) state, this, res); } - + /** * Begin the second layer on top of the first layer. - * + * * @param durationMillis The length of the transition in milliseconds */ public void startTransition(int durationMillis) { @@ -116,7 +116,7 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba mTransitionState = TRANSITION_STARTING; invalidateSelf(); } - + /** * Show only the first layer. */ @@ -184,7 +184,7 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba } break; } - + final int alpha = mAlpha; final boolean crossFade = mCrossFade; final ChildDrawable[] array = mLayerState.mChildren; @@ -217,7 +217,7 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba d.draw(canvas); d.setAlpha(0xFF); } - + if (!done) { invalidateSelf(); } diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index c531c224e2e6..a1e1f7612a60 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -14,6 +14,7 @@ package android.graphics.drawable; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; @@ -25,8 +26,10 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.PixelFormat; +import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.PorterDuff.Mode; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; @@ -137,16 +140,23 @@ public class VectorDrawable extends Drawable { private final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>(); + private PorterDuffColorFilter mTintFilter; + public VectorDrawable() { mVectorState = new VectorDrawableState(null); } 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); + mVectorState.mVPathRenderer.setColorFilter(mTintFilter); } Object getTargetByName(String name) { @@ -182,11 +192,46 @@ public class VectorDrawable extends Drawable { @Override public void setColorFilter(ColorFilter colorFilter) { - mVectorState.mVPathRenderer.setColorFilter(colorFilter); + final VectorDrawableState state = mVectorState; + if (colorFilter != null) { + // Color filter overrides tint. + mTintFilter = null; + } else if (state.mTint != null && state.mTintMode != null) { + // Restore the tint filter, if we need one. + final int color = state.mTint.getColorForState(getState(), Color.TRANSPARENT); + mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); + colorFilter = mTintFilter; + } + + state.mVPathRenderer.setColorFilter(colorFilter); invalidateSelf(); } @Override + public void setTint(ColorStateList tint, Mode tintMode) { + final VectorDrawableState state = mVectorState; + if (state.mTint != tint || state.mTintMode != tintMode) { + state.mTint = tint; + state.mTintMode = tintMode; + + mTintFilter = updateTintFilter(mTintFilter, tint, tintMode); + mVectorState.mVPathRenderer.setColorFilter(mTintFilter); + invalidateSelf(); + } + } + + @Override + protected boolean onStateChange(int[] stateSet) { + final VectorDrawableState state = mVectorState; + if (state.mTint != null && state.mTintMode != null) { + mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); + mVectorState.mVPathRenderer.setColorFilter(mTintFilter); + return true; + } + return false; + } + + @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @@ -397,6 +442,8 @@ public class VectorDrawable extends Drawable { int mChangingConfigurations; VPathRenderer mVPathRenderer; Rect mPadding; + ColorStateList mTint; + Mode mTintMode; public VectorDrawableState(VectorDrawableState copy) { if (copy != null) { @@ -404,6 +451,8 @@ public class VectorDrawable extends Drawable { // TODO: Make sure the constant state are handled correctly. mVPathRenderer = new VPathRenderer(copy.mVPathRenderer); mPadding = new Rect(copy.mPadding); + mTint = copy.mTint; + mTintMode = copy.mTintMode; } } diff --git a/include/androidfw/ByteBucketArray.h b/include/androidfw/ByteBucketArray.h new file mode 100644 index 000000000000..87c6b128eca1 --- /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 4d8e512d1651..e612c0a92a51 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -237,6 +237,7 @@ enum { #define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF)) #define Res_MAXPACKAGE 255 +#define Res_MAXTYPE 255 /** * Representation of a value in a resource, supplying type @@ -510,6 +511,23 @@ private: uint32_t mStylePoolSize; // number of uint32_t }; +/** + * Wrapper class that allows the caller to retrieve a string from + * a string pool without knowing which string pool to look. + */ +class StringPoolRef { +public: + StringPoolRef(); + StringPoolRef(const ResStringPool* pool, uint32_t index); + + const char* string8(size_t* outLen) const; + const char16_t* string16(size_t* outLen) const; + +private: + const ResStringPool* mPool; + uint32_t mIndex; +}; + /** ******************************************************************** * XML Tree * @@ -835,6 +853,8 @@ struct ResTable_package // Last index into keyStrings that is for public use by others. uint32_t lastPublicKey; + + uint32_t typeIdOffset; }; // The most specific locale can consist of: @@ -1469,9 +1489,13 @@ public: bool copyData=false); ~ResTable(); - status_t add(Asset* asset, const int32_t cookie, bool copyData, - const void* idmap = NULL); - status_t add(const void *data, size_t size); + status_t add(const void* data, size_t size, const int32_t cookie=-1, bool copyData=false); + status_t add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, + const int32_t cookie=-1, bool copyData=false); + + status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false); + status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false); + status_t add(ResTable* src); status_t addEmpty(const int32_t cookie); @@ -1610,13 +1634,14 @@ public: uint32_t typeSpecFlags; Res_value value; }; + struct type_info { size_t numEntries; theme_entry* entries; }; + struct package_info { - size_t numTypes; - type_info types[]; + type_info types[Res_MAXTYPE + 1]; }; void free_package(package_info* pi); @@ -1711,6 +1736,7 @@ public: size_t getBasePackageCount() const; const String16 getBasePackageName(size_t idx) const; uint32_t getBasePackageId(size_t idx) const; + uint32_t getLastTypeIdForPackage(size_t idx) const; // Return the number of resource tables that the object contains. size_t getTableCount() const; @@ -1740,13 +1766,15 @@ public: void** outData, size_t* outSize) const; enum { - IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t) + 2 * 256, + IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256, }; + // Retrieve idmap meta-data. // // This function only requires the idmap header (the first // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file. static bool getIdmapInfo(const void* idmap, size_t size, + uint32_t* pVersion, uint32_t* pTargetCrc, uint32_t* pOverlayCrc, String8* pTargetPath, String8* pOverlayPath); @@ -1756,21 +1784,24 @@ public: private: struct Header; struct Type; + struct Entry; struct Package; struct PackageGroup; struct bag_set; + typedef Vector<Type*> TypeList; - status_t addInternal(const void* data, size_t size, const int32_t cookie, - bool copyData, const Asset* idmap); + status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, + const int32_t cookie, bool copyData); ssize_t getResourcePackageIndex(uint32_t resID) const; - ssize_t getEntry( - const Package* package, int typeIndex, int entryIndex, + + status_t getEntry( + const PackageGroup* packageGroup, int typeIndex, int entryIndex, const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const; + Entry* outEntry) const; + status_t parsePackage( - const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id); + const ResTable_package* const pkg, const Header* const header); void print_value(const Package* pkg, const Res_value& value) const; diff --git a/include/androidfw/TypeWrappers.h b/include/androidfw/TypeWrappers.h new file mode 100644 index 000000000000..7bdf8af0ad4c --- /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/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index d21197e3a673..957809d82d0e 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -25,6 +25,7 @@ commonSources := \ ObbFile.cpp \ ResourceTypes.cpp \ StreamingZipInflater.cpp \ + TypeWrappers.cpp \ ZipFileRO.cpp \ ZipUtils.cpp diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 91dda75026ab..03409286ea6c 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -256,7 +256,7 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie) String8 targetPath; String8 overlayPath; if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(), - NULL, NULL, &targetPath, &overlayPath)) { + NULL, NULL, NULL, &targetPath, &overlayPath)) { ALOGW("failed to read idmap file %s\n", idmapPath.string()); delete idmap; return false; @@ -311,7 +311,7 @@ bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApk ALOGW("failed to find resources.arsc in %s\n", ap.path.string()); return false; } - tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */); + tables[i].add(ass); } return tables[0].createIdmap(tables[1], targetCrc, overlayCrc, @@ -617,7 +617,7 @@ const ResTable* AssetManager::getResTable(bool required) const // can quickly copy it out for others. ALOGV("Creating shared resources for %s", ap.path.string()); sharedRes = new ResTable(); - sharedRes->add(ass, i + 1, false, idmap); + sharedRes->add(ass, idmap, i + 1, false); #ifdef HAVE_ANDROID_OS const char* data = getenv("ANDROID_DATA"); LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set"); @@ -646,7 +646,7 @@ const ResTable* AssetManager::getResTable(bool required) const mResources->add(sharedRes); } else { ALOGV("Parsing resources for %s", ap.path.string()); - mResources->add(ass, i + 1, !shared, idmap); + mResources->add(ass, idmap, i + 1, !shared); } onlyEmptyResources = false; @@ -654,7 +654,7 @@ const ResTable* AssetManager::getResTable(bool required) const delete ass; } } else { - ALOGW("Installing empty resources in to table %p\n", mResources); + ALOGV("Installing empty resources in to table %p\n", mResources); mResources->addEmpty(i + 1); } @@ -736,7 +736,7 @@ void AssetManager::addSystemOverlays(const char* pathOverlaysList, if (oass != NULL) { Asset* oidmap = openIdmapLocked(oap); offset++; - sharedRes->add(oass, offset + 1, false, oidmap); + sharedRes->add(oass, oidmap, offset + 1, false); const_cast<AssetManager*>(this)->mAssetPaths.add(oap); const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap); } @@ -1768,6 +1768,7 @@ bool AssetManager::fncScanAndMergeDirLocked( } mergeInfoLocked(pMergedInfo, pContents); + delete pContents; return true; } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index a4b78a605496..2e3abb53363c 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -17,7 +17,9 @@ #define LOG_TAG "ResourceType" //#define LOG_NDEBUG 0 +#include <androidfw/ByteBucketArray.h> #include <androidfw/ResourceTypes.h> +#include <androidfw/TypeWrappers.h> #include <utils/Atomic.h> #include <utils/ByteOrder.h> #include <utils/Debug.h> @@ -30,6 +32,7 @@ #include <memory.h> #include <ctype.h> #include <stdint.h> +#include <stddef.h> #ifndef INT32_MAX #define INT32_MAX ((int32_t)(2147483647)) @@ -42,7 +45,7 @@ #define TABLE_SUPER_NOISY(x) //x #define LOAD_TABLE_NOISY(x) //x #define TABLE_THEME(x) //x -#define LIB_NOISY(x) x +#define LIB_NOISY(x) //x namespace android { @@ -63,9 +66,8 @@ namespace android { #endif #endif -#define IDMAP_MAGIC 0x706d6469 -// size measured in sizeof(uint32_t) -#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) +#define IDMAP_MAGIC 0x504D4449 +#define IDMAP_CURRENT_VERSION 0x00000001 #define APP_PACKAGE_ID 0x7f #define SYS_PACKAGE_ID 0x01 @@ -77,6 +79,11 @@ inline int isspace16(char16_t c) { return (c < 0x0080 && isspace(c)); } +template<typename T> +inline static T max(T a, T b) { + return a > b ? a : b; +} + // range checked; guaranteed to NUL-terminate within the stated number of available slots // NOTE: if this truncates the dst string due to running out of space, no attempt is // made to avoid splitting surrogate pairs. @@ -215,104 +222,179 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData)); } -static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes) -{ - if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) { - ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes); +static bool assertIdmapHeader(const void* idmap, size_t size) { + if (reinterpret_cast<uintptr_t>(idmap) & 0x03) { + ALOGE("idmap: header is not word aligned"); + return false; + } + + if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) { + ALOGW("idmap: header too small (%d bytes)", (uint32_t) size); return false; } - if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess - ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n", - *map, htodl(IDMAP_MAGIC)); + + const uint32_t magic = htodl(*reinterpret_cast<const uint32_t*>(idmap)); + if (magic != IDMAP_MAGIC) { + ALOGW("idmap: no magic found in header (is 0x%08x, expected 0x%08x)", + magic, IDMAP_MAGIC); + return false; + } + + const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1)); + if (version != IDMAP_CURRENT_VERSION) { + // We are strict about versions because files with this format are + // auto-generated and don't need backwards compatibility. + ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)", + version, IDMAP_CURRENT_VERSION); return false; } return true; } -static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue) -{ - // see README for details on the format of map - if (!assertIdmapHeader(map, sizeBytes)) { - return UNKNOWN_ERROR; - } - map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment - // size of data block, in uint32_t - const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t); - const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id - const uint32_t entry = Res_GETENTRY(key); - const uint32_t typeCount = *map; +class IdmapEntries { +public: + IdmapEntries() : mData(NULL) {} - if (type > typeCount) { - ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount); - return UNKNOWN_ERROR; + bool hasEntries() const { + if (mData == NULL) { + return false; + } + + return (dtohs(*mData) > 0); } - if (typeCount > size) { - ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size); - return UNKNOWN_ERROR; + + size_t byteSize() const { + if (mData == NULL) { + return 0; + } + uint16_t entryCount = dtohs(mData[2]); + return (sizeof(uint16_t) * 4) + (sizeof(uint32_t) * static_cast<size_t>(entryCount)); } - const uint32_t typeOffset = map[type]; - if (typeOffset == 0) { - *outValue = 0; - return NO_ERROR; + + uint8_t targetTypeId() const { + if (mData == NULL) { + return 0; + } + return dtohs(mData[0]); } - if (typeOffset + 1 > size) { - ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", - typeOffset, (int)size); - return UNKNOWN_ERROR; + + uint8_t overlayTypeId() const { + if (mData == NULL) { + return 0; + } + return dtohs(mData[1]); } - const uint32_t entryCount = map[typeOffset]; - const uint32_t entryOffset = map[typeOffset + 1]; - if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) { - *outValue = 0; + + status_t setTo(const void* entryHeader, size_t size) { + if (reinterpret_cast<uintptr_t>(entryHeader) & 0x03) { + ALOGE("idmap: entry header is not word aligned"); + return UNKNOWN_ERROR; + } + + if (size < sizeof(uint16_t) * 4) { + ALOGE("idmap: entry header is too small (%u bytes)", (uint32_t) size); + return UNKNOWN_ERROR; + } + + const uint16_t* header = reinterpret_cast<const uint16_t*>(entryHeader); + const uint16_t targetTypeId = dtohs(header[0]); + const uint16_t overlayTypeId = dtohs(header[1]); + if (targetTypeId == 0 || overlayTypeId == 0 || targetTypeId > 255 || overlayTypeId > 255) { + ALOGE("idmap: invalid type map (%u -> %u)", targetTypeId, overlayTypeId); + return UNKNOWN_ERROR; + } + + uint16_t entryCount = dtohs(header[2]); + if (size < sizeof(uint32_t) * (entryCount + 2)) { + ALOGE("idmap: too small (%u bytes) for the number of entries (%u)", + (uint32_t) size, (uint32_t) entryCount); + return UNKNOWN_ERROR; + } + mData = header; return NO_ERROR; } - const uint32_t index = typeOffset + 2 + entry - entryOffset; - if (index > size) { - ALOGW("Resource ID map: entry index=%u exceeds size of map=%d\n", index, (int)size); - *outValue = 0; + + status_t lookup(uint16_t entryId, uint16_t* outEntryId) const { + uint16_t entryCount = dtohs(mData[2]); + uint16_t offset = dtohs(mData[3]); + + if (entryId < offset) { + // The entry is not present in this idmap + return BAD_INDEX; + } + + entryId -= offset; + + if (entryId >= entryCount) { + // The entry is not present in this idmap + return BAD_INDEX; + } + + // It is safe to access the type here without checking the size because + // we have checked this when it was first loaded. + const uint32_t* entries = reinterpret_cast<const uint32_t*>(mData) + 2; + uint32_t mappedEntry = dtohl(entries[entryId]); + if (mappedEntry == 0xffffffff) { + // This entry is not present in this idmap + return BAD_INDEX; + } + *outEntryId = static_cast<uint16_t>(mappedEntry); return NO_ERROR; } - *outValue = map[index]; - return NO_ERROR; -} +private: + const uint16_t* mData; +}; -static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId) -{ - if (!assertIdmapHeader(map, mapSize)) { +status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) { + if (!assertIdmapHeader(idmap, size)) { return UNKNOWN_ERROR; } - if (mapSize <= IDMAP_HEADER_SIZE + 1) { - ALOGW("corrupt idmap: map size %d too short\n", (int)mapSize); + + size -= ResTable::IDMAP_HEADER_SIZE_BYTES; + if (size < sizeof(uint16_t) * 2) { + ALOGE("idmap: too small to contain any mapping"); return UNKNOWN_ERROR; } - uint32_t typeCount = *(map + IDMAP_HEADER_SIZE); - if (typeCount == 0) { - ALOGW("corrupt idmap: no types\n"); + + const uint16_t* data = reinterpret_cast<const uint16_t*>( + reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES); + + uint16_t targetPackageId = dtohs(*(data++)); + if (targetPackageId == 0 || targetPackageId > 255) { + ALOGE("idmap: target package ID is invalid (%02x)", targetPackageId); return UNKNOWN_ERROR; } - if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) { - ALOGW("corrupt idmap: number of types %u extends past idmap size %d\n", typeCount, (int)mapSize); + + uint16_t mapCount = dtohs(*(data++)); + if (mapCount == 0) { + ALOGE("idmap: no mappings"); return UNKNOWN_ERROR; } - const uint32_t* p = map + IDMAP_HEADER_SIZE + 1; - // find first defined type - while (*p == 0) { - ++p; - if (--typeCount == 0) { - ALOGW("corrupt idmap: types declared, none found\n"); - return UNKNOWN_ERROR; - } + + if (mapCount > 255) { + ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount); } - // determine package id from first entry of first type - const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2; - if (offset > mapSize) { - ALOGW("corrupt idmap: entry offset %u points outside map size %d\n", offset, (int)mapSize); - return UNKNOWN_ERROR; + while (size > sizeof(uint16_t) * 4) { + IdmapEntries entries; + status_t err = entries.setTo(data, size); + if (err != NO_ERROR) { + return err; + } + + ssize_t index = outMap->add(entries.overlayTypeId(), entries); + if (index < 0) { + return NO_MEMORY; + } + + data += entries.byteSize() / sizeof(uint16_t); + size -= entries.byteSize(); } - *outId = (map[offset] >> 24) & 0x000000ff; + if (outPackageId != NULL) { + *outPackageId = static_cast<uint8_t>(targetPackageId); + } return NO_ERROR; } @@ -2726,7 +2808,7 @@ struct ResTable::Header free(resourceIDMap); } - ResTable* const owner; + const ResTable* const owner; void* ownedData; const ResTable_header* header; size_t size; @@ -2739,6 +2821,17 @@ struct ResTable::Header size_t resourceIDMapSize; }; +struct ResTable::Entry { + ResTable_config config; + const ResTable_entry* entry; + const ResTable_type* type; + uint32_t specFlags; + const Package* package; + + StringPoolRef typeStr; + StringPoolRef keyStr; +}; + struct ResTable::Type { Type(const Header* _header, const Package* _package, size_t count) @@ -2749,33 +2842,29 @@ struct ResTable::Type const size_t entryCount; const ResTable_typeSpec* typeSpec; const uint32_t* typeSpecFlags; + IdmapEntries idmapEntries; Vector<const ResTable_type*> configs; }; struct ResTable::Package { Package(ResTable* _owner, const Header* _header, const ResTable_package* _package) - : owner(_owner), header(_header), package(_package) { } - ~Package() - { - size_t i = types.size(); - while (i > 0) { - i--; - delete types[i]; + : owner(_owner), header(_header), package(_package), typeIdOffset(0) { + if (dtohs(package->header.headerSize) == sizeof(package)) { + // The package structure is the same size as the definition. + // This means it contains the typeIdOffset field. + typeIdOffset = package->typeIdOffset; } } - ResTable* const owner; + const ResTable* const owner; const Header* const header; const ResTable_package* const package; - Vector<Type*> types; ResStringPool typeStrings; ResStringPool keyStrings; - const Type* getType(size_t idx) const { - return idx < types.size() ? types[idx] : NULL; - } + size_t typeIdOffset; }; // A group of objects describing a particular resource package. @@ -2787,13 +2876,24 @@ struct ResTable::PackageGroup : owner(_owner) , name(_name) , id(_id) - , typeCount(0) + , largestTypeId(0) , bags(NULL) , dynamicRefTable(static_cast<uint8_t>(_id)) { } ~PackageGroup() { clearBagCache(); + const size_t numTypes = types.size(); + for (size_t i = 0; i < numTypes; i++) { + const TypeList& typeList = types[i]; + const size_t numInnerTypes = typeList.size(); + for (size_t j = 0; j < numInnerTypes; j++) { + if (typeList[j]->package->owner == owner) { + delete typeList[j]; + } + } + } + const size_t N = packages.size(); for (size_t i=0; i<N; i++) { Package* pkg = packages[i]; @@ -2806,17 +2906,15 @@ struct ResTable::PackageGroup void clearBagCache() { if (bags) { TABLE_NOISY(printf("bags=%p\n", bags)); - Package* pkg = packages[0]; - TABLE_NOISY(printf("typeCount=%x\n", typeCount)); - for (size_t i=0; i<typeCount; i++) { + for (size_t i = 0; i < bags->size(); i++) { TABLE_NOISY(printf("type=%d\n", i)); - const Type* type = pkg->getType(i); - if (type != NULL) { - bag_set** typeBags = bags[i]; + const TypeList& typeList = types[i]; + if (typeList.isEmpty()) { + bag_set** typeBags = bags->get(i); TABLE_NOISY(printf("typeBags=%p\n", typeBags)); if (typeBags) { - TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); - const size_t N = type->entryCount; + const size_t N = typeList[0]->entryCount; + TABLE_NOISY(printf("type->entryCount=%x\n", N)); for (size_t j=0; j<N; j++) { if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) free(typeBags[j]); @@ -2825,25 +2923,38 @@ struct ResTable::PackageGroup } } } - free(bags); + delete bags; bags = NULL; } } - ResTable* const owner; + ssize_t findType16(const char16_t* type, size_t len) const { + const size_t N = packages.size(); + for (size_t i = 0; i < N; i++) { + ssize_t index = packages[i]->typeStrings.indexOfString(type, len); + if (index >= 0) { + return index + packages[i]->typeIdOffset; + } + } + return -1; + } + + const ResTable* const owner; String16 const name; uint32_t const id; + + // This is mainly used to keep track of the loaded packages + // and to clean them up properly. Accessing resources happens from + // the 'types' array. Vector<Package*> packages; - // This is for finding typeStrings and other common package stuff. - Package* basePackage; + ByteBucketArray<TypeList> types; - // For quick access. - size_t typeCount; + uint8_t largestTypeId; // Computed attribute bags, first indexed by the type and second // by the entry in that type. - bag_set*** bags; + ByteBucketArray<bag_set**>* bags; // The table mapping dynamic references to resolved references for // this package group. @@ -2879,7 +2990,7 @@ ResTable::Theme::~Theme() void ResTable::Theme::free_package(package_info* pi) { - for (size_t j=0; j<pi->numTypes; j++) { + for (size_t j = 0; j <= Res_MAXTYPE; j++) { theme_entry* te = pi->types[j].entries; if (te != NULL) { free(te); @@ -2890,10 +3001,8 @@ void ResTable::Theme::free_package(package_info* pi) ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) { - package_info* newpi = (package_info*)malloc( - sizeof(package_info) + (pi->numTypes*sizeof(type_info))); - newpi->numTypes = pi->numTypes; - for (size_t j=0; j<newpi->numTypes; j++) { + package_info* newpi = (package_info*)malloc(sizeof(package_info)); + for (size_t j = 0; j <= Res_MAXTYPE; j++) { size_t cnt = pi->types[j].numEntries; newpi->types[j].numEntries = cnt; theme_entry* te = pi->types[j].entries; @@ -2946,17 +3055,14 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) curPI = mPackages[pidx]; if (curPI == NULL) { PackageGroup* const grp = mTable.mPackageGroups[pidx]; - int cnt = grp->typeCount; - curPI = (package_info*)malloc( - sizeof(package_info) + (cnt*sizeof(type_info))); - curPI->numTypes = cnt; - memset(curPI->types, 0, cnt*sizeof(type_info)); + curPI = (package_info*)malloc(sizeof(package_info)); + memset(curPI, 0, sizeof(*curPI)); mPackages[pidx] = curPI; } curType = 0xffffffff; } if (curType != t) { - if (t >= curPI->numTypes) { + if (t > Res_MAXTYPE) { ALOGE("Style contains key with bad type: 0x%08x\n", attrRes); bag++; continue; @@ -2965,8 +3071,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) curEntries = curPI->types[t].entries; if (curEntries == NULL) { PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; - const Type* type = grp->packages[0]->getType(t); - int cnt = type != NULL ? type->entryCount : 0; + const TypeList& typeList = grp->types[t]; + int cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount; curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); curPI->types[t].numEntries = cnt; @@ -2981,8 +3087,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) } theme_entry* curEntry = curEntries + e; TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", - attrRes, bag->map.value.dataType, bag->map.value.data, - curEntry->value.dataType)); + attrRes, bag->map.value.dataType, bag->map.value.data, + curEntry->value.dataType)); if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { curEntry->stringBlock = bag->stringBlock; curEntry->typeSpecFlags |= bagTypeSpecFlags; @@ -3057,8 +3163,8 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, const package_info* const pi = mPackages[p]; TABLE_THEME(ALOGI("Found package: %p", pi)); if (pi != NULL) { - TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, pi->numTypes)); - if (t < pi->numTypes) { + TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, Res_MAXTYPE + 1)); + if (t <= Res_MAXTYPE) { const type_info& ti = pi->types[t]; TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries)); if (e < ti.numEntries) { @@ -3120,14 +3226,13 @@ void ResTable::Theme::dumpToLog() const package_info* pi = mPackages[i]; if (pi == NULL) continue; - ALOGI(" Package #0x%02x:\n", (int)(i+1)); - for (size_t j=0; j<pi->numTypes; j++) { + ALOGI(" Package #0x%02x:\n", (int)(i + 1)); + for (size_t j = 0; j <= Res_MAXTYPE; j++) { type_info& ti = pi->types[j]; if (ti.numEntries == 0) continue; - - ALOGI(" Type #0x%02x:\n", (int)(j+1)); - for (size_t k=0; k<ti.numEntries; k++) { - theme_entry& te = ti.entries[k]; + ALOGI(" Type #0x%02x:\n", (int)(j + 1)); + for (size_t k = 0; k < ti.numEntries; k++) { + const theme_entry& te = ti.entries[k]; if (te.value.dataType == Res_value::TYPE_NULL) continue; ALOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n", (int)Res_MAKEID(i, j, k), @@ -3150,7 +3255,7 @@ ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool cop { memset(&mParams, 0, sizeof(mParams)); memset(mPackageMap, 0, sizeof(mPackageMap)); - addInternal(data, size, cookie, copyData, NULL /* idMap */); + addInternal(data, size, NULL, 0, cookie, copyData); LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table"); //ALOGI("Creating ResTable %p\n", this); } @@ -3166,21 +3271,45 @@ inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1; } -status_t ResTable::add(const void* data, size_t size) { - return addInternal(data, size, 0 /* cookie */, - false /* copyData */, NULL /* idMap */); +status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) { + return addInternal(data, size, NULL, 0, cookie, copyData); } -status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const void* idmap) -{ +status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, + const int32_t cookie, bool copyData) { + return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData); +} + +status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) { const void* data = asset->getBuffer(true); if (data == NULL) { ALOGW("Unable to get buffer of resource asset file"); return UNKNOWN_ERROR; } - size_t size = (size_t)asset->getLength(); - return addInternal(data, size, cookie, copyData, - reinterpret_cast<const Asset*>(idmap)); + + return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData); +} + +status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) { + const void* data = asset->getBuffer(true); + if (data == NULL) { + ALOGW("Unable to get buffer of resource asset file"); + return UNKNOWN_ERROR; + } + + size_t idmapSize = 0; + const void* idmapData = NULL; + if (idmapAsset != NULL) { + idmapData = idmapAsset->getBuffer(true); + if (idmapData == NULL) { + ALOGW("Unable to get buffer of idmap asset file"); + return UNKNOWN_ERROR; + } + idmapSize = static_cast<size_t>(idmapAsset->getLength()); + } + + return addInternal(data, static_cast<size_t>(asset->getLength()), + idmapData, idmapSize, cookie, copyData); } status_t ResTable::add(ResTable* src) @@ -3197,8 +3326,16 @@ status_t ResTable::add(ResTable* src) for (size_t j=0; j<srcPg->packages.size(); j++) { pg->packages.add(srcPg->packages[j]); } - pg->basePackage = srcPg->basePackage; - pg->typeCount = srcPg->typeCount; + + for (size_t j = 0; j < srcPg->types.size(); j++) { + if (srcPg->types[j].isEmpty()) { + continue; + } + + TypeList& typeList = pg->types.editItemAt(j); + typeList.appendVector(srcPg->types[j]); + } + pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId); mPackageGroups.add(pg); } @@ -3224,38 +3361,39 @@ status_t ResTable::addEmpty(const int32_t cookie) { return (mError=NO_ERROR); } -status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie, - bool copyData, const Asset* idmap) +status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize, + const int32_t cookie, bool copyData) { - if (!data) return NO_ERROR; + if (!data) { + return NO_ERROR; + } + Header* header = new Header(this); header->index = mHeaders.size(); header->cookie = cookie; - if (idmap != NULL) { - const size_t idmap_size = idmap->getLength(); - const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true); - header->resourceIDMap = (uint32_t*)malloc(idmap_size); + if (idmapData != NULL) { + header->resourceIDMap = (uint32_t*) malloc(idmapDataSize); if (header->resourceIDMap == NULL) { delete header; return (mError = NO_MEMORY); } - memcpy((void*)header->resourceIDMap, idmap_data, idmap_size); - header->resourceIDMapSize = idmap_size; + memcpy(header->resourceIDMap, idmapData, idmapDataSize); + header->resourceIDMapSize = idmapDataSize; } mHeaders.add(header); const bool notDeviceEndian = htods(0xf0) != 0xf0; LOAD_TABLE_NOISY( - ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d " - "idmap=%p\n", data, size, cookie, asset, copyData, idmap)); + ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, copy=%d " + "idmap=%p\n", data, dataSize, cookie, copyData, idmap)); if (copyData || notDeviceEndian) { - header->ownedData = malloc(size); + header->ownedData = malloc(dataSize); if (header->ownedData == NULL) { return (mError=NO_MEMORY); } - memcpy(header->ownedData, data, size); + memcpy(header->ownedData, data, dataSize); data = header->ownedData; } @@ -3265,10 +3403,10 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook // dtohl(header->header->header.size), header->header->header.size); LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header)); if (dtohs(header->header->header.headerSize) > header->size - || header->size > size) { + || header->size > dataSize) { ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", (int)dtohs(header->header->header.headerSize), - (int)header->size, (int)size); + (int)header->size, (int)dataSize); return (mError=BAD_TYPE); } if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { @@ -3313,16 +3451,8 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook dtohl(header->header->packageCount)); return (mError=BAD_TYPE); } - uint32_t idmap_id = 0; - if (idmap != NULL) { - uint32_t tmp; - if (getIdmapPackageId(header->resourceIDMap, - header->resourceIDMapSize, - &tmp) == NO_ERROR) { - idmap_id = tmp; - } - } - if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) { + + if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) { return mError; } curPackage++; @@ -3405,46 +3535,38 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou ALOGW("Bad identifier when getting name for resource number 0x%08x", resID); return false; } - if (grp->packages.size() > 0) { - const Package* const package = grp->packages[0]; - const ResTable_type* type; - const ResTable_entry* entry; - ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); - if (offset <= 0) { - return false; - } + Entry entry; + status_t err = getEntry(grp, t, e, NULL, &entry); + if (err != NO_ERROR) { + return false; + } - outName->package = grp->name.string(); - outName->packageLen = grp->name.size(); - if (allowUtf8) { - outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen); - outName->name8 = grp->basePackage->keyStrings.string8At( - dtohl(entry->key.index), &outName->nameLen); - } else { - outName->type8 = NULL; - outName->name8 = NULL; - } - if (outName->type8 == NULL) { - outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); - // If we have a bad index for some reason, we should abort. - if (outName->type == NULL) { - return false; - } + outName->package = grp->name.string(); + outName->packageLen = grp->name.size(); + if (allowUtf8) { + outName->type8 = entry.typeStr.string8(&outName->typeLen); + outName->name8 = entry.keyStr.string8(&outName->nameLen); + } else { + outName->type8 = NULL; + outName->name8 = NULL; + } + if (outName->type8 == NULL) { + outName->type = entry.typeStr.string16(&outName->typeLen); + // If we have a bad index for some reason, we should abort. + if (outName->type == NULL) { + return false; } - if (outName->name8 == NULL) { - outName->name = grp->basePackage->keyStrings.stringAt( - dtohl(entry->key.index), &outName->nameLen); - // If we have a bad index for some reason, we should abort. - if (outName->name == NULL) { - return false; - } + } + if (outName->name8 == NULL) { + outName->name = entry.keyStr.string16(&outName->nameLen); + // If we have a bad index for some reason, we should abort. + if (outName->name == NULL) { + return false; } - - return true; } - return false; + return true; } ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density, @@ -3471,15 +3593,6 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag return BAD_INDEX; } - const Res_value* bestValue = NULL; - const Package* bestPackage = NULL; - ResTable_config bestItem; - memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up - - if (outSpecFlags != NULL) *outSpecFlags = 0; - - // Look through all resource packages, starting with the most - // recently added. const PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { ALOGW("Bad identifier when getting value for resource number 0x%08x", resID); @@ -3487,142 +3600,62 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag } // Allow overriding density - const ResTable_config* desiredConfig = &mParams; - ResTable_config* overrideConfig = NULL; + ResTable_config desiredConfig = mParams; if (density > 0) { - overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); - if (overrideConfig == NULL) { - ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); - return BAD_INDEX; - } - memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); - overrideConfig->density = density; - desiredConfig = overrideConfig; - } - - ssize_t rc = BAD_VALUE; - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } - - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", - resID, T, E, ip, (int)offset); - rc = offset; - goto out; - } - continue; - } + desiredConfig.density = density; + } - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { - if (!mayBeBag) { - ALOGW("Requesting resource 0x%x failed because it is complex\n", - resID); - } - continue; - } + Entry entry; + status_t err = getEntry(grp, t, e, &desiredConfig, &entry); + if (err != NO_ERROR) { + ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n", + resID, t, e, err); + return err; + } - if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { - ALOGW("ResTable_item at %d is beyond type chunk data %d", - (int)offset, dtohl(type->header.size)); - rc = BAD_TYPE; - goto out; + if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) { + if (!mayBeBag) { + ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID); } + return BAD_VALUE; + } - const Res_value* item = - (const Res_value*)(((const uint8_t*)type) + offset); - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); + const Res_value* value = reinterpret_cast<const Res_value*>( + reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size); - if (outSpecFlags != NULL) { - if (typeClass->typeSpecFlags != NULL) { - *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - *outSpecFlags = -1; - } - } + outValue->size = dtohs(value->size); + outValue->res0 = value->res0; + outValue->dataType = value->dataType; + outValue->data = dtohl(value->data); - if (bestPackage != NULL && - (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) { - // Discard thisConfig not only if bestItem is more specific, but also if the two configs - // are identical (diff == 0), or overlay packages will not take effect. - continue; - } - - bestItem = thisConfig; - bestValue = item; - bestPackage = package; + // The reference may be pointing to a resource in a shared library. These + // references have build-time generated package IDs. These ids may not match + // the actual package IDs of the corresponding packages in this ResTable. + // We need to fix the package ID based on a mapping. + if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) { + ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data); + return BAD_VALUE; } - TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); - - if (bestValue) { - outValue->size = dtohs(bestValue->size); - outValue->res0 = bestValue->res0; - outValue->dataType = bestValue->dataType; - outValue->data = dtohl(bestValue->data); - - // The reference may be pointing to a resource in a shared library. These - // references have build-time generated package IDs. These ids may not match - // the actual package IDs of the corresponding packages in this ResTable. - // We need to fix the package ID based on a mapping. - status_t err = grp->dynamicRefTable.lookupResourceValue(outValue); - if (err != NO_ERROR) { - ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data); - rc = BAD_VALUE; - goto out; - } + TABLE_NOISY(size_t len; + printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", + entry.package->header->index, + outValue->dataType, + outValue->dataType == Res_value::TYPE_STRING + ? String8(entry.package->header->values.stringAt( + outValue->data, &len)).string() + : "", + outValue->data)); - if (outConfig != NULL) { - *outConfig = bestItem; - } - TABLE_NOISY(size_t len; - printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", - bestPackage->header->index, - outValue->dataType, - outValue->dataType == bestValue->TYPE_STRING - ? String8(bestPackage->header->values.stringAt( - outValue->data, &len)).string() - : "", - outValue->data)); - rc = bestPackage->header->index; - goto out; + if (outSpecFlags != NULL) { + *outSpecFlags = entry.specFlags; } -out: - if (overrideConfig != NULL) { - free(overrideConfig); + if (outConfig != NULL) { + *outConfig = entry.config; } - return rc; + return entry.package->header->index; } ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, @@ -3721,29 +3754,25 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID); - return false; + return BAD_INDEX; } - if (t >= (int)grp->typeCount) { - ALOGW("Type identifier 0x%x is larger than type count 0x%x", - t+1, (int)grp->typeCount); + const TypeList& typeConfigs = grp->types[t]; + if (typeConfigs.isEmpty()) { + ALOGW("Type identifier 0x%x does not exist.", t+1); return BAD_INDEX; } - const Package* const basePackage = grp->packages[0]; - - const Type* const typeConfigs = basePackage->getType(t); - - const size_t NENTRY = typeConfigs->entryCount; + const size_t NENTRY = typeConfigs[0]->entryCount; if (e >= (int)NENTRY) { ALOGW("Entry identifier 0x%x is larger than entry count 0x%x", - e, (int)typeConfigs->entryCount); + e, (int)typeConfigs[0]->entryCount); return BAD_INDEX; } // First see if we've already computed this bag... if (grp->bags) { - bag_set** typeSet = grp->bags[t]; + bag_set** typeSet = grp->bags->get(t); if (typeSet) { bag_set* set = typeSet[e]; if (set) { @@ -3764,229 +3793,174 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, // Bag not found, we need to compute it! if (!grp->bags) { - grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*)); + grp->bags = new ByteBucketArray<bag_set**>(); if (!grp->bags) return NO_MEMORY; } - bag_set** typeSet = grp->bags[t]; + bag_set** typeSet = grp->bags->get(t); if (!typeSet) { typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*)); if (!typeSet) return NO_MEMORY; - grp->bags[t] = typeSet; + grp->bags->set(t, typeSet); } // Mark that we are currently working on this one. typeSet[e] = (bag_set*)0xFFFFFFFF; - // This is what we are building. - bag_set* set = NULL; - TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID)); - ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); + // Now collect all bag attributes + Entry entry; + status_t err = getEntry(grp, t, e, &mParams, &entry); + if (err != NO_ERROR) { + return err; + } - // Now collect all bag attributes from all packages. - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } + const uint16_t entrySize = dtohs(entry.entry->size); + const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0; + const uint32_t count = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0; - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E); - ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass); - ALOGV("Resulting offset=%d\n", (int)offset); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - if (set) free(set); - return offset; - } - continue; - } + size_t N = count; - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { - ALOGW("Skipping entry 0x%x in package table %zu because it is not complex!\n", - resID, ip); - continue; + TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n", + entrySize, parent, count)); + + // If this map inherits from another, we need to start + // with its parent's values. Otherwise start out empty. + TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", + entrySize, parent)); + + // This is what we are building. + bag_set* set = NULL; + + if (parent) { + uint32_t resolvedParent = parent; + + // Bags encode a parent reference without using the standard + // Res_value structure. That means we must always try to + // resolve a parent reference in case it is actually a + // TYPE_DYNAMIC_REFERENCE. + status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent); + if (err != NO_ERROR) { + ALOGE("Failed resolving bag parent id 0x%08x", parent); + return UNKNOWN_ERROR; } - if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) { - continue; + const bag_entry* parentBag; + uint32_t parentTypeSpecFlags = 0; + const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags); + const size_t NT = ((NP >= 0) ? NP : 0) + N; + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); + if (set == NULL) { + return NO_MEMORY; } - bestConfig = type->config; - if (set) { - free(set); - set = NULL; + if (NP > 0) { + memcpy(set+1, parentBag, NP*sizeof(bag_entry)); + set->numAttrs = NP; + TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP)); + } else { + TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n")); + set->numAttrs = 0; } + set->availAttrs = NT; + set->typeSpecFlags = parentTypeSpecFlags; + } else { + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); + if (set == NULL) { + return NO_MEMORY; + } + set->numAttrs = 0; + set->availAttrs = N; + set->typeSpecFlags = 0; + } - const uint16_t entrySize = dtohs(entry->size); - const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; - const uint32_t count = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; - - size_t N = count; + set->typeSpecFlags |= entry.specFlags; - TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n", - entrySize, parent, count)); + // Now merge in the new attributes... + size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type)) + + dtohs(entry.entry->size); + const ResTable_map* map; + bag_entry* entries = (bag_entry*)(set+1); + size_t curEntry = 0; + uint32_t pos = 0; + TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + while (pos < count) { + TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); - // If this map inherits from another, we need to start - // with its parent's values. Otherwise start out empty. - TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", - entrySize, parent)); - if (parent) { - uint32_t resolvedParent = parent; + if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) { + ALOGW("ResTable_map at %d is beyond type chunk data %d", + (int)curOff, dtohl(entry.type->header.size)); + return BAD_TYPE; + } + map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff); + N++; - // Bags encode a parent reference without using the standard - // Res_value structure. That means we must always try to - // resolve a parent reference in case it is actually a - // TYPE_DYNAMIC_REFERENCE. - status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent); - if (err != NO_ERROR) { - ALOGE("Failed resolving bag parent id 0x%08x", parent); - return UNKNOWN_ERROR; - } + const uint32_t newName = htodl(map->name.ident); + bool isInside; + uint32_t oldName = 0; + while ((isInside=(curEntry < set->numAttrs)) + && (oldName=entries[curEntry].map.name.ident) < newName) { + TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", + curEntry, entries[curEntry].map.name.ident)); + curEntry++; + } - const bag_entry* parentBag; - uint32_t parentTypeSpecFlags = 0; - const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags); - const size_t NT = ((NP >= 0) ? NP : 0) + N; - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); - if (set == NULL) { - return NO_MEMORY; - } - if (NP > 0) { - memcpy(set+1, parentBag, NP*sizeof(bag_entry)); - set->numAttrs = NP; - TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP)); - } else { - TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n")); - set->numAttrs = 0; - } - set->availAttrs = NT; - set->typeSpecFlags = parentTypeSpecFlags; + if ((!isInside) || oldName != newName) { + // This is a new attribute... figure out what to do with it. + if (set->numAttrs >= set->availAttrs) { + // Need to alloc more memory... + const size_t newAvail = set->availAttrs+N; + set = (bag_set*)realloc(set, + sizeof(bag_set) + + sizeof(bag_entry)*newAvail); + if (set == NULL) { + return NO_MEMORY; + } + set->availAttrs = newAvail; + entries = (bag_entry*)(set+1); + TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + } + if (isInside) { + // Going in the middle, need to make space. + memmove(entries+curEntry+1, entries+curEntry, + sizeof(bag_entry)*(set->numAttrs-curEntry)); + set->numAttrs++; + } + TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", + curEntry, newName)); } else { - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); - if (set == NULL) { - return NO_MEMORY; - } - set->numAttrs = 0; - set->availAttrs = N; - set->typeSpecFlags = 0; + TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", + curEntry, oldName)); } - if (typeClass->typeSpecFlags != NULL) { - set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - set->typeSpecFlags = -1; - } - - // Now merge in the new attributes... - ssize_t curOff = offset; - const ResTable_map* map; - bag_entry* entries = (bag_entry*)(set+1); - size_t curEntry = 0; - uint32_t pos = 0; - TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - while (pos < count) { - TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); - - if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { - ALOGW("ResTable_map at %d is beyond type chunk data %d", - (int)curOff, dtohl(type->header.size)); - return BAD_TYPE; - } - map = (const ResTable_map*)(((const uint8_t*)type) + curOff); - N++; - - const uint32_t newName = htodl(map->name.ident); - bool isInside; - uint32_t oldName = 0; - while ((isInside=(curEntry < set->numAttrs)) - && (oldName=entries[curEntry].map.name.ident) < newName) { - TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", - curEntry, entries[curEntry].map.name.ident)); - curEntry++; - } - - if ((!isInside) || oldName != newName) { - // This is a new attribute... figure out what to do with it. - if (set->numAttrs >= set->availAttrs) { - // Need to alloc more memory... - const size_t newAvail = set->availAttrs+N; - set = (bag_set*)realloc(set, - sizeof(bag_set) - + sizeof(bag_entry)*newAvail); - if (set == NULL) { - return NO_MEMORY; - } - set->availAttrs = newAvail; - entries = (bag_entry*)(set+1); - TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - } - if (isInside) { - // Going in the middle, need to make space. - memmove(entries+curEntry+1, entries+curEntry, - sizeof(bag_entry)*(set->numAttrs-curEntry)); - set->numAttrs++; - } - TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", - curEntry, newName)); - } else { - TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", - curEntry, oldName)); - } + bag_entry* cur = entries+curEntry; - bag_entry* cur = entries+curEntry; + cur->stringBlock = entry.package->header->index; + cur->map.name.ident = newName; + cur->map.value.copyFrom_dtoh(map->value); + status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value); + if (err != NO_ERROR) { + ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data); + return UNKNOWN_ERROR; + } - cur->stringBlock = package->header->index; - cur->map.name.ident = newName; - cur->map.value.copyFrom_dtoh(map->value); - status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value); - if (err != NO_ERROR) { - ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data); - return UNKNOWN_ERROR; - } + TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", + curEntry, cur, cur->stringBlock, cur->map.name.ident, + cur->map.value.dataType, cur->map.value.data)); - TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", - curEntry, cur, cur->stringBlock, cur->map.name.ident, - cur->map.value.dataType, cur->map.value.data)); + // On to the next! + curEntry++; + pos++; + const size_t size = dtohs(map->value.size); + curOff += size + sizeof(*map)-sizeof(map->value); + }; - // On to the next! - curEntry++; - pos++; - const size_t size = dtohs(map->value.size); - curOff += size + sizeof(*map)-sizeof(map->value); - }; - if (curEntry > set->numAttrs) { - set->numAttrs = curEntry; - } + if (curEntry > set->numAttrs) { + set->numAttrs = curEntry; } // And this is it... @@ -4154,80 +4128,63 @@ nope: continue; } - const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen); + const ssize_t ti = group->findType16(type, typeLen); if (ti < 0) { TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); continue; } - const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen); - if (ei < 0) { - TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); - continue; - } - - TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); - - const Type* const typeConfigs = group->packages[0]->getType(ti); - if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { - TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", + const TypeList& typeList = group->types[ti]; + if (typeList.isEmpty()) { + TABLE_NOISY(printf("Expected type structure not found in package %s for index %d\n", String8(group->name).string(), ti)); + continue; } - size_t NTC = typeConfigs->configs.size(); - for (size_t tci=0; tci<NTC; tci++) { - const ResTable_type* const ty = typeConfigs->configs[tci]; - const uint32_t typeOffset = dtohl(ty->entriesStart); - - const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); - - const size_t NE = dtohl(ty->entryCount); - for (size_t i=0; i<NE; i++) { - uint32_t offset = dtohl(eindex[i]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - offset += typeOffset; + const size_t typeCount = typeList.size(); + for (size_t i = 0; i < typeCount; i++) { + const Type* t = typeList[i]; + const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen); + if (ei < 0) { + continue; + } - if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) { - ALOGW("ResTable_entry at %d is beyond type chunk data %d", - offset, dtohl(ty->header.size)); - return 0; - } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", - (int)offset, (int)group->id, (int)ti+1, (int)i, - String8(package, packageLen).string(), - String8(type, typeLen).string(), - String8(name, nameLen).string()); - return 0; - } + const size_t configCount = t->configs.size(); + for (size_t j = 0; j < configCount; j++) { + const TypeVariant tv(t->configs[j]); + for (TypeVariant::iterator iter = tv.beginEntries(); + iter != tv.endEntries(); + iter++) { + const ResTable_entry* entry = *iter; + if (entry == NULL) { + continue; + } - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)ty) + offset); - if (dtohs(entry->size) < sizeof(*entry)) { - ALOGW("ResTable_entry size %d is too small", dtohs(entry->size)); - return BAD_TYPE; - } + if (dtohl(entry->key.index) == (size_t) ei) { + uint32_t resId = Res_MAKEID(group->id - 1, ti, iter.index()); + if (outTypeSpecFlags) { + Entry result; + if (getEntry(group, ti, iter.index(), NULL, &result) != NO_ERROR) { + ALOGW("Failed to find spec flags for %s:%s/%s (0x%08x)", + String8(group->name).string(), + String8(String16(type, typeLen)).string(), + String8(String16(name, nameLen)).string(), + resId); + return 0; + } + *outTypeSpecFlags = result.specFlags; - TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", - i, ei, dtohl(entry->key.index))); - if (dtohl(entry->key.index) == (size_t)ei) { - if (outTypeSpecFlags) { - *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; - if (fakePublic) { - *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC; + if (fakePublic) { + *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC; + } } + return resId; } - return Res_MAKEID(group->id-1, ti, i); } } } + break; } - return 0; } @@ -5260,6 +5217,18 @@ uint32_t ResTable::getBasePackageId(size_t idx) const return mPackageGroups[idx]->id; } +uint32_t ResTable::getLastTypeIdForPackage(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + const PackageGroup* const group = mPackageGroups[idx]; + return group->largestTypeId; +} + size_t ResTable::getTableCount() const { return mHeaders.size(); @@ -5292,32 +5261,31 @@ const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) con void ResTable::getConfigurations(Vector<ResTable_config>* configs) const { - const size_t I = mPackageGroups.size(); - for (size_t i=0; i<I; i++) { + const size_t packageCount = mPackageGroups.size(); + for (size_t i = 0; i < packageCount; i++) { const PackageGroup* packageGroup = mPackageGroups[i]; - const size_t J = packageGroup->packages.size(); - for (size_t j=0; j<J; j++) { - const Package* package = packageGroup->packages[j]; - const size_t K = package->types.size(); - for (size_t k=0; k<K; k++) { - const Type* type = package->types[k]; - if (type == NULL) continue; - const size_t L = type->configs.size(); - for (size_t l=0; l<L; l++) { - const ResTable_type* config = type->configs[l]; + const size_t typeCount = packageGroup->types.size(); + for (size_t j = 0; j < typeCount; j++) { + const TypeList& typeList = packageGroup->types[j]; + const size_t numTypes = typeList.size(); + for (size_t k = 0; k < numTypes; k++) { + const Type* type = typeList[k]; + const size_t numConfigs = type->configs.size(); + for (size_t m = 0; m < numConfigs; m++) { + const ResTable_type* config = type->configs[m]; ResTable_config cfg; memset(&cfg, 0, sizeof(ResTable_config)); cfg.copyFromDtoH(config->config); // only insert unique - const size_t M = configs->size(); - size_t m; - for (m=0; m<M; m++) { - if (0 == (*configs)[m].compare(cfg)) { + const size_t N = configs->size(); + size_t n; + for (n = 0; n < N; n++) { + if (0 == (*configs)[n].compare(cfg)) { break; } } // if we didn't find it - if (m == M) { + if (n == N) { configs->add(cfg); } } @@ -5350,122 +5318,180 @@ void ResTable::getLocales(Vector<String8>* locales) const } } -ssize_t ResTable::getEntry( - const Package* package, int typeIndex, int entryIndex, - const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const -{ - ALOGV("Getting entry from package %p\n", package); - const ResTable_package* const pkg = package->package; +StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index) + : mPool(pool), mIndex(index) {} - const Type* allTypes = package->getType(typeIndex); - ALOGV("allTypes=%p\n", allTypes); - if (allTypes == NULL) { - ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); - return 0; +StringPoolRef::StringPoolRef() + : mPool(NULL), mIndex(0) {} + +const char* StringPoolRef::string8(size_t* outLen) const { + if (mPool != NULL) { + return mPool->string8At(mIndex, outLen); } + if (outLen != NULL) { + *outLen = 0; + } + return NULL; +} - if ((size_t)entryIndex >= allTypes->entryCount) { - ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", - entryIndex, (int)allTypes->entryCount); +const char16_t* StringPoolRef::string16(size_t* outLen) const { + if (mPool != NULL) { + return mPool->stringAt(mIndex, outLen); + } + if (outLen != NULL) { + *outLen = 0; + } + return NULL; +} + +status_t ResTable::getEntry( + const PackageGroup* packageGroup, int typeIndex, int entryIndex, + const ResTable_config* config, + Entry* outEntry) const +{ + const TypeList& typeList = packageGroup->types[typeIndex]; + if (typeList.isEmpty()) { + ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); return BAD_TYPE; } - const ResTable_type* type = NULL; - uint32_t offset = ResTable_type::NO_ENTRY; + const ResTable_type* bestType = NULL; + uint32_t bestOffset = ResTable_type::NO_ENTRY; + const Package* bestPackage = NULL; + uint32_t specFlags = 0; + uint8_t actualTypeIndex = typeIndex; ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up - - const size_t NT = allTypes->configs.size(); - for (size_t i=0; i<NT; i++) { - const ResTable_type* const thisType = allTypes->configs[i]; - if (thisType == NULL) continue; - - ResTable_config thisConfig; - thisConfig.copyFromDtoH(thisType->config); + memset(&bestConfig, 0, sizeof(bestConfig)); - TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n", - entryIndex, typeIndex+1, dtohl(thisType->config.size), - thisConfig.toString().string())); + // Iterate over the Types of each package. + const size_t typeCount = typeList.size(); + for (size_t i = 0; i < typeCount; i++) { + const Type* const typeSpec = typeList[i]; + + int realEntryIndex = entryIndex; + int realTypeIndex = typeIndex; + bool currentTypeIsOverlay = false; + + // Runtime overlay packages provide a mapping of app resource + // ID to package resource ID. + if (typeSpec->idmapEntries.hasEntries()) { + uint16_t overlayEntryIndex; + if (typeSpec->idmapEntries.lookup(entryIndex, &overlayEntryIndex) != NO_ERROR) { + // No such mapping exists + continue; + } + realEntryIndex = overlayEntryIndex; + realTypeIndex = typeSpec->idmapEntries.overlayTypeId() - 1; + currentTypeIsOverlay = true; + } - // Check to make sure this one is valid for the current parameters. - if (config && !thisConfig.match(*config)) { - TABLE_GETENTRY(ALOGI("Does not match config!\n")); + if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) { + ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)", + Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex), + entryIndex, static_cast<int>(typeSpec->entryCount)); + // We should normally abort here, but some legacy apps declare + // resources in the 'android' package (old bug in AAPT). continue; } - // Check if there is the desired entry in this type. + // Aggregate all the flags for each package that defines this entry. + if (typeSpec->typeSpecFlags != NULL) { + specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]); + } else { + specFlags = -1; + } - const uint8_t* const end = ((const uint8_t*)thisType) - + dtohl(thisType->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); + const size_t numConfigs = typeSpec->configs.size(); + for (size_t c = 0; c < numConfigs; c++) { + const ResTable_type* const thisType = typeSpec->configs[c]; + if (thisType == NULL) { + continue; + } - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n")); - continue; - } + ResTable_config thisConfig; + thisConfig.copyFromDtoH(thisType->config); - if (type != NULL) { - // Check if this one is less specific than the last found. If so, - // we will skip it. We check starting with things we most care - // about to those we least care about. - if (!thisConfig.isBetterThan(bestConfig, config)) { - TABLE_GETENTRY(ALOGI("This config is worse than last!\n")); + // Check to make sure this one is valid for the current parameters. + if (config != NULL && !thisConfig.match(*config)) { + continue; + } + + // Check if there is the desired entry in this type. + const uint8_t* const end = reinterpret_cast<const uint8_t*>(thisType) + + dtohl(thisType->header.size); + const uint32_t* const eindex = reinterpret_cast<const uint32_t*>( + reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[realEntryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + // There is no entry for this index and configuration. continue; } - } - type = thisType; - offset = thisOffset; - bestConfig = thisConfig; - TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n")); - if (!config) break; + if (bestType != NULL) { + // Check if this one is less specific than the last found. If so, + // we will skip it. We check starting with things we most care + // about to those we least care about. + if (!thisConfig.isBetterThan(bestConfig, config)) { + if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) { + continue; + } + } + } + + bestType = thisType; + bestOffset = thisOffset; + bestConfig = thisConfig; + bestPackage = typeSpec->package; + actualTypeIndex = realTypeIndex; + + // If no config was specified, any type will do, so skip + if (config == NULL) { + break; + } + } } - if (type == NULL) { - TABLE_GETENTRY(ALOGI("No value found for requested entry!\n")); + if (bestType == NULL) { return BAD_INDEX; } - offset += dtohl(type->entriesStart); - TABLE_NOISY(ALOGD("Looking in resource table %p, typeOff=%p, offset=%p", - package->header->header, (void*)(((const char*)type)-((const char*)package->header->header)), - (void*)offset)); + bestOffset += dtohl(bestType->entriesStart); - if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { + if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) { ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", - offset, dtohl(type->header.size)); + bestOffset, dtohl(bestType->header.size)); return BAD_TYPE; } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at 0x%x is not on an integer boundary", - offset); + if ((bestOffset & 0x3) != 0) { + ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset); return BAD_TYPE; } - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)type) + offset); + const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<const uint8_t*>(bestType) + bestOffset); if (dtohs(entry->size) < sizeof(*entry)) { ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); return BAD_TYPE; } - *outType = type; - *outEntry = entry; - if (outTypeClass != NULL) { - *outTypeClass = allTypes; + if (outEntry != NULL) { + outEntry->entry = entry; + outEntry->config = bestConfig; + outEntry->type = bestType; + outEntry->specFlags = specFlags; + outEntry->package = bestPackage; + outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset); + outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index)); } - return offset + dtohs(entry->size); + return NO_ERROR; } status_t ResTable::parsePackage(const ResTable_package* const pkg, - const Header* const header, uint32_t idmap_id) + const Header* const header) { const uint8_t* base = (const uint8_t*)pkg; - status_t err = validate_chunk(&pkg->header, sizeof(*pkg), + status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset), header->dataEnd, "ResTable_package"); if (err != NO_ERROR) { return (mError=err); @@ -5494,89 +5520,88 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=BAD_TYPE); } - Package* package = NULL; - PackageGroup* group = NULL; - uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id); - // If at this point id == 0, pkg is an overlay package without a - // corresponding idmap. During regular usage, overlay packages are - // always loaded alongside their idmaps, but during idmap creation - // the package is temporarily loaded by itself. - if (id < 256) { - - package = new Package(this, header, pkg); - if (package == NULL) { - return (mError=NO_MEMORY); - } - - if (idmap_id == 0) { - err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), - header->dataEnd-(base+dtohl(pkg->typeStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } + uint32_t id = dtohl(pkg->id); + KeyedVector<uint8_t, IdmapEntries> idmapEntries; - err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), - header->dataEnd-(base+dtohl(pkg->keyStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } + if (header->resourceIDMap != NULL) { + uint8_t targetPackageId = 0; + status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries); + if (err != NO_ERROR) { + ALOGW("Overlay is broken"); + return (mError=err); } + id = targetPackageId; + } - if (id == 0) { - // This is a library so assign an ID - id = mNextPackageId++; - } + if (id >= 256) { + LOG_ALWAYS_FATAL("Package id out of range"); + return NO_ERROR; + } else if (id == 0) { + // This is a library so assign an ID + id = mNextPackageId++; + } - size_t idx = mPackageMap[id]; - if (idx == 0) { - idx = mPackageGroups.size()+1; + PackageGroup* group = NULL; + Package* package = new Package(this, header, pkg); + if (package == NULL) { + return (mError=NO_MEMORY); + } - char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; - strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); - group = new PackageGroup(this, String16(tmpName), id); - if (group == NULL) { - delete package; - return (mError=NO_MEMORY); - } + err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), + header->dataEnd-(base+dtohl(pkg->typeStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } - //printf("Adding new package id %d at index %d\n", id, idx); - err = mPackageGroups.add(group); - if (err < NO_ERROR) { - return (mError=err); - } - group->basePackage = package; + err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), + header->dataEnd-(base+dtohl(pkg->keyStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } - mPackageMap[id] = (uint8_t)idx; + size_t idx = mPackageMap[id]; + if (idx == 0) { + idx = mPackageGroups.size() + 1; - // Find all packages that reference this package - size_t N = mPackageGroups.size(); - for (size_t i = 0; i < N; i++) { - mPackageGroups[i]->dynamicRefTable.addMapping( - group->name, static_cast<uint8_t>(group->id)); - } - } else { - group = mPackageGroups.itemAt(idx-1); - if (group == NULL) { - return (mError=UNKNOWN_ERROR); - } + char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; + strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); + group = new PackageGroup(this, String16(tmpName), id); + if (group == NULL) { + delete package; + return (mError=NO_MEMORY); } - err = group->packages.add(package); + + //printf("Adding new package id %d at index %d\n", id, idx); + err = mPackageGroups.add(group); if (err < NO_ERROR) { return (mError=err); } + + mPackageMap[id] = static_cast<uint8_t>(idx); + + // Find all packages that reference this package + size_t N = mPackageGroups.size(); + for (size_t i = 0; i < N; i++) { + mPackageGroups[i]->dynamicRefTable.addMapping( + group->name, static_cast<uint8_t>(group->id)); + } } else { - LOG_ALWAYS_FATAL("Package id out of range"); - return NO_ERROR; + group = mPackageGroups.itemAt(idx - 1); + if (group == NULL) { + return (mError=UNKNOWN_ERROR); + } } + err = group->packages.add(package); + if (err < NO_ERROR) { + return (mError=err); + } // Iterate through all chunks. - size_t curPackage = 0; - const ResChunk_header* chunk = (const ResChunk_header*)(((const uint8_t*)pkg) + dtohs(pkg->header.headerSize)); @@ -5597,6 +5622,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } const size_t typeSpecSize = dtohl(typeSpec->header.size); + const size_t newEntryCount = dtohl(typeSpec->entryCount); LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", (void*)(base-(const uint8_t*)chunk), @@ -5605,12 +5631,11 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (void*)typeSpecSize)); // look for block overrun or int overflow when multiplying by 4 if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) - || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) + || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount) > typeSpecSize)) { ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", - (void*)(dtohs(typeSpec->header.headerSize) - +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), - (void*)typeSpecSize); + (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)), + (void*)typeSpecSize); return (mError=BAD_TYPE); } @@ -5619,21 +5644,36 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=BAD_TYPE); } - while (package->types.size() < typeSpec->id) { - package->types.add(NULL); - } - Type* t = package->types[typeSpec->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(typeSpec->entryCount)); - package->types.editItemAt(typeSpec->id-1) = t; - } else if (dtohl(typeSpec->entryCount) != t->entryCount) { - ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", - (int)dtohl(typeSpec->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); + if (newEntryCount > 0) { + uint8_t typeIndex = typeSpec->id - 1; + ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id); + if (idmapIndex >= 0) { + typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1; + } + + TypeList& typeList = group->types.editItemAt(typeIndex); + if (!typeList.isEmpty()) { + const Type* existingType = typeList[0]; + if (existingType->entryCount != newEntryCount && idmapIndex < 0) { + ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", + (int) newEntryCount, (int) existingType->entryCount); + // We should normally abort here, but some legacy apps declare + // resources in the 'android' package (old bug in AAPT). + } + } + + Type* t = new Type(header, package, newEntryCount); + t->typeSpec = typeSpec; + t->typeSpecFlags = (const uint32_t*)( + ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); + if (idmapIndex >= 0) { + t->idmapEntries = idmapEntries[idmapIndex]; + } + typeList.add(t); + group->largestTypeId = max(group->largestTypeId, typeSpec->id); + } else { + ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id); } - t->typeSpecFlags = (const uint32_t*)( - ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); - t->typeSpec = typeSpec; } else if (ctype == RES_TABLE_TYPE_TYPE) { const ResTable_type* type = (const ResTable_type*)(chunk); @@ -5644,50 +5684,69 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } const uint32_t typeSize = dtohl(type->header.size); + const size_t newEntryCount = dtohl(type->entryCount); LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", (void*)(base-(const uint8_t*)chunk), dtohs(type->header.type), dtohs(type->header.headerSize), (void*)typeSize)); - if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) - > typeSize) { + if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) + > typeSize) { ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.", - (void*)(dtohs(type->header.headerSize) - +(sizeof(uint32_t)*dtohl(type->entryCount))), - typeSize); + (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)), + typeSize); return (mError=BAD_TYPE); } - if (dtohl(type->entryCount) != 0 + + if (newEntryCount != 0 && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.", dtohl(type->entriesStart), typeSize); return (mError=BAD_TYPE); } + if (type->id == 0) { ALOGW("ResTable_type has an id of 0."); return (mError=BAD_TYPE); } - while (package->types.size() < type->id) { - package->types.add(NULL); - } - Type* t = package->types[type->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(type->entryCount)); - package->types.editItemAt(type->id-1) = t; - } else if (dtohl(type->entryCount) != t->entryCount) { - ALOGW("ResTable_type entry count inconsistent: given %d, previously %d", - (int)dtohl(type->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); + if (newEntryCount > 0) { + uint8_t typeIndex = type->id - 1; + ssize_t idmapIndex = idmapEntries.indexOfKey(type->id); + if (idmapIndex >= 0) { + typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1; + } + + TypeList& typeList = group->types.editItemAt(typeIndex); + if (typeList.isEmpty()) { + ALOGE("No TypeSpec for type %d", type->id); + return (mError=BAD_TYPE); + } + + Type* t = typeList.editItemAt(typeList.size() - 1); + if (newEntryCount != t->entryCount) { + ALOGE("ResTable_type entry count inconsistent: given %d, previously %d", + (int)newEntryCount, (int)t->entryCount); + return (mError=BAD_TYPE); + } + + if (t->package != package) { + ALOGE("No TypeSpec for type %d", type->id); + return (mError=BAD_TYPE); + } + + t->configs.add(type); + + TABLE_GETENTRY( + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + ALOGI("Adding config to type %d: %s\n", + type->id, thisConfig.toString().string())); + } else { + ALOGV("Skipping empty ResTable_type for type %d", type->id); } - TABLE_GETENTRY( - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - ALOGI("Adding config to type %d: %s\n", - type->id, thisConfig.toString().string())); - t->configs.add(type); } else if (ctype == RES_TABLE_LIBRARY_TYPE) { if (group->dynamicRefTable.entries().size() == 0) { status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk); @@ -5714,10 +5773,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (((const uint8_t*)chunk) + csize); } - if (group->typeCount == 0) { - group->typeCount = package->types.size(); - } - return NO_ERROR; } @@ -5818,6 +5873,12 @@ status_t DynamicRefTable::lookupResourceValue(Res_value* value) const { return NO_ERROR; } +struct IdmapTypeMap { + ssize_t overlayTypeId; + size_t entryOffset; + Vector<uint32_t> entryMap; +}; + status_t ResTable::createIdmap(const ResTable& overlay, uint32_t targetCrc, uint32_t overlayCrc, const char* targetPath, const char* overlayPath, @@ -5828,41 +5889,46 @@ status_t ResTable::createIdmap(const ResTable& overlay, ALOGW("idmap: target package has no package groups, cannot create idmap\n"); return UNKNOWN_ERROR; } + if (mPackageGroups[0]->packages.size() == 0) { ALOGW("idmap: target package has no packages in its first package group, " "cannot create idmap\n"); return UNKNOWN_ERROR; } - Vector<Vector<uint32_t> > map; + KeyedVector<uint8_t, IdmapTypeMap> map; + // overlaid packages are assumed to contain only one package group const PackageGroup* pg = mPackageGroups[0]; - const Package* pkg = pg->packages[0]; - size_t typeCount = pkg->types.size(); - // starting size is header + first item (number of types in map) - *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t); + + // starting size is header + *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; + + // target package id and number of types in map + *outSize += 2 * sizeof(uint16_t); + // overlay packages are assumed to contain only one package group const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name); - const uint32_t pkg_id = pkg->package->id << 24; - - for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) { - ssize_t first = -1; - ssize_t last = -1; - const Type* typeConfigs = pkg->getType(typeIndex); - ssize_t mapIndex = map.add(); - if (mapIndex < 0) { - return NO_MEMORY; + + for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) { + const TypeList& typeList = pg->types[typeIndex]; + if (typeList.isEmpty()) { + continue; } - Vector<uint32_t>& vector = map.editItemAt(mapIndex); + + const Type* typeConfigs = typeList[0]; + + IdmapTypeMap typeMap; + typeMap.overlayTypeId = -1; + typeMap.entryOffset = 0; + for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) { - uint32_t resID = pkg_id - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); + uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex); resource_name resName; if (!this->getResourceName(resID, false, &resName)) { - ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); - // add dummy value, or trimming leading/trailing zeroes later will fail - vector.push(0); + if (typeMap.entryMap.isEmpty()) { + typeMap.entryOffset++; + } continue; } @@ -5874,49 +5940,55 @@ status_t ResTable::createIdmap(const ResTable& overlay, overlayType.size(), overlayPackage.string(), overlayPackage.size()); - if (overlayResID != 0) { - overlayResID = pkg_id | (0x00ffffff & overlayResID); - last = Res_GETENTRY(resID); - if (first == -1) { - first = Res_GETENTRY(resID); + if (overlayResID == 0) { + if (typeMap.entryMap.isEmpty()) { + typeMap.entryOffset++; } + continue; } - vector.push(overlayResID); -#if 0 - if (overlayResID != 0) { - ALOGD("%s/%s 0x%08x -> 0x%08x\n", - String8(String16(resName.type)).string(), - String8(String16(resName.name)).string(), - resID, overlayResID); + + if (typeMap.overlayTypeId == -1) { + typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1; + } + + if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) { + ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x" + " but entries should map to resources of type %02x", + resID, overlayResID, typeMap.overlayTypeId); + return BAD_TYPE; + } + + if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) { + // Resize to accomodate this entry and the 0's in between. + if (typeMap.entryMap.resize((entryIndex - typeMap.entryOffset) + 1) < 0) { + return NO_MEMORY; + } + typeMap.entryMap.editTop() = Res_GETENTRY(overlayResID); + } else { + typeMap.entryMap.add(Res_GETENTRY(overlayResID)); } -#endif } - if (first != -1) { - // shave off trailing entries which lack overlay values - const size_t last_past_one = last + 1; - if (last_past_one < vector.size()) { - vector.removeItemsAt(last_past_one, vector.size() - last_past_one); + if (!typeMap.entryMap.isEmpty()) { + if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) { + return NO_MEMORY; } - // shave off leading entries which lack overlay values - vector.removeItemsAt(0, first); - // store offset to first overlaid resource ID of this type - vector.insertAt((uint32_t)first, 0, 1); - // reserve space for number and offset of entries, and the actual entries - *outSize += (2 + vector.size()) * sizeof(uint32_t); - } else { - // no entries of current type defined in overlay package - vector.clear(); - // reserve space for type offset - *outSize += 1 * sizeof(uint32_t); + *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t)); } } + if (map.isEmpty()) { + ALOGW("idmap: no resources in overlay package present in base package"); + return UNKNOWN_ERROR; + } + if ((*outData = malloc(*outSize)) == NULL) { return NO_MEMORY; } + uint32_t* data = (uint32_t*)*outData; *data++ = htodl(IDMAP_MAGIC); + *data++ = htodl(IDMAP_CURRENT_VERSION); *data++ = htodl(targetCrc); *data++ = htodl(overlayCrc); const char* paths[] = { targetPath, overlayPath }; @@ -5934,44 +6006,30 @@ status_t ResTable::createIdmap(const ResTable& overlay, data += 256 / sizeof(uint32_t); } const size_t mapSize = map.size(); - *data++ = htodl(mapSize); - size_t offset = mapSize; + uint16_t* typeData = reinterpret_cast<uint16_t*>(data); + *typeData++ = htods(pg->id); + *typeData++ = htods(mapSize); for (size_t i = 0; i < mapSize; ++i) { - const Vector<uint32_t>& vector = map.itemAt(i); - const size_t N = vector.size(); - if (N == 0) { - *data++ = htodl(0); - } else { - offset++; - *data++ = htodl(offset); - offset += N; - } - } - if (offset == mapSize) { - ALOGW("idmap: no resources in overlay package present in base package\n"); - return UNKNOWN_ERROR; - } - for (size_t i = 0; i < mapSize; ++i) { - const Vector<uint32_t>& vector = map.itemAt(i); - const size_t N = vector.size(); - if (N == 0) { - continue; - } - if (N == 1) { // vector expected to hold (offset) + (N > 0 entries) - ALOGW("idmap: type %u supposedly has entries, but no entries found\n", (uint32_t)i); - return UNKNOWN_ERROR; - } - *data++ = htodl(N - 1); // do not count the offset (which is vector's first element) - for (size_t j = 0; j < N; ++j) { - const uint32_t& overlayResID = vector.itemAt(j); - *data++ = htodl(overlayResID); + uint8_t targetTypeId = map.keyAt(i); + const IdmapTypeMap& typeMap = map[i]; + *typeData++ = htods(targetTypeId + 1); + *typeData++ = htods(typeMap.overlayTypeId); + *typeData++ = htods(typeMap.entryMap.size()); + *typeData++ = htods(typeMap.entryOffset); + + const size_t entryCount = typeMap.entryMap.size(); + uint32_t* entries = reinterpret_cast<uint32_t*>(typeData); + for (size_t j = 0; j < entryCount; j++) { + entries[j] = htodl(typeMap.entryMap[j]); } + typeData += entryCount * 2; } return NO_ERROR; } bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, + uint32_t* pVersion, uint32_t* pTargetCrc, uint32_t* pOverlayCrc, String8* pTargetPath, String8* pOverlayPath) { @@ -5979,17 +6037,20 @@ bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, if (!assertIdmapHeader(map, sizeBytes)) { return false; } + if (pVersion) { + *pVersion = dtohl(map[1]); + } if (pTargetCrc) { - *pTargetCrc = map[1]; + *pTargetCrc = dtohl(map[2]); } if (pOverlayCrc) { - *pOverlayCrc = map[2]; + *pOverlayCrc = dtohl(map[3]); } if (pTargetPath) { - pTargetPath->setTo(reinterpret_cast<const char*>(map + 3)); + pTargetPath->setTo(reinterpret_cast<const char*>(map + 4)); } if (pOverlayPath) { - pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t))); + pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t))); } return true; } @@ -6138,184 +6199,184 @@ void ResTable::print(bool inclValues) const size_t pkgCount = pg->packages.size(); for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) { const Package* pkg = pg->packages[pkgIndex]; - size_t typeCount = pkg->types.size(); - printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, - pkg->package->id, String8(String16(pkg->package->name)).string(), - (int)typeCount); - for (size_t typeIndex=0; typeIndex<typeCount; typeIndex++) { - const Type* typeConfigs = pkg->getType(typeIndex); - if (typeConfigs == NULL) { - printf(" type %d NULL\n", (int)typeIndex); + printf(" Package %d id=%d name=%s\n", (int)pkgIndex, + pkg->package->id, String8(String16(pkg->package->name)).string()); + } + + for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) { + const TypeList& typeList = pg->types[typeIndex]; + if (typeList.isEmpty()) { + //printf(" type %d NULL\n", (int)typeIndex); + continue; + } + const Type* typeConfigs = typeList[0]; + const size_t NTC = typeConfigs->configs.size(); + printf(" type %d configCount=%d entryCount=%d\n", + (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); + if (typeConfigs->typeSpecFlags != NULL) { + for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) { + uint32_t resID = (0xff000000 & ((pg->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + // Since we are creating resID without actually + // iterating over them, we have no idea which is a + // dynamic reference. We must check. + pg->dynamicRefTable.lookupResourceId(&resID); + + resource_name resName; + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); + } else { + name8 = String8(resName.name, resName.nameLen); + } + printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", + resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + type8.string(), name8.string(), + dtohl(typeConfigs->typeSpecFlags[entryIndex])); + } else { + printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); + } + } + } + for (size_t configIndex=0; configIndex<NTC; configIndex++) { + const ResTable_type* type = typeConfigs->configs[configIndex]; + if ((((uint64_t)type)&0x3) != 0) { + printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } - const size_t NTC = typeConfigs->configs.size(); - printf(" type %d configCount=%d entryCount=%d\n", - (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); - if (typeConfigs->typeSpecFlags != NULL) { - for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) { - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - // Since we are creating resID without actually - // iterating over them, we have no idea which is a - // dynamic reference. We must check. - pg->dynamicRefTable.lookupResourceId(&resID); - - resource_name resName; - if (this->getResourceName(resID, true, &resName)) { - String8 type8; - String8 name8; - if (resName.type8 != NULL) { - type8 = String8(resName.type8, resName.typeLen); - } else { - type8 = String8(resName.type, resName.typeLen); - } - if (resName.name8 != NULL) { - name8 = String8(resName.name8, resName.nameLen); - } else { - name8 = String8(resName.name, resName.nameLen); - } - printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", - resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - type8.string(), name8.string(), - dtohl(typeConfigs->typeSpecFlags[entryIndex])); + String8 configStr = type->config.toString(); + printf(" config %s:\n", configStr.size() > 0 + ? configStr.string() : "(default)"); + size_t entryCount = dtohl(type->entryCount); + uint32_t entriesStart = dtohl(type->entriesStart); + if ((entriesStart&0x3) != 0) { + printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart); + continue; + } + uint32_t typeSize = dtohl(type->header.size); + if ((typeSize&0x3) != 0) { + printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize); + continue; + } + for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { + + const uint8_t* const end = ((const uint8_t*)type) + + dtohl(type->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)type) + dtohs(type->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + continue; + } + + uint32_t resID = (0xff000000 & ((pg->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + pg->dynamicRefTable.lookupResourceId(&resID); + resource_name resName; + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); } else { - printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); + name8 = String8(resName.name, resName.nameLen); } + printf(" resource 0x%08x %s:%s/%s: ", resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + type8.string(), name8.string()); + } else { + printf(" INVALID RESOURCE 0x%08x: ", resID); } - } - for (size_t configIndex=0; configIndex<NTC; configIndex++) { - const ResTable_type* type = typeConfigs->configs[configIndex]; - if ((((uint64_t)type)&0x3) != 0) { - printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); + if ((thisOffset&0x3) != 0) { + printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset); continue; } - String8 configStr = type->config.toString(); - printf(" config %s:\n", configStr.size() > 0 - ? configStr.string() : "(default)"); - size_t entryCount = dtohl(type->entryCount); - uint32_t entriesStart = dtohl(type->entriesStart); - if ((entriesStart&0x3) != 0) { - printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart); + if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { + printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n", + entriesStart, thisOffset, typeSize); continue; } - uint32_t typeSize = dtohl(type->header.size); - if ((typeSize&0x3) != 0) { - printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize); + + const ResTable_entry* ent = (const ResTable_entry*) + (((const uint8_t*)type) + entriesStart + thisOffset); + if (((entriesStart + thisOffset)&0x3) != 0) { + printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n", + (entriesStart + thisOffset)); continue; } - for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { - const uint8_t* const end = ((const uint8_t*)type) - + dtohl(type->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)type) + dtohs(type->header.headerSize)); + uintptr_t esize = dtohs(ent->size); + if ((esize&0x3) != 0) { + printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize); + continue; + } + if ((thisOffset+esize) > typeSize) { + printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n", + entriesStart, thisOffset, (void *)esize, typeSize); + continue; + } - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - continue; - } + const Res_value* valuePtr = NULL; + const ResTable_map_entry* bagPtr = NULL; + Res_value value; + if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { + printf("<bag>"); + bagPtr = (const ResTable_map_entry*)ent; + } else { + valuePtr = (const Res_value*) + (((const uint8_t*)ent) + esize); + value.copyFrom_dtoh(*valuePtr); + printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", + (int)value.dataType, (int)value.data, + (int)value.size, (int)value.res0); + } - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - pg->dynamicRefTable.lookupResourceId(&resID); - resource_name resName; - if (this->getResourceName(resID, true, &resName)) { - String8 type8; - String8 name8; - if (resName.type8 != NULL) { - type8 = String8(resName.type8, resName.typeLen); - } else { - type8 = String8(resName.type, resName.typeLen); - } - if (resName.name8 != NULL) { - name8 = String8(resName.name8, resName.nameLen); - } else { - name8 = String8(resName.name, resName.nameLen); + if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { + printf(" (PUBLIC)"); + } + printf("\n"); + + if (inclValues) { + if (valuePtr != NULL) { + printf(" "); + print_value(typeConfigs->package, value); + } else if (bagPtr != NULL) { + const int N = dtohl(bagPtr->count); + const uint8_t* baseMapPtr = (const uint8_t*)ent; + size_t mapOffset = esize; + const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); + const uint32_t parent = dtohl(bagPtr->parent.ident); + uint32_t resolvedParent = parent; + status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent); + if (err != NO_ERROR) { + resolvedParent = 0; } - printf(" resource 0x%08x %s:%s/%s: ", resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - type8.string(), name8.string()); - } else { - printf(" INVALID RESOURCE 0x%08x: ", resID); - } - if ((thisOffset&0x3) != 0) { - printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset); - continue; - } - if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { - printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n", - entriesStart, thisOffset, typeSize); - continue; - } - - const ResTable_entry* ent = (const ResTable_entry*) - (((const uint8_t*)type) + entriesStart + thisOffset); - if (((entriesStart + thisOffset)&0x3) != 0) { - printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n", - (entriesStart + thisOffset)); - continue; - } - - uintptr_t esize = dtohs(ent->size); - if ((esize&0x3) != 0) { - printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize); - continue; - } - if ((thisOffset+esize) > typeSize) { - printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n", - entriesStart, thisOffset, (void *)esize, typeSize); - continue; - } - - const Res_value* valuePtr = NULL; - const ResTable_map_entry* bagPtr = NULL; - Res_value value; - if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { - printf("<bag>"); - bagPtr = (const ResTable_map_entry*)ent; - } else { - valuePtr = (const Res_value*) - (((const uint8_t*)ent) + esize); - value.copyFrom_dtoh(*valuePtr); - printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", - (int)value.dataType, (int)value.data, - (int)value.size, (int)value.res0); - } - - if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { - printf(" (PUBLIC)"); - } - printf("\n"); - - if (inclValues) { - if (valuePtr != NULL) { - printf(" "); - print_value(pkg, value); - } else if (bagPtr != NULL) { - const int N = dtohl(bagPtr->count); - const uint8_t* baseMapPtr = (const uint8_t*)ent; - size_t mapOffset = esize; - const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - const uint32_t parent = dtohl(bagPtr->parent.ident); - uint32_t resolvedParent = parent; - status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent); - if (err != NO_ERROR) { - resolvedParent = 0; - } - printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n", - parent, resolvedParent, N); - for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { - printf(" #%i (Key=0x%08x): ", - i, dtohl(mapPtr->name.ident)); - value.copyFrom_dtoh(mapPtr->value); - print_value(pkg, value); - const size_t size = dtohs(mapPtr->value.size); - mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); - mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - } + printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n", + parent, resolvedParent, N); + for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { + printf(" #%i (Key=0x%08x): ", + i, dtohl(mapPtr->name.ident)); + value.copyFrom_dtoh(mapPtr->value); + print_value(typeConfigs->package, value); + const size_t size = dtohs(mapPtr->value.size); + mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); + mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); } } } diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp new file mode 100644 index 000000000000..8929b66bbdc4 --- /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 %p", mIndex, (void*) 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 9e9649cf3675..4ff6eecf1685 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -1,33 +1,66 @@ -# Build the unit tests. +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ========================================================== +# Setup some common variables for the different build +# targets here. +# ========================================================== LOCAL_PATH:= $(call my-dir) +testFiles := \ + ByteBucketArray_test.cpp \ + Idmap_test.cpp \ + ResourceTypes_test.cpp \ + ResTable_test.cpp \ + Split_test.cpp \ + TypeWrappers_test.cpp \ + ZipUtils_test.cpp + +# ========================================================== +# Build the host tests: libandroidfw_tests +# ========================================================== +include $(CLEAR_VARS) + +LOCAL_MODULE := libandroidfw_tests + +LOCAL_SRC_FILES := $(testFiles) +LOCAL_STATIC_LIBRARIES := \ + libandroidfw \ + libutils \ + libcutils \ + liblog + +include $(BUILD_HOST_NATIVE_TEST) + + +# ========================================================== +# Build the device tests: libandroidfw_tests +# ========================================================== include $(CLEAR_VARS) -# Build the unit tests. -test_src_files := \ +LOCAL_MODULE := libandroidfw_tests + +LOCAL_SRC_FILES := $(testFiles) \ BackupData_test.cpp \ - ObbFile_test.cpp \ - ZipUtils_test.cpp \ - ResourceTypes_test.cpp + ObbFile_test.cpp -shared_libraries := \ +LOCAL_SHARED_LIBRARIES := \ libandroidfw \ libcutils \ libutils \ libui \ libstlport -static_libraries := \ - libgtest \ - libgtest_main - -$(foreach file,$(test_src_files), \ - $(eval include $(CLEAR_VARS)) \ - $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ - $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ - $(eval LOCAL_SRC_FILES := $(file)) \ - $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ - $(eval include $(BUILD_NATIVE_TEST)) \ -) - -# Build the manual test programs. -include $(call all-makefiles-under, $(LOCAL_PATH)) +include $(BUILD_NATIVE_TEST) diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp new file mode 100644 index 000000000000..376e79c6e7cb --- /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 000000000000..d829b7603ad8 --- /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 000000000000..54d42c39752b --- /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 4888b4a2ea7b..6041e08009e3 100644 --- a/libs/androidfw/tests/ResourceTypes_test.cpp +++ b/libs/androidfw/tests/ResourceTypes_test.cpp @@ -64,8 +64,8 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) { config.packLanguage("eng"); // 1-00110-01 101-00100 - EXPECT_EQ(0x99, config.language[0]); - EXPECT_EQ(0xa4, config.language[1]); + EXPECT_EQ('\x99', config.language[0]); + EXPECT_EQ('\xA4', config.language[1]); char out[4] = { 1, 1, 1, 1}; config.unpackLanguage(out); diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp new file mode 100644 index 000000000000..dbfdeae04d8e --- /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 000000000000..75a233acad26 --- /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 000000000000..d69abe5d0f11 --- /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 000000000000..c05cfb043024 --- /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 000000000000..a56ac18e900b --- /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 000000000000..6532076d3493 --- /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 000000000000..237342c8dc40 --- /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 000000000000..9d523071dd07 --- /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 000000000000..662eda6a4ed5 --- /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 000000000000..a56ac18e900b --- /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 000000000000..87cf6de4c933 --- /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 000000000000..5bd98b28409d --- /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 000000000000..227e88973cc7 --- /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 000000000000..e0321e9bf492 --- /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 000000000000..6f6a41626e0f --- /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/Caches.cpp b/libs/hwui/Caches.cpp index 6fd9999d132d..402f28bd3c5a 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -565,11 +565,8 @@ void Caches::deleteTexture(GLuint texture) { // 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 @@ void Caches::resetBoundTextures() { 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 b4b5927b6511..83a5d9aafa3c 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -261,6 +261,11 @@ public: 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 1cb87f2c6bd2..8b32c40600eb 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -33,6 +33,7 @@ NullDamageAccumulator* NullDamageAccumulator::instance() { } enum TransformType { + TransformInvalid = 0, TransformRenderNode, TransformMatrix4, TransformNone, @@ -56,6 +57,7 @@ DamageAccumulator::DamageAccumulator() { 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 @@ void DamageAccumulator::popTransform() { 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 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { } 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 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { 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/Layer.cpp b/libs/hwui/Layer.cpp index 6a2ef2a8f4d1..1002e1374e32 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -185,6 +185,7 @@ void Layer::deleteTexture() { } void Layer::clearTexture() { + caches.unbindTexture(texture.id); texture.id = 0; } diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index df9aee55eed5..a5fd375db2bc 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -334,8 +334,10 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) { 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/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index 59e15e13a8c5..310b1075f5d5 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -57,17 +57,26 @@ namespace uirenderer { #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 @@ struct PaintInfo { 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 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo 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 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo // 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 @@ void PathTessellator::recursiveCubicBezierVertices( 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 @@ void PathTessellator::recursiveQuadraticBezierVertices( 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 378183a7cb5a..05f4edf6e75e 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -117,8 +117,8 @@ void RenderNode::prepareTree(TreeInfo& info) { } 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? diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 8c6cc9e967fc..227d56eb3bd6 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 @@ public: } 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 @@ public: } 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 @@ public: 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/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 60b4b96012f5..1001cae0ff70 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -288,19 +288,19 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo switch (bitmap->config()) { case SkBitmap::kA8_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), + uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); texture->blend = true; break; case SkBitmap::kRGB_565_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); - uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), + uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels()); texture->blend = false; break; case SkBitmap::kARGB_8888_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); - uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), + uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); // Do this after calling getPixels() to make sure Skia's deferred // decoding happened @@ -340,27 +340,49 @@ void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap, SkCanvas canvas(rgbaBitmap); canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); - uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height, - GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); + uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(), + width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); } -void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, +void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data) { - // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer - // if the stride doesn't match the width const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength(); - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); - } + if ((stride == width) || useStride) { + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + } - if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); + } + + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); - } + // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer + // if the stride doesn't match the width + + GLvoid * temp = (GLvoid *) malloc(width * height * bpp); + if (!temp) return; + + uint8_t * pDst = (uint8_t *)temp; + uint8_t * pSrc = (uint8_t *)data; + for (GLsizei i = 0; i < height; i++) { + memcpy(pDst, pSrc, width * bpp); + pDst += width * bpp; + pSrc += stride * bpp; + } + + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); + } - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + free(temp); } } diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index e5b5c1a0048f..61db5b01f277 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -142,7 +142,7 @@ private: void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false); void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height); - void uploadToTexture(bool resize, GLenum format, GLsizei stride, + void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data); void init(); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 440f9659de12..dc1951beec72 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -537,9 +537,12 @@ void CanvasContext::invokeFunctor(Functor* functor) { requireGlContext(); mode = DrawGlInfo::kModeProcess; } - (*functor)(mode, NULL); if (mCanvas) { + mCanvas->interrupt(); + } + (*functor)(mode, NULL); + if (mCanvas) { mCanvas->resume(); } } diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index f70110cd5910..bdd119582add 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -583,7 +583,8 @@ public class Location implements Parcelable { } /** - * 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 @@ public class Location implements Parcelable { } /** - * 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 4b4be1bbcb65..e05aef06e238 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -152,6 +152,8 @@ public class AudioFormat { switch (audioFormat) { case ENCODING_PCM_8BIT: return 1; + case ENCODING_PCM_FLOAT: + return 4; case ENCODING_PCM_16BIT: case ENCODING_DEFAULT: return 2; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 6280fdeca01f..2f1e11efb0f7 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -50,12 +50,6 @@ import java.util.ArrayList; */ public class AudioManager { - // If we should use the new sessions APIs. - private final static boolean USE_SESSIONS = true; - // If we should use the legacy APIs. If both are true information will be - // duplicated through both paths. Currently this flag isn't used. - private final static boolean USE_LEGACY = true; - private final Context mContext; private long mVolumeKeyUpTime; private final boolean mUseMasterVolume; @@ -483,17 +477,8 @@ public class AudioManager { * or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}. */ public void dispatchMediaKeyEvent(KeyEvent keyEvent) { - if (USE_SESSIONS) { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); - helper.sendMediaButtonEvent(keyEvent, false); - } else { - IAudioService service = getService(); - try { - service.dispatchMediaKeyEvent(keyEvent); - } catch (RemoteException e) { - Log.e(TAG, "dispatchMediaKeyEvent threw exception ", e); - } - } + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + helper.sendMediaButtonEvent(keyEvent, false); } /** @@ -644,12 +629,8 @@ public class AudioManager { if (mUseMasterVolume) { service.adjustMasterVolume(direction, flags, mContext.getOpPackageName()); } else { - if (USE_SESSIONS) { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); - helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags); - } else { - service.adjustVolume(direction, flags, mContext.getOpPackageName()); - } + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags); } } catch (RemoteException e) { Log.e(TAG, "Dead object in adjustVolume", e); @@ -679,13 +660,8 @@ public class AudioManager { if (mUseMasterVolume) { service.adjustMasterVolume(direction, flags, mContext.getOpPackageName()); } else { - if (USE_SESSIONS) { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); - helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags); - } else { - service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, - mContext.getOpPackageName()); - } + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags); } } catch (RemoteException e) { Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e); @@ -1425,7 +1401,12 @@ public class AudioManager { * <var>false</var> to turn mute off */ public void setMicrophoneMute(boolean on){ - AudioSystem.muteMicrophone(on); + IAudioService service = getService(); + try { + service.setMicrophoneMute(on, mContext.getOpPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Dead object in setMicrophoneMute", e); + } } /** @@ -2210,16 +2191,15 @@ public class AudioManager { } IAudioService service = getService(); try { - // pi != null + // 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); } - if (USE_SESSIONS) { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); - helper.addMediaButtonListener(pi, mContext); - } + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + helper.addMediaButtonListener(pi, mContext); } /** @@ -2293,10 +2273,8 @@ public class AudioManager { } catch (RemoteException e) { Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e); } - if (USE_SESSIONS) { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); - helper.removeMediaButtonListener(pi); - } + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + helper.removeMediaButtonListener(pi); } /** @@ -2310,20 +2288,7 @@ public class AudioManager { if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) { return; } - IAudioService service = getService(); - try { - int rcseId = service.registerRemoteControlClient( - rcClient.getRcMediaIntent(), /* mediaIntent */ - rcClient.getIRemoteControlClient(),/* rcClient */ - // used to match media button event receiver and audio focus - mContext.getPackageName()); /* packageName */ - rcClient.setRcseId(rcseId); - } catch (RemoteException e) { - Log.e(TAG, "Dead object in registerRemoteControlClient"+e); - } - if (USE_SESSIONS) { - rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext)); - } + rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext)); } /** @@ -2336,16 +2301,7 @@ public class AudioManager { if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) { return; } - IAudioService service = getService(); - try { - service.unregisterRemoteControlClient(rcClient.getRcMediaIntent(), /* mediaIntent */ - rcClient.getIRemoteControlClient()); /* rcClient */ - } catch (RemoteException e) { - Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e); - } - if (USE_SESSIONS) { - rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext)); - } + rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext)); } /** @@ -2363,25 +2319,8 @@ public class AudioManager { if (rctlr == null) { return false; } - if (USE_SESSIONS) { - rctlr.startListeningToSessions(); - return true; - } else { - IAudioService service = getService(); - final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener(); - final ComponentName listenerComponent = new ComponentName(mContext, l.getClass()); - try { - int[] artworkDimensions = rctlr.getArtworkSize(); - boolean reg = service.registerRemoteController(rctlr.getRcDisplay(), - artworkDimensions[0]/* w */, artworkDimensions[1]/* h */, - listenerComponent); - rctlr.setIsRegistered(reg); - return reg; - } catch (RemoteException e) { - Log.e(TAG, "Dead object in registerRemoteController " + e); - return false; - } - } + rctlr.startListeningToSessions(); + return true; } /** @@ -2393,17 +2332,7 @@ public class AudioManager { if (rctlr == null) { return; } - if (USE_SESSIONS) { - rctlr.stopListeningToSessions(); - } else { - IAudioService service = getService(); - try { - service.unregisterRemoteControlDisplay(rctlr.getRcDisplay()); - rctlr.setIsRegistered(false); - } catch (RemoteException e) { - Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e); - } - } + rctlr.stopListeningToSessions(); } /** diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 2f782cca9494..72f4a580532c 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -108,8 +108,7 @@ public class AudioService extends IAudioService.Stub { /** Debug volumes */ protected static final boolean DEBUG_VOL = false; - /** Reroute calls to media session apis */ - private static final boolean USE_SESSIONS = true; + /** debug calls to media session apis */ private static final boolean DEBUG_SESSIONS = true; /** Allow volume changes to set ringer mode to silent? */ @@ -1437,6 +1436,16 @@ public class AudioService extends IAudioService.Stub { } } + /** @see AudioManager#setMicrophoneMute(boolean) */ + public void setMicrophoneMute(boolean on, String callingPackage) { + if (mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, Binder.getCallingUid(), + callingPackage) != AppOpsManager.MODE_ALLOWED) { + return; + } + + AudioSystem.muteMicrophone(on); + } + /** @see AudioManager#getRingerMode() */ public int getRingerMode() { synchronized(mSettingsLock) { @@ -4474,27 +4483,19 @@ public class AudioService extends IAudioService.Stub { } public void dispatchMediaKeyEvent(KeyEvent keyEvent) { - if (USE_SESSIONS) { - if (DEBUG_SESSIONS) { - int pid = getCallingPid(); - Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid); - } - MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false); - } else { - mMediaFocusControl.dispatchMediaKeyEvent(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 (USE_SESSIONS) { - if (DEBUG_SESSIONS) { - int pid = getCallingPid(); - Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid); - } - MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true); - } else { - mMediaFocusControl.dispatchMediaKeyEventUnderWakelock(keyEvent); + if (DEBUG_SESSIONS) { + int pid = getCallingPid(); + Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid); } + MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true); } //========================================================================================== diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java new file mode 100644 index 000000000000..ec33c5c53e6b --- /dev/null +++ b/media/java/android/media/ClosedCaptionRenderer.java @@ -0,0 +1,1464 @@ +/* + * 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.media; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.os.Parcel; +import android.text.ParcelableSpan; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.style.CharacterStyle; +import android.text.style.StyleSpan; +import android.text.style.UnderlineSpan; +import android.text.style.UpdateAppearance; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.CaptioningManager; +import android.view.accessibility.CaptioningManager.CaptionStyle; +import android.view.accessibility.CaptioningManager.CaptioningChangeListener; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Vector; + +/** @hide */ +public class ClosedCaptionRenderer extends SubtitleController.Renderer { + private final Context mContext; + private ClosedCaptionWidget mRenderingWidget; + + public ClosedCaptionRenderer(Context context) { + mContext = context; + } + + @Override + public boolean supports(MediaFormat format) { + if (format.containsKey(MediaFormat.KEY_MIME)) { + return format.getString(MediaFormat.KEY_MIME).equals( + MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608); + } + return false; + } + + @Override + public SubtitleTrack createTrack(MediaFormat format) { + if (mRenderingWidget == null) { + mRenderingWidget = new ClosedCaptionWidget(mContext); + } + return new ClosedCaptionTrack(mRenderingWidget, format); + } +} + +/** @hide */ +class ClosedCaptionTrack extends SubtitleTrack { + private final ClosedCaptionWidget mRenderingWidget; + private final CCParser mCCParser; + + ClosedCaptionTrack(ClosedCaptionWidget renderingWidget, MediaFormat format) { + super(format); + + mRenderingWidget = renderingWidget; + mCCParser = new CCParser(renderingWidget); + } + + @Override + public void onData(byte[] data, boolean eos, long runID) { + mCCParser.parse(data); + } + + @Override + public RenderingWidget getRenderingWidget() { + return mRenderingWidget; + } + + @Override + public void updateView(Vector<Cue> activeCues) { + // Overriding with NO-OP, CC rendering by-passes this + } +} + +/** + * @hide + * + * CCParser processes CEA-608 closed caption data. + * + * It calls back into OnDisplayChangedListener upon + * display change with styled text for rendering. + * + */ +class CCParser { + public static final int MAX_ROWS = 15; + public static final int MAX_COLS = 32; + + private static final String TAG = "CCParser"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final int INVALID = -1; + + // EIA-CEA-608: Table 70 - Control Codes + private static final int RCL = 0x20; + private static final int BS = 0x21; + private static final int AOF = 0x22; + private static final int AON = 0x23; + private static final int DER = 0x24; + private static final int RU2 = 0x25; + private static final int RU3 = 0x26; + private static final int RU4 = 0x27; + private static final int FON = 0x28; + private static final int RDC = 0x29; + private static final int TR = 0x2a; + private static final int RTD = 0x2b; + private static final int EDM = 0x2c; + private static final int CR = 0x2d; + private static final int ENM = 0x2e; + private static final int EOC = 0x2f; + + // Transparent Space + private static final char TS = '\u00A0'; + + // Captioning Modes + private static final int MODE_UNKNOWN = 0; + private static final int MODE_PAINT_ON = 1; + private static final int MODE_ROLL_UP = 2; + private static final int MODE_POP_ON = 3; + private static final int MODE_TEXT = 4; + + private final DisplayListener mListener; + + private int mMode = MODE_PAINT_ON; + private int mRollUpSize = 4; + + private CCMemory mDisplay = new CCMemory(); + private CCMemory mNonDisplay = new CCMemory(); + private CCMemory mTextMem = new CCMemory(); + + CCParser(DisplayListener listener) { + mListener = listener; + } + + void parse(byte[] data) { + CCData[] ccData = CCData.fromByteArray(data); + + for (int i = 0; i < ccData.length; i++) { + if (DEBUG) { + Log.d(TAG, ccData[i].toString()); + } + + if (handleCtrlCode(ccData[i]) + || handleTabOffsets(ccData[i]) + || handlePACCode(ccData[i]) + || handleMidRowCode(ccData[i])) { + continue; + } + + handleDisplayableChars(ccData[i]); + } + } + + interface DisplayListener { + public void onDisplayChanged(SpannableStringBuilder[] styledTexts); + public CaptionStyle getCaptionStyle(); + } + + private CCMemory getMemory() { + // get the CC memory to operate on for current mode + switch (mMode) { + case MODE_POP_ON: + return mNonDisplay; + case MODE_TEXT: + // TODO(chz): support only caption mode for now, + // in text mode, dump everything to text mem. + return mTextMem; + case MODE_PAINT_ON: + case MODE_ROLL_UP: + return mDisplay; + default: + Log.w(TAG, "unrecoginized mode: " + mMode); + } + return mDisplay; + } + + private boolean handleDisplayableChars(CCData ccData) { + if (!ccData.isDisplayableChar()) { + return false; + } + + // Extended char includes 1 automatic backspace + if (ccData.isExtendedChar()) { + getMemory().bs(); + } + + getMemory().writeText(ccData.getDisplayText()); + + if (mMode == MODE_PAINT_ON || mMode == MODE_ROLL_UP) { + updateDisplay(); + } + + return true; + } + + private boolean handleMidRowCode(CCData ccData) { + StyleCode m = ccData.getMidRow(); + if (m != null) { + getMemory().writeMidRowCode(m); + return true; + } + return false; + } + + private boolean handlePACCode(CCData ccData) { + PAC pac = ccData.getPAC(); + + if (pac != null) { + if (mMode == MODE_ROLL_UP) { + getMemory().moveBaselineTo(pac.getRow(), mRollUpSize); + } + getMemory().writePAC(pac); + return true; + } + + return false; + } + + private boolean handleTabOffsets(CCData ccData) { + int tabs = ccData.getTabOffset(); + + if (tabs > 0) { + getMemory().tab(tabs); + return true; + } + + return false; + } + + private boolean handleCtrlCode(CCData ccData) { + int ctrlCode = ccData.getCtrlCode(); + switch(ctrlCode) { + case RCL: + // select pop-on style + mMode = MODE_POP_ON; + break; + case BS: + getMemory().bs(); + break; + case DER: + getMemory().der(); + break; + case RU2: + case RU3: + case RU4: + mRollUpSize = (ctrlCode - 0x23); + // erase memory if currently in other style + if (mMode != MODE_ROLL_UP) { + mDisplay.erase(); + mNonDisplay.erase(); + } + // select roll-up style + mMode = MODE_ROLL_UP; + break; + case FON: + Log.i(TAG, "Flash On"); + break; + case RDC: + // select paint-on style + mMode = MODE_PAINT_ON; + break; + case TR: + mMode = MODE_TEXT; + mTextMem.erase(); + break; + case RTD: + mMode = MODE_TEXT; + break; + case EDM: + // erase display memory + mDisplay.erase(); + updateDisplay(); + break; + case CR: + if (mMode == MODE_ROLL_UP) { + getMemory().rollUp(mRollUpSize); + } else { + getMemory().cr(); + } + if (mMode == MODE_ROLL_UP) { + updateDisplay(); + } + break; + case ENM: + // erase non-display memory + mNonDisplay.erase(); + break; + case EOC: + // swap display/non-display memory + swapMemory(); + // switch to pop-on style + mMode = MODE_POP_ON; + updateDisplay(); + break; + case INVALID: + default: + // not handled + return false; + } + + // handled + return true; + } + + private void updateDisplay() { + if (mListener != null) { + CaptionStyle captionStyle = mListener.getCaptionStyle(); + mListener.onDisplayChanged(mDisplay.getStyledText(captionStyle)); + } + } + + private void swapMemory() { + CCMemory temp = mDisplay; + mDisplay = mNonDisplay; + mNonDisplay = temp; + } + + private static class StyleCode { + static final int COLOR_WHITE = 0; + static final int COLOR_GREEN = 1; + static final int COLOR_BLUE = 2; + static final int COLOR_CYAN = 3; + static final int COLOR_RED = 4; + static final int COLOR_YELLOW = 5; + static final int COLOR_MAGENTA = 6; + static final int COLOR_INVALID = 7; + + static final int STYLE_ITALICS = 0x00000001; + static final int STYLE_UNDERLINE = 0x00000002; + + static final String[] mColorMap = { + "WHITE", "GREEN", "BLUE", "CYAN", "RED", "YELLOW", "MAGENTA", "INVALID" + }; + + final int mStyle; + final int mColor; + + static StyleCode fromByte(byte data2) { + int style = 0; + int color = (data2 >> 1) & 0x7; + + if ((data2 & 0x1) != 0) { + style |= STYLE_UNDERLINE; + } + + if (color == COLOR_INVALID) { + // WHITE ITALICS + color = COLOR_WHITE; + style |= STYLE_ITALICS; + } + + return new StyleCode(style, color); + } + + StyleCode(int style, int color) { + mStyle = style; + mColor = color; + } + + boolean isItalics() { + return (mStyle & STYLE_ITALICS) != 0; + } + + boolean isUnderline() { + return (mStyle & STYLE_UNDERLINE) != 0; + } + + int getColor() { + return mColor; + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("{"); + str.append(mColorMap[mColor]); + if ((mStyle & STYLE_ITALICS) != 0) { + str.append(", ITALICS"); + } + if ((mStyle & STYLE_UNDERLINE) != 0) { + str.append(", UNDERLINE"); + } + str.append("}"); + + return str.toString(); + } + } + + private static class PAC extends StyleCode { + final int mRow; + final int mCol; + + static PAC fromBytes(byte data1, byte data2) { + int[] rowTable = {11, 1, 3, 12, 14, 5, 7, 9}; + int row = rowTable[data1 & 0x07] + ((data2 & 0x20) >> 5); + int style = 0; + if ((data2 & 1) != 0) { + style |= STYLE_UNDERLINE; + } + if ((data2 & 0x10) != 0) { + // indent code + int indent = (data2 >> 1) & 0x7; + return new PAC(row, indent * 4, style, COLOR_WHITE); + } else { + // style code + int color = (data2 >> 1) & 0x7; + + if (color == COLOR_INVALID) { + // WHITE ITALICS + color = COLOR_WHITE; + style |= STYLE_ITALICS; + } + return new PAC(row, -1, style, color); + } + } + + PAC(int row, int col, int style, int color) { + super(style, color); + mRow = row; + mCol = col; + } + + boolean isIndentPAC() { + return (mCol >= 0); + } + + int getRow() { + return mRow; + } + + int getCol() { + return mCol; + } + + @Override + public String toString() { + return String.format("{%d, %d}, %s", + mRow, mCol, super.toString()); + } + } + + /* CCLineBuilder keeps track of displayable chars, as well as + * MidRow styles and PACs, for a single line of CC memory. + * + * It generates styled text via getStyledText() method. + */ + private static class CCLineBuilder { + private final StringBuilder mDisplayChars; + private final StyleCode[] mMidRowStyles; + private final StyleCode[] mPACStyles; + + CCLineBuilder(String str) { + mDisplayChars = new StringBuilder(str); + mMidRowStyles = new StyleCode[mDisplayChars.length()]; + mPACStyles = new StyleCode[mDisplayChars.length()]; + } + + void setCharAt(int index, char ch) { + mDisplayChars.setCharAt(index, ch); + mMidRowStyles[index] = null; + } + + void setMidRowAt(int index, StyleCode m) { + mDisplayChars.setCharAt(index, ' '); + mMidRowStyles[index] = m; + } + + void setPACAt(int index, PAC pac) { + mPACStyles[index] = pac; + } + + char charAt(int index) { + return mDisplayChars.charAt(index); + } + + int length() { + return mDisplayChars.length(); + } + + void applyStyleSpan( + SpannableStringBuilder styledText, + StyleCode s, int start, int end) { + if (s.isItalics()) { + styledText.setSpan( + new StyleSpan(android.graphics.Typeface.ITALIC), + start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + if (s.isUnderline()) { + styledText.setSpan( + new UnderlineSpan(), + start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + SpannableStringBuilder getStyledText(CaptionStyle captionStyle) { + SpannableStringBuilder styledText = new SpannableStringBuilder(mDisplayChars); + int start = -1, next = 0; + int styleStart = -1; + StyleCode curStyle = null; + while (next < mDisplayChars.length()) { + StyleCode newStyle = null; + if (mMidRowStyles[next] != null) { + // apply mid-row style change + newStyle = mMidRowStyles[next]; + } else if (mPACStyles[next] != null + && (styleStart < 0 || start < 0)) { + // apply PAC style change, only if: + // 1. no style set, or + // 2. style set, but prev char is none-displayable + newStyle = mPACStyles[next]; + } + if (newStyle != null) { + curStyle = newStyle; + if (styleStart >= 0 && start >= 0) { + applyStyleSpan(styledText, newStyle, styleStart, next); + } + styleStart = next; + } + + if (mDisplayChars.charAt(next) != TS) { + if (start < 0) { + start = next; + } + } else if (start >= 0) { + int expandedStart = mDisplayChars.charAt(start) == ' ' ? start : start - 1; + int expandedEnd = mDisplayChars.charAt(next - 1) == ' ' ? next : next + 1; + styledText.setSpan( + new MutableBackgroundColorSpan(captionStyle.backgroundColor), + expandedStart, expandedEnd, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + if (styleStart >= 0) { + applyStyleSpan(styledText, curStyle, styleStart, expandedEnd); + } + start = -1; + } + next++; + } + + return styledText; + } + } + + /* + * CCMemory models a console-style display. + */ + private static class CCMemory { + private final String mBlankLine; + private final CCLineBuilder[] mLines = new CCLineBuilder[MAX_ROWS + 2]; + private int mRow; + private int mCol; + + CCMemory() { + char[] blank = new char[MAX_COLS + 2]; + Arrays.fill(blank, TS); + mBlankLine = new String(blank); + } + + void erase() { + // erase all lines + for (int i = 0; i < mLines.length; i++) { + mLines[i] = null; + } + mRow = MAX_ROWS; + mCol = 1; + } + + void der() { + if (mLines[mRow] != null) { + for (int i = 0; i < mCol; i++) { + if (mLines[mRow].charAt(i) != TS) { + for (int j = mCol; j < mLines[mRow].length(); j++) { + mLines[j].setCharAt(j, TS); + } + return; + } + } + mLines[mRow] = null; + } + } + + void tab(int tabs) { + moveCursorByCol(tabs); + } + + void bs() { + moveCursorByCol(-1); + if (mLines[mRow] != null) { + mLines[mRow].setCharAt(mCol, TS); + if (mCol == MAX_COLS - 1) { + // Spec recommendation: + // if cursor was at col 32, move cursor + // back to col 31 and erase both col 31&32 + mLines[mRow].setCharAt(MAX_COLS, TS); + } + } + } + + void cr() { + moveCursorTo(mRow + 1, 1); + } + + void rollUp(int windowSize) { + int i; + for (i = 0; i <= mRow - windowSize; i++) { + mLines[i] = null; + } + int startRow = mRow - windowSize + 1; + if (startRow < 1) { + startRow = 1; + } + for (i = startRow; i < mRow; i++) { + mLines[i] = mLines[i + 1]; + } + for (i = mRow; i < mLines.length; i++) { + // clear base row + mLines[i] = null; + } + // default to col 1, in case PAC is not sent + mCol = 1; + } + + void writeText(String text) { + for (int i = 0; i < text.length(); i++) { + getLineBuffer(mRow).setCharAt(mCol, text.charAt(i)); + moveCursorByCol(1); + } + } + + void writeMidRowCode(StyleCode m) { + getLineBuffer(mRow).setMidRowAt(mCol, m); + moveCursorByCol(1); + } + + void writePAC(PAC pac) { + if (pac.isIndentPAC()) { + moveCursorTo(pac.getRow(), pac.getCol()); + } else { + moveCursorToRow(pac.getRow()); + } + getLineBuffer(mRow).setPACAt(mCol, pac); + } + + SpannableStringBuilder[] getStyledText(CaptionStyle captionStyle) { + ArrayList<SpannableStringBuilder> rows = + new ArrayList<SpannableStringBuilder>(MAX_ROWS); + for (int i = 1; i <= MAX_ROWS; i++) { + rows.add(mLines[i] != null ? + mLines[i].getStyledText(captionStyle) : null); + } + return rows.toArray(new SpannableStringBuilder[MAX_ROWS]); + } + + private static int clamp(int x, int min, int max) { + return x < min ? min : (x > max ? max : x); + } + + private void moveCursorTo(int row, int col) { + mRow = clamp(row, 1, MAX_ROWS); + mCol = clamp(col, 1, MAX_COLS); + } + + private void moveCursorToRow(int row) { + mRow = clamp(row, 1, MAX_ROWS); + } + + private void moveCursorByCol(int col) { + mCol = clamp(mCol + col, 1, MAX_COLS); + } + + private void moveBaselineTo(int baseRow, int windowSize) { + if (mRow == baseRow) { + return; + } + int actualWindowSize = windowSize; + if (baseRow < actualWindowSize) { + actualWindowSize = baseRow; + } + if (mRow < actualWindowSize) { + actualWindowSize = mRow; + } + + int i; + if (baseRow < mRow) { + // copy from bottom to top row + for (i = actualWindowSize - 1; i >= 0; i--) { + mLines[baseRow - i] = mLines[mRow - i]; + } + } else { + // copy from top to bottom row + for (i = 0; i < actualWindowSize; i++) { + mLines[baseRow - i] = mLines[mRow - i]; + } + } + // clear rest of the rows + for (i = 0; i <= baseRow - windowSize; i++) { + mLines[i] = null; + } + for (i = baseRow + 1; i < mLines.length; i++) { + mLines[i] = null; + } + } + + private CCLineBuilder getLineBuffer(int row) { + if (mLines[row] == null) { + mLines[row] = new CCLineBuilder(mBlankLine); + } + return mLines[row]; + } + } + + /* + * CCData parses the raw CC byte pair into displayable chars, + * misc control codes, Mid-Row or Preamble Address Codes. + */ + private static class CCData { + private final byte mType; + private final byte mData1; + private final byte mData2; + + private static final String[] mCtrlCodeMap = { + "RCL", "BS" , "AOF", "AON", + "DER", "RU2", "RU3", "RU4", + "FON", "RDC", "TR" , "RTD", + "EDM", "CR" , "ENM", "EOC", + }; + + private static final String[] mSpecialCharMap = { + "\u00AE", + "\u00B0", + "\u00BD", + "\u00BF", + "\u2122", + "\u00A2", + "\u00A3", + "\u266A", // Eighth note + "\u00E0", + "\u00A0", // Transparent space + "\u00E8", + "\u00E2", + "\u00EA", + "\u00EE", + "\u00F4", + "\u00FB", + }; + + private static final String[] mSpanishCharMap = { + // Spanish and misc chars + "\u00C1", // A + "\u00C9", // E + "\u00D3", // I + "\u00DA", // O + "\u00DC", // U + "\u00FC", // u + "\u2018", // opening single quote + "\u00A1", // inverted exclamation mark + "*", + "'", + "\u2014", // em dash + "\u00A9", // Copyright + "\u2120", // Servicemark + "\u2022", // round bullet + "\u201C", // opening double quote + "\u201D", // closing double quote + // French + "\u00C0", + "\u00C2", + "\u00C7", + "\u00C8", + "\u00CA", + "\u00CB", + "\u00EB", + "\u00CE", + "\u00CF", + "\u00EF", + "\u00D4", + "\u00D9", + "\u00F9", + "\u00DB", + "\u00AB", + "\u00BB" + }; + + private static final String[] mProtugueseCharMap = { + // Portuguese + "\u00C3", + "\u00E3", + "\u00CD", + "\u00CC", + "\u00EC", + "\u00D2", + "\u00F2", + "\u00D5", + "\u00F5", + "{", + "}", + "\\", + "^", + "_", + "|", + "~", + // German and misc chars + "\u00C4", + "\u00E4", + "\u00D6", + "\u00F6", + "\u00DF", + "\u00A5", + "\u00A4", + "\u2502", // vertical bar + "\u00C5", + "\u00E5", + "\u00D8", + "\u00F8", + "\u250C", // top-left corner + "\u2510", // top-right corner + "\u2514", // lower-left corner + "\u2518", // lower-right corner + }; + + static CCData[] fromByteArray(byte[] data) { + CCData[] ccData = new CCData[data.length / 3]; + + for (int i = 0; i < ccData.length; i++) { + ccData[i] = new CCData( + data[i * 3], + data[i * 3 + 1], + data[i * 3 + 2]); + } + + return ccData; + } + + CCData(byte type, byte data1, byte data2) { + mType = type; + mData1 = data1; + mData2 = data2; + } + + int getCtrlCode() { + if ((mData1 == 0x14 || mData1 == 0x1c) + && mData2 >= 0x20 && mData2 <= 0x2f) { + return mData2; + } + return INVALID; + } + + StyleCode getMidRow() { + // only support standard Mid-row codes, ignore + // optional background/foreground mid-row codes + if ((mData1 == 0x11 || mData1 == 0x19) + && mData2 >= 0x20 && mData2 <= 0x2f) { + return StyleCode.fromByte(mData2); + } + return null; + } + + PAC getPAC() { + if ((mData1 & 0x70) == 0x10 + && (mData2 & 0x40) == 0x40 + && ((mData1 & 0x07) != 0 || (mData2 & 0x20) == 0)) { + return PAC.fromBytes(mData1, mData2); + } + return null; + } + + int getTabOffset() { + if ((mData1 == 0x17 || mData1 == 0x1f) + && mData2 >= 0x21 && mData2 <= 0x23) { + return mData2 & 0x3; + } + return 0; + } + + boolean isDisplayableChar() { + return isBasicChar() || isSpecialChar() || isExtendedChar(); + } + + String getDisplayText() { + String str = getBasicChars(); + + if (str == null) { + str = getSpecialChar(); + + if (str == null) { + str = getExtendedChar(); + } + } + + return str; + } + + private String ctrlCodeToString(int ctrlCode) { + return mCtrlCodeMap[ctrlCode - 0x20]; + } + + private boolean isBasicChar() { + return mData1 >= 0x20 && mData1 <= 0x7f; + } + + private boolean isSpecialChar() { + return ((mData1 == 0x11 || mData1 == 0x19) + && mData2 >= 0x30 && mData2 <= 0x3f); + } + + private boolean isExtendedChar() { + return ((mData1 == 0x12 || mData1 == 0x1A + || mData1 == 0x13 || mData1 == 0x1B) + && mData2 >= 0x20 && mData2 <= 0x3f); + } + + private char getBasicChar(byte data) { + char c; + // replace the non-ASCII ones + switch (data) { + case 0x2A: c = '\u00E1'; break; + case 0x5C: c = '\u00E9'; break; + case 0x5E: c = '\u00ED'; break; + case 0x5F: c = '\u00F3'; break; + case 0x60: c = '\u00FA'; break; + case 0x7B: c = '\u00E7'; break; + case 0x7C: c = '\u00F7'; break; + case 0x7D: c = '\u00D1'; break; + case 0x7E: c = '\u00F1'; break; + case 0x7F: c = '\u2588'; break; // Full block + default: c = (char) data; break; + } + return c; + } + + private String getBasicChars() { + if (mData1 >= 0x20 && mData1 <= 0x7f) { + StringBuilder builder = new StringBuilder(2); + builder.append(getBasicChar(mData1)); + if (mData2 >= 0x20 && mData2 <= 0x7f) { + builder.append(getBasicChar(mData2)); + } + return builder.toString(); + } + + return null; + } + + private String getSpecialChar() { + if ((mData1 == 0x11 || mData1 == 0x19) + && mData2 >= 0x30 && mData2 <= 0x3f) { + return mSpecialCharMap[mData2 - 0x30]; + } + + return null; + } + + private String getExtendedChar() { + if ((mData1 == 0x12 || mData1 == 0x1A) + && mData2 >= 0x20 && mData2 <= 0x3f){ + // 1 Spanish/French char + return mSpanishCharMap[mData2 - 0x20]; + } else if ((mData1 == 0x13 || mData1 == 0x1B) + && mData2 >= 0x20 && mData2 <= 0x3f){ + // 1 Portuguese/German/Danish char + return mProtugueseCharMap[mData2 - 0x20]; + } + + return null; + } + + @Override + public String toString() { + String str; + + if (mData1 < 0x10 && mData2 < 0x10) { + // Null Pad, ignore + return String.format("[%d]Null: %02x %02x", mType, mData1, mData2); + } + + int ctrlCode = getCtrlCode(); + if (ctrlCode != INVALID) { + return String.format("[%d]%s", mType, ctrlCodeToString(ctrlCode)); + } + + int tabOffset = getTabOffset(); + if (tabOffset > 0) { + return String.format("[%d]Tab%d", mType, tabOffset); + } + + PAC pac = getPAC(); + if (pac != null) { + return String.format("[%d]PAC: %s", mType, pac.toString()); + } + + StyleCode m = getMidRow(); + if (m != null) { + return String.format("[%d]Mid-row: %s", mType, m.toString()); + } + + if (isDisplayableChar()) { + return String.format("[%d]Displayable: %s (%02x %02x)", + mType, getDisplayText(), mData1, mData2); + } + + return String.format("[%d]Invalid: %02x %02x", mType, mData1, mData2); + } + } +} + +/** + * @hide + * + * MutableBackgroundColorSpan + * + * This is a mutable version of BackgroundSpan to facilitate text + * rendering with edge styles. + * + */ +class MutableBackgroundColorSpan extends CharacterStyle + implements UpdateAppearance, ParcelableSpan { + private int mColor; + + public MutableBackgroundColorSpan(int color) { + mColor = color; + } + public MutableBackgroundColorSpan(Parcel src) { + mColor = src.readInt(); + } + public void setBackgroundColor(int color) { + mColor = color; + } + public int getBackgroundColor() { + return mColor; + } + @Override + public int getSpanTypeId() { + return TextUtils.BACKGROUND_COLOR_SPAN; + } + @Override + public int describeContents() { + return 0; + } + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mColor); + } + @Override + public void updateDrawState(TextPaint ds) { + ds.bgColor = mColor; + } +} + +/** + * Widget capable of rendering CEA-608 closed captions. + * + * @hide + */ +class ClosedCaptionWidget extends ViewGroup implements + SubtitleTrack.RenderingWidget, + CCParser.DisplayListener { + private static final String TAG = "ClosedCaptionWidget"; + + private static final Rect mTextBounds = new Rect(); + private static final String mDummyText = "1234567890123456789012345678901234"; + private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT; + + /** Captioning manager, used to obtain and track caption properties. */ + private final CaptioningManager mManager; + + /** Callback for rendering changes. */ + private OnChangedListener mListener; + + /** Current caption style. */ + private CaptionStyle mCaptionStyle; + + /* Closed caption layout. */ + private CCLayout mClosedCaptionLayout; + + /** Whether a caption style change listener is registered. */ + private boolean mHasChangeListener; + + public ClosedCaptionWidget(Context context) { + this(context, null); + } + + public ClosedCaptionWidget(Context context, AttributeSet attrs) { + this(context, null, 0); + } + + public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // Cannot render text over video when layer type is hardware. + setLayerType(View.LAYER_TYPE_SOFTWARE, null); + + mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); + mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle()); + + mClosedCaptionLayout = new CCLayout(context); + mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); + addView(mClosedCaptionLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + + requestLayout(); + } + + @Override + public void setOnChangedListener(OnChangedListener listener) { + mListener = listener; + } + + @Override + public void setSize(int width, int height) { + final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); + final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); + + measure(widthSpec, heightSpec); + layout(0, 0, width, height); + } + + @Override + public void setVisible(boolean visible) { + if (visible) { + setVisibility(View.VISIBLE); + } else { + setVisibility(View.GONE); + } + + manageChangeListener(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + manageChangeListener(); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + manageChangeListener(); + } + + @Override + public void onDisplayChanged(SpannableStringBuilder[] styledTexts) { + mClosedCaptionLayout.update(styledTexts); + + if (mListener != null) { + mListener.onChanged(this); + } + } + + @Override + public CaptionStyle getCaptionStyle() { + return mCaptionStyle; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mClosedCaptionLayout.measure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + mClosedCaptionLayout.layout(l, t, r, b); + } + + /** + * Manages whether this renderer is listening for caption style changes. + */ + private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() { + @Override + public void onUserStyleChanged(CaptionStyle userStyle) { + mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle); + mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); + } + }; + + private void manageChangeListener() { + final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE; + if (mHasChangeListener != needsListener) { + mHasChangeListener = needsListener; + + if (needsListener) { + mManager.addCaptioningChangeListener(mCaptioningListener); + } else { + mManager.removeCaptioningChangeListener(mCaptioningListener); + } + } + } + + private static class CCLineBox extends TextView { + private static final float FONT_PADDING_RATIO = 0.75f; + private static final float EDGE_OUTLINE_RATIO = 0.1f; + private static final float EDGE_SHADOW_RATIO = 0.05f; + private float mOutlineWidth; + private float mShadowRadius; + private float mShadowOffset; + + private int mTextColor = Color.WHITE; + private int mBgColor = Color.BLACK; + private int mEdgeType = CaptionStyle.EDGE_TYPE_NONE; + private int mEdgeColor = Color.TRANSPARENT; + + CCLineBox(Context context) { + super(context); + setGravity(Gravity.CENTER); + setBackgroundColor(Color.TRANSPARENT); + setTextColor(Color.WHITE); + setTypeface(Typeface.MONOSPACE); + setVisibility(View.INVISIBLE); + + final Resources res = getContext().getResources(); + + // get the default (will be updated later during measure) + mOutlineWidth = res.getDimensionPixelSize( + com.android.internal.R.dimen.subtitle_outline_width); + mShadowRadius = res.getDimensionPixelSize( + com.android.internal.R.dimen.subtitle_shadow_radius); + mShadowOffset = res.getDimensionPixelSize( + com.android.internal.R.dimen.subtitle_shadow_offset); + } + + void setCaptionStyle(CaptionStyle captionStyle) { + mTextColor = captionStyle.foregroundColor; + mBgColor = captionStyle.backgroundColor; + mEdgeType = captionStyle.edgeType; + mEdgeColor = captionStyle.edgeColor; + + setTextColor(mTextColor); + if (mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) { + setShadowLayer(mShadowRadius, mShadowOffset, mShadowOffset, mEdgeColor); + } else { + setShadowLayer(0, 0, 0, 0); + } + invalidate(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + float fontSize = MeasureSpec.getSize(heightMeasureSpec) + * FONT_PADDING_RATIO; + setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); + + mOutlineWidth = EDGE_OUTLINE_RATIO * fontSize + 1.0f; + mShadowRadius = EDGE_SHADOW_RATIO * fontSize + 1.0f;; + mShadowOffset = mShadowRadius; + + // set font scale in the X direction to match the required width + setScaleX(1.0f); + getPaint().getTextBounds(mDummyText, 0, mDummyText.length(), mTextBounds); + float actualTextWidth = mTextBounds.width(); + float requiredTextWidth = MeasureSpec.getSize(widthMeasureSpec); + setScaleX(requiredTextWidth / actualTextWidth); + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onDraw(Canvas c) { + if (mEdgeType == CaptionStyle.EDGE_TYPE_UNSPECIFIED + || mEdgeType == CaptionStyle.EDGE_TYPE_NONE + || mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) { + // these edge styles don't require a second pass + super.onDraw(c); + return; + } + + if (mEdgeType == CaptionStyle.EDGE_TYPE_OUTLINE) { + drawEdgeOutline(c); + } else { + // Raised or depressed + drawEdgeRaisedOrDepressed(c); + } + } + + private void drawEdgeOutline(Canvas c) { + TextPaint textPaint = getPaint(); + + Paint.Style previousStyle = textPaint.getStyle(); + Paint.Join previousJoin = textPaint.getStrokeJoin(); + float previousWidth = textPaint.getStrokeWidth(); + + setTextColor(mEdgeColor); + textPaint.setStyle(Paint.Style.FILL_AND_STROKE); + textPaint.setStrokeJoin(Paint.Join.ROUND); + textPaint.setStrokeWidth(mOutlineWidth); + + // Draw outline and background only. + super.onDraw(c); + + // Restore original settings. + setTextColor(mTextColor); + textPaint.setStyle(previousStyle); + textPaint.setStrokeJoin(previousJoin); + textPaint.setStrokeWidth(previousWidth); + + // Remove the background. + setBackgroundSpans(Color.TRANSPARENT); + // Draw foreground only. + super.onDraw(c); + // Restore the background. + setBackgroundSpans(mBgColor); + } + + private void drawEdgeRaisedOrDepressed(Canvas c) { + TextPaint textPaint = getPaint(); + + Paint.Style previousStyle = textPaint.getStyle(); + textPaint.setStyle(Paint.Style.FILL); + + final boolean raised = mEdgeType == CaptionStyle.EDGE_TYPE_RAISED; + final int colorUp = raised ? Color.WHITE : mEdgeColor; + final int colorDown = raised ? mEdgeColor : Color.WHITE; + final float offset = mShadowRadius / 2f; + + // Draw background and text with shadow up + setShadowLayer(mShadowRadius, -offset, -offset, colorUp); + super.onDraw(c); + + // Remove the background. + setBackgroundSpans(Color.TRANSPARENT); + + // Draw text with shadow down + setShadowLayer(mShadowRadius, +offset, +offset, colorDown); + super.onDraw(c); + + // Restore settings + textPaint.setStyle(previousStyle); + + // Restore the background. + setBackgroundSpans(mBgColor); + } + + private void setBackgroundSpans(int color) { + CharSequence text = getText(); + if (text instanceof Spannable) { + Spannable spannable = (Spannable) text; + MutableBackgroundColorSpan[] bgSpans = spannable.getSpans( + 0, spannable.length(), MutableBackgroundColorSpan.class); + for (int i = 0; i < bgSpans.length; i++) { + bgSpans[i].setBackgroundColor(color); + } + } + } + } + + private static class CCLayout extends LinearLayout { + private static final int MAX_ROWS = CCParser.MAX_ROWS; + private static final float SAFE_AREA_RATIO = 0.9f; + + private final CCLineBox[] mLineBoxes = new CCLineBox[MAX_ROWS]; + + CCLayout(Context context) { + super(context); + setGravity(Gravity.START); + setOrientation(LinearLayout.VERTICAL); + for (int i = 0; i < MAX_ROWS; i++) { + mLineBoxes[i] = new CCLineBox(getContext()); + addView(mLineBoxes[i], LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + } + + void setCaptionStyle(CaptionStyle captionStyle) { + for (int i = 0; i < MAX_ROWS; i++) { + mLineBoxes[i].setCaptionStyle(captionStyle); + } + } + + void update(SpannableStringBuilder[] textBuffer) { + for (int i = 0; i < MAX_ROWS; i++) { + if (textBuffer[i] != null) { + mLineBoxes[i].setText(textBuffer[i], TextView.BufferType.SPANNABLE); + mLineBoxes[i].setVisibility(View.VISIBLE); + } else { + mLineBoxes[i].setVisibility(View.INVISIBLE); + } + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + int safeWidth = getMeasuredWidth(); + int safeHeight = getMeasuredHeight(); + + // CEA-608 assumes 4:3 video + if (safeWidth * 3 >= safeHeight * 4) { + safeWidth = safeHeight * 4 / 3; + } else { + safeHeight = safeWidth * 3 / 4; + } + safeWidth *= SAFE_AREA_RATIO; + safeHeight *= SAFE_AREA_RATIO; + + int lineHeight = safeHeight / MAX_ROWS; + int lineHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + lineHeight, MeasureSpec.EXACTLY); + int lineWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + safeWidth, MeasureSpec.EXACTLY); + + for (int i = 0; i < MAX_ROWS; i++) { + mLineBoxes[i].measure(lineWidthMeasureSpec, lineHeightMeasureSpec); + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + // safe caption area + int viewPortWidth = r - l; + int viewPortHeight = b - t; + int safeWidth, safeHeight; + // CEA-608 assumes 4:3 video + if (viewPortWidth * 3 >= viewPortHeight * 4) { + safeWidth = viewPortHeight * 4 / 3; + safeHeight = viewPortHeight; + } else { + safeWidth = viewPortWidth; + safeHeight = viewPortWidth * 3 / 4; + } + safeWidth *= SAFE_AREA_RATIO; + safeHeight *= SAFE_AREA_RATIO; + int left = (viewPortWidth - safeWidth) / 2; + int top = (viewPortHeight - safeHeight) / 2; + + for (int i = 0; i < MAX_ROWS; i++) { + mLineBoxes[i].layout( + left, + top + safeHeight * i / MAX_ROWS, + left + safeWidth, + top + safeHeight * (i + 1) / MAX_ROWS); + } + } + } +}; diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index ba3cfb603408..c29e967868fe 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -78,6 +78,8 @@ interface IAudioService { int getLastAudibleMasterVolume(); + void setMicrophoneMute(boolean on, String callingPackage); + void setRingerMode(int ringerMode); int getRingerMode(); diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index f2580630addf..22db3445b374 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -782,7 +782,7 @@ final public class MediaCodec { private void postEventFromNative( int what, int arg1, int arg2, Object obj) { - if (mEventHandler != null) { + if (mEventHandler != null && mNotificationCallback != null) { Message msg = mEventHandler.obtainMessage(what, arg1, arg2, obj); mEventHandler.sendMessage(msg); } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 1b924102bd6b..66175d04680d 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1649,8 +1649,8 @@ public class MediaPlayer implements SubtitleController.Listener mFormat = MediaFormat.createSubtitleFormat( MEDIA_MIMETYPE_TEXT_SUBRIP, language); } else if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { - mFormat = MediaFormat.createSubtitleFormat( - MEDIA_MIMETYPE_TEXT_VTT, language); + String mime = in.readString(); + mFormat = MediaFormat.createSubtitleFormat(mime, language); mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt()); mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt()); mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt()); @@ -1683,12 +1683,40 @@ public class MediaPlayer implements SubtitleController.Listener dest.writeString(getLanguage()); if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { + dest.writeString(mFormat.getString(MediaFormat.KEY_MIME)); dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT)); dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT)); dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE)); } } + @Override + public String toString() { + StringBuilder out = new StringBuilder(128); + out.append(getClass().getName()); + out.append('{'); + switch (mTrackType) { + case MEDIA_TRACK_TYPE_VIDEO: + out.append("VIDEO"); + break; + case MEDIA_TRACK_TYPE_AUDIO: + out.append("AUDIO"); + break; + case MEDIA_TRACK_TYPE_TIMEDTEXT: + out.append("TIMEDTEXT"); + break; + case MEDIA_TRACK_TYPE_SUBTITLE: + out.append("SUBTITLE"); + break; + default: + out.append("UNKNOWN"); + break; + } + out.append(", " + mFormat.toString()); + out.append("}"); + return out.toString(); + } + /** * Used to read a TrackInfo from a Parcel. */ @@ -1757,6 +1785,12 @@ public class MediaPlayer implements SubtitleController.Listener */ public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt"; + /** + * MIME type for CEA-608 closed caption data. + * @hide + */ + public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608"; + /* * A helper function to check if the mime type is supported by media framework. */ @@ -1792,16 +1826,7 @@ public class MediaPlayer implements SubtitleController.Listener } SubtitleTrack track = mInbandSubtitleTracks[index]; if (track != null) { - try { - long runID = data.getStartTimeUs() + 1; - // TODO: move conversion into track - track.onData(new String(data.getData(), "UTF-8"), true /* eos */, runID); - track.setRunDiscardTimeMs( - runID, - (data.getStartTimeUs() + data.getDurationUs()) / 1000); - } catch (java.io.UnsupportedEncodingException e) { - Log.w(TAG, "subtitle data for track " + index + " is not UTF-8 encoded: " + e); - } + track.onData(data); } } }; @@ -1872,7 +1897,7 @@ public class MediaPlayer implements SubtitleController.Listener } scanner.close(); mOutOfBandSubtitleTracks.add(track); - track.onData(contents, true /* eos */, ~0 /* runID: keep forever */); + track.onData(contents.getBytes(), true /* eos */, ~0 /* runID: keep forever */); return MEDIA_INFO_EXTERNAL_METADATA_UPDATE; } diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 1da02156d804..ddd5b720826c 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -60,7 +60,6 @@ import java.util.concurrent.CopyOnWriteArrayList; public class MediaRouter { private static final String TAG = "MediaRouter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final boolean USE_SESSIONS = true; static class Static implements DisplayManager.DisplayListener { final Context mAppContext; @@ -2104,11 +2103,7 @@ public class MediaRouter { public void setPlaybackType(int type) { if (mPlaybackType != type) { mPlaybackType = type; - if (USE_SESSIONS) { - configureSessionVolume(); - } else { - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type); - } + configureSessionVolume(); } } @@ -2121,12 +2116,7 @@ public class MediaRouter { public void setVolumeHandling(int volumeHandling) { if (mVolumeHandling != volumeHandling) { mVolumeHandling = volumeHandling; - if (USE_SESSIONS) { - configureSessionVolume(); - } else { - setPlaybackInfoOnRcc( - RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling); - } + configureSessionVolume(); } } @@ -2139,12 +2129,8 @@ public class MediaRouter { volume = Math.max(0, Math.min(volume, getVolumeMax())); if (mVolume != volume) { mVolume = volume; - if (USE_SESSIONS) { - if (mSvp != null) { - mSvp.notifyVolumeChanged(); - } - } else { - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume); + if (mSvp != null) { + mSvp.notifyVolumeChanged(); } dispatchRouteVolumeChanged(this); if (mGroup != null) { @@ -2184,11 +2170,7 @@ public class MediaRouter { public void setVolumeMax(int volumeMax) { if (mVolumeMax != volumeMax) { mVolumeMax = volumeMax; - if (USE_SESSIONS) { - configureSessionVolume(); - } else { - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax); - } + configureSessionVolume(); } } @@ -2199,40 +2181,12 @@ public class MediaRouter { public void setPlaybackStream(int stream) { if (mPlaybackStream != stream) { mPlaybackStream = stream; - if (USE_SESSIONS) { - configureSessionVolume(); - } else { - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream); - } + configureSessionVolume(); } } private void updatePlaybackInfoOnRcc() { - if (USE_SESSIONS) { - configureSessionVolume(); - } else { - if ((mRcc != null) - && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) { - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType); - // let AudioService know whom to call when remote volume - // needs to be updated - try { - sStatic.mAudioService.registerRemoteVolumeObserverForRcc( - mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */); - } catch (RemoteException e) { - Log.e(TAG, "Error registering remote volume observer", e); - } - } - } + configureSessionVolume(); } private void configureSessionVolume() { @@ -2272,12 +2226,6 @@ public class MediaRouter { } } - private void setPlaybackInfoOnRcc(int what, int value) { - if (mRcc != null) { - mRcc.setPlaybackInformation(what, value); - } - } - class SessionVolumeProvider extends RemoteVolumeProvider { public SessionVolumeProvider(int volumeControl, int maxVolume) { diff --git a/media/java/android/media/PlayerRecord.java b/media/java/android/media/PlayerRecord.java index f9708c36d91f..664ddcf7362c 100644 --- a/media/java/android/media/PlayerRecord.java +++ b/media/java/android/media/PlayerRecord.java @@ -56,7 +56,7 @@ class PlayerRecord implements DeathRecipient { */ final private ComponentName mReceiverComponent; - private int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED; + private int mRccId = -1; /** * A non-null token implies this record tracks a "live" player whose death is being monitored. diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index 0caea5fb577f..73bc61a5875c 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -561,6 +561,8 @@ public class RemoteControlClient return; } synchronized (mCacheLock) { + // Still build the old metadata so when creating a new editor + // you get the expected values. // assign the edited data mMetadata = new Bundle(mEditorMetadata); // add the information about editable keys @@ -570,16 +572,6 @@ public class RemoteControlClient } mOriginalArtwork = mEditorArtwork; mEditorArtwork = null; - if (mMetadataChanged & mArtworkChanged) { - // send to remote control display if conditions are met - sendMetadataWithArtwork_syncCacheLock(null, 0, 0); - } else if (mMetadataChanged) { - // send to remote control display if conditions are met - sendMetadata_syncCacheLock(null); - } else if (mArtworkChanged) { - // send to remote control display if conditions are met - sendArtwork_syncCacheLock(null, 0, 0); - } // USE_SESSIONS if (mSession != null && mMetadataBuilder != null) { @@ -687,14 +679,6 @@ public class RemoteControlClient // keep track of when the state change occurred mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime(); - // send to remote control display if conditions are met - sendPlaybackState_syncCacheLock(null); - // update AudioService - sendAudioServiceNewPlaybackState_syncCacheLock(); - - // handle automatic playback position refreshes - initiateCheckForDrift_syncCacheLock(); - // USE_SESSIONS if (mSession != null) { int pbState = PlaybackState.getStateFromRccState(state); @@ -707,29 +691,7 @@ public class RemoteControlClient } } - private void initiateCheckForDrift_syncCacheLock() { - if (mEventHandler == null) { - return; - } - mEventHandler.removeMessages(MSG_POSITION_DRIFT_CHECK); - if (!mNeedsPositionSync) { - return; - } - if (mPlaybackPositionMs < 0) { - // the current playback state has no known playback position, it's no use - // trying to see if there is any drift at this point - // (this also bypasses this mechanism for older apps that use the old - // setPlaybackState(int) API) - return; - } - if (playbackPositionShouldMove(mPlaybackState)) { - // playback position moving, schedule next position drift check - mEventHandler.sendMessageDelayed( - mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK), - getCheckPeriodFromSpeed(mPlaybackSpeed)); - } - } - + // TODO investigate if we still need position drift checking private void onPositionDriftCheck() { if (DEBUG) { Log.d(TAG, "onPositionDriftCheck()"); } synchronized(mCacheLock) { @@ -781,9 +743,6 @@ public class RemoteControlClient // store locally mTransportControlFlags = transportControlFlags; - // send to remote control display if conditions are met - sendTransportControlInfo_syncCacheLock(null); - // USE_SESSIONS if (mSession != null) { mSessionPlaybackState.setActions(PlaybackState @@ -866,17 +825,7 @@ public class RemoteControlClient */ public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) { synchronized(mCacheLock) { - int oldCapa = mPlaybackPositionCapabilities; - if (l != null) { - mPlaybackPositionCapabilities |= MEDIA_POSITION_WRITABLE; - } else { - mPlaybackPositionCapabilities &= ~MEDIA_POSITION_WRITABLE; - } mPositionUpdateListener = l; - if (oldCapa != mPlaybackPositionCapabilities) { - // tell RCDs that this RCC's playback position capabilities have changed - sendTransportControlInfo_syncCacheLock(null); - } } } @@ -888,17 +837,7 @@ public class RemoteControlClient */ public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) { synchronized(mCacheLock) { - int oldCapa = mPlaybackPositionCapabilities; - if (l != null) { - mPlaybackPositionCapabilities |= MEDIA_POSITION_READABLE; - } else { - mPlaybackPositionCapabilities &= ~MEDIA_POSITION_READABLE; - } mPositionProvider = l; - if (oldCapa != mPlaybackPositionCapabilities) { - // tell RCDs that this RCC's playback position capabilities have changed - sendTransportControlInfo_syncCacheLock(null); - } if ((mPositionProvider != null) && (mEventHandler != null) && playbackPositionShouldMove(mPlaybackState)) { // playback position is already moving, but now we have a position provider, @@ -925,124 +864,12 @@ public class RemoteControlClient */ public static int MEDIA_POSITION_WRITABLE = 1 << 1; - private int mPlaybackPositionCapabilities = 0; - /** @hide */ public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE; /** @hide */ // hard-coded to the same number of steps as AudioService.MAX_STREAM_VOLUME[STREAM_MUSIC] public final static int DEFAULT_PLAYBACK_VOLUME = 15; - private int mPlaybackType = PLAYBACK_TYPE_LOCAL; - private int mPlaybackVolumeMax = DEFAULT_PLAYBACK_VOLUME; - private int mPlaybackVolume = DEFAULT_PLAYBACK_VOLUME; - private int mPlaybackVolumeHandling = DEFAULT_PLAYBACK_VOLUME_HANDLING; - private int mPlaybackStream = AudioManager.STREAM_MUSIC; - - /** - * @hide - * Set information describing information related to the playback of media so the system - * can implement additional behavior to handle non-local playback usecases. - * @param what a key to specify the type of information to set. Valid keys are - * {@link #PLAYBACKINFO_PLAYBACK_TYPE}, - * {@link #PLAYBACKINFO_USES_STREAM}, - * {@link #PLAYBACKINFO_VOLUME}, - * {@link #PLAYBACKINFO_VOLUME_MAX}, - * and {@link #PLAYBACKINFO_VOLUME_HANDLING}. - * @param value the value for the supplied information to set. - */ - public void setPlaybackInformation(int what, int value) { - synchronized(mCacheLock) { - switch (what) { - case PLAYBACKINFO_PLAYBACK_TYPE: - if ((value >= PLAYBACK_TYPE_MIN) && (value <= PLAYBACK_TYPE_MAX)) { - if (mPlaybackType != value) { - mPlaybackType = value; - sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value); - } - } else { - Log.w(TAG, "using invalid value for PLAYBACKINFO_PLAYBACK_TYPE"); - } - break; - case PLAYBACKINFO_VOLUME: - if ((value > -1) && (value <= mPlaybackVolumeMax)) { - if (mPlaybackVolume != value) { - mPlaybackVolume = value; - sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value); - } - } else { - Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME"); - } - break; - case PLAYBACKINFO_VOLUME_MAX: - if (value > 0) { - if (mPlaybackVolumeMax != value) { - mPlaybackVolumeMax = value; - sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value); - } - } else { - Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME_MAX"); - } - break; - case PLAYBACKINFO_USES_STREAM: - if ((value >= 0) && (value < AudioSystem.getNumStreamTypes())) { - mPlaybackStream = value; - } else { - Log.w(TAG, "using invalid value for PLAYBACKINFO_USES_STREAM"); - } - break; - case PLAYBACKINFO_VOLUME_HANDLING: - if ((value >= PLAYBACK_VOLUME_FIXED) && (value <= PLAYBACK_VOLUME_VARIABLE)) { - if (mPlaybackVolumeHandling != value) { - mPlaybackVolumeHandling = value; - sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value); - } - } else { - Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME_HANDLING"); - } - break; - default: - // not throwing an exception or returning an error if more keys are to be - // supported in the future - Log.w(TAG, "setPlaybackInformation() ignoring unknown key " + what); - break; - } - } - } - - /** - * @hide - * Return playback information represented as an integer value. - * @param what a key to specify the type of information to retrieve. Valid keys are - * {@link #PLAYBACKINFO_PLAYBACK_TYPE}, - * {@link #PLAYBACKINFO_USES_STREAM}, - * {@link #PLAYBACKINFO_VOLUME}, - * {@link #PLAYBACKINFO_VOLUME_MAX}, - * and {@link #PLAYBACKINFO_VOLUME_HANDLING}. - * @return the current value for the given information type, or - * {@link #PLAYBACKINFO_INVALID_VALUE} if an error occurred or the request is invalid, or - * the value is unknown. - */ - public int getIntPlaybackInformation(int what) { - synchronized(mCacheLock) { - switch (what) { - case PLAYBACKINFO_PLAYBACK_TYPE: - return mPlaybackType; - case PLAYBACKINFO_VOLUME: - return mPlaybackVolume; - case PLAYBACKINFO_VOLUME_MAX: - return mPlaybackVolumeMax; - case PLAYBACKINFO_USES_STREAM: - return mPlaybackStream; - case PLAYBACKINFO_VOLUME_HANDLING: - return mPlaybackVolumeHandling; - default: - Log.e(TAG, "getIntPlaybackInformation() unknown key " + what); - return PLAYBACKINFO_INVALID_VALUE; - } - } - } - /** * Lock for all cached data */ @@ -1102,13 +929,6 @@ public class RemoteControlClient * The current remote control client generation ID across the system, as known by this object */ private int mCurrentClientGenId = -1; - /** - * The remote control client generation ID, the last time it was told it was the current RC. - * If (mCurrentClientGenId == mInternalClientGenId) is true, it means that this remote control - * client is the "focused" one, and that whenever this client's info is updated, it needs to - * send it to the known IRemoteControlDisplay interfaces. - */ - private int mInternalClientGenId = -2; /** * The media button intent description associated with this remote control client @@ -1134,186 +954,18 @@ public class RemoteControlClient private MediaMetadata mMediaMetadata; /** - * A class to encapsulate all the information about a remote control display. - * A RemoteControlClient's metadata and state may be displayed on multiple IRemoteControlDisplay - */ - private class DisplayInfoForClient { - /** may never be null */ - private IRemoteControlDisplay mRcDisplay; - private int mArtworkExpectedWidth; - private int mArtworkExpectedHeight; - private boolean mWantsPositionSync = false; - private boolean mEnabled = true; - - DisplayInfoForClient(IRemoteControlDisplay rcd, int w, int h) { - mRcDisplay = rcd; - mArtworkExpectedWidth = w; - mArtworkExpectedHeight = h; - } - } - - /** - * The list of remote control displays to which this client will send information. - * Accessed and modified synchronized on mCacheLock - */ - private ArrayList<DisplayInfoForClient> mRcDisplays = new ArrayList<DisplayInfoForClient>(1); - - /** * @hide * Accessor to media button intent description (includes target component) */ public PendingIntent getRcMediaIntent() { return mRcMediaIntent; } - /** - * @hide - * Accessor to IRemoteControlClient - */ - public IRemoteControlClient getIRemoteControlClient() { - return mIRCC; - } - - /** - * The IRemoteControlClient implementation - */ - private final IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() { - - //TODO change name to informationRequestForAllDisplays() - public void onInformationRequested(int generationId, int infoFlags) { - // only post messages, we can't block here - if (mEventHandler != null) { - // signal new client - mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN); - mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_NEW_INTERNAL_CLIENT_GEN, - /*arg1*/ generationId, /*arg2, ignored*/ 0)); - // send the information - mEventHandler.removeMessages(MSG_REQUEST_PLAYBACK_STATE); - mEventHandler.removeMessages(MSG_REQUEST_METADATA); - mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL); - mEventHandler.removeMessages(MSG_REQUEST_ARTWORK); - mEventHandler.removeMessages(MSG_REQUEST_METADATA_ARTWORK); - mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, null)); - mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, null)); - mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK, - 0, 0, null)); - } - } - - public void informationRequestForDisplay(IRemoteControlDisplay rcd, int w, int h) { - // only post messages, we can't block here - if (mEventHandler != null) { - mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, rcd)); - mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, rcd)); - if ((w > 0) && (h > 0)) { - mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK, w, h, rcd)); - } else { - mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_REQUEST_METADATA, rcd)); - } - } - } - - public void setCurrentClientGenerationId(int clientGeneration) { - // only post messages, we can't block here - if (mEventHandler != null) { - mEventHandler.removeMessages(MSG_NEW_CURRENT_CLIENT_GEN); - mEventHandler.sendMessage(mEventHandler.obtainMessage( - MSG_NEW_CURRENT_CLIENT_GEN, clientGeneration, 0/*ignored*/)); - } - } - - public void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) { - // only post messages, we can't block here - if ((mEventHandler != null) && (rcd != null)) { - mEventHandler.sendMessage(mEventHandler.obtainMessage( - MSG_PLUG_DISPLAY, w, h, rcd)); - } - } - - public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) { - // only post messages, we can't block here - if ((mEventHandler != null) && (rcd != null)) { - mEventHandler.sendMessage(mEventHandler.obtainMessage( - MSG_UNPLUG_DISPLAY, rcd)); - } - } - - public void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h) { - // only post messages, we can't block here - if ((mEventHandler != null) && (rcd != null)) { - mEventHandler.sendMessage(mEventHandler.obtainMessage( - MSG_UPDATE_DISPLAY_ARTWORK_SIZE, w, h, rcd)); - } - } - - public void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync) { - // only post messages, we can't block here - if ((mEventHandler != null) && (rcd != null)) { - mEventHandler.sendMessage(mEventHandler.obtainMessage( - MSG_DISPLAY_WANTS_POS_SYNC, wantsSync ? 1 : 0, 0/*arg2 ignored*/, rcd)); - } - } - - public void enableRemoteControlDisplay(IRemoteControlDisplay rcd, boolean enabled) { - // only post messages, we can't block here - if ((mEventHandler != null) && (rcd != null)) { - mEventHandler.sendMessage(mEventHandler.obtainMessage( - MSG_DISPLAY_ENABLE, enabled ? 1 : 0, 0/*arg2 ignored*/, rcd)); - } - } - - public void seekTo(int generationId, long timeMs) { - // only post messages, we can't block here - if (mEventHandler != null) { - mEventHandler.removeMessages(MSG_SEEK_TO); - mEventHandler.sendMessage(mEventHandler.obtainMessage( - MSG_SEEK_TO, generationId /* arg1 */, 0 /* arg2, ignored */, - new Long(timeMs))); - } - } - - public void updateMetadata(int generationId, int key, Rating value) { - // only post messages, we can't block here - if (mEventHandler != null) { - mEventHandler.sendMessage(mEventHandler.obtainMessage( - MSG_UPDATE_METADATA, generationId /* arg1 */, key /* arg2*/, value)); - } - } - }; /** * @hide * Default value for the unique identifier */ public final static int RCSE_ID_UNREGISTERED = -1; - /** - * Unique identifier of the RemoteControlStackEntry in AudioService with which - * this RemoteControlClient is associated. - */ - private int mRcseId = RCSE_ID_UNREGISTERED; - /** - * @hide - * To be only used by AudioManager after it has received the unique id from - * IAudioService.registerRemoteControlClient() - * @param id the unique identifier of the RemoteControlStackEntry in AudioService with which - * this RemoteControlClient is associated. - */ - public void setRcseId(int id) { - mRcseId = id; - } - - /** - * @hide - */ - public int getRcseId() { - return mRcseId; - } // USE_SESSIONS private MediaSession.TransportControlsCallback mTransportListener @@ -1327,31 +979,13 @@ public class RemoteControlClient @Override public void onSetRating(Rating rating) { if ((mTransportControlFlags & FLAG_KEY_MEDIA_RATING) != 0) { - if (mEventHandler != null) { - mEventHandler.sendMessage(mEventHandler.obtainMessage( - MSG_UPDATE_METADATA, mCurrentClientGenId, - MetadataEditor.RATING_KEY_BY_USER, rating)); - } + onUpdateMetadata(mCurrentClientGenId, MetadataEditor.RATING_KEY_BY_USER, rating); } } }; private EventHandler mEventHandler; - private final static int MSG_REQUEST_PLAYBACK_STATE = 1; - private final static int MSG_REQUEST_METADATA = 2; - private final static int MSG_REQUEST_TRANSPORTCONTROL = 3; - private final static int MSG_REQUEST_ARTWORK = 4; - private final static int MSG_NEW_INTERNAL_CLIENT_GEN = 5; - private final static int MSG_NEW_CURRENT_CLIENT_GEN = 6; - private final static int MSG_PLUG_DISPLAY = 7; - private final static int MSG_UNPLUG_DISPLAY = 8; - private final static int MSG_UPDATE_DISPLAY_ARTWORK_SIZE = 9; - private final static int MSG_SEEK_TO = 10; private final static int MSG_POSITION_DRIFT_CHECK = 11; - private final static int MSG_DISPLAY_WANTS_POS_SYNC = 12; - private final static int MSG_UPDATE_METADATA = 13; - private final static int MSG_REQUEST_METADATA_ARTWORK = 14; - private final static int MSG_DISPLAY_ENABLE = 15; private class EventHandler extends Handler { public EventHandler(RemoteControlClient rcc, Looper looper) { @@ -1361,63 +995,9 @@ public class RemoteControlClient @Override public void handleMessage(Message msg) { switch(msg.what) { - case MSG_REQUEST_PLAYBACK_STATE: - synchronized (mCacheLock) { - sendPlaybackState_syncCacheLock((IRemoteControlDisplay)msg.obj); - } - break; - case MSG_REQUEST_METADATA: - synchronized (mCacheLock) { - sendMetadata_syncCacheLock((IRemoteControlDisplay)msg.obj); - } - break; - case MSG_REQUEST_TRANSPORTCONTROL: - synchronized (mCacheLock) { - sendTransportControlInfo_syncCacheLock((IRemoteControlDisplay)msg.obj); - } - break; - case MSG_REQUEST_ARTWORK: - synchronized (mCacheLock) { - sendArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj, - msg.arg1, msg.arg2); - } - break; - case MSG_REQUEST_METADATA_ARTWORK: - synchronized (mCacheLock) { - sendMetadataWithArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj, - msg.arg1, msg.arg2); - } - break; - case MSG_NEW_INTERNAL_CLIENT_GEN: - onNewInternalClientGen(msg.arg1); - break; - case MSG_NEW_CURRENT_CLIENT_GEN: - onNewCurrentClientGen(msg.arg1); - break; - case MSG_PLUG_DISPLAY: - onPlugDisplay((IRemoteControlDisplay)msg.obj, msg.arg1, msg.arg2); - break; - case MSG_UNPLUG_DISPLAY: - onUnplugDisplay((IRemoteControlDisplay)msg.obj); - break; - case MSG_UPDATE_DISPLAY_ARTWORK_SIZE: - onUpdateDisplayArtworkSize((IRemoteControlDisplay)msg.obj, msg.arg1, msg.arg2); - break; - case MSG_SEEK_TO: - onSeekTo(msg.arg1, ((Long)msg.obj).longValue()); - break; case MSG_POSITION_DRIFT_CHECK: onPositionDriftCheck(); break; - case MSG_DISPLAY_WANTS_POS_SYNC: - onDisplayWantsSync((IRemoteControlDisplay)msg.obj, msg.arg1 == 1); - break; - case MSG_UPDATE_METADATA: - onUpdateMetadata(msg.arg1, msg.arg2, msg.obj); - break; - case MSG_DISPLAY_ENABLE: - onDisplayEnable((IRemoteControlDisplay)msg.obj, msg.arg1 == 1); - break; default: Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler"); } @@ -1425,346 +1005,8 @@ public class RemoteControlClient } //=========================================================== - // Communication with the IRemoteControlDisplay (the displays known to the system) - - private void sendPlaybackState_syncCacheLock(IRemoteControlDisplay target) { - if (mCurrentClientGenId == mInternalClientGenId) { - if (target != null) { - try { - target.setPlaybackState(mInternalClientGenId, - mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs, - mPlaybackSpeed); - } catch (RemoteException e) { - Log.e(TAG, "Error in setPlaybackState() for dead display " + target, e); - } - return; - } - // target == null implies all displays must be updated - final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext()) { - final DisplayInfoForClient di = displayIterator.next(); - if (di.mEnabled) { - try { - di.mRcDisplay.setPlaybackState(mInternalClientGenId, - mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs, - mPlaybackSpeed); - } catch (RemoteException e) { - Log.e(TAG, "Error in setPlaybackState(), dead display " + di.mRcDisplay, e); - displayIterator.remove(); - } - } - } - } - } - - private void sendMetadata_syncCacheLock(IRemoteControlDisplay target) { - if (mCurrentClientGenId == mInternalClientGenId) { - if (target != null) { - try { - target.setMetadata(mInternalClientGenId, mMetadata); - } catch (RemoteException e) { - Log.e(TAG, "Error in setMetadata() for dead display " + target, e); - } - return; - } - // target == null implies all displays must be updated - final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext()) { - final DisplayInfoForClient di = displayIterator.next(); - if (di.mEnabled) { - try { - di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata); - } catch (RemoteException e) { - Log.e(TAG, "Error in setMetadata(), dead display " + di.mRcDisplay, e); - displayIterator.remove(); - } - } - } - } - } - - private void sendTransportControlInfo_syncCacheLock(IRemoteControlDisplay target) { - if (mCurrentClientGenId == mInternalClientGenId) { - if (target != null) { - try { - target.setTransportControlInfo(mInternalClientGenId, - mTransportControlFlags, mPlaybackPositionCapabilities); - } catch (RemoteException e) { - Log.e(TAG, "Error in setTransportControlFlags() for dead display " + target, - e); - } - return; - } - // target == null implies all displays must be updated - final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext()) { - final DisplayInfoForClient di = displayIterator.next(); - if (di.mEnabled) { - try { - di.mRcDisplay.setTransportControlInfo(mInternalClientGenId, - mTransportControlFlags, mPlaybackPositionCapabilities); - } catch (RemoteException e) { - Log.e(TAG, "Error in setTransportControlFlags(), dead display " + di.mRcDisplay, - e); - displayIterator.remove(); - } - } - } - } - } - - private void sendArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) { - // FIXME modify to cache all requested sizes? - if (mCurrentClientGenId == mInternalClientGenId) { - if (target != null) { - final DisplayInfoForClient di = new DisplayInfoForClient(target, w, h); - sendArtworkToDisplay(di); - return; - } - // target == null implies all displays must be updated - final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext()) { - if (!sendArtworkToDisplay(displayIterator.next())) { - displayIterator.remove(); - } - } - } - } - - /** - * Send artwork to an IRemoteControlDisplay. - * @param di encapsulates the IRemoteControlDisplay that will receive the artwork, and its - * dimension requirements. - * @return false if there was an error communicating with the IRemoteControlDisplay. - */ - private boolean sendArtworkToDisplay(DisplayInfoForClient di) { - if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) { - Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork, - di.mArtworkExpectedWidth, di.mArtworkExpectedHeight); - try { - di.mRcDisplay.setArtwork(mInternalClientGenId, artwork); - } catch (RemoteException e) { - Log.e(TAG, "Error in sendArtworkToDisplay(), dead display " + di.mRcDisplay, e); - return false; - } - } - return true; - } - - private void sendMetadataWithArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) { - // FIXME modify to cache all requested sizes? - if (mCurrentClientGenId == mInternalClientGenId) { - if (target != null) { - try { - if ((w > 0) && (h > 0)) { - Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork, w, h); - target.setAllMetadata(mInternalClientGenId, mMetadata, artwork); - } else { - target.setMetadata(mInternalClientGenId, mMetadata); - } - } catch (RemoteException e) { - Log.e(TAG, "Error in set(All)Metadata() for dead display " + target, e); - } - return; - } - // target == null implies all displays must be updated - final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext()) { - final DisplayInfoForClient di = displayIterator.next(); - try { - if (di.mEnabled) { - if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) { - Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork, - di.mArtworkExpectedWidth, di.mArtworkExpectedHeight); - di.mRcDisplay.setAllMetadata(mInternalClientGenId, mMetadata, artwork); - } else { - di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata); - } - } - } catch (RemoteException e) { - Log.e(TAG, "Error when setting metadata, dead display " + di.mRcDisplay, e); - displayIterator.remove(); - } - } - } - } - - //=========================================================== - // Communication with AudioService - - private static IAudioService sService; - - private static IAudioService getService() - { - if (sService != null) { - return sService; - } - IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); - sService = IAudioService.Stub.asInterface(b); - return sService; - } - - private void sendAudioServiceNewPlaybackInfo_syncCacheLock(int what, int value) { - if (mRcseId == RCSE_ID_UNREGISTERED) { - return; - } - //Log.d(TAG, "sending to AudioService key=" + what + ", value=" + value); - IAudioService service = getService(); - try { - service.setPlaybackInfoForRcc(mRcseId, what, value); - } catch (RemoteException e) { - Log.e(TAG, "Dead object in setPlaybackInfoForRcc", e); - } - } - - private void sendAudioServiceNewPlaybackState_syncCacheLock() { - if (mRcseId == RCSE_ID_UNREGISTERED) { - return; - } - IAudioService service = getService(); - try { - service.setPlaybackStateForRcc(mRcseId, - mPlaybackState, mPlaybackPositionMs, mPlaybackSpeed); - } catch (RemoteException e) { - Log.e(TAG, "Dead object in setPlaybackStateForRcc", e); - } - } - - //=========================================================== // Message handlers - private void onNewInternalClientGen(int clientGeneration) { - synchronized (mCacheLock) { - // this remote control client is told it is the "focused" one: - // it implies that now (mCurrentClientGenId == mInternalClientGenId) is true - mInternalClientGenId = clientGeneration; - } - } - - private void onNewCurrentClientGen(int clientGeneration) { - synchronized (mCacheLock) { - mCurrentClientGenId = clientGeneration; - } - } - - /** pre-condition rcd != null */ - private void onPlugDisplay(IRemoteControlDisplay rcd, int w, int h) { - synchronized(mCacheLock) { - // do we have this display already? - boolean displayKnown = false; - final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext() && !displayKnown) { - final DisplayInfoForClient di = displayIterator.next(); - displayKnown = di.mRcDisplay.asBinder().equals(rcd.asBinder()); - if (displayKnown) { - // this display was known but the change in artwork size will cause the - // artwork to be refreshed - if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) { - di.mArtworkExpectedWidth = w; - di.mArtworkExpectedHeight = h; - if (!sendArtworkToDisplay(di)) { - displayIterator.remove(); - } - } - } - } - if (!displayKnown) { - mRcDisplays.add(new DisplayInfoForClient(rcd, w, h)); - } - } - } - - /** pre-condition rcd != null */ - private void onUnplugDisplay(IRemoteControlDisplay rcd) { - synchronized(mCacheLock) { - Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext()) { - final DisplayInfoForClient di = displayIterator.next(); - if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { - displayIterator.remove(); - break; - } - } - // list of RCDs has changed, reevaluate whether position check is still needed - boolean oldNeedsPositionSync = mNeedsPositionSync; - boolean newNeedsPositionSync = false; - displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext()) { - final DisplayInfoForClient di = displayIterator.next(); - if (di.mWantsPositionSync) { - newNeedsPositionSync = true; - break; - } - } - mNeedsPositionSync = newNeedsPositionSync; - if (oldNeedsPositionSync != mNeedsPositionSync) { - // update needed? - initiateCheckForDrift_syncCacheLock(); - } - } - } - - /** pre-condition rcd != null */ - private void onUpdateDisplayArtworkSize(IRemoteControlDisplay rcd, int w, int h) { - synchronized(mCacheLock) { - final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext()) { - final DisplayInfoForClient di = displayIterator.next(); - if (di.mRcDisplay.asBinder().equals(rcd.asBinder()) && - ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h))) { - di.mArtworkExpectedWidth = w; - di.mArtworkExpectedHeight = h; - if (di.mEnabled) { - if (!sendArtworkToDisplay(di)) { - displayIterator.remove(); - } - } - break; - } - } - } - } - - /** pre-condition rcd != null */ - private void onDisplayWantsSync(IRemoteControlDisplay rcd, boolean wantsSync) { - synchronized(mCacheLock) { - boolean oldNeedsPositionSync = mNeedsPositionSync; - boolean newNeedsPositionSync = false; - final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - // go through the list of RCDs and for each entry, check both whether this is the RCD - // that gets upated, and whether the list has one entry that wants position sync - while (displayIterator.hasNext()) { - final DisplayInfoForClient di = displayIterator.next(); - if (di.mEnabled) { - if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { - di.mWantsPositionSync = wantsSync; - } - if (di.mWantsPositionSync) { - newNeedsPositionSync = true; - } - } - } - mNeedsPositionSync = newNeedsPositionSync; - if (oldNeedsPositionSync != mNeedsPositionSync) { - // update needed? - initiateCheckForDrift_syncCacheLock(); - } - } - } - - /** pre-condition rcd != null */ - private void onDisplayEnable(IRemoteControlDisplay rcd, boolean enable) { - synchronized(mCacheLock) { - final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); - while (displayIterator.hasNext()) { - final DisplayInfoForClient di = displayIterator.next(); - if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { - di.mEnabled = enable; - } - } - } - } - private void onSeekTo(int generationId, long timeMs) { synchronized (mCacheLock) { if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) { @@ -1785,42 +1027,6 @@ public class RemoteControlClient // Internal utilities /** - * Scale a bitmap to fit the smallest dimension by uniformly scaling the incoming bitmap. - * If the bitmap fits, then do nothing and return the original. - * - * @param bitmap - * @param maxWidth - * @param maxHeight - * @return - */ - - private Bitmap scaleBitmapIfTooBig(Bitmap bitmap, int maxWidth, int maxHeight) { - if (bitmap != null) { - final int width = bitmap.getWidth(); - final int height = bitmap.getHeight(); - if (width > maxWidth || height > maxHeight) { - float scale = Math.min((float) maxWidth / width, (float) maxHeight / height); - int newWidth = Math.round(scale * width); - int newHeight = Math.round(scale * height); - Bitmap.Config newConfig = bitmap.getConfig(); - if (newConfig == null) { - newConfig = Bitmap.Config.ARGB_8888; - } - Bitmap outBitmap = Bitmap.createBitmap(newWidth, newHeight, newConfig); - Canvas canvas = new Canvas(outBitmap); - Paint paint = new Paint(); - paint.setAntiAlias(true); - paint.setFilterBitmap(true); - canvas.drawBitmap(bitmap, null, - new RectF(0, 0, outBitmap.getWidth(), outBitmap.getHeight()), paint); - bitmap = outBitmap; - } - } - return bitmap; - } - - - /** * Returns whether, for the given playback state, the playback position is expected to * be changing. * @param playstate the playback state to evaluate diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java index 06063de66a0b..9fedf63f2190 100644 --- a/media/java/android/media/SubtitleTrack.java +++ b/media/java/android/media/SubtitleTrack.java @@ -75,6 +75,14 @@ public abstract class SubtitleTrack implements MediaTimeProvider.OnMediaTimeList 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 @@ -83,7 +91,7 @@ public abstract class SubtitleTrack implements MediaTimeProvider.OnMediaTimeList * indicating the last section of the run. Calls from different * runs must not be intermixed. * - * @param data + * @param data subtitle data byte buffer * @param eos true if this is the last section of the run. * @param runID mostly-unique ID for this run of data. Subtitle cues * with runID of 0 are discarded immediately after @@ -92,10 +100,8 @@ public abstract class SubtitleTrack implements MediaTimeProvider.OnMediaTimeList * with other runID-s are discarded at the end of the * run, which defaults to the latest timestamp of * any of its cues (with this runID). - * - * TODO use ByteBuffer */ - public abstract void onData(String data, boolean eos, long runID); + public abstract void onData(byte[] data, boolean eos, long runID); /** * Called when adding the subtitle rendering widget to the view hierarchy, diff --git a/media/java/android/media/TtmlRenderer.java b/media/java/android/media/TtmlRenderer.java index 0309334c163f..75133c9fbd18 100644 --- a/media/java/android/media/TtmlRenderer.java +++ b/media/java/android/media/TtmlRenderer.java @@ -563,28 +563,35 @@ class TtmlTrack extends SubtitleTrack implements TtmlNodeListener { } @Override - public void onData(String data, boolean eos, long runID) { - // implement intermixing restriction for TTML. - synchronized(mParser) { - if (mCurrentRunID != null && runID != mCurrentRunID) { - throw new IllegalStateException( - "Run #" + mCurrentRunID + - " in progress. Cannot process run #" + runID); - } - mCurrentRunID = runID; - mParsingData += data; - if (eos) { - try { - mParser.parse(mParsingData, mCurrentRunID); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); + public void onData(byte[] data, boolean eos, long runID) { + try { + // TODO: handle UTF-8 conversion properly + String str = new String(data, "UTF-8"); + + // implement intermixing restriction for TTML. + synchronized(mParser) { + if (mCurrentRunID != null && runID != mCurrentRunID) { + throw new IllegalStateException( + "Run #" + mCurrentRunID + + " in progress. Cannot process run #" + runID); + } + mCurrentRunID = runID; + mParsingData += str; + if (eos) { + try { + mParser.parse(mParsingData, mCurrentRunID); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + finishedRun(runID); + mParsingData = ""; + mCurrentRunID = null; } - finishedRun(runID); - mParsingData = ""; - mCurrentRunID = null; } + } catch (java.io.UnsupportedEncodingException e) { + Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e); } } diff --git a/media/java/android/media/WebVttRenderer.java b/media/java/android/media/WebVttRenderer.java index 7977988626f0..a9374d5c9855 100644 --- a/media/java/android/media/WebVttRenderer.java +++ b/media/java/android/media/WebVttRenderer.java @@ -1001,22 +1001,28 @@ class WebVttTrack extends SubtitleTrack implements WebVttCueListener { } @Override - public void onData(String data, boolean eos, long runID) { - // implement intermixing restriction for WebVTT only for now - synchronized(mParser) { - if (mCurrentRunID != null && runID != mCurrentRunID) { - throw new IllegalStateException( - "Run #" + mCurrentRunID + - " in progress. Cannot process run #" + runID); - } - mCurrentRunID = runID; - mParser.parse(data); - if (eos) { - finishedRun(runID); - mParser.eos(); - mRegions.clear(); - mCurrentRunID = null; + public void onData(byte[] data, boolean eos, long runID) { + try { + String str = new String(data, "UTF-8"); + + // implement intermixing restriction for WebVTT only for now + synchronized(mParser) { + if (mCurrentRunID != null && runID != mCurrentRunID) { + throw new IllegalStateException( + "Run #" + mCurrentRunID + + " in progress. Cannot process run #" + runID); + } + mCurrentRunID = runID; + mParser.parse(str); + if (eos) { + finishedRun(runID); + mParser.eos(); + mRegions.clear(); + mCurrentRunID = null; + } } + } catch (java.io.UnsupportedEncodingException e) { + Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e); } } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 8eceee887bfa..2e6b86ea322b 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -133,7 +133,8 @@ public final class MediaSessionManager { ArrayList<MediaController> controllers = new ArrayList<MediaController>(); try { List<IBinder> binders = mService.getSessions(notificationListener, userId); - for (int i = binders.size() - 1; i >= 0; i--) { + int size = binders.size(); + for (int i = 0; i < size; i++) { MediaController controller = MediaController.fromBinder(ISessionController.Stub .asInterface(binders.get(i))); controllers.add(controller); diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 7ff2c2ea8060..bbe650d12c1c 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -144,35 +144,62 @@ public final class TvContract { /** * 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 channelUri The URI of the channel to return programs for. + * @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(Uri channelUri, long startTime, + public static final Uri buildProgramsUriForChannel(long channelId, long startTime, long endTime) { - Uri uri = buildProgramsUriForChannel(channelUri); + Uri uri = buildProgramsUriForChannel(channelId); return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime)) .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build(); } /** + * Builds a URI that points to programs on a specific channel whose schedules overlap with the + * given time frame. + * + * @param channelUri The URI 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(Uri channelUri, long startTime, + long endTime) { + if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) { + throw new IllegalArgumentException("Not a channel: " + channelUri); + } + return buildProgramsUriForChannel(ContentUris.parseId(channelUri), startTime, endTime); + } + + /** * Builds a URI that points to a specific program the user watched. * * @param watchedProgramId The ID of the watched program to point to. @@ -821,7 +848,7 @@ public final class TvContract { * * @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/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index edfdd60defc6..daa700953466 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -533,12 +533,11 @@ public final class TvInputManager { /** * 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 @@ public final class TvInputManager { * 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 @@ public final class TvInputManager { * * @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 @@ public final class TvInputManager { * * @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 @@ public final class TvInputManager { * @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 @@ public final class TvInputManager { 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 @@ public final class TvInputManager { * * @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 @@ public final class TvInputManager { /** * 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/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index 8a7e64288412..7dba21da4c9d 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.ICameraClient; 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 @@ import android.util.Log; * </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 @@ public class CameraBinderTest extends AndroidTestCase { } } + @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() { @@ -158,6 +213,7 @@ public class CameraBinderTest extends AndroidTestCase { * 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 +226,7 @@ public class CameraBinderTest extends AndroidTestCase { * 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 +240,7 @@ public class CameraBinderTest extends AndroidTestCase { * 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 +251,7 @@ public class CameraBinderTest extends AndroidTestCase { * (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 1be2a6261fcc..6be538ae3597 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 @@ public class CameraBinderTestUtils { 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/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index ec87c6e75549..2ed3d731b97f 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -170,7 +170,7 @@ public class DefaultContainerService extends IntentService { DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); - PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packagePath, 0); + PackageParser.ApkLite pkg = PackageParser.parseApkLite(packagePath, 0); if (pkg == null) { Slog.w(TAG, "Failed to parse package"); diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml index 2917faa629b1..006b1ee2d440 100644 --- a/packages/Keyguard/res/layout/keyguard_status_view.xml +++ b/packages/Keyguard/res/layout/keyguard_status_view.xml @@ -56,7 +56,7 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/date_owner_info_margin" android:layout_gravity="center_horizontal" - android:textColor="#99ffffff" + android:textColor="#ccffffff" android:textSize="@dimen/widget_label_font_size" android:ellipsize="marquee" android:singleLine="true" /> diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml index e9e9b896466d..69bf44fa8008 100644 --- a/packages/Keyguard/res/values-sw600dp/dimens.xml +++ b/packages/Keyguard/res/values-sw600dp/dimens.xml @@ -63,14 +63,11 @@ <dimen name="keyguard_muliuser_selector_margin">12dp</dimen> <!-- Overload default clock widget parameters --> - <dimen name="widget_big_font_size">96dp</dimen> + <dimen name="widget_big_font_size">120dp</dimen> <dimen name="widget_label_font_size">16sp</dimen> - <dimen name="bottom_text_spacing_digital">-8dp</dimen> + <dimen name="bottom_text_spacing_digital">-16dp</dimen> <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text. Should be 0 on devices with plenty of room (e.g. tablets) --> <dimen name="eca_overlap">0dip</dimen> - - <!-- The vertical margin between the date and the owner info. --> - <dimen name="date_owner_info_margin">4dp</dimen> </resources> diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml index f971522c2b60..4b113ff896b6 100644 --- a/packages/Keyguard/res/values/dimens.xml +++ b/packages/Keyguard/res/values/dimens.xml @@ -155,15 +155,15 @@ <dimen name="eca_overlap">-10dip</dimen> <!-- Default clock parameters --> - <dimen name="bottom_text_spacing_digital">-6dp</dimen> + <dimen name="bottom_text_spacing_digital">-10dp</dimen> <dimen name="label_font_size">14dp</dimen> - <dimen name="widget_label_font_size">14sp</dimen> - <dimen name="widget_big_font_size">68dp</dimen> + <dimen name="widget_label_font_size">16sp</dimen> + <dimen name="widget_big_font_size">96dp</dimen> <dimen name="big_font_size">120dp</dimen> <!-- The y translation to apply at the start in appear animations. --> <dimen name="appear_y_translation_start">32dp</dimen> <!-- The vertical margin between the date and the owner info. --> - <dimen name="date_owner_info_margin">2dp</dimen> + <dimen name="date_owner_info_margin">10dp</dimen> </resources> diff --git a/packages/Keyguard/res/values/donottranslate.xml b/packages/Keyguard/res/values/donottranslate.xml index 16f5a3e77c3e..78636dbf2d46 100644 --- a/packages/Keyguard/res/values/donottranslate.xml +++ b/packages/Keyguard/res/values/donottranslate.xml @@ -16,7 +16,7 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Skeleton string format for displaying the date. --> - <string name="abbrev_wday_month_day_no_year">EEEMMMMd</string> + <string name="abbrev_wday_month_day_no_year">EEEEMMMMd</string> <!-- Skeleton string format for displaying the time in 12-hour format. --> <string name="clock_12hr_format">hm</string> diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java index a8b49e26b468..79187552f7c1 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java @@ -130,11 +130,6 @@ public class KeyguardStatusView extends GridLayout { } } - @Override - public boolean hasOverlappingRendering() { - return false; - } - private void updateOwnerInfo() { String ownerInfo = getOwnerInfo(); if (!TextUtils.isEmpty(ownerInfo)) { diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml index bac8cb69dfb8..305a82f7c93c 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 b0f8807f2754..863591f9d221 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 2857c04f1665..adcefe074b02 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 1139e7201728..863591f9d221 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 Binary files differindex d4fdbf388a76..17100f773a16 100644 --- a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png +++ b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png diff --git a/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png Binary files differnew file mode 100644 index 000000000000..e969d4c26717 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png Binary files differindex 9fc1a3bffd57..b53bd8f92a00 100644 --- a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png +++ b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png Binary files differnew file mode 100644 index 000000000000..657f710ac8d6 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png Binary files differindex f38de93264d3..09606f629b67 100644 --- a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png +++ b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png Binary files differnew file mode 100644 index 000000000000..a444c551d430 --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png Binary files differindex 8194605e1646..427cad9f6326 100644 --- a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png +++ b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png Binary files differnew file mode 100644 index 000000000000..29cf44bf381f --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png 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 000000000000..a4cb0885587c --- /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_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 9bf42b2cfc90..db5983bda12f 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -46,7 +46,7 @@ android:id="@+id/keyguard_indication_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="100dp" + android:layout_marginBottom="70dp" android:layout_gravity="bottom|center_horizontal" android:textStyle="italic" android:textColor="#ffffff" 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 000000000000..24928d0c7311 --- /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 23f2796ccbff..1bab67af7094 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_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml index d24d21ecb12f..353368b0a8c7 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,7 +73,7 @@ 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_marginEnd="2dp" android:layout_marginStart="16dp" /> @@ -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/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 4021f69fc45e..71fb8b82cca4 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 b3b4c9b5144a..b1dc30217379 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-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 571e9ba39070..03ac011ad3d9 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 bcb28e96e8ac..fcad161c054a 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -245,10 +245,8 @@ <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="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 bd8b8292224f..024be828334a 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 ee8fa8a442cb..4424338aa458 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 3a8aab8ec972..5dbd49069c0b 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-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index d595089e8bd6..6ebd5c731c30 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 9f7e9bfd606e..8f1f30a3c4f6 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-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index aa4f27da8d17..0ffb4b8721fc 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-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index 5114935576bf..7d8a1ad8625a 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 rendah"</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">"<xliff:g id="NUMBER">%d%%</xliff:g> yang tinggal. 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">"Hidupkan penjimat bateri?"</string> + <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Bermula"</string> + <string name="battery_saver_start_action" msgid="7245333922937402896">"Hidupkan penjimat bateri"</string> + <string name="battery_saver_confirmation_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> @@ -264,10 +255,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 be9af97e1e2a..cecbe7a6e079 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 37aae0ab92e8..657e03feeebc 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 a540efbb2ce6..bcf7431a6810 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 e057b76890f3..c099dd9030cc 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> @@ -264,10 +255,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 3f95a0c14e8a..8c59e05f4a3b 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 ad12b1816c61..10ba09f58cf2 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 22a89a97a2a4..3bee434d9101 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/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-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml new file mode 100644 index 000000000000..f9b01c84d6e3 --- /dev/null +++ b/packages/SystemUI/res/values-sw600dp-land/config.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 + --> +<resources> + <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow + card. --> + <integer name="keyguard_max_notification_count">3</integer> +</resources> diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index 326f6022441b..5367fbce1b06 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -23,5 +23,8 @@ <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item> <fraction name="keyguard_clock_y_fraction_max">37%</fraction> - <fraction name="keyguard_clock_y_fraction_min">14%</fraction> + <fraction name="keyguard_clock_y_fraction_min">20%</fraction> + + <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen> + <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml index 6dea81f556d1..47581a99b001 100644 --- a/packages/SystemUI/res/values-sw600dp/config.xml +++ b/packages/SystemUI/res/values-sw600dp/config.xml @@ -28,4 +28,8 @@ <!-- The number of columns that the top level tiles span in the QuickSettings --> <integer name="quick_settings_user_time_settings_tile_span">1</integer> + + <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow + card. --> + <integer name="keyguard_max_notification_count">5</integer> </resources> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 313e2e85cc14..a5e392406a60 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -16,8 +16,8 @@ */ --> <resources> - <!-- The width of the notification panel window: 446 + 16 + 16 (padding in the bg drawable) --> - <dimen name="notification_panel_width">478dp</dimen> + <!-- The width of the notification panel window: 400 + 16 + 16 (padding in the bg drawable) --> + <dimen name="notification_panel_width">432dp</dimen> <!-- Gravity for the notification panel --> <!-- 0x31 = top|center_horizontal --> @@ -56,10 +56,10 @@ max value is used when no notifications are displaying, and the min value is when the highest possible number of notifications are showing. --> <fraction name="keyguard_clock_y_fraction_max">34%</fraction> - <fraction name="keyguard_clock_y_fraction_min">25%</fraction> + <fraction name="keyguard_clock_y_fraction_min">24%</fraction> <!-- The margin between the clock and the notifications on Keyguard. See keyguard_clock_height_fraction_* for the difference between min and max.--> - <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen> - <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen> + <dimen name="keyguard_clock_notifications_margin_min">44dp</dimen> + <dimen name="keyguard_clock_notifications_margin_max">44dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index b49e1a5a57e0..42f262bce00f 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 7bbf9ee84fb3..7635c011e2cd 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -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 45677f2c706a..e15d3e5ccb8c 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -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 0d27e2995efb..cfd4fe9716ec 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> @@ -266,10 +257,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 c5805a11a9ac..4b5cf08842e5 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 8f2d2478a06d..a780ad1d699c 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/config.xml b/packages/SystemUI/res/values/config.xml index c8851dc7d83c..c64a182c788f 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 diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 5ffe3b3baccc..bbcc9c13c9ea 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> @@ -292,12 +292,12 @@ <!-- The fraction of the screen height where the clock on the Keyguard has its center. The max value is used when no notifications are displaying, and the min value is when the highest possible number of notifications are showing. --> - <fraction name="keyguard_clock_y_fraction_max">29.5%</fraction> - <fraction name="keyguard_clock_y_fraction_min">18%</fraction> + <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction> + <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction> <!-- The margin between the clock and the notifications on Keyguard. See keyguard_clock_height_fraction_* for the difference between min and max.--> - <dimen name="keyguard_clock_notifications_margin_min">22dp</dimen> + <dimen name="keyguard_clock_notifications_margin_min">24dp</dimen> <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen> <dimen name="heads_up_window_height">250dp</dimen> @@ -330,6 +330,6 @@ phone hints. --> <dimen name="edge_tap_area_width">48dp</dimen> - <!-- the distance the panel moves up when starting the up motion on Keyguard --> - <dimen name="keyguard_panel_move_up_distance">100dp</dimen> + <!-- end margin for multi user switch in expanded quick settings --> + <dimen name="multi_user_switch_expanded_margin">8dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 43560a3908fe..c117eba8aefe 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -63,7 +63,7 @@ <style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon"> <!-- Note: must be dp to fit in status bar --> <item name="android:textSize">16dp</item> - <item name="android:textStyle">normal</item> + <item name="android:fontFamily">sans-serif-medium</item> <item name="android:textColor">@color/status_bar_clock_color</item> </style> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index ffd76a7d5367..b9e2e1baf892 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 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { } mStatusBarKeyguardViewManager.show(options); + mHiding = false; mShowing = true; mKeyguardDonePending = false; updateActivityLockScreenState(); @@ -1191,7 +1198,7 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { 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 e8f3745af5e7..41b1f75be94e 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -126,7 +126,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { } private void updateNotification() { - Slog.d(TAG, "updateNotification mWarning=" + mWarning + if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning + " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger); if (mInvalidCharger) { showInvalidChargerNotification(); @@ -152,6 +152,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { .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 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { .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 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { .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), @@ -238,13 +241,13 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { @Override public void dismissLowBatteryWarning() { - Slog.i(TAG, "dismissing low battery warning: level=" + mBatteryLevel); + if (DEBUG) Slog.d(TAG, "dismissing low battery warning: level=" + mBatteryLevel); dismissLowBatteryNotification(); mFallbackDialogs.dismissLowBatteryWarning(); } private void dismissLowBatteryNotification() { - Slog.i(TAG, "dismissing low battery notification"); + if (mWarning) Slog.i(TAG, "dismissing low battery notification"); mWarning = false; updateNotification(); } @@ -307,7 +310,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { } private void dismissInvalidChargerNotification() { - Slog.i(TAG, "dismissing invalid charger notification"); + if (mInvalidCharger) Slog.i(TAG, "dismissing invalid charger notification"); mInvalidCharger = false; updateNotification(); } 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 b981ed6a041c..c4bdb19ce2b4 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 @@ public class NotificationsTile extends QSTile<NotificationsTile.NotificationsSta @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 1d355cd2daf4..ddea0bfe33b3 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 @@ public class Constants { // 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 @@ public class Constants { // 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 d66968b2b28d..88ff726c9551 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView FrameLayout mContainerView; RecentsView mRecentsView; View mEmptyView; + View mStatusBarScrimView; View mNavBarScrimView; FullScreenTransitionView mFullScreenshotView; @@ -117,7 +118,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } } 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView /** 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView 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 */ @@ -284,7 +282,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // We really shouldn't hit this, but if we do, just animate out (aka. finish) ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, null, mFinishRunnable, null); - mRecentsView.startOnExitAnimation( + mRecentsView.startExitToHomeAnimation( new ViewAnimation.TaskViewExitContext(exitTrigger)); } } @@ -326,6 +324,10 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // 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 +339,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } mContainerView = new FrameLayout(this); + mContainerView.addView(mStatusBarScrimView); mContainerView.addView(mRecentsView); mContainerView.addView(mEmptyView); if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { @@ -376,7 +379,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView 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 +550,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // 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 +558,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // 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 +567,38 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } public void onEnterAnimationTriggered() { - // Fade in the scrim - if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) { + // 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.setAlpha(0f); - mNavBarScrimView.animate().alpha(1f) + 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 +619,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } @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 3b8c6c9b34ee..63ef773a124b 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 class RecentsConfiguration { public Interpolator quintOutInterpolator; /** Filtering */ - public int filteringCurrentViewsMinAnimDuration; - public int filteringNewViewsMinAnimDuration; + public int filteringCurrentViewsAnimDuration; + public int filteringNewViewsAnimDuration; /** Insets */ public Rect systemInsets = new Rect(); @@ -97,6 +97,7 @@ public class RecentsConfiguration { /** Launch states */ public boolean launchedWithAltTab; + public boolean launchedWithNoRecentTasks; public boolean launchedFromAppWithThumbnail; public boolean launchedFromAppWithScreenshot; public boolean launchedFromHome; @@ -105,12 +106,30 @@ public class RecentsConfiguration { 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 +150,11 @@ public class RecentsConfiguration { 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); @@ -216,11 +225,6 @@ public class RecentsConfiguration { 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 +249,7 @@ public class RecentsConfiguration { * members. */ public void updateOnConfigurationChange() { launchedWithAltTab = false; + launchedWithNoRecentTasks = false; launchedFromAppWithThumbnail = false; launchedFromAppWithScreenshot = false; launchedFromHome = false; @@ -255,14 +260,26 @@ public class RecentsConfiguration { 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 dbcdb94578ea..7762111c7ac2 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 @@ class TaskResourceLoader implements Runnable { TaskResourceLoadQueue mLoadQueue; DrawableLruCache mApplicationIconCache; BitmapLruCache mThumbnailCache; + Bitmap mDefaultThumbnail; boolean mCancelled; boolean mWaitingOnLoadQueue; @@ -125,10 +126,12 @@ class TaskResourceLoader implements Runnable { /** 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 @@ class TaskResourceLoader implements Runnable { 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 @@ public class RecentsTaskLoader { BitmapDrawable mDefaultApplicationIcon; Bitmap mDefaultThumbnail; + Bitmap mLoadingThumbnail; /** Private Constructor */ private RecentsTaskLoader(Context context) { @@ -356,18 +361,22 @@ public class RecentsTaskLoader { 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 @@ public class RecentsTaskLoader { 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 @@ public class RecentsTaskLoader { 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 1dd1be6c2ef9..20be4156c377 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 @@ public class SpaceNode { 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 ad2fa8dd2f1c..cadfc5604698 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.animation.ObjectAnimator; 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 @@ public class FullScreenTransitionView extends FrameLayout { FullScreenTransitionViewCallbacks mCb; ImageView mScreenshotView; - Rect mClipRect = new Rect(); + Paint mLayerPaint = new Paint(); boolean mIsAnimating; AnimatorSet mEnterAnimation; @@ -99,12 +100,17 @@ public class FullScreenTransitionView extends FrameLayout { @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 @@ public class FullScreenTransitionView extends FrameLayout { 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 @@ public class FullScreenTransitionView extends FrameLayout { // 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 @@ public class FullScreenTransitionView extends FrameLayout { // 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 6f79683ceb30..724875867faf 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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV /** The RecentsView callbacks */ public interface RecentsViewCallbacks { - public void onTaskLaunching(boolean isTaskInStackBounds); + public void onTaskLaunching(); + public void onExitAnimationTriggered(); } RecentsConfiguration mConfig; @@ -160,19 +161,19 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } /** 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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV View child = getChildAt(i); if (child instanceof TaskStackView) { TaskStackView stackView = (TaskStackView) child; - stackView.startOnExitAnimation(ctx); + stackView.startExitToHomeAnimation(ctx); } } } @@ -191,6 +192,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // 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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV 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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV 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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV | 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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV 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 d4f381b46df2..bf2576016857 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.Paint; 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 @@ class TaskBarView extends FrameLayout { Drawable mLightDismissDrawable; Drawable mDarkDismissDrawable; + Paint mLayerPaint = new Paint(); static Paint sHighlightPaint; public TaskBarView(Context context) { @@ -91,6 +93,13 @@ class TaskBarView extends FrameLayout { 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 @@ class TaskBarView extends FrameLayout { } } + @Override + public boolean hasOverlappingRendering() { + return false; + } + /** Binds the bar view to the task */ void rebindToTask(Task t, boolean animate) { mTask = t; @@ -158,12 +172,12 @@ class TaskBarView extends FrameLayout { /** 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 +191,7 @@ class TaskBarView extends FrameLayout { } /** 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 +208,22 @@ class TaskBarView extends FrameLayout { .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 +236,7 @@ class TaskBarView extends FrameLayout { } /** 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 +246,11 @@ class TaskBarView extends FrameLayout { /** 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 79bfa5e23b49..6c12218b9c4a 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { TaskView tv = (TaskView) getChildAt(i); - tv.animateOnNoUserInteraction(); + tv.startNoUserInteractionAnimation(); } } }); @@ -187,12 +187,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + /** 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } /** 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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); } } @@ -944,6 +977,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal movement = Math.max(movement, Math.abs(toTransform.translationY - (int) tv.getTranslationY())); } - childViewTransformsOut.put(tv, new Pair(0, toTransform)); + + int startDelay = offset * + Constants.Values.TaskStackView.FilterStartDelay; + childViewTransformsOut.put(tv, new Pair(startDelay, toTransform)); + offset++; } - return Utilities.calculateTranslationAnimationDuration(movement, - mConfig.filteringCurrentViewsMinAnimDuration); + return mConfig.filteringCurrentViewsAnimDuration; } /** @@ -1009,8 +1046,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } } - return Utilities.calculateTranslationAnimationDuration(movement, - mConfig.filteringNewViewsMinAnimDuration); + return mConfig.filteringNewViewsAnimDuration; } /** Orchestrates the animations of the current child views and any new views. */ @@ -1179,7 +1215,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // 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 +1311,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 dc8a42000913..c2b209462963 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 @@ public class TaskThumbnailView extends ImageView { @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 b7e834b4f83d..0b191622a716 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.animation.ValueAnimator; 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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On Point mLastTouchDown = new Point(); Path mRoundedRectClipPath = new Path(); Rect mTmpRect = new Rect(); + Paint mLayerPaint = new Paint(); TaskThumbnailView mThumbnailView; TaskBarView mBarView; @@ -259,15 +261,15 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On /** 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 +280,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On } 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 +288,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On } 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 +305,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On @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 +327,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On } 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 +348,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On .scaleX(transform.scale) .scaleY(transform.scale) .translationY(transform.translationY) + .translationZ(transform.translationZ) .setStartDelay(delay) .setUpdateListener(null) .setInterpolator(mConfig.quintOutInterpolator) @@ -355,8 +359,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On } } - /** 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 +373,27 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On 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); - - // Animate the dim - if (mDim > 0) { - ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0); - anim.setDuration(mConfig.taskBarExitAnimDuration); - anim.setInterpolator(mConfig.fastOutLinearInInterpolator); - anim.start(); + 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(); + } + } 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 +421,16 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On .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 +442,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On /** 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 +498,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On @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) { @@ -581,7 +591,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On } else if (v == mBarView.mDismissButton) { // Animate out the view and call the callback final TaskView tv = this; - animateRemoval(new Runnable() { + startDeleteTaskAnimation(new Runnable() { @Override public void run() { mCb.onTaskDismissed(tv); 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 6c420e10b494..1947e3015cfb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java @@ -28,7 +28,7 @@ public class TaskViewTransform { public float dismissAlpha = 1f; public boolean visible = false; public Rect rect = new Rect(); - float t; + float t = 0f; public TaskViewTransform() { // Do nothing @@ -45,6 +45,18 @@ public class TaskViewTransform { t = o.t; } + /** Resets the current transform */ + public void reset() { + 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); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 20684a131293..5bc23b5e4057 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 @@ public abstract class BaseStatusBar extends SystemUI implements 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/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index db85b140442c..6a83a5e2226a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -88,15 +88,13 @@ public class KeyguardClockPositionAlgorithm { public void run(Result result) { int y = getClockY() - mKeyguardStatusHeight/2; - float topAdjustment = getTopExpansionAdjustment(); + float clockAdjustment = getClockYExpansionAdjustment(); float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier(); - result.stackScrollerPaddingAdjustment = (int) (topAdjustment*topPaddingAdjMultiplier); + result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier); int clockNotificationsPadding = getClockNotificationsPadding() + result.stackScrollerPaddingAdjustment; int padding = y + clockNotificationsPadding; - if (mNotificationCount == 0) { - y += topAdjustment; - } + y += clockAdjustment; result.clockY = y; result.stackScrollerPadding = mKeyguardStatusHeight + padding; result.clockAlpha = getClockAlpha(result.stackScrollerPadding @@ -119,8 +117,8 @@ public class KeyguardClockPositionAlgorithm { return (int) (getClockYFraction() * mHeight); } - private float getTopExpansionAdjustment() { - float rubberbandFactor = getTopExpansionRubberbandFactor(); + private float getClockYExpansionAdjustment() { + float rubberbandFactor = getClockYExpansionRubberbandFactor(); float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight)); float t = value / mMaxPanelHeight; float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR @@ -132,7 +130,7 @@ public class KeyguardClockPositionAlgorithm { } } - private float getTopExpansionRubberbandFactor() { + private float getClockYExpansionRubberbandFactor() { float t = getNotificationAmountT(); t = Math.min(t, 1.0f); t = (float) Math.pow(t, 0.3f); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java index ca49408e5a0c..e312d5850fb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java @@ -246,9 +246,7 @@ public class KeyguardPageSwipeHelper { private void startHintTranslationAnimations(float target, long duration, Interpolator interpolator) { ArrayList<View> targetViews = mCallback.getTranslationViews(); - int length = targetViews.size(); - for (int i = 0; i < length; i++) { - View targetView = targetViews.get(i); + for (View targetView : targetViews) { targetView.animate() .setDuration(duration) .setInterpolator(interpolator) @@ -261,16 +259,8 @@ public class KeyguardPageSwipeHelper { } private void cancelAnimations() { - ArrayList<View> translatingViews = mCallback.getTranslationViews(); - int length = translatingViews.size(); - for (int i = 0; i < length; i++) { - View target = translatingViews.get(i); - target.animate().cancel(); - } - ArrayList<View> fadingViews = mCallback.getFadingViews(); - length = fadingViews.size(); - for (int i = 0; i < length; i++) { - View target = fadingViews.get(i); + ArrayList<View> targetViews = mCallback.getTranslationViews(); + for (View target : targetViews) { target.animate().cancel(); } View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon; @@ -301,11 +291,6 @@ public class KeyguardPageSwipeHelper { // translation Animation startTranslationAnimations(vel, target); - // fade animations - if (snapBack) { - startFadeInAnimations(); - } - // animate left / right icon startIconAnimation(vel, snapBack, target); @@ -361,20 +346,9 @@ public class KeyguardPageSwipeHelper { mSwipeAnimator = animator; } - private void startFadeInAnimations() { - ArrayList<View> fadingViews = mCallback.getFadingViews(); - int length = fadingViews.size(); - for (int i = 0; i < length; i++) { - View targetView = fadingViews.get(i); - targetView.animate().alpha(1.0f); - } - } - private void startTranslationAnimations(float vel, float target) { ArrayList<View> targetViews = mCallback.getTranslationViews(); - int length = targetViews.size(); - for (int i = 0; i < length; i++) { - View targetView = targetViews.get(i); + for (View targetView : targetViews) { ViewPropertyAnimator animator = targetView.animate(); mFlingAnimationUtils.apply(animator, mTranslation, target, vel); animator.translationX(target); @@ -401,18 +375,8 @@ public class KeyguardPageSwipeHelper { translation = leftSwipePossible() ? translation : Math.min(0, translation); if (translation != mTranslation || isReset) { ArrayList<View> translatedViews = mCallback.getTranslationViews(); - int length = translatedViews.size(); - for (int i = 0; i < length; i++) { - View target = translatedViews.get(i); - target.setTranslationX(translation); - } - float targetAlpha = 1.0f - Math.abs(translation / mMinTranslationAmount); - targetAlpha = Math.max(0.0f, targetAlpha); - ArrayList<View> fadingViews = mCallback.getFadingViews(); - length = fadingViews.size(); - for (int i = 0; i < length; i++) { - View view = fadingViews.get(i); - view.setAlpha(targetAlpha); + for (View view : translatedViews) { + view.setTranslationX(translation); } if (translation == 0.0f) { boolean animate = !isReset; @@ -530,8 +494,6 @@ public class KeyguardPageSwipeHelper { ArrayList<View> getTranslationViews(); - ArrayList<View> getFadingViews(); - View getLeftIcon(); View getCenterIcon(); 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 5c686fc5e652..dde95bf46a77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -108,7 +108,6 @@ public class NotificationPanelView extends PanelView implements private KeyguardBottomAreaView mKeyguardBottomArea; private boolean mBlockTouches; private ArrayList<View> mSwipeTranslationViews = new ArrayList<>(); - private ArrayList<View> mSwipeFadingViews = new ArrayList<>(); public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -153,7 +152,7 @@ public class NotificationPanelView extends PanelView implements android.R.interpolator.fast_out_linear_in); mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area); mSwipeTranslationViews.add(mNotificationStackScroller); - mSwipeFadingViews.add(mKeyguardStatusView); + mSwipeTranslationViews.add(mKeyguardStatusView); mPageSwiper = new KeyguardPageSwipeHelper(this, getContext()); } @@ -689,6 +688,9 @@ public class NotificationPanelView extends PanelView implements @Override protected boolean isScrolledToBottom() { + if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) { + return true; + } if (!isInSettings()) { return mNotificationStackScroller.isScrolledToBottom(); } @@ -696,11 +698,6 @@ public class NotificationPanelView extends PanelView implements } @Override - protected boolean hasNotifications() { - return mNotificationStackScroller.getNotGoneChildCount() > 0; - } - - @Override protected int getMaxPanelHeight() { // TODO: Figure out transition for collapsing when QS is open, adjust height here. int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); @@ -964,11 +961,6 @@ public class NotificationPanelView extends PanelView implements } @Override - public ArrayList<View> getFadingViews() { - return mSwipeFadingViews; - } - - @Override public View getLeftIcon() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL ? mKeyguardBottomArea.getCameraImageView() 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 08305dc1e6f0..1f3098d94d41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -30,8 +30,6 @@ import android.view.ViewConfiguration; import android.view.ViewTreeObserver; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; -import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import com.android.systemui.R; @@ -45,8 +43,6 @@ public abstract class PanelView extends FrameLayout { public static final boolean DEBUG = PanelBar.DEBUG; public static final String TAG = PanelView.class.getSimpleName(); - private static final long KEYGUARD_MOVE_UP_LENGTH = 300; - private final void logf(String fmt, Object... args) { Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); } @@ -66,9 +62,6 @@ public abstract class PanelView extends FrameLayout { protected int mTouchSlop; protected boolean mHintAnimationRunning; private boolean mOverExpandedBeforeFling; - private boolean mKeyguardMovingUp; - private int mKeyguardMoveUpDistance; - private float mKeyguardFingerHeight; private ValueAnimator mHeightAnimator; private ObjectAnimator mPeekAnimator; @@ -89,8 +82,6 @@ public abstract class PanelView extends FrameLayout { private Interpolator mLinearOutSlowInInterpolator; private Interpolator mBounceInterpolator; - private Interpolator mKeyguardMoveUpInterpolator; - private final Interpolator mLinearInterpolator = new LinearInterpolator(); protected void onExpandingFinished() { mBar.onExpandingFinished(); @@ -118,7 +109,6 @@ public abstract class PanelView extends FrameLayout { mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); mBounceInterpolator = new BounceInterpolator(); - mKeyguardMoveUpInterpolator = new PathInterpolator(0.6f, 0f, 0.4f, 1f); } protected void loadDimens() { @@ -130,8 +120,6 @@ public abstract class PanelView extends FrameLayout { mTouchSlop = configuration.getScaledTouchSlop(); mHintDistance = res.getDimension(R.dimen.hint_move_distance); mEdgeTapAreaWidth = res.getDimensionPixelSize(R.dimen.edge_tap_area_width); - mKeyguardMoveUpDistance = - res.getDimensionPixelSize(R.dimen.keyguard_panel_move_up_distance); } private void trackMovement(MotionEvent event) { @@ -229,13 +217,8 @@ public abstract class PanelView extends FrameLayout { mJustPeeked = false; } if (!mJustPeeked && (!waitForTouchSlop || mTracking)) { - if (mStatusBar.getBarState() == StatusBarState.KEYGUARD && - !hasNotifications()) { - setExpandedHeightKeyguard(newHeight); - } else { - setExpandedHeightInternal(newHeight); - mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); - } + setExpandedHeightInternal(newHeight); + mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); } trackMovement(event); @@ -264,56 +247,10 @@ public abstract class PanelView extends FrameLayout { return !waitForTouchSlop || mTracking; } - protected abstract boolean hasNotifications(); - - private void setExpandedHeightKeyguard(float newHeight) { - mKeyguardFingerHeight = newHeight; - if (newHeight < getMaxPanelHeight() && !mKeyguardMovingUp) { - mKeyguardMovingUp = true; - ValueAnimator anim = createHeightAnimator( - getMaxPanelHeight() - mKeyguardMoveUpDistance); - anim.setDuration(KEYGUARD_MOVE_UP_LENGTH); - anim.setInterpolator(mKeyguardMoveUpInterpolator); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mHeightAnimator = null; - } - }); - mHeightAnimator = anim; - anim.start(); - postOnAnimationDelayed(new Runnable() { - @Override - public void run() { - if (mKeyguardFingerHeight < mExpandedHeight && mHeightAnimator != null - && mKeyguardMovingUp) { - mHeightAnimator.cancel(); - float target = getMaxPanelHeight() - 1.75f * mKeyguardMoveUpDistance; - float diff = mExpandedHeight - target; - ValueAnimator anim = createHeightAnimator(target); - float velocity = 2.5f * mKeyguardMoveUpDistance / - (KEYGUARD_MOVE_UP_LENGTH / 1000f); - anim.setInterpolator(mLinearInterpolator); - anim.setDuration(Math.max(0, (long) (diff / velocity * 1000f))); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mHeightAnimator = null; - } - }); - mHeightAnimator = anim; - anim.start(); - } - } - }, KEYGUARD_MOVE_UP_LENGTH / 2); - } - } - protected abstract boolean hasConflictingGestures(); protected void onTrackingStopped(boolean expand) { mTracking = false; - mKeyguardMovingUp = false; mBar.onTrackingStopped(PanelView.this, expand); } @@ -444,9 +381,6 @@ public abstract class PanelView extends FrameLayout { protected void fling(float vel, boolean expand) { cancelPeek(); - if (mHeightAnimator != null) { - mHeightAnimator.cancel(); - } float target = expand ? getMaxPanelHeight() : 0.0f; if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) { onExpandingFinished(); 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 b23992de977f..1da7dabb55be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -2532,6 +2532,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, repositionNavigationBar(); updateExpandedViewPos(EXPANDED_LEAVE_ALONE); updateShowSearchHoldoff(); + updateRowStates(); } @Override 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 c8ab02758a38..1712124e18d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -70,6 +70,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT; private int mNormalWidth; + private int mPadding; + private int mMultiUserExpandedMargin; private ActivityStarter mActivityStarter; private BrightnessController mBrightnessController; @@ -120,6 +122,10 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL 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); + } public void setActivityStarter(ActivityStarter activityStarter) { @@ -147,6 +153,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL updateZTranslation(); updateClickTargets(); updateWidth(); + updatePadding(); + updateMultiUserSwitch(); if (mQSPanel != null) { mQSPanel.setExpanded(expanded && !overscrolled); } @@ -256,6 +264,21 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } } + 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 +324,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL updateWidth(); updateVisibilities(); updateZTranslation(); + updatePadding(); + updateMultiUserSwitch(); } public void setUserInfoController(UserInfoController userInfoController) { 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 94472a37af4d..ccbaed346e08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -241,11 +241,12 @@ public class NotificationStackScrollLayout extends ViewGroup mBottomStackPeekSize = context.getResources() .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount); mStackScrollAlgorithm = new StackScrollAlgorithm(context); + mStackScrollAlgorithm.setDimmed(mAmbientState.isDimmed()); mPaddingBetweenElementsDimmed = context.getResources() .getDimensionPixelSize(R.dimen.notification_padding_dimmed); mPaddingBetweenElementsNormal = context.getResources() .getDimensionPixelSize(R.dimen.notification_padding); - updatePadding(false); + updatePadding(mAmbientState.isDimmed()); int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height); int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height); mExpandHelper = new ExpandHelper(getContext(), this, @@ -761,11 +762,12 @@ public class NotificationStackScrollLayout extends ViewGroup } } } - - mActivePointerId = INVALID_POINTER; - endDrag(); } + + mActivePointerId = INVALID_POINTER; + endDrag(); } + break; case MotionEvent.ACTION_CANCEL: if (mIsBeingDragged && getChildCount() > 0) { @@ -967,7 +969,7 @@ public class NotificationStackScrollLayout extends ViewGroup * @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); } /** @@ -1003,7 +1005,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (animate) { mStateAnimator.animateOverScrollToAmount(amount, onTop); } else { - setOverScrolledPixels(amount / getRubberBandFactor(), onTop); + setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop); mAmbientState.setOverScrollAmount(amount, onTop); if (onTop) { notifyOverscrollTopListener(amount); @@ -1225,13 +1227,14 @@ public class NotificationStackScrollLayout extends ViewGroup 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; @@ -1273,7 +1276,10 @@ public class NotificationStackScrollLayout extends ViewGroup 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) { 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 cbad9dc2fb00..602c22b37aea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -587,6 +587,7 @@ public class StackScrollAlgorithm { 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 +618,20 @@ public class StackScrollAlgorithm { 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/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java index b2ecb61625af..4ad45a8e234e 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java +++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java @@ -40,9 +40,6 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { private static String TAG = "PhoneFallbackEventHandler"; private static final boolean DEBUG = false; - // Use the new sessions APIs - private static final boolean USE_SESSIONS = true; - Context mContext; View mView; @@ -294,21 +291,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { } private void handleMediaKeyEvent(KeyEvent keyEvent) { - if (USE_SESSIONS) { - MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false); - } else { - IAudioService audioService = IAudioService.Stub.asInterface( - ServiceManager.checkService(Context.AUDIO_SERVICE)); - if (audioService != null) { - try { - audioService.dispatchMediaKeyEvent(keyEvent); - } catch (RemoteException e) { - Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e); - } - } else { - Slog.w(TAG, "Unable to find IAudioService for media key event."); - } - } + MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false); } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 5598972b4502..bc267101156b 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -144,9 +144,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final boolean ENABLE_CAR_DOCK_HOME_CAPTURE = true; static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false; - // Whether to use the new Session APIs - static final boolean USE_SESSIONS = true; - static final int SHORT_PRESS_POWER_NOTHING = 0; static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1; static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2; @@ -3974,6 +3971,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { * controlled by this device, or through remote submix). */ boolean isMusicActive() { + final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); if (am == null) { Log.w(TAG, "isMusicActive: couldn't get AudioManager reference"); @@ -3982,43 +3980,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return am.isLocalOrRemoteMusicActive(); } - /** - * Tell the audio service to adjust the volume appropriate to the event. - * @param keycode - */ - void handleVolumeKey(int stream, int keycode) { - IAudioService audioService = getAudioService(); - if (audioService == null) { - return; - } - try { - // when audio is playing locally, we shouldn't have to hold a wake lock - // during the call, but we do it as a precaution for the rare possibility - // that the music stops right before we call this. - // Otherwise we might also be in a remote playback case. - // TODO: Actually handle MUTE. - mBroadcastWakeLock.acquire(); - if (stream == AudioSystem.STREAM_MUSIC) { - audioService.adjustLocalOrRemoteStreamVolume(stream, - keycode == KeyEvent.KEYCODE_VOLUME_UP - ? AudioManager.ADJUST_RAISE - : AudioManager.ADJUST_LOWER, - mContext.getOpPackageName()); - } else { - audioService.adjustStreamVolume(stream, - keycode == KeyEvent.KEYCODE_VOLUME_UP - ? AudioManager.ADJUST_RAISE - : AudioManager.ADJUST_LOWER, - 0, - mContext.getOpPackageName()); - } - } catch (RemoteException e) { - Log.w(TAG, "IAudioService.adjust*StreamVolume() threw RemoteException " + e); - } finally { - mBroadcastWakeLock.release(); - } - } - final Object mScreenshotLock = new Object(); ServiceConnection mScreenshotConnection = null; @@ -4201,16 +4162,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (telephonyManager.isOffhook() && (result & ACTION_PASS_TO_USER) == 0) { // If we are in call but we decided not to pass the key to - // the application, handle the volume change here. - handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); + // the application, just pass it to the session service. + + MediaSessionLegacyHelper.getHelper(mContext) + .sendMediaButtonEvent(event, true); break; } } - if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) { - // If music is playing but we decided not to pass the key to the - // application, handle the volume change here. - handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); + if ((result & ACTION_PASS_TO_USER) == 0) { + // If we aren't passing to the user and no one else + // handled it send it to the session manager to figure + // out. + MediaSessionLegacyHelper.getHelper(mContext) + .sendMediaButtonEvent(event, true); break; } } @@ -4459,18 +4424,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { void dispatchMediaKeyWithWakeLockToAudioService(KeyEvent event) { if (ActivityManagerNative.isSystemReady()) { - if (USE_SESSIONS) { - MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true); - } else { - IAudioService audioService = getAudioService(); - if (audioService != null) { - try { - audioService.dispatchMediaKeyEventUnderWakelock(event); - } catch (RemoteException e) { - Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e); - } - } - } + MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true); } } diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java index b22aeb7c04b7..4318b9d4d21c 100644 --- a/rs/java/android/renderscript/Font.java +++ b/rs/java/android/renderscript/Font.java @@ -111,10 +111,10 @@ public class Font extends BaseObj { 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/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index cef68301853e..3b1e88a11d2f 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.BackupDataInput; 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 @@ public class BackupManagerService extends IBackupManager.Stub { 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 @@ public class BackupManagerService extends IBackupManager.Stub { 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 aeb195fe945e..912a181ef944 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -18,6 +18,7 @@ package com.android.server; 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.Context; 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 @@ import java.io.PrintWriter; * 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 @@ public final class BatteryService extends Binder { 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 @@ public final class BatteryService extends Binder { 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 @@ public final class BatteryService extends Binder { } 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 @@ public final class BatteryService extends Binder { 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 @@ public final class BatteryService extends Binder { 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 @@ public final class BatteryService extends Binder { && (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 @@ public final class BatteryService 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; - } - + 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 @@ public final class BatteryService extends Binder { } } } + + 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/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index e2a8ca2b99ed..07c904883f9c 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -308,6 +308,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public IBluetooth registerAdapter(IBluetoothManagerCallback callback){ + if (callback == null) { + Log.w(TAG, "Callback is null in registerAdapter"); + return null; + } Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); msg.obj = callback; mHandler.sendMessage(msg); @@ -317,6 +321,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public void unregisterAdapter(IBluetoothManagerCallback callback) { + if (callback == null) { + Log.w(TAG, "Callback is null in unregisterAdapter"); + return; + } mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER); 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 acc6abe89b33..000000000000 --- 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 bcb6e9ee9d68..215d92d8a49b 100644 --- a/services/core/java/com/android/server/MountServiceIdler.java +++ b/services/core/java/com/android/server/MountServiceIdler.java @@ -30,8 +30,7 @@ public class MountServiceIdler extends JobService { 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 d26f3fce816b..70222944b2a6 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.content.Context; 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 class NetworkManagementService extends INetworkManagementService.Stub 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } + /** + * 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub } 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 9d92421a6bc4..1bd837b03b7b 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -89,7 +89,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { @Override public String toString() { - return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid + + return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid + " subId=" + subId + " events=" + Integer.toHexString(events) + "}"; } } @@ -208,11 +208,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { 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 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { // 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 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { 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/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 697e1f203336..34c1ecd7b72f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2194,6 +2194,11 @@ public final class ActivityManagerService extends ActivityManagerNative 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 { @@ -7625,14 +7630,24 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private boolean isLockTaskAuthorized(ComponentName name) { + private boolean isLockTaskAuthorized(String pkg) { final DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); - return dpm != null && dpm.isLockTaskPermitted(name); + try { + int uid = mContext.getPackageManager().getPackageUid(pkg, + Binder.getCallingUserHandle().getIdentifier()); + return (uid == Binder.getCallingUid()) && dpm != null && dpm.isLockTaskPermitted(pkg); + } catch (NameNotFoundException e) { + return false; + } } private void startLockTaskMode(TaskRecord task) { - if (!isLockTaskAuthorized(task.intent.getComponent())) { + final String pkg; + synchronized (this) { + pkg = task.intent.getComponent().getPackageName(); + } + if (!isLockTaskAuthorized(pkg)) { return; } long ident = Binder.clearCallingIdentity(); @@ -7641,6 +7656,9 @@ public final class ActivityManagerService extends ActivityManagerNative // Since we lost lock on task, make sure it is still there. task = mStackSupervisor.anyTaskForIdLocked(task.taskId); if (task != null) { + if ((mFocusedActivity == null) || (task != mFocusedActivity.task)) { + throw new IllegalArgumentException("Invalid task, not in foreground"); + } mStackSupervisor.setLockTaskModeLocked(task); } } @@ -7651,25 +7669,25 @@ public final class ActivityManagerService extends ActivityManagerNative @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,24 +7695,27 @@ public final class ActivityManagerService extends ActivityManagerNative } task = r.task; } - if (task != null) { - startLockTaskMode(task); - } } finally { Binder.restoreCallingIdentity(ident); } + if (task != null) { + startLockTaskMode(task); + } } @Override public void stopLockTaskMode() { - // Check if the calling task is eligible to use lock task - final int uid = Binder.getCallingUid(); + // Verify that the user matches the package of the intent for the TaskRecord + // we are locked to. This will ensure the same caller for startLockTaskMode and + // stopLockTaskMode. try { - final String name = AppGlobals.getPackageManager().getNameForUid(uid); - if (!isLockTaskAuthorized(new ComponentName(name, name))) { - return; + String pkg = mStackSupervisor.mLockTaskModeTask.intent.getPackage(); + int uid = mContext.getPackageManager().getPackageUid(pkg, + Binder.getCallingUserHandle().getIdentifier()); + if (uid != Binder.getCallingUid()) { + throw new SecurityException("Invalid uid, expected " + uid); } - } catch (RemoteException e) { + } catch (NameNotFoundException e) { Log.d(TAG, "stopLockTaskMode " + e); return; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 11f855e2eb46..dd9cae90dd00 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 @@ package com.android.server.am; 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.Slog; 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.io.PrintWriter; 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 class ActivityRecord { 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 @@ final class ActivityRecord { 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 @@ final class ActivityRecord { 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 @@ final class ActivityRecord { 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 @@ final class ActivityRecord { realActivity = null; taskAffinity = null; stateNotNeeded = false; - baseDir = null; - resDir = null; - dataDir = null; + appInfo = null; processName = null; packageName = null; fullscreen = true; @@ -568,7 +567,10 @@ final class ActivityRecord { } 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() { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index d33d6dc5ae2d..e07463d83fac 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -230,14 +230,14 @@ public final class ActivityStackSupervisor implements DisplayListener { * 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); @@ -254,16 +254,20 @@ public final class ActivityStackSupervisor implements DisplayListener { /** If non-null then the task specified remains in front and no other tasks may be started * until the task exits or #stopLockTaskMode() is called. */ - private TaskRecord mLockTaskModeTask; + TaskRecord mLockTaskModeTask; public ActivityStackSupervisor(ActivityManagerService service) { mService = service; + 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); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index eb253ebeaa19..f403d0875306 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub 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/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index e54c95ee16fb..f79c02601c4d 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 android.util.TimeUtils; 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 class ServiceRecord extends Binder { 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 @@ final class ServiceRecord extends Binder { } 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 @@ final class ServiceRecord extends Binder { 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 bb289fab3f58..c79b33d1f301 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 @@ public class TaskPersister { 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 @@ public class TaskPersister { 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 @@ public class TaskPersister { 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 @@ public class TaskPersister { 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 @@ public class TaskPersister { } } if (!DEBUG && deleteFile) { + if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName()); taskFile.delete(); } } @@ -209,7 +213,7 @@ public class TaskPersister { 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 @@ public class TaskPersister { 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 @@ public class TaskPersister { 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 @@ public class TaskPersister { task.needsPersisting = false; } } + } else { + if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task); } } } @@ -330,6 +338,8 @@ public class TaskPersister { // 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 81a0b363cac1..1cde41fb92ad 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -652,8 +652,9 @@ final class TaskRecord extends ThumbnailHolder { 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; } diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index a77443dbc1cf..c7f4f6a61e59 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 @@ final class DisplayDeviceInfo { 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 @@ final class DisplayDeviceInfo { 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 @@ final class DisplayDeviceInfo { && 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 @@ final class DisplayDeviceInfo { 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 @@ final class DisplayDeviceInfo { 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 e80aecdcfb44..098537cd56bb 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 d61a35b77ffd..ed619d9c60cb 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 @@ final class LogicalDisplay { 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 bfd8372c52e3..3b23b6add8a0 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 @@ final class OverlayDisplayAdapter extends DisplayAdapter { 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 @@ final class OverlayDisplayAdapter extends DisplayAdapter { 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 @@ final class OverlayDisplayAdapter extends DisplayAdapter { mWidth = width; mHeight = height; mRefreshRate = refreshRate; + mDisplayPresentationDeadlineNanos = presentationDeadlineNanos; mDensityDpi = densityDpi; mSecure = secure; mState = state; @@ -249,6 +251,8 @@ final class OverlayDisplayAdapter extends DisplayAdapter { 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 @@ final class OverlayDisplayAdapter extends DisplayAdapter { // 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 06891f37a6ea..9ca5fda608b2 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 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { 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 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { */ 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 14ef5a9162b4..ec14cf1e1d10 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 @@ final class VirtualDisplayAdapter extends DisplayAdapter { 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 cd57941bcc3e..a05bf2c84dcd 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { 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 000000000000..29415745c3b8 --- /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 bcd08ebfb94a..74eaf2a6fc13 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 @@ import android.util.Slog; /** * 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 @@ final class ActiveSourceHandler { * @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 @@ final class ActiveSourceHandler { 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 @@ final class ActiveSourceHandler { // 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 f7392e92b63a..02f088fe3dab 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 @@ final class DeviceDiscoveryAction extends FeatureAction { /** * 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 @@ final class DeviceDiscoveryAction extends FeatureAction { 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 @@ final class DeviceDiscoveryAction extends FeatureAction { 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 @@ final class DeviceDiscoveryAction extends FeatureAction { 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 @@ final class DeviceDiscoveryAction extends FeatureAction { 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 @@ final class DeviceDiscoveryAction extends FeatureAction { 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 63c21829b13a..51df473b6c75 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 @@ package com.android.server.hdmi; * 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 @@ final class DevicePowerStatusAction extends FeatureAction { 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 @@ final class DevicePowerStatusAction extends FeatureAction { } 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 a8696a156d7d..dbe3b8091f2b 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 @@ final class DeviceSelectAction extends FeatureAction { 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 @@ final class DeviceSelectAction extends FeatureAction { 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 @@ final class DeviceSelectAction extends FeatureAction { 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 @@ final class DeviceSelectAction extends FeatureAction { 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 ae272b40bd89..0ec17f6bc102 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.os.Message; 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 @@ import com.android.internal.annotations.VisibleForTesting; * 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 @@ abstract class FeatureAction { // 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 @@ abstract class FeatureAction { 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 @@ abstract class FeatureAction { 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 a0c635db5d14..6d05e82cad5d 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 @@ final class HdmiCecController { 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 @@ final class HdmiCecController { 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 @@ final class HdmiCecController { */ 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 @@ final class HdmiCecController { 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 @@ final class HdmiCecController { // 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 @@ final class HdmiCecController { 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 index ebb6f5024bff..7080a566fc38 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 @@ import android.view.KeyEvent; /** * 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 class HdmiCecKeycodeTranslator { 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 7a2a6cc2b7bc..f86d655af470 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 @@ package com.android.server.hdmi; 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 @@ abstract class HdmiCecLocalDevice { 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 @@ abstract class HdmiCecLocalDevice { * @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 @@ abstract class HdmiCecLocalDevice { 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 @@ abstract class HdmiCecLocalDevice { } protected boolean handleGiveDeviceVendorId() { + assertRunOnServiceThread(); + int vendorId = mService.getVendorId(); HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand( mAddress, vendorId); @@ -112,6 +177,8 @@ abstract class HdmiCecLocalDevice { } protected boolean handleGetCecVersion(HdmiCecMessage message) { + assertRunOnServiceThread(); + int version = mService.getCecVersion(); HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(), message.getSource(), version); @@ -120,6 +187,8 @@ abstract class HdmiCecLocalDevice { } 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 @@ abstract class HdmiCecLocalDevice { } 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 @@ abstract class HdmiCecLocalDevice { 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 d79e283b3c85..01345ef0db97 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 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { 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 625b2560d48a..353f6036bcfc 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.HdmiCecMessage; 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 @@ import java.util.Locale; 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,14 +77,68 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { * @param callback callback object to report the result with */ void deviceSelect(int targetAddress, IHdmiControlCallback callback) { + assertRunOnServiceThread(); HdmiCecDeviceInfo targetDevice = mService.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) { @@ -79,6 +151,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @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 +165,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @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 +178,16 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // 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 +212,319 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } 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); + + // 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 + */ + final void removeCecDevice(int address) { + assertRunOnServiceThread(); + removeDeviceInfo(address); + mCecMessageCache.flushMessagesFrom(address); + + // TODO: announce a device removal. + } + + /** + * 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 8dbfd85e7c73..361a063b729d 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 @@ public class HdmiCecMessageBuilder { 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 b558f2bbe443..5294506f2125 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 @@ final class HdmiConstants { 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 5fffada46045..fddb833c7c88 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -37,14 +37,11 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; -import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; /** @@ -57,18 +54,6 @@ public final class HdmiControlService extends SystemService { // 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 +86,6 @@ public final class HdmiControlService extends SystemService { // 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(); @@ -130,8 +111,6 @@ public final class HdmiControlService extends SystemService { // Handler running on service thread. It's 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 +121,10 @@ public final class HdmiControlService extends SystemService { // 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 @@ -345,44 +297,6 @@ public final class HdmiControlService extends SystemService { 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 +313,11 @@ public final class HdmiControlService extends SystemService { HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { assertRunOnServiceThread(); - return mCecController.getDeviceInfo(logicalAddress); + HdmiCecLocalDeviceTv tv = tv(); + if (tv == null) { + return null; + } + return tv.getDeviceInfo(logicalAddress); } /** @@ -410,67 +328,6 @@ public final class HdmiControlService extends SystemService { } /** - * 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 +340,7 @@ public final class HdmiControlService extends SystemService { 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 +355,6 @@ public final class HdmiControlService extends SystemService { } /** - * 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 +369,17 @@ public final class HdmiControlService extends SystemService { } 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 +396,9 @@ public final class HdmiControlService extends SystemService { */ 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 +418,28 @@ public final class HdmiControlService extends SystemService { } 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 +449,6 @@ public final class HdmiControlService extends SystemService { 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 +505,9 @@ public final class HdmiControlService extends SystemService { 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 +517,41 @@ public final class HdmiControlService extends SystemService { } @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 +607,32 @@ public final class HdmiControlService extends SystemService { } @Override - public void portSelect(int portId, IHdmiControlCallback callback) { - // TODO: Implement this - } - - @Override - public void sendKeyEvent(int keyCode) { - // 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) { @@ -1003,43 +683,6 @@ public final class HdmiControlService extends SystemService { } } - 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); synchronized (mLock) { @@ -1058,39 +701,16 @@ public final class HdmiControlService extends SystemService { } } - 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 b534377286a9..9b7cc8d04376 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 @@ package com.android.server.hdmi; 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 @@ final class HdmiUtils { 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 ae20eda99e9e..647cc88e78fa 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 @@ final class HotplugDetectionAction extends FeatureAction { /** * 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 @@ final class HotplugDetectionAction extends FeatureAction { if (mTimeoutCount == 0) { pollAllDevices(); } else { - if (mService.getSystemAudioMode()) { + if (tv().getSystemAudioMode()) { pollAudioSystem(); } } @@ -121,29 +120,29 @@ final class HotplugDetectionAction extends FeatureAction { 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 @@ final class HotplugDetectionAction extends FeatureAction { 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 @@ final class HotplugDetectionAction extends FeatureAction { mayCancelOneTouchRecord(removedAddress); mayDisableSystemAudioAndARC(removedAddress); - mService.removeCecDevice(removedAddress); + tv().removeCecDevice(removedAddress); } private void mayChangeRoutingPath(int address) { @@ -217,7 +217,7 @@ final class HotplugDetectionAction extends FeatureAction { } 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 @@ final class HotplugDetectionAction extends FeatureAction { // 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 @@ final class HotplugDetectionAction extends FeatureAction { } // 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 2cae507797b4..4a49f099aa1a 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 @@ final class NewDeviceAction extends FeatureAction { /** * 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 @@ final class NewDeviceAction extends FeatureAction { @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 @@ final class NewDeviceAction extends FeatureAction { return true; } - sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, + sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), mDeviceLogicalAddress)); addTimer(mState, TIMEOUT_MS); return true; @@ -155,14 +154,14 @@ final class NewDeviceAction extends FeatureAction { 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 @@ final class NewDeviceAction extends FeatureAction { } 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 @@ final class NewDeviceAction extends FeatureAction { 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 @@ final class NewDeviceAction extends FeatureAction { 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 69fad139eb7b..e0a3a8b03181 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 @@ import android.util.Slog; * <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 @@ public final class OneTouchPlayAction extends FeatureAction { // 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 @@ public final class OneTouchPlayAction extends FeatureAction { } 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 08ca306f8372..a2e08f1c7366 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 @@ abstract class RequestArcAction extends FeatureAction { /** * @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 @@ abstract class RequestArcAction extends FeatureAction { 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 343aff798990..55fb65ae588d 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 @@ final class RequestArcInitiationAction extends RequestArcAction { * * 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 d4a35f8d1c00..62ca8f69cbfd 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 @@ final class RequestArcTerminationAction extends RequestArcAction { * * @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 19974ea6d418..0d657b2624c9 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 @@ import com.android.server.hdmi.HdmiControlService.SendMessageCallback; * <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 @@ public class RoutingControlAction extends FeatureAction { // 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 @@ public class RoutingControlAction extends FeatureAction { } 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 @@ public class RoutingControlAction extends FeatureAction { } 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 @@ public class RoutingControlAction extends FeatureAction { } 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 @@ public class RoutingControlAction extends FeatureAction { 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 @@ public class RoutingControlAction extends FeatureAction { // 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 @@ public class RoutingControlAction extends FeatureAction { } private void queryDevicePowerStatus(int address, SendMessageCallback callback) { - sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, address), + sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address), callback); } @@ -216,7 +216,7 @@ public class RoutingControlAction extends FeatureAction { 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 8e6998ff225f..c3078a2182cd 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 @@ final class SendKeyAction extends FeatureAction { /** * 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 @@ final class SendKeyAction extends FeatureAction { 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 @@ final class SendKeyAction extends FeatureAction { // 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 d53d88db2292..cdedd6b3bf42 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 @@ final class SetArcTransmissionStateAction extends FeatureAction { /** * @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 @@ final class SetArcTransmissionStateAction extends FeatureAction { 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 @@ final class SetArcTransmissionStateAction extends FeatureAction { } 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 dde33423be5a..e48b0dc3e862 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java @@ -47,28 +47,27 @@ abstract class SystemAudioAction extends FeatureAction { /** * 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,16 +89,16 @@ abstract class SystemAudioAction extends FeatureAction { } protected void setSystemAudioMode(boolean mode) { - mService.setSystemAudioMode(mode); + tv().setSystemAudioMode(mode); } protected void sendGiveAudioStatus() { - HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress, + HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(), mAvrLogicalAddress); 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_REPORT_AUDIO_STATUS; addTimer(mState, TIMEOUT_MS); } else { @@ -112,7 +111,7 @@ abstract class SystemAudioAction extends FeatureAction { private void handleSendGiveAudioStatusFailure() { // TODO: Notify the failure status. - int uiCommand = mService.getSystemAudioMode() + int uiCommand = tv().getSystemAudioMode() ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON : HdmiConstants.UI_COMMAND_MUTE_FUNCTION; // SystemAudioMode: OFF sendUserControlPressedAndReleased(uiCommand); @@ -121,9 +120,9 @@ abstract class SystemAudioAction extends FeatureAction { private void sendUserControlPressedAndReleased(int uiCommand) { sendCommand(HdmiCecMessageBuilder.buildUserControlPressed( - mSourceAddress, mAvrLogicalAddress, uiCommand)); + getSourceAddress(), mAvrLogicalAddress, uiCommand)); sendCommand(HdmiCecMessageBuilder.buildUserControlReleased( - mSourceAddress, mAvrLogicalAddress)); + getSourceAddress(), mAvrLogicalAddress)); } @Override @@ -158,7 +157,7 @@ abstract class SystemAudioAction extends FeatureAction { if (params.length > 0) { boolean mute = (params[0] & 0x80) == 0x80; int volume = params[0] & 0x7F; - mService.setAudioStatus(mute, volume); + tv().setAudioStatus(mute, volume); if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) { // Toggle AVR's mute status to match with the system audio status. sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE); @@ -171,8 +170,8 @@ abstract class SystemAudioAction extends FeatureAction { } protected void removeSystemAudioActionInProgress() { - mService.removeActionExcept(SystemAudioActionFromTv.class, this); - mService.removeActionExcept(SystemAudioActionFromAvr.class, this); + removeActionExcept(SystemAudioActionFromTv.class, this); + removeActionExcept(SystemAudioActionFromAvr.class, this); } @Override diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java index c5eb44b44156..3907b714b4da 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 @@ final class SystemAudioActionFromAvr extends SystemAudioAction { /** * 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 @@ final class SystemAudioActionFromAvr extends SystemAudioAction { } 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(); diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java index 9994de6f01ff..e0c4ff47a2f7 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 @@ final class SystemAudioActionFromTv extends SystemAudioAction { /** * 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 000000000000..e4d82ef88eaa --- /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 000000000000..75e4fefb1f17 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java @@ -0,0 +1,123 @@ +/* + * 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 + */ +// TODO: refactor SystemAudioMode so that it uses this class instead of internal state. +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 9d457020fecc..c954b5020ee5 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 @@ public class VendorSpecificAction extends FeatureAction { 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/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java index 4aef2d3142cf..538a2520311b 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 @@ package com.android.server.job.controllers; 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 @@ public class BatteryController extends StateController { 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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 06732caef530..825dc84dd876 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2268,16 +2268,15 @@ public class NotificationManagerService extends SystemService { // lock on mNotificationList int indexOfNotificationLocked(String key) { - NotificationRecord r = mNotificationsByKey.get(key); - if (r == null) { - return -1; + final int N = mNotificationList.size(); + for (int i = 0; i < N; i++) { + if (key.equals(mNotificationList.get(i).getKey())) { + return i; + } } - int index = Collections.binarySearch(mNotificationList, r, mRankingComparator); - // Guarantee to return -1 when not found. - return (index >= 0) ? index : -1; + return -1; } - private void updateNotificationPulse() { synchronized (mNotificationList) { updateLightsLocked(); diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index f2db79117346..2a7b4f60a69a 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 @@ import java.util.concurrent.atomic.AtomicBoolean; /** * {@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 = 808; + private static ComponentName sDexoptServiceName = new ComponentName( + BackgroundDexOptService.class.getPackage().getName(), + 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/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 3ed73f73dfea..dd3377142364 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -28,7 +28,7 @@ import android.content.pm.IPackageInstallerSession; import android.content.pm.PackageInstallerParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser; -import android.content.pm.PackageParser.PackageLite; +import android.content.pm.PackageParser.ApkLite; import android.content.pm.Signature; import android.os.Build; import android.os.Bundle; @@ -297,7 +297,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Verify that all staged packages are internally consistent for (File file : files) { - final PackageLite info = PackageParser.parsePackageLite(file.getAbsolutePath(), + final ApkLite info = PackageParser.parseApkLite(file.getAbsolutePath(), PackageParser.PARSE_GET_SIGNATURES); if (info == null) { throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, @@ -356,7 +356,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Missing existing base package for " + mPackageName); } - final PackageLite info = PackageParser.parsePackageLite(app.sourceDir, + final ApkLite info = PackageParser.parseApkLite(app.sourceDir, PackageParser.PARSE_GET_SIGNATURES); if (info == null) { throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2394a6c3c861..dc7645576896 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.app.ResolverActivity; 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; @@ -93,6 +94,7 @@ import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstallerParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser.ActivityIntentInfo; +import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageParser; import android.content.pm.PackageStats; import android.content.pm.PackageUserState; @@ -136,6 +138,7 @@ import android.system.ErrnoException; import android.system.Os; import android.system.StructStat; import android.text.TextUtils; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.DisplayMetrics; import android.util.EventLog; @@ -412,9 +415,6 @@ public class PackageManagerService extends IPackageManager.Stub { final HashMap<String, SharedLibraryEntry> mSharedLibraries = new HashMap<String, SharedLibraryEntry>(); - // Temporary for building the final shared libraries for an .apk. - String[] mTmpSharedLibraries = null; - // These are the features this devices supports that were read from the // etc/permissions.xml file. final HashMap<String, FeatureInfo> mAvailableFeatures = @@ -1248,7 +1248,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - 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); @@ -2243,12 +2243,12 @@ public class PackageManagerService extends IPackageManager.Stub { if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) { return null; } - // TODO: teach about reading split name - pkg = new PackageParser.Package(packageName, 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; @@ -4072,19 +4072,20 @@ public class PackageManagerService extends IPackageManager.Stub { private boolean createIdmapForPackagePairLI(PackageParser.Package pkg, PackageParser.Package opkg) { if (!opkg.mTrustedOverlay) { - Slog.w(TAG, "Skipping target and overlay pair " + pkg.mScanPath + " and " + - opkg.mScanPath + ": overlay not trusted"); + Slog.w(TAG, "Skipping target and overlay pair " + pkg.codePath + " and " + + opkg.codePath + ": overlay not trusted"); return false; } HashMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName); if (overlaySet == null) { - Slog.e(TAG, "was about to create idmap for " + pkg.mScanPath + " and " + - opkg.mScanPath + " but target package has no known overlays"); + Slog.e(TAG, "was about to create idmap for " + pkg.codePath + " and " + + opkg.codePath + " but target package has no known overlays"); return false; } final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); - if (mInstaller.idmap(pkg.mScanPath, opkg.mScanPath, sharedGid) != 0) { - Slog.e(TAG, "Failed to generate idmap for " + pkg.mScanPath + " and " + opkg.mScanPath); + // 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; } PackageParser.Package[] overlayArray = @@ -4179,8 +4180,10 @@ public class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, srcFile.toString() + " changed; collecting certs"); } - if (!pp.collectCertificates(pkg, parseFlags)) { - mLastScanError = pp.getParseError(); + try { + pp.collectCertificates(pkg, parseFlags); + } catch (PackageParserException e) { + mLastScanError = e.error; return false; } return true; @@ -4199,11 +4202,13 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser pp = new PackageParser(scanPath); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); - final PackageParser.Package pkg = pp.parsePackage(scanFile, - scanPath, mMetrics, parseFlags, (scanMode & SCAN_TRUSTED_OVERLAY) != 0); - if (pkg == null) { - mLastScanError = pp.getParseError(); + final PackageParser.Package pkg; + try { + pkg = pp.parseMonolithicPackage(scanFile, mMetrics, parseFlags, + (scanMode & SCAN_TRUSTED_OVERLAY) != 0); + } catch (PackageParserException e) { + mLastScanError = e.error; return null; } @@ -4360,22 +4365,30 @@ public class PackageManagerService extends IPackageManager.Stub { } } - 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.mScanPath; + resPath = pkg.codePath; + splitResPaths = pkg.splitCodePaths; } - codePath = pkg.mScanPath; // Set application objects path explicitly. - setApplicationInfoPaths(pkg, codePath, resPath); + 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); @@ -4400,13 +4413,6 @@ public class PackageManagerService extends IPackageManager.Stub { return scannedPkg; } - private static void setApplicationInfoPaths(PackageParser.Package pkg, String destCodePath, - String destResPath) { - pkg.mPath = pkg.mScanPath = destCodePath; - pkg.applicationInfo.sourceDir = destCodePath; - pkg.applicationInfo.publicSourceDir = destResPath; - } - private static String fixProcessName(String defProcessName, String processName, int uid) { if (processName == null) { @@ -4630,52 +4636,56 @@ public class PackageManagerService extends IPackageManager.Stub { } } - boolean performed = false; - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - String path = pkg.mScanPath; - 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; + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { + final ArrayList<String> paths = new ArrayList<>(); + paths.add(pkg.codePath); + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + Collections.addAll(paths, pkg.splitCodePaths); + } + + 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; } - return DEX_OPT_PERFORMED; - } - if (defer && isDexOptNeededInternal) { - if (mDeferredDexOpt == null) { - mDeferredDexOpt = new HashSet<PackageParser.Package>(); + if (defer && isDexOptNeededInternal) { + if (mDeferredDexOpt == null) { + mDeferredDexOpt = new HashSet<PackageParser.Package>(); + } + mDeferredDexOpt.add(pkg); + return DEX_OPT_DEFERRED; } - 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; } - 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; @@ -4805,11 +4815,11 @@ public class PackageManagerService extends IPackageManager.Stub { return res; } - private int addSharedLibraryLPw(final SharedLibraryEntry file, int num, + private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file, PackageParser.Package changingLib) { if (file.path != null) { - mTmpSharedLibraries[num] = file.path; - return num+1; + usesLibraryFiles.add(file.path); + return; } PackageParser.Package p = mPackages.get(file.apk); if (changingLib != null && changingLib.packageName.equals(file.apk)) { @@ -4822,16 +4832,11 @@ public class PackageManagerService extends IPackageManager.Stub { } } if (p != null) { - String path = p.mPath; - for (int i=0; i<num; i++) { - if (mTmpSharedLibraries[i].equals(path)) { - return num; - } + usesLibraryFiles.add(p.codePath); + if (!ArrayUtils.isEmpty(p.splitCodePaths)) { + Collections.addAll(usesLibraryFiles, p.splitCodePaths); } - mTmpSharedLibraries[num] = p.mPath; - return num+1; } - return num; } private boolean updateSharedLibrariesLPw(PackageParser.Package pkg, @@ -4845,11 +4850,7 @@ public class PackageManagerService extends IPackageManager.Stub { } if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) { - if (mTmpSharedLibraries == null || - mTmpSharedLibraries.length < mSharedLibraries.size()) { - mTmpSharedLibraries = new String[mSharedLibraries.size()]; - } - int num = 0; + final ArraySet<String> usesLibraryFiles = new ArraySet<>(); int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0; for (int i=0; i<N; i++) { final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesLibraries.get(i)); @@ -4860,7 +4861,7 @@ public class PackageManagerService extends IPackageManager.Stub { mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; return false; } - num = addSharedLibraryLPw(file, num, changingLib); + addSharedLibraryLPw(usesLibraryFiles, file, changingLib); } N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0; for (int i=0; i<N; i++) { @@ -4870,13 +4871,12 @@ public class PackageManagerService extends IPackageManager.Stub { + " desires unavailable shared library " + pkg.usesOptionalLibraries.get(i) + "; ignoring!"); } else { - num = addSharedLibraryLPw(file, num, changingLib); + addSharedLibraryLPw(usesLibraryFiles, file, changingLib); } } - if (num > 0) { - pkg.usesLibraryFiles = new String[num]; - System.arraycopy(mTmpSharedLibraries, 0, - pkg.usesLibraryFiles, 0, num); + N = usesLibraryFiles.size(); + if (N > 0) { + pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[N]); } else { pkg.usesLibraryFiles = null; } @@ -4922,8 +4922,8 @@ public class PackageManagerService extends IPackageManager.Stub { private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) { - File scanFile = new File(pkg.mScanPath); - if (scanFile == null || pkg.applicationInfo.sourceDir == null || + final File scanFile = new File(pkg.codePath); + 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"); @@ -5360,7 +5360,7 @@ public class PackageManagerService extends IPackageManager.Stub { pkgSetting.uidError = uidError; } - String path = scanFile.getPath(); + final String path = scanFile.getPath(); /* Note: We don't want to unpack the native binaries for * system applications, unless they have been updated * (the binaries are already under /system/lib). @@ -5372,6 +5372,7 @@ public class PackageManagerService extends IPackageManager.Stub { * 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 @@ -5491,7 +5492,6 @@ public class PackageManagerService extends IPackageManager.Stub { handle.close(); } } - pkg.mScanPath = path; if ((scanMode&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) { // We don't do this here during boot because we can do it all @@ -5636,7 +5636,7 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { // We don't expect installation to fail beyond this point, if ((scanMode&SCAN_MONITOR) != 0) { - mAppDirs.put(pkg.mPath, pkg); + mAppDirs.put(pkg.codePath, pkg); } // Add the new setting to mSettings mSettings.insertPackageSettingLPw(pkgSetting, pkg); @@ -5926,6 +5926,8 @@ public class PackageManagerService extends IPackageManager.Stub { 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); @@ -6257,8 +6259,8 @@ public class PackageManagerService extends IPackageManager.Stub { // writer synchronized (mPackages) { mPackages.remove(pkg.applicationInfo.packageName); - if (pkg.mPath != null) { - mAppDirs.remove(pkg.mPath); + if (pkg.codePath != null) { + mAppDirs.remove(pkg.codePath); } cleanPackageDataStructuresLILPw(pkg, chatty); } @@ -9039,6 +9041,10 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ -9127,10 +9133,6 @@ public class PackageManagerService extends IPackageManager.Stub { } } - String getCodePath() { - return codeFileName; - } - void createCopyFile() { installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; codeFileName = createTempPackageFile(installDir).getPath(); @@ -9286,10 +9288,6 @@ public class PackageManagerService extends IPackageManager.Stub { return status; } - String getResourcePath() { - return resourceFileName; - } - private String getResourcePathFromCodePath() { final String codePath = getCodePath(); if (isFwdLocked()) { @@ -9320,6 +9318,16 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + String getCodePath() { + return codeFileName; + } + + @Override + String getResourcePath() { + return resourceFileName; + } + + @Override String getNativeLibraryPath() { if (libraryPath == null) { libraryPath = getLibraryPathFromCodePath(); @@ -9786,6 +9794,20 @@ public class PackageManagerService extends IPackageManager.Stub { 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; @@ -9825,7 +9847,7 @@ public class PackageManagerService extends IPackageManager.Stub { res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; return; } - if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) { + if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.codePath)) { // Don't allow installation over an existing package with the same name. Slog.w(TAG, "Attempt to re-install " + pkgName + " without first uninstalling."); @@ -9837,15 +9859,12 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode, System.currentTimeMillis(), user, abiOverride); if (newPackage == null) { - Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); + Slog.w(TAG, "Package couldn't be installed in " + pkg.codePath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; } } else { - updateSettingsLI(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) { @@ -9930,15 +9949,12 @@ public class PackageManagerService extends IPackageManager.Stub { newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME, System.currentTimeMillis(), user, abiOverride); if (newPackage == null) { - Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); + Slog.w(TAG, "Package couldn't be installed in " + pkg.codePath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; } } else { - updateSettingsLI(newPackage, - installerPackageName, - allUsers, perUserInstalled, - res); + updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res); updatedSettings = true; } } @@ -9959,7 +9975,7 @@ public class PackageManagerService extends IPackageManager.Stub { // package that we deleted. if (deletedPkg) { if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage); - File restoreFile = new File(deletedPackage.mPath); + File restoreFile = new File(deletedPackage.codePath); // Parse old package boolean oldOnSd = isExternal(deletedPackage); int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | @@ -10044,7 +10060,7 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user, abiOverride); if (newPackage == null) { - Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); + Slog.w(TAG, "Package couldn't be installed in " + pkg.codePath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; } @@ -10090,10 +10106,11 @@ public class PackageManagerService extends IPackageManager.Stub { } // Utility method used to move dex files during install. - private int moveDexFilesLI(PackageParser.Package newPackage) { + 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); - int retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath, + int retCode = mInstaller.movedex(oldCodePath, newPackage.codePath, instructionSet); if (retCode != 0) { /* @@ -10105,8 +10122,8 @@ public class PackageManagerService extends IPackageManager.Stub { * file from a previous version of the package. */ newPackage.mDexOptNeeded = true; - mInstaller.rmdex(newPackage.mScanPath, instructionSet); - mInstaller.rmdex(newPackage.mPath, instructionSet); + mInstaller.rmdex(oldCodePath, instructionSet); + mInstaller.rmdex(newPackage.codePath, instructionSet); } } return PackageManager.INSTALL_SUCCEEDED; @@ -10124,13 +10141,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.writeLPr(); } - if ((res.returnCode = moveDexFilesLI(newPackage)) - != PackageManager.INSTALL_SUCCEEDED) { - // Discontinue if moving dex files failed. - return; - } - - if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.mPath); + if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath); synchronized (mPackages) { updatePermissionsLPw(newPackage.packageName, newPackage, @@ -10197,12 +10208,16 @@ public class PackageManagerService extends IPackageManager.Stub { | (onSd ? PackageParser.PARSE_ON_SDCARD : 0); PackageParser pp = new PackageParser(tmpPackageFile.getPath()); pp.setSeparateProcesses(mSeparateProcesses); - final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, - null, mMetrics, parseFlags); - if (pkg == null) { - res.returnCode = pp.getParseError(); + + final PackageParser.Package pkg; + try { + pkg = pp.parseMonolithicPackage(tmpPackageFile, mMetrics, + parseFlags); + } catch (PackageParserException e) { + res.returnCode = e.error; return; } + String pkgName = res.name = pkg.packageName; if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) { if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) { @@ -10210,8 +10225,11 @@ public class PackageManagerService extends IPackageManager.Stub { return; } } - if (!pp.collectCertificates(pkg, parseFlags)) { - res.returnCode = pp.getParseError(); + + try { + pp.collectCertificates(pkg, parseFlags); + } catch (PackageParserException e) { + res.returnCode = e.error; return; } @@ -10316,7 +10334,12 @@ public class PackageManagerService extends IPackageManager.Stub { return; } // Set application objects path explicitly after the rename - setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath()); + 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, @@ -11196,7 +11219,7 @@ public class PackageManagerService extends IPackageManager.Stub { publicSrcDir = applicationInfo.publicSourceDir; } } - int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, libDirPath, + int res = mInstaller.getSizeInfo(packageName, userHandle, p.codePath, libDirPath, publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps), pStats); if (res < 0) { @@ -12829,7 +12852,7 @@ public class PackageManagerService extends IPackageManager.Stub { + " Aborting move and returning error"); returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR; } else { - final String oldCodePath = pkg.mPath; + final String oldCodePath = pkg.codePath; final String newCodePath = mp.targetArgs.getCodePath(); final String newResPath = mp.targetArgs.getResourcePath(); final String newNativePath = mp.targetArgs @@ -12859,18 +12882,17 @@ public class PackageManagerService extends IPackageManager.Stub { } if (returnCode == PackageManager.MOVE_SUCCEEDED) { - pkg.mPath = newCodePath; + pkg.codePath = newCodePath; // Move dex files around - if (moveDexFilesLI(pkg) != PackageManager.INSTALL_SUCCEEDED) { + if (moveDexFilesLI(oldCodePath, pkg) != PackageManager.INSTALL_SUCCEEDED) { // Moving of dex files failed. Set // error code and abort move. - pkg.mPath = pkg.mScanPath; + pkg.codePath = oldCodePath; returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; } } if (returnCode == PackageManager.MOVE_SUCCEEDED) { - pkg.mScanPath = newCodePath; pkg.applicationInfo.sourceDir = newCodePath; pkg.applicationInfo.publicSourceDir = newResPath; pkg.applicationInfo.nativeLibraryDir = newNativePath; diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b9416576cecc..1839259b6f0a 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 @@ public class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { 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 fb4b8f0b69c6..bd80b54f6c10 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 @@ package com.android.server.power; 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; 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 @@ public final class PowerManagerService extends com.android.server.SystemService 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 final class PowerManagerService extends com.android.server.SystemService 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 final class PowerManagerService extends com.android.server.SystemService 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 @@ public final class PowerManagerService extends com.android.server.SystemService // 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 @@ public final class PowerManagerService extends com.android.server.SystemService 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 @@ public final class PowerManagerService extends com.android.server.SystemService 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 @@ public final class PowerManagerService extends com.android.server.SystemService final boolean wasStayOn = mStayOn; if (mStayOnWhilePluggedInSetting != 0 && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) { - mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting); + mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting); } else { mStayOn = false; } @@ -3076,10 +3066,5 @@ public final class PowerManagerService extends com.android.server.SystemService mLowPowerModeListeners.add(listener); } } - - @Override - public void setPolicy(WindowManagerPolicy policy) { - PowerManagerService.this.setPolicy(policy); - } } } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index e34f42bde915..d72ed9e3f73e 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -116,6 +116,19 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } + 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 +146,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { 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 +172,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { return; } if (connection.getHardwareLocked() != hardware - || connection.getCallingUidLocked() != callingUid - || connection.getResolvedUserIdLocked() != resolvedUserId) { + || checkUidChangedLocked(connection, callingUid, resolvedUserId)) { return; } connection.resetLocked(null, null, null, null); @@ -226,11 +237,11 @@ class TvInputHardwareManager implements TvInputHal.Callback { return mConfigs; } - public int getCallingUidLocked() { + public Integer getCallingUidLocked() { return mCallingUid; } - public int getResolvedUserIdLocked() { + public Integer getResolvedUserIdLocked() { return mResolvedUserId; } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 10a67c4e421d..cbbcc58854b0 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 @@ package com.android.server.tv; 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 com.android.server.SystemService; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; - import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** This class provides a system service that manages television inputs. */ public final class TvInputManagerService extends SystemService { @@ -123,6 +127,44 @@ public final class TvInputManagerService extends SystemService { buildTvInputListLocked(mCurrentUserId); } } + + @Override + public void onPackageRemoved(String packageName, int uid) { + synchronized (mLock) { + UserState userState = getUserStateLocked(mCurrentUserId); + if (!userState.packageList.contains(packageName)) { + // Not a TV input package. + return; + } + } + + ArrayList<ContentProviderOperation> operations = + new ArrayList<ContentProviderOperation>(); + + String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?"; + String[] selectionArgs = { packageName }; + + operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI) + .withSelection(selection, selectionArgs).build()); + operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI) + .withSelection(selection, selectionArgs).build()); + operations.add(ContentProviderOperation + .newDelete(TvContract.WatchedPrograms.CONTENT_URI) + .withSelection(selection, selectionArgs).build()); + + ContentProviderResult[] results = null; + try { + results = mContentResolver.applyBatch(TvContract.AUTHORITY, operations); + } catch (RemoteException | OperationApplicationException e) { + Slog.e(TAG, "error in applyBatch" + e); + } + + if (DEBUG) { + Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid + + ")"); + Slog.d(TAG, "results=" + results); + } + } }; monitor.register(mContext, null, UserHandle.ALL, true); @@ -145,6 +187,7 @@ public final class TvInputManagerService extends SystemService { private void buildTvInputListLocked(int userId) { UserState userState = getUserStateLocked(userId); userState.inputMap.clear(); + userState.packageList.clear(); if (DEBUG) Slog.d(TAG, "buildTvInputList"); PackageManager pm = mContext.getPackageManager(); @@ -162,6 +205,7 @@ public final class TvInputManagerService extends SystemService { TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri); if (DEBUG) Slog.d(TAG, "add " + info.getId()); userState.inputMap.put(info.getId(), info); + userState.packageList.add(si.packageName); } catch (IOException | XmlPullParserException e) { Slog.e(TAG, "Can't load TV input " + si.name, e); } @@ -348,7 +392,7 @@ public final class TvInputManagerService extends SystemService { if (session == null) { removeSessionStateLocked(sessionToken, userId); sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, - null, null, sessionState.mSeq, userId); + null, null, sessionState.mSeq); } else { try { session.asBinder().linkToDeath(sessionState, 0); @@ -364,7 +408,7 @@ public final class TvInputManagerService extends SystemService { clientState.mSessionTokens.add(sessionState.mSessionToken); sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, - sessionToken, channels[0], sessionState.mSeq, userId); + sessionToken, channels[0], sessionState.mSeq); } channels[0].dispose(); } @@ -449,13 +493,13 @@ public final class TvInputManagerService extends SystemService { 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) { @@ -672,7 +716,7 @@ public final class TvInputManagerService extends SystemService { } // 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 +828,10 @@ public final class TvInputManagerService extends SystemService { } // 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 +978,9 @@ public final class TvInputManagerService extends SystemService { // 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 +1145,7 @@ public final class TvInputManagerService extends SystemService { 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 6cb6b7690d66..d05d0c714126 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 @@ final class AccessibilityController { // 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 45326f77635b..f1d0585d19bf 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 @@ public class WindowAnimator { 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 2d8a34b6512f..d04d668a8ef0 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 @@ public class WindowManagerService extends IWindowManager.Stub mDisplaySettings = new DisplaySettings(context); mDisplaySettings.readSettingsLocked(); + LocalServices.addService(WindowManagerPolicy.class, mPolicy); + mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG)); mFxSession = new SurfaceSession(); @@ -779,7 +781,6 @@ public class WindowManagerService extends IWindowManager.Stub 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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 405ab378ebcc..5cf571330593 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { = 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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); @@ -3723,38 +3721,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * 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))) { - - // 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); + 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.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,30 +3763,33 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { /** * 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; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 3102cce4ea79..0d05c5fe7fa6 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.content.ContentService; 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 @@ public final class SystemServer { 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 final class SystemServer { } public SystemServer() { + // Check for factory test mode. mFactoryTestMode = FactoryTest.getMode(); } @@ -243,7 +250,7 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { } try { - pm.performBootDexOpt(); + mPackageManagerService.performBootDexOpt(); } catch (Throwable e) { reportWtf("performing boot dexopt", e); } @@ -559,13 +571,9 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { * 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 @@ public final class SystemServer { } 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { } } - try { - Slog.i(TAG, "IdleMaintenanceService"); - new IdleMaintenanceService(context, battery); - } catch (Throwable e) { - reportWtf("starting IdleMaintenanceService", e); - } - - try { - if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) { - mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS); - } - } catch (Throwable e) { - reportWtf("starting Print Service", e); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) { + mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS); } - try { - mSystemServiceManager.startService(RestrictionsManagerService.class); - } catch (Throwable e) { - reportWtf("starting RestrictionsManagerService", e); - } + mSystemServiceManager.startService(RestrictionsManagerService.class); - try { - mSystemServiceManager.startService(MediaSessionService.class); - } catch (Throwable e) { - reportWtf("starting MediaSessionService", e); - } + mSystemServiceManager.startService(MediaSessionService.class); - try { - mSystemServiceManager.startService(HdmiControlService.class); - } catch (Throwable e) { - reportWtf("starting HdmiControlService", e); - } + mSystemServiceManager.startService(HdmiControlService.class); - 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 d97450900a95..8a4e12388c97 100644 --- a/telecomm/java/android/telecomm/ConnectionService.java +++ b/telecomm/java/android/telecomm/ConnectionService.java @@ -20,15 +20,9 @@ import android.net.Uri; 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 new file mode 100644 index 000000000000..1da54e0d5bbe --- /dev/null +++ b/telecomm/java/android/telecomm/PhoneApplication.java @@ -0,0 +1,183 @@ +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 000000000000..6327fcc15dd8 --- /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 f187f4d176be..964db4a5c05b 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 @@ public class Subscription implements Parcelable { } }; - 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 8300c92f803e..09520979050b 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,6 +56,28 @@ public final class TelecommConstants { 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}. + */ + 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"; + + /** + * 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 EXTRA_START_CALL_WITH_SPEAKERPHONE = + "android.intent.extra.START_CALL_WITH_SPEAKERPHONE"; + + /** * Extra for {@link #ACTION_INCOMING_CALL} containing the {@link CallServiceDescriptor} that * describes the call service to use for the incoming call. */ diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java new file mode 100644 index 000000000000..a97e7e499f5f --- /dev/null +++ b/telecomm/java/android/telecomm/TelecommManager.java @@ -0,0 +1,48 @@ +/* + * 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.telecomm; + +import android.content.Context; + +import com.android.internal.telecomm.ITelecommService; + +/** + * Provides access to Telecomm-related functionality. + */ +public class TelecommManager { + private static final String TAG = "TelecommManager"; + + private final Context mContext; + private final ITelecommService mService; + + /** @hide */ + public TelecommManager(Context context, ITelecommService service) { + Context appContext = context.getApplicationContext(); + if (appContext != null) { + mContext = appContext; + } else { + mContext = context; + } + + mService = service; + } + + /** {@hide} */ + public static TelecommManager from(Context context) { + return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE); + } +} diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl index 0e94ffb584a4..dc2b8694c66e 100644 --- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl +++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl @@ -16,6 +16,9 @@ package com.android.internal.telecomm; +import android.telecomm.Subscription; +import android.content.ComponentName; + /** * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing * commands that were previously handled by ITelephony. @@ -38,4 +41,24 @@ interface ITelecommService { * @param showDialpad if true, make the dialpad visible initially. */ void showCallScreen(boolean showDialpad); + + /** + * Returns the component name of the phone application installed on the system partition. + */ + ComponentName getSystemPhoneApplication(); + + /** + * Gets a list of Subscriptions. + */ + 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 670def798d72..ced8e2f72665 100644 --- a/telephony/java/android/telephony/SubInfoRecord.java +++ b/telephony/java/android/telephony/SubInfoRecord.java @@ -105,4 +105,11 @@ public class SubInfoRecord implements Parcelable { 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 859a890b7236..79e9fd5c4c38 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -697,12 +697,16 @@ public class SubscriptionManager implements BaseColumns { 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 aaee99f3e280..124a8ec6cf69 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -19,15 +19,12 @@ package android.telephony; 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 com.android.internal.telephony.TelephonyProperties; 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 class TelephonyManager { 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. * @@ -1414,6 +1421,9 @@ public class TelephonyManager { // FIXME the argument to pass is subId ?? public int getSimState(int slotId) { long[] subId = SubscriptionManager.getSubId(slotId); + if (subId == null) { + return SIM_STATE_ABSENT; + } // FIXME Do not use a property to determine SIM_STATE, call // appropriate method on some object. String prop = @@ -3025,28 +3035,6 @@ public class TelephonyManager { /** @hide */ @SystemApi - public int enableApnType(String type) { - try { - return getITelephony().enableApnType(type); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#enableApnType", e); - } - return PhoneConstants.APN_REQUEST_FAILED; - } - - /** @hide */ - @SystemApi - public int disableApnType(String type) { - try { - return getITelephony().disableApnType(type); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#disableApnType", e); - } - return PhoneConstants.APN_REQUEST_FAILED; - } - - /** @hide */ - @SystemApi public boolean enableDataConnectivity() { try { return getITelephony().enableDataConnectivity(); @@ -3109,4 +3097,40 @@ public class TelephonyManager { } 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/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 407da87292b8..beee6167e422 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -310,27 +310,6 @@ interface ITelephony { */ void disableLocationUpdatesUsingSubId(long subId); - - /** - * Enable a specific APN type. - */ - int enableApnType(String type); - - /** - * Disable a specific APN type. - */ - int disableApnType(String type); - - /** - * Enable a specific APN type with subscription. - */ - int enableApnTypeUsingSub(long subId, String type); - - /** - * Disable a specific APN type with subscription. - */ - int disableApnTypeUsingSub(long subId, String type); - /** * Allow mobile data connections. */ diff --git a/tests/IdleServiceTest/Android.mk b/tests/IdleServiceTest/Android.mk deleted file mode 100644 index a7879c591840..000000000000 --- 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 16d2324dcd04..000000000000 --- 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 022ebcf6e927..000000000000 --- 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 7e9805fb913c..000000000000 --- 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/UnpermissionedTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java deleted file mode 100644 index b9fe32bf3923..000000000000 --- 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/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml index db2efc391463..890214f2dc03 100644 --- a/tests/VectorDrawableTest/AndroidManifest.xml +++ b/tests/VectorDrawableTest/AndroidManifest.xml @@ -33,6 +33,16 @@ </intent-filter> </activity> <activity + android:name="BitmapDrawableDupe" + android:label="Bitmap Performance of clones" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="com.android.test.dynamic.TEST" /> + </intent-filter> + + </activity> + <activity android:name="VectorDrawableAnimation" android:label="VectorTestAnimation" > <intent-filter> diff --git a/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg b/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg Binary files differnew file mode 100644 index 000000000000..dc8c19716be5 --- /dev/null +++ b/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java new file mode 100644 index 000000000000..36c8f2b4adf2 --- /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 f10904c871f4..3d93bbe62f67 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -589,11 +589,11 @@ static bool applyFileOverlay(Bundle *bundle, if (bundle->getVerbose()) { printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string()); } - size_t baseIndex = UNKNOWN_ERROR; + ssize_t baseIndex = -1; if (baseSet->get() != NULL) { baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex)); } - if (baseIndex < UNKNOWN_ERROR) { + if (baseIndex >= 0) { // look for same flavor. For a given file (strings.xml, for example) // there may be a locale specific or other flavors - we want to match // the same flavor. @@ -619,10 +619,10 @@ static bool applyFileOverlay(Bundle *bundle, for (size_t overlayGroupIndex = 0; overlayGroupIndex<overlayGroupSize; overlayGroupIndex++) { - size_t baseFileIndex = + ssize_t baseFileIndex = baseGroup->getFiles().indexOfKey(overlayFiles. keyAt(overlayGroupIndex)); - if (baseFileIndex < UNKNOWN_ERROR) { + if (baseFileIndex >= 0) { if (bundle->getVerbose()) { printf("found a match (" ZD ") for overlay file %s, for flavor %s\n", (ZD_TYPE) baseFileIndex, @@ -1363,7 +1363,11 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (split->isBase()) { resFile = flattenedTable; - finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + if (err != NO_ERROR) { + fprintf(stderr, "Generated resource table is corrupt.\n"); + return err; + } } else { sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), AaptGroupEntry(), String8()); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index efbba40f950d..1a9f1b95dada 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2292,8 +2292,14 @@ uint32_t ResourceTable::getCustomResourceWithCreation( if (resId != 0 || !createIfNotFound) { return resId; } - String16 value("false"); + if (mAssetsPackage != package) { + mCurrentXmlPos.warning("creating resource for external package %s: %s/%s.", + String8(package).string(), String8(type).string(), String8(name).string()); + mCurrentXmlPos.printf("This will be an error in a future version of AAPT."); + } + + String16 value("false"); status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true); if (status == NO_ERROR) { resId = getResId(package, type, name); @@ -3062,8 +3068,9 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& for (size_t i = 0; i < N; ++i) { if (!validResources[i]) { sp<ConfigList> c = t->getOrderedConfigs().itemAt(i); - fprintf(stderr, "%s: no entries written for %s/%s\n", log_prefix, - String8(typeName).string(), String8(c->getName()).string()); + fprintf(stderr, "%s: no entries written for %s/%s (0x%08x)\n", log_prefix, + String8(typeName).string(), String8(c->getName()).string(), + Res_MAKEID(p->getAssignedId() - 1, ti, i)); missing_entry = true; } } diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java index 54ac71e0fda6..33ccad54dc1c 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java @@ -493,7 +493,8 @@ public class WifiPasspointCredential implements Parcelable { } if (this.mType.equals("TLS")) { result = this.mRealm.equals(other.mRealm) && - this.mHomeSpFqdn.equals(other.mHomeSpFqdn); + this.mHomeSpFqdn.equals(other.mHomeSpFqdn) && + this.mClientCert.equals(other.mClientCert); } if (this.mType.equals("SIM")) { result = this.mMcc.equals(other.mMcc) && diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java index aec87976d444..33db3f5491f8 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java @@ -213,22 +213,22 @@ public class WifiPasspointInfo implements Parcelable { 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 @@ public class WifiPasspointInfo implements Parcelable { /** 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 @@ public class WifiPasspointInfo implements Parcelable { 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 @@ public class WifiPasspointInfo implements Parcelable { .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 @@ public class WifiPasspointInfo implements Parcelable { 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 @@ public class WifiPasspointInfo implements Parcelable { 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 @@ public class WifiPasspointInfo implements Parcelable { 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 @@ public class WifiPasspointInfo implements Parcelable { 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 @@ public class WifiPasspointInfo implements Parcelable { 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 @@ public class WifiPasspointInfo implements Parcelable { 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 @@ public class WifiPasspointInfo implements Parcelable { 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 2f158c29ee83..e7e6767e3706 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java @@ -325,7 +325,7 @@ public class WifiPasspointManager { 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 @@ public class WifiPasspointManager { * * @return The list of credentials */ - public List<WifiPasspointCredential> getSavedCredentials() { + public List<WifiPasspointCredential> getCredentials() { return null; } @@ -529,7 +529,7 @@ public class WifiPasspointManager { Log.d(TAG, "startOsu end"); } - public void startUserRemediation(Channel c, OsuRemListener listener) { + public void startRemediation(Channel c, OsuRemListener listener) { } public void connect(WifiPasspointPolicy policy) { diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java index 9fccf0aa7eec..f84ac88c0caf 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java @@ -16,10 +16,18 @@ package android.net.wifi.passpoint; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.ScanResult; import android.os.Parcelable; import android.os.Parcel; +import android.security.Credentials; import android.util.Log; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; + + /** @hide */ public class WifiPasspointPolicy implements Parcelable { @@ -43,6 +51,20 @@ public class WifiPasspointPolicy implements Parcelable { private int mRestriction;// Permitted values are "HomeSP", "RoamingPartner", or "Unrestricted" private boolean mIsHomeSp; + private final String INT_PRIVATE_KEY = "private_key"; + private final String INT_PHASE2 = "phase2"; + private final String INT_PASSWORD = "password"; + private final String INT_IDENTITY = "identity"; + private final String INT_EAP = "eap"; + private final String INT_CLIENT_CERT = "client_cert"; + private final String INT_CA_CERT = "ca_cert"; + private final String INT_ANONYMOUS_IDENTITY = "anonymous_identity"; + private final String INT_SIM_SLOT = "sim_slot"; + private final String INT_ENTERPRISEFIELD_NAME ="android.net.wifi.WifiConfiguration$EnterpriseField"; + private final String ISO8601DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + private final String ENTERPRISE_PHASE2_MSCHAPV2 = "auth=MSCHAPV2"; + private final String ENTERPRISE_PHASE2_MSCHAP = "auth=MSCHAP"; + /** @hide */ public WifiPasspointPolicy(String name, String ssid, String bssid, WifiPasspointCredential pc, @@ -89,7 +111,7 @@ public class WifiPasspointPolicy implements Parcelable { } /** @hide */ - public boolean getHomeSp() { + public boolean isHomeSp() { return mIsHomeSp; } @@ -121,6 +143,142 @@ public class WifiPasspointPolicy implements Parcelable { return mRoamingPriority; } + public WifiConfiguration createWifiConfiguration() { + WifiConfiguration wfg = new WifiConfiguration(); + if (mBssid != null) { + Log.d(TAG, "create bssid:" + mBssid); + wfg.BSSID = mBssid; + } + + if (mSsid != null) { + Log.d(TAG, "create ssid:" + mSsid); + wfg.SSID = mSsid; + } + //TODO: 1. add pmf configuration + // 2. add ocsp configuration + // 3. add eap-sim configuration + /*Key management*/ + wfg.status = WifiConfiguration.Status.ENABLED; + wfg.allowedKeyManagement.clear(); + wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); + + /*Group Ciphers*/ + wfg.allowedGroupCiphers.clear(); + wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); + + /*Protocols*/ + wfg.allowedProtocols.clear(); + wfg.allowedProtocols.set(WifiConfiguration.Protocol.RSN); + wfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA); + + Class[] enterpriseFieldArray = WifiConfiguration.class.getClasses(); + Class<?> enterpriseFieldClass = null; + + + for(Class<?> myClass : enterpriseFieldArray) { + if(myClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) { + enterpriseFieldClass = myClass; + break; + } + } + Log.d(TAG, "class chosen " + enterpriseFieldClass.getName() ); + + + Field anonymousId = null, caCert = null, clientCert = null, + eap = null, identity = null, password = null, + phase2 = null, privateKey = null; + + Field[] fields = WifiConfiguration.class.getFields(); + + + for (Field tempField : fields) { + if (tempField.getName().trim().equals(INT_ANONYMOUS_IDENTITY)) { + anonymousId = tempField; + Log.d(TAG, "field " + anonymousId.getName() ); + } else if (tempField.getName().trim().equals(INT_CA_CERT)) { + caCert = tempField; + } else if (tempField.getName().trim().equals(INT_CLIENT_CERT)) { + clientCert = tempField; + Log.d(TAG, "field " + clientCert.getName() ); + } else if (tempField.getName().trim().equals(INT_EAP)) { + eap = tempField; + Log.d(TAG, "field " + eap.getName() ); + } else if (tempField.getName().trim().equals(INT_IDENTITY)) { + identity = tempField; + Log.d(TAG, "field " + identity.getName() ); + } else if (tempField.getName().trim().equals(INT_PASSWORD)) { + password = tempField; + Log.d(TAG, "field " + password.getName() ); + } else if (tempField.getName().trim().equals(INT_PHASE2)) { + phase2 = tempField; + Log.d(TAG, "field " + phase2.getName() ); + + } else if (tempField.getName().trim().equals(INT_PRIVATE_KEY)) { + privateKey = tempField; + } + } + + + Method setValue = null; + + for(Method m: enterpriseFieldClass.getMethods()) { + if(m.getName().trim().equals("setValue")) { + Log.d(TAG, "method " + m.getName() ); + setValue = m; + break; + } + } + + try { + // EAP + String eapmethod = mCredential.getType(); + Log.d(TAG, "eapmethod:" + eapmethod); + setValue.invoke(eap.get(wfg), eapmethod); + + // Username, password, EAP Phase 2 + if ("TTLS".equals(eapmethod)) { + setValue.invoke(phase2.get(wfg), ENTERPRISE_PHASE2_MSCHAPV2); + setValue.invoke(identity.get(wfg), mCredential.getUserName()); + setValue.invoke(password.get(wfg), mCredential.getPassword()); + setValue.invoke(anonymousId.get(wfg), "anonymous@" + mCredential.getRealm()); + } + + // EAP CA Certificate + String cacertificate = null; + String rootCA = mCredential.getCaRootCertPath(); + if (rootCA == null){ + cacertificate = null; + } else { + cacertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.CA_CERTIFICATE + rootCA; + } + Log.d(TAG, "cacertificate:" + cacertificate); + setValue.invoke(caCert.get(wfg), cacertificate); + + //User certificate + if ("TLS".equals(eapmethod)) { + String usercertificate = null; + String privatekey = null; + String clientCertPath = mCredential.getClientCertPath(); + if (clientCertPath != null){ + privatekey = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_PRIVATE_KEY + clientCertPath; + usercertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_CERTIFICATE + clientCertPath; + } + Log.d(TAG, "privatekey:" + privatekey); + Log.d(TAG, "usercertificate:" + usercertificate); + if (privatekey != null && usercertificate != null) { + setValue.invoke(privateKey.get(wfg), privatekey); + setValue.invoke(clientCert.get(wfg), usercertificate); + } + } + } catch (Exception e) { + Log.d(TAG, "createWifiConfiguration err:" + e); + } + + return wfg; + } + /** {@inheritDoc} @hide */ public int compareTo(WifiPasspointPolicy another) { Log.d(TAG, "this:" + this); @@ -128,11 +286,11 @@ public class WifiPasspointPolicy implements Parcelable { if (another == null) { return -1; - } else if (this.mIsHomeSp == true && another.getHomeSp() == false) { + } else if (this.mIsHomeSp == true && another.isHomeSp() == false) { //home sp priority is higher then roaming Log.d(TAG, "compare HomeSP first, this is HomeSP, another isn't"); return -1; - } else if ((this.mIsHomeSp == true && another.getHomeSp() == true)) { + } else if ((this.mIsHomeSp == true && another.isHomeSp() == true)) { Log.d(TAG, "both HomeSP"); //if both home sp, compare credential priority if (this.mCredentialPriority < another.getCredentialPriority()) { @@ -160,7 +318,7 @@ public class WifiPasspointPolicy implements Parcelable { } else { return 1; } - } else if ((this.mIsHomeSp == false && another.getHomeSp() == false)) { + } else if ((this.mIsHomeSp == false && another.isHomeSp() == false)) { Log.d(TAG, "both RoamingSp"); //if both roaming sp, compare roaming priority(preferredRoamingPartnerList/<X+>/priority) if (this.mRoamingPriority < another.getRoamingPriority()) { |