Merge "Empty triangle displayed for radio signal but calls & data work"
diff --git a/Android.mk b/Android.mk
index 3a3756b..216d8a2d4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -94,8 +94,6 @@
core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
core/java/android/app/backup/IRestoreObserver.aidl \
core/java/android/app/backup/IRestoreSession.aidl \
- core/java/android/app/maintenance/IIdleCallback.aidl \
- core/java/android/app/maintenance/IIdleService.aidl \
core/java/android/bluetooth/IBluetooth.aidl \
core/java/android/bluetooth/IBluetoothA2dp.aidl \
core/java/android/bluetooth/IBluetoothA2dpSink.aidl \
@@ -610,8 +608,12 @@
framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
# The since flag (-since N.xml API_LEVEL) is used to add API Level information
# to the reference documentation. Must be in order of oldest to newest.
+#
+# Conscrypt (com.android.org.conscrypt) is an implementation detail and should
+# not be referenced in the documentation.
framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-knowntags ./frameworks/base/docs/knowntags.txt \
+ -hidePackage com.android.org.conscrypt \
-since $(SRC_API_DIR)/1.xml 1 \
-since $(SRC_API_DIR)/2.xml 2 \
-since $(SRC_API_DIR)/3.xml 3 \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 5d92792..1968a78 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -34,9 +34,9 @@
# made today requires touching the same file, just copy the old
# touch step and add it to the end of the list.
#
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
+# *****************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
+# *****************************************************************
# For example:
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
@@ -194,10 +194,15 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/ITv*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates)
-
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/task)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/task)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/TaskManager)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/maintenance)
+
+# ******************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
+# ******************************************************************
diff --git a/api/current.txt b/api/current.txt
index 2f813ac..cd58b21 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -91,6 +91,7 @@
field public static final java.lang.String NFC = "android.permission.NFC";
field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
+ field public static final java.lang.String READ_ALL_VOICEMAIL = "com.android.voicemail.permission.READ_ALL_VOICEMAIL";
field public static final java.lang.String READ_CALENDAR = "android.permission.READ_CALENDAR";
field public static final java.lang.String READ_CALL_LOG = "android.permission.READ_CALL_LOG";
field public static final java.lang.String READ_CONTACTS = "android.permission.READ_CONTACTS";
@@ -320,6 +321,8 @@
field public static final int backgroundDimEnabled = 16843295; // 0x101021f
field public static final int backgroundSplit = 16843659; // 0x101038b
field public static final int backgroundStacked = 16843658; // 0x101038a
+ field public static final int backgroundTint = 16843885; // 0x101046d
+ field public static final int backgroundTintMode = 16843886; // 0x101046e
field public static final int backupAgent = 16843391; // 0x101027f
field public static final int banner = 16843762; // 0x10103f2
field public static final int baseline = 16843548; // 0x101031c
@@ -344,6 +347,8 @@
field public static final int buttonStyleInset = 16842826; // 0x101004a
field public static final int buttonStyleSmall = 16842825; // 0x1010049
field public static final int buttonStyleToggle = 16842827; // 0x101004b
+ field public static final int buttonTint = 16843889; // 0x1010471
+ field public static final int buttonTintMode = 16843890; // 0x1010472
field public static final int cacheColorHint = 16843009; // 0x1010101
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
@@ -554,6 +559,8 @@
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
+ field public static final int foregroundTint = 16843887; // 0x101046f
+ field public static final int foregroundTintMode = 16843888; // 0x1010470
field public static final int format = 16843013; // 0x1010105
field public static final int format12Hour = 16843722; // 0x10103ca
field public static final int format24Hour = 16843723; // 0x10103cb
@@ -636,6 +643,8 @@
field public static final int indeterminateDuration = 16843069; // 0x101013d
field public static final int indeterminateOnly = 16843066; // 0x101013a
field public static final int indeterminateProgressStyle = 16843544; // 0x1010318
+ field public static final int indeterminateTint = 16843883; // 0x101046b
+ field public static final int indeterminateTintMode = 16843884; // 0x101046c
field public static final int indicatorEnd = 16843730; // 0x10103d2
field public static final int indicatorLeft = 16843021; // 0x101010d
field public static final int indicatorRight = 16843022; // 0x101010e
@@ -885,7 +894,7 @@
field public static final int permissionFlags = 16843719; // 0x10103c7
field public static final int permissionGroup = 16842762; // 0x101000a
field public static final int permissionGroupFlags = 16843717; // 0x10103c5
- field public static final int persistable = 16843823; // 0x101042f
+ field public static final int persistableMode = 16843823; // 0x101042f
field public static final int persistent = 16842765; // 0x101000d
field public static final int persistentDrawingCache = 16842990; // 0x10100ee
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
@@ -911,6 +920,8 @@
field public static final int privateImeOptions = 16843299; // 0x1010223
field public static final int process = 16842769; // 0x1010011
field public static final int progress = 16843063; // 0x1010137
+ field public static final int progressBackgroundTint = 16843879; // 0x1010467
+ field public static final int progressBackgroundTintMode = 16843880; // 0x1010468
field public static final int progressBarPadding = 16843545; // 0x1010319
field public static final int progressBarStyle = 16842871; // 0x1010077
field public static final int progressBarStyleHorizontal = 16842872; // 0x1010078
@@ -921,6 +932,8 @@
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 @@
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 @@
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 @@
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);
@@ -5229,7 +5249,7 @@
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String);
method public boolean isDeviceOwnerApp(java.lang.String);
- method public boolean isLockTaskPermitted(android.content.ComponentName);
+ method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
method public boolean isProfileOwnerApp(java.lang.String);
method public void lockNow();
@@ -5243,7 +5263,7 @@
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
- method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException;
+ method public void setLockTaskPackages(java.lang.String[]) throws java.lang.SecurityException;
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -8042,6 +8062,7 @@
field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1
field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
+ field public static final int DO_NOT_PERSIST = 1; // 0x1
field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
@@ -8053,13 +8074,14 @@
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
@@ -8084,6 +8106,7 @@
field public int maxRecents;
field public java.lang.String parentActivityName;
field public java.lang.String permission;
+ field public int persistableMode;
field public int screenOrientation;
field public int softInputMode;
field public java.lang.String targetActivity;
@@ -8141,6 +8164,8 @@
field public int requiresSmallestWidthDp;
field public java.lang.String[] sharedLibraryFiles;
field public java.lang.String sourceDir;
+ field public java.lang.String[] splitPublicSourceDirs;
+ field public java.lang.String[] splitSourceDirs;
field public int targetSdkVersion;
field public java.lang.String taskAffinity;
field public int theme;
@@ -8209,6 +8234,8 @@
field public boolean handleProfiling;
field public java.lang.String publicSourceDir;
field public java.lang.String sourceDir;
+ field public java.lang.String[] splitPublicSourceDirs;
+ field public java.lang.String[] splitSourceDirs;
field public java.lang.String targetPackage;
}
@@ -8238,15 +8265,26 @@
}
public class LauncherApps {
+ method public void addOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
method public void addOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
method public boolean isActivityEnabledForProfile(android.content.ComponentName, android.os.UserHandle);
method public boolean isPackageEnabledForProfile(java.lang.String, android.os.UserHandle);
+ method public void removeOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
method public void removeOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
method public void startActivityForProfile(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
}
+ public static abstract class LauncherApps.OnAppsChangedCallback {
+ ctor public LauncherApps.OnAppsChangedCallback();
+ method public abstract void onPackageAdded(java.lang.String, android.os.UserHandle);
+ method public abstract void onPackageChanged(java.lang.String, android.os.UserHandle);
+ method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle);
+ method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean);
+ method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
+ }
+
public static abstract interface LauncherApps.OnAppsChangedListener {
method public abstract void onPackageAdded(android.os.UserHandle, java.lang.String);
method public abstract void onPackageChanged(android.os.UserHandle, java.lang.String);
@@ -10208,6 +10246,7 @@
method public void concat(android.graphics.Matrix);
method public void drawARGB(int, int, int, int);
method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
+ method public void drawArc(float, float, float, float, float, float, boolean, android.graphics.Paint);
method public void drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint);
method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint);
method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint);
@@ -10222,6 +10261,7 @@
method public void drawLines(float[], int, int, android.graphics.Paint);
method public void drawLines(float[], android.graphics.Paint);
method public void drawOval(android.graphics.RectF, android.graphics.Paint);
+ method public void drawOval(float, float, float, float, android.graphics.Paint);
method public void drawPaint(android.graphics.Paint);
method public void drawPath(android.graphics.Path, android.graphics.Paint);
method public void drawPicture(android.graphics.Picture);
@@ -10731,6 +10771,7 @@
ctor public Path();
ctor public Path(android.graphics.Path);
method public void addArc(android.graphics.RectF, float, float);
+ method public void addArc(float, float, float, float, float, float);
method public void addCircle(float, float, float, android.graphics.Path.Direction);
method public void addOval(android.graphics.RectF, android.graphics.Path.Direction);
method public void addOval(float, float, float, float, android.graphics.Path.Direction);
@@ -10740,9 +10781,12 @@
method public void addRect(android.graphics.RectF, android.graphics.Path.Direction);
method public void addRect(float, float, float, float, android.graphics.Path.Direction);
method public void addRoundRect(android.graphics.RectF, float, float, android.graphics.Path.Direction);
+ method public void addRoundRect(float, float, float, float, float, float, android.graphics.Path.Direction);
method public void addRoundRect(android.graphics.RectF, float[], android.graphics.Path.Direction);
+ method public void addRoundRect(float, float, float, float, float[], android.graphics.Path.Direction);
method public void arcTo(android.graphics.RectF, float, float, boolean);
method public void arcTo(android.graphics.RectF, float, float);
+ method public void arcTo(float, float, float, float, float, float, boolean);
method public void close();
method public void computeBounds(android.graphics.RectF, boolean);
method public void cubicTo(float, float, float, float, float, float);
@@ -15791,7 +15835,9 @@
method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName);
method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName, boolean);
method public static final android.net.Uri buildProgramUri(long);
+ method public static final android.net.Uri buildProgramsUriForChannel(long);
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+ method public static final android.net.Uri buildProgramsUriForChannel(long, long, long);
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
field public static final java.lang.String AUTHORITY = "android.media.tv";
}
@@ -16370,6 +16416,18 @@
method public android.net.NetworkRequest.Builder removeTransportType(int);
}
+ public abstract interface PSKKeyManager {
+ method public abstract java.lang.String chooseClientKeyIdentity(java.lang.String, java.net.Socket);
+ method public abstract java.lang.String chooseClientKeyIdentity(java.lang.String, javax.net.ssl.SSLEngine);
+ method public abstract java.lang.String chooseServerKeyIdentityHint(java.net.Socket);
+ method public abstract java.lang.String chooseServerKeyIdentityHint(javax.net.ssl.SSLEngine);
+ method public abstract javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, java.net.Socket);
+ method public abstract javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, javax.net.ssl.SSLEngine);
+ field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80
+ field public static final int MAX_IDENTITY_LENGTH_BYTES = 128; // 0x80
+ field public static final int MAX_KEY_LENGTH_BYTES = 256; // 0x100
+ }
+
public class ParseException extends java.lang.RuntimeException {
field public java.lang.String response;
}
@@ -25993,29 +26051,44 @@
package android.service.fingerprint {
public class FingerprintManager {
- ctor public FingerprintManager(android.content.Context);
method public void enroll(long);
+ method public void enrollCancel();
method public boolean enrolledAndEnabled();
method public void remove(int);
method public void startListening(android.service.fingerprint.FingerprintManagerReceiver);
method public void stopListening();
+ field public static final int FINGERPRINT_ACQUIRED = 1; // 0x1
+ field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
+ field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4; // 0x4
+ field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
+ field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
+ field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16; // 0x10
+ field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8; // 0x8
field public static final int FINGERPRINT_ERROR = -1; // 0xffffffff
- field public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; // 0x2
field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; // 0xfffffff6
field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
- field public static final int FINGERPRINT_SCANNED = 1; // 0x1
- field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; // 0x2
+ field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
+ field public static final int FINGERPRINT_PROCESSED = 2; // 0x2
+ field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3; // 0x3
field public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; // 0x4
}
public class FingerprintManagerReceiver {
ctor public FingerprintManagerReceiver();
+ method public void onAcquired(int);
method public void onEnrollResult(int, int);
method public void onError(int);
+ method public void onProcessed(int);
method public void onRemoved(int);
- method public void onScanned(int, int);
+ }
+
+ public class FingerprintUtils {
+ ctor public FingerprintUtils();
+ method public static void addFingerprintIdForUser(int, android.content.ContentResolver, int);
+ method public static int[] getFingerprintIdsForUser(android.content.ContentResolver, int);
+ method public static boolean removeFingerprintIdForUser(int, android.content.ContentResolver, int);
}
}
@@ -27665,8 +27738,17 @@
}
public class Subscription implements android.os.Parcelable {
- ctor public Subscription();
+ ctor public Subscription(android.content.ComponentName, java.lang.String, android.net.Uri, int, int, int, boolean, boolean);
method public int describeContents();
+ method public android.content.ComponentName getComponentName();
+ method public android.net.Uri getHandle();
+ method public android.graphics.drawable.Drawable getIcon(android.content.Context);
+ method public android.graphics.drawable.Drawable getIcon(android.content.Context, int);
+ method public java.lang.String getId();
+ method public java.lang.String getLabel(android.content.Context);
+ method public java.lang.String getShortDescription(android.content.Context);
+ method public boolean isEnabled();
+ method public boolean isSystemDefault();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -27685,6 +27767,7 @@
field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
+ field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
}
public class TelecommManager {
@@ -28129,6 +28212,7 @@
method public java.lang.String getSimSerialNumber();
method public int getSimState();
method public java.lang.String getSubscriberId();
+ method public java.util.List<android.telecomm.Subscription> getSubscriptions();
method public java.lang.String getVoiceMailAlphaTag();
method public java.lang.String getVoiceMailNumber();
method public boolean hasIccCard();
@@ -28153,6 +28237,7 @@
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
field public static final java.lang.String EXTRA_STATE_RINGING;
+ field public static final java.lang.String EXTRA_SUBSCRIPTION = "subscription";
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -32488,6 +32573,8 @@
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
method public android.graphics.drawable.Drawable getBackground();
+ method public android.content.res.ColorStateList getBackgroundTint();
+ method public android.graphics.PorterDuff.Mode getBackgroundTintMode();
method public int getBaseline();
method public final int getBottom();
method protected float getBottomFadingEdgeStrength();
@@ -32765,6 +32852,8 @@
method public void setBackgroundColor(int);
method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
method public void setBackgroundResource(int);
+ method public void setBackgroundTint(android.content.res.ColorStateList);
+ method public void setBackgroundTintMode(android.graphics.PorterDuff.Mode);
method public final void setBottom(int);
method public void setCameraDistance(float);
method public void setClickable(boolean);
@@ -35047,12 +35136,15 @@
method public java.lang.String getCookie(java.lang.String);
method public static synchronized android.webkit.CookieManager getInstance();
method public synchronized boolean hasCookies();
- method public void removeAllCookie();
- method public void removeExpiredCookie();
- method public void removeSessionCookie();
+ method public deprecated void removeAllCookie();
+ method public void removeAllCookies(android.webkit.ValueCallback<java.lang.Boolean>);
+ method public deprecated void removeExpiredCookie();
+ method public deprecated void removeSessionCookie();
+ method public void removeSessionCookies(android.webkit.ValueCallback<java.lang.Boolean>);
method public synchronized void setAcceptCookie(boolean);
method public static void setAcceptFileSchemeCookies(boolean);
method public void setCookie(java.lang.String, java.lang.String);
+ method public void setCookie(java.lang.String, java.lang.String, android.webkit.ValueCallback<java.lang.Boolean>);
}
public final class CookieSyncManager extends android.webkit.WebSyncManager {
@@ -35728,10 +35820,14 @@
method public boolean getSplitTrack();
method public android.graphics.drawable.Drawable getThumb();
method public int getThumbOffset();
+ method public android.content.res.ColorStateList getThumbTint();
+ method public android.graphics.PorterDuff.Mode getThumbTintMode();
method public void setKeyProgressIncrement(int);
method public void setSplitTrack(boolean);
method public void setThumb(android.graphics.drawable.Drawable);
method public void setThumbOffset(int);
+ method public void setThumbTint(android.content.res.ColorStateList);
+ method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
}
public abstract class AbsSpinner extends android.widget.AdapterView {
@@ -36146,9 +36242,13 @@
ctor public CompoundButton(android.content.Context, android.util.AttributeSet);
ctor public CompoundButton(android.content.Context, android.util.AttributeSet, int);
ctor public CompoundButton(android.content.Context, android.util.AttributeSet, int, int);
+ method public android.content.res.ColorStateList getButtonTint();
+ method public android.graphics.PorterDuff.Mode getButtonTintMode();
method public boolean isChecked();
method public void setButtonDrawable(int);
method public void setButtonDrawable(android.graphics.drawable.Drawable);
+ method public void setButtonTint(android.content.res.ColorStateList);
+ method public void setButtonTintMode(android.graphics.PorterDuff.Mode);
method public void setChecked(boolean);
method public void setOnCheckedChangeListener(android.widget.CompoundButton.OnCheckedChangeListener);
method public void toggle();
@@ -36409,10 +36509,14 @@
method public deprecated boolean getConsiderGoneChildrenWhenMeasuring();
method public android.graphics.drawable.Drawable getForeground();
method public int getForegroundGravity();
+ method public android.content.res.ColorStateList getForegroundTint();
+ method public android.graphics.PorterDuff.Mode getForegroundTintMode();
method public boolean getMeasureAllChildren();
method protected void onLayout(boolean, int, int, int, int);
method public void setForeground(android.graphics.drawable.Drawable);
method public void setForegroundGravity(int);
+ method public void setForegroundTint(android.content.res.ColorStateList);
+ method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
method public void setMeasureAllChildren(boolean);
}
@@ -36621,6 +36725,8 @@
method public int getMaxHeight();
method public int getMaxWidth();
method public android.widget.ImageView.ScaleType getScaleType();
+ method public android.content.res.ColorStateList getTint();
+ method public android.graphics.PorterDuff.Mode getTintMode();
method public int[] onCreateDrawableState(int);
method public void setAdjustViewBounds(boolean);
method public deprecated void setAlpha(int);
@@ -36642,6 +36748,8 @@
method public void setMaxHeight(int);
method public void setMaxWidth(int);
method public void setScaleType(android.widget.ImageView.ScaleType);
+ method public void setTint(android.content.res.ColorStateList);
+ method public void setTintMode(android.graphics.PorterDuff.Mode);
}
public static final class ImageView.ScaleType extends java.lang.Enum {
@@ -37014,11 +37122,19 @@
ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int);
ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int, int);
method public android.graphics.drawable.Drawable getIndeterminateDrawable();
+ method public android.content.res.ColorStateList getIndeterminateTint();
+ method public android.graphics.PorterDuff.Mode getIndeterminateTintMode();
method public android.view.animation.Interpolator getInterpolator();
method public synchronized int getMax();
method public synchronized int getProgress();
+ method public android.content.res.ColorStateList getProgressBackgroundTint();
+ method public android.graphics.PorterDuff.Mode getProgressBackgroundTintMode();
method public android.graphics.drawable.Drawable getProgressDrawable();
+ method public android.content.res.ColorStateList getProgressTint();
+ method public android.graphics.PorterDuff.Mode getProgressTintMode();
method public synchronized int getSecondaryProgress();
+ method public android.content.res.ColorStateList getSecondaryProgressTint();
+ method public android.graphics.PorterDuff.Mode getSecondaryProgressTintMode();
method public final synchronized void incrementProgressBy(int);
method public final synchronized void incrementSecondaryProgressBy(int);
method public synchronized boolean isIndeterminate();
@@ -37027,13 +37143,21 @@
method public synchronized void setIndeterminate(boolean);
method public void setIndeterminateDrawable(android.graphics.drawable.Drawable);
method public void setIndeterminateDrawableTiled(android.graphics.drawable.Drawable);
+ method public void setIndeterminateTint(android.content.res.ColorStateList);
+ method public void setIndeterminateTintMode(android.graphics.PorterDuff.Mode);
method public void setInterpolator(android.content.Context, int);
method public void setInterpolator(android.view.animation.Interpolator);
method public synchronized void setMax(int);
method public synchronized void setProgress(int);
+ method public void setProgressBackgroundTint(android.content.res.ColorStateList);
+ method public void setProgressBackgroundTintMode(android.graphics.PorterDuff.Mode);
method public void setProgressDrawable(android.graphics.drawable.Drawable);
method public void setProgressDrawableTiled(android.graphics.drawable.Drawable);
+ method public void setProgressTint(android.content.res.ColorStateList);
+ method public void setProgressTintMode(android.graphics.PorterDuff.Mode);
method public synchronized void setSecondaryProgress(int);
+ method public void setSecondaryProgressTint(android.content.res.ColorStateList);
+ method public void setSecondaryProgressTintMode(android.graphics.PorterDuff.Mode);
}
public class QuickContactBadge extends android.widget.ImageView implements android.view.View.OnClickListener {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9c1f1d1..127b0fc 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -44,6 +44,7 @@
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.AndroidException;
import android.view.IWindowManager;
import android.view.View;
@@ -1717,11 +1718,7 @@
}
System.out.println("config: " + Configuration.resourceQualifierString(config));
- System.out.print("abi: " + Build.CPU_ABI);
- if (!Build.CPU_ABI2.isEmpty()) {
- System.out.print("," + Build.CPU_ABI2);
- }
- System.out.println();
+ System.out.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
} catch (RemoteException e) {
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 47047b8..f85a7dc 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -58,11 +58,13 @@
import java.util.Comparator;
import java.util.List;
import java.util.WeakHashMap;
+
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.android.internal.content.PackageHelper;
+import com.android.internal.util.ArrayUtils;
public final class Pm {
IPackageManager mPm;
@@ -1548,6 +1550,12 @@
if (info != null && info.applicationInfo != null) {
System.out.print("package:");
System.out.println(info.applicationInfo.sourceDir);
+ if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) {
+ for (String splitSourceDir : info.applicationInfo.splitSourceDirs) {
+ System.out.print("package:");
+ System.out.println(splitSourceDir);
+ }
+ }
}
} catch (RemoteException e) {
System.err.println(e.toString());
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f6883e2..90615d3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -929,7 +929,8 @@
/**
* Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
- * the attribute {@link android.R.attr#persistable} set true.
+ * the attribute {@link android.R.attr#persistableMode} set to
+ * <code>persistAcrossReboots</code>.
*
* @param savedInstanceState if the activity is being re-initialized after
* previously being shut down then this Bundle contains the data it most
@@ -1012,8 +1013,9 @@
/**
* This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities
- * created with the attribute {@link android.R.attr#persistable}. The {@link
- * android.os.PersistableBundle} passed came from the restored PersistableBundle first
+ * created with the attribute {@link android.R.attr#persistableMode} set to
+ * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
+ * came from the restored PersistableBundle first
* saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
*
* <p>This method is called between {@link #onStart} and
@@ -1111,7 +1113,8 @@
/**
* This is the same as {@link #onPostCreate(Bundle)} but is called for activities
- * created with the attribute {@link android.R.attr#persistable}.
+ * created with the attribute {@link android.R.attr#persistableMode} set to
+ * <code>persistAcrossReboots</code>.
*
* @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState}
* @param persistentState The data caming from the PersistableBundle first
@@ -1352,10 +1355,10 @@
/**
* This is the same as {@link #onSaveInstanceState} but is called for activities
- * created with the attribute {@link android.R.attr#persistable}. The {@link
- * android.os.PersistableBundle} passed in will be saved and presented in
- * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity
- * is restarted following the next device reboot.
+ * created with the attribute {@link android.R.attr#persistableMode} set to
+ * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
+ * in will be saved and presented in {@link #onCreate(Bundle, PersistableBundle)}
+ * the first time that this activity is restarted following the next device reboot.
*
* @param outState Bundle in which to place your saved state.
* @param outPersistentState State which will be saved across reboots.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ea46044..f5514f8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -191,11 +191,13 @@
/** Reference to singleton {@link ActivityThread} */
private static ActivityThread sCurrentActivityThread;
Instrumentation mInstrumentation;
+ String mInstrumentationPackageName = null;
String mInstrumentationAppDir = null;
- String mInstrumentationAppLibraryDir = null;
- String mInstrumentationAppPackage = null;
+ String[] mInstrumentationSplitAppDirs = null;
+ String mInstrumentationLibDir = null;
String mInstrumentedAppDir = null;
- String mInstrumentedAppLibraryDir = null;
+ String[] mInstrumentedSplitAppDirs = null;
+ String mInstrumentedLibDir = null;
boolean mSystemThread = false;
boolean mJitEnabled = false;
@@ -317,7 +319,7 @@
}
public boolean isPersistable() {
- return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+ return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
}
public String toString() {
@@ -1585,11 +1587,11 @@
/**
* Creates the top level resources for the given package.
*/
- Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs,
- int displayId, Configuration overrideConfiguration,
+ Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
+ String[] libDirs, int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
- return mResourcesManager.getTopLevelResources(resDir, overlayDirs, libDirs, displayId,
- overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
+ return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
+ displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
}
final Handler getHandler() {
@@ -4315,16 +4317,20 @@
+ data.instrumentationName);
}
+ mInstrumentationPackageName = ii.packageName;
mInstrumentationAppDir = ii.sourceDir;
- mInstrumentationAppLibraryDir = ii.nativeLibraryDir;
- mInstrumentationAppPackage = ii.packageName;
+ mInstrumentationSplitAppDirs = ii.splitSourceDirs;
+ mInstrumentationLibDir = ii.nativeLibraryDir;
mInstrumentedAppDir = data.info.getAppDir();
- mInstrumentedAppLibraryDir = data.info.getLibDir();
+ mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
+ mInstrumentedLibDir = data.info.getLibDir();
ApplicationInfo instrApp = new ApplicationInfo();
instrApp.packageName = ii.packageName;
instrApp.sourceDir = ii.sourceDir;
instrApp.publicSourceDir = ii.publicSourceDir;
+ instrApp.splitSourceDirs = ii.splitSourceDirs;
+ instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs;
instrApp.dataDir = ii.dataDir;
instrApp.nativeLibraryDir = ii.nativeLibraryDir;
LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 0cccedc..c7030b0 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -206,6 +206,9 @@
protected ResultReceiver mResultReceiver;
final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
final protected boolean mIsReturning;
+ private Runnable mPendingTransition;
+ private boolean mIsStartingTransition;
+
public ActivityTransitionCoordinator(Window window,
ArrayList<String> allSharedElementNames,
@@ -290,13 +293,17 @@
if (transition == null || views == null || views.isEmpty()) {
return null;
}
+ // Add the targets to a set containing transition so that transition
+ // remains unaffected. We don't want to modify the targets of transition itself.
TransitionSet set = new TransitionSet();
- set.addTransition(transition);
if (views != null) {
- for (View view: views) {
+ for (View view : views) {
set.addTarget(view);
}
}
+ // By adding the transition after addTarget, we prevent addTarget from
+ // affecting transition.
+ set.addTransition(transition);
return set;
}
@@ -523,7 +530,7 @@
* @param transitionArgs Bundle to store shared element placement information.
* @param tempBounds A temporary Rect for capturing the current location of views.
*/
- private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
+ protected static void captureSharedElementState(View view, String name, Bundle transitionArgs,
Rect tempBounds) {
Bundle sharedElementBundle = new Bundle();
tempBounds.set(0, 0, view.getWidth(), view.getHeight());
@@ -559,6 +566,32 @@
transitionArgs.putBundle(name, sharedElementBundle);
}
+
+ protected void startTransition(Runnable runnable) {
+ if (mIsStartingTransition) {
+ mPendingTransition = runnable;
+ } else {
+ mIsStartingTransition = true;
+ runnable.run();
+ }
+ }
+
+ protected void transitionStarted() {
+ mIsStartingTransition = false;
+ }
+
+ protected class ContinueTransitionListener extends Transition.TransitionListenerAdapter {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ mIsStartingTransition = false;
+ Runnable pending = mPendingTransition;
+ mPendingTransition = null;
+ if (pending != null) {
+ startTransition(pending);
+ }
+ }
+ }
+
private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
if (scaleType == SCALE_TYPE_VALUES[i]) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 84673d9..de0396e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -823,8 +823,10 @@
if (app.packageName.equals("system")) {
return mContext.mMainThread.getSystemContext().getResources();
}
+ final boolean sameUid = (app.uid == Process.myUid());
Resources r = mContext.mMainThread.getTopLevelResources(
- app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
+ sameUid ? app.sourceDir : app.publicSourceDir,
+ sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
app.resourceDirs, null, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
if (r != null) {
return r;
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 89ee145..01a388f 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -16,36 +16,53 @@
package android.app;
+import com.android.internal.util.FastPrintWriter;
+
+import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.transition.Transition;
+import android.transition.TransitionInflater;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.LogWriter;
-import com.android.internal.util.FastPrintWriter;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewGroup;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
final class BackStackState implements Parcelable {
final int[] mOps;
final int mTransition;
final int mTransitionStyle;
+ final int mCustomTransition;
+ final int mSceneRoot;
final String mName;
final int mIndex;
final int mBreadCrumbTitleRes;
final CharSequence mBreadCrumbTitleText;
final int mBreadCrumbShortTitleRes;
final CharSequence mBreadCrumbShortTitleText;
+ final ArrayList<String> mSharedElementSourceNames;
+ final ArrayList<String> mSharedElementTargetNames;
public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
int numRemoved = 0;
BackStackRecord.Op op = bse.mHead;
while (op != null) {
- if (op.removed != null) numRemoved += op.removed.size();
+ if (op.removed != null) {
+ numRemoved += op.removed.size();
+ }
op = op.next;
}
- mOps = new int[bse.mNumOp*7 + numRemoved];
+ mOps = new int[bse.mNumOp * 7 + numRemoved];
if (!bse.mAddToBackStack) {
throw new IllegalStateException("Not on back stack");
@@ -63,7 +80,7 @@
if (op.removed != null) {
final int N = op.removed.size();
mOps[pos++] = N;
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
mOps[pos++] = op.removed.get(i).mIndex;
}
} else {
@@ -79,6 +96,10 @@
mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
+ mCustomTransition = bse.mCustomTransition;
+ mSceneRoot = bse.mSceneRoot;
+ mSharedElementSourceNames = bse.mSharedElementSourceNames;
+ mSharedElementTargetNames = bse.mSharedElementTargetNames;
}
public BackStackState(Parcel in) {
@@ -91,6 +112,10 @@
mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mBreadCrumbShortTitleRes = in.readInt();
mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mCustomTransition = in.readInt();
+ mSceneRoot = in.readInt();
+ mSharedElementSourceNames = in.createStringArrayList();
+ mSharedElementTargetNames = in.createStringArrayList();
}
public BackStackRecord instantiate(FragmentManagerImpl fm) {
@@ -100,8 +125,10 @@
while (pos < mOps.length) {
BackStackRecord.Op op = new BackStackRecord.Op();
op.cmd = mOps[pos++];
- if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
- "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(FragmentManagerImpl.TAG,
+ "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
+ }
int findex = mOps[pos++];
if (findex >= 0) {
Fragment f = fm.mActive.get(findex);
@@ -116,9 +143,11 @@
final int N = mOps[pos++];
if (N > 0) {
op.removed = new ArrayList<Fragment>(N);
- for (int i=0; i<N; i++) {
- if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
- "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
+ for (int i = 0; i < N; i++) {
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(FragmentManagerImpl.TAG,
+ "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
+ }
Fragment r = fm.mActive.get(mOps[pos++]);
op.removed.add(r);
}
@@ -135,6 +164,10 @@
bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
+ bse.mCustomTransition = mCustomTransition;
+ bse.mSceneRoot = mSceneRoot;
+ bse.mSharedElementSourceNames = mSharedElementSourceNames;
+ bse.mSharedElementTargetNames = mSharedElementTargetNames;
bse.bumpBackStackNesting(1);
return bse;
}
@@ -153,6 +186,10 @@
TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
dest.writeInt(mBreadCrumbShortTitleRes);
TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
+ dest.writeInt(mCustomTransition);
+ dest.writeInt(mSceneRoot);
+ dest.writeStringList(mSharedElementSourceNames);
+ dest.writeStringList(mSharedElementTargetNames);
}
public static final Parcelable.Creator<BackStackState> CREATOR
@@ -217,6 +254,11 @@
int mBreadCrumbShortTitleRes;
CharSequence mBreadCrumbShortTitleText;
+ int mCustomTransition;
+ int mSceneRoot;
+ ArrayList<String> mSharedElementSourceNames;
+ ArrayList<String> mSharedElementTargetNames;
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
@@ -240,78 +282,112 @@
void dump(String prefix, PrintWriter writer, boolean full) {
if (full) {
- writer.print(prefix); writer.print("mName="); writer.print(mName);
- writer.print(" mIndex="); writer.print(mIndex);
- writer.print(" mCommitted="); writer.println(mCommitted);
+ writer.print(prefix);
+ writer.print("mName=");
+ writer.print(mName);
+ writer.print(" mIndex=");
+ writer.print(mIndex);
+ writer.print(" mCommitted=");
+ writer.println(mCommitted);
if (mTransition != FragmentTransaction.TRANSIT_NONE) {
- writer.print(prefix); writer.print("mTransition=#");
- writer.print(Integer.toHexString(mTransition));
- writer.print(" mTransitionStyle=#");
- writer.println(Integer.toHexString(mTransitionStyle));
+ writer.print(prefix);
+ writer.print("mTransition=#");
+ writer.print(Integer.toHexString(mTransition));
+ writer.print(" mTransitionStyle=#");
+ writer.println(Integer.toHexString(mTransitionStyle));
}
- if (mEnterAnim != 0 || mExitAnim !=0) {
- writer.print(prefix); writer.print("mEnterAnim=#");
- writer.print(Integer.toHexString(mEnterAnim));
- writer.print(" mExitAnim=#");
- writer.println(Integer.toHexString(mExitAnim));
+ if (mEnterAnim != 0 || mExitAnim != 0) {
+ writer.print(prefix);
+ writer.print("mEnterAnim=#");
+ writer.print(Integer.toHexString(mEnterAnim));
+ writer.print(" mExitAnim=#");
+ writer.println(Integer.toHexString(mExitAnim));
}
- if (mPopEnterAnim != 0 || mPopExitAnim !=0) {
- writer.print(prefix); writer.print("mPopEnterAnim=#");
- writer.print(Integer.toHexString(mPopEnterAnim));
- writer.print(" mPopExitAnim=#");
- writer.println(Integer.toHexString(mPopExitAnim));
+ if (mPopEnterAnim != 0 || mPopExitAnim != 0) {
+ writer.print(prefix);
+ writer.print("mPopEnterAnim=#");
+ writer.print(Integer.toHexString(mPopEnterAnim));
+ writer.print(" mPopExitAnim=#");
+ writer.println(Integer.toHexString(mPopExitAnim));
}
if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) {
- writer.print(prefix); writer.print("mBreadCrumbTitleRes=#");
- writer.print(Integer.toHexString(mBreadCrumbTitleRes));
- writer.print(" mBreadCrumbTitleText=");
- writer.println(mBreadCrumbTitleText);
+ writer.print(prefix);
+ writer.print("mBreadCrumbTitleRes=#");
+ writer.print(Integer.toHexString(mBreadCrumbTitleRes));
+ writer.print(" mBreadCrumbTitleText=");
+ writer.println(mBreadCrumbTitleText);
}
if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) {
- writer.print(prefix); writer.print("mBreadCrumbShortTitleRes=#");
- writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
- writer.print(" mBreadCrumbShortTitleText=");
- writer.println(mBreadCrumbShortTitleText);
+ writer.print(prefix);
+ writer.print("mBreadCrumbShortTitleRes=#");
+ writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
+ writer.print(" mBreadCrumbShortTitleText=");
+ writer.println(mBreadCrumbShortTitleText);
}
}
if (mHead != null) {
- writer.print(prefix); writer.println("Operations:");
+ writer.print(prefix);
+ writer.println("Operations:");
String innerPrefix = prefix + " ";
Op op = mHead;
int num = 0;
while (op != null) {
String cmdStr;
switch (op.cmd) {
- case OP_NULL: cmdStr="NULL"; break;
- case OP_ADD: cmdStr="ADD"; break;
- case OP_REPLACE: cmdStr="REPLACE"; break;
- case OP_REMOVE: cmdStr="REMOVE"; break;
- case OP_HIDE: cmdStr="HIDE"; break;
- case OP_SHOW: cmdStr="SHOW"; break;
- case OP_DETACH: cmdStr="DETACH"; break;
- case OP_ATTACH: cmdStr="ATTACH"; break;
- default: cmdStr="cmd=" + op.cmd; break;
+ case OP_NULL:
+ cmdStr = "NULL";
+ break;
+ case OP_ADD:
+ cmdStr = "ADD";
+ break;
+ case OP_REPLACE:
+ cmdStr = "REPLACE";
+ break;
+ case OP_REMOVE:
+ cmdStr = "REMOVE";
+ break;
+ case OP_HIDE:
+ cmdStr = "HIDE";
+ break;
+ case OP_SHOW:
+ cmdStr = "SHOW";
+ break;
+ case OP_DETACH:
+ cmdStr = "DETACH";
+ break;
+ case OP_ATTACH:
+ cmdStr = "ATTACH";
+ break;
+ default:
+ cmdStr = "cmd=" + op.cmd;
+ break;
}
- writer.print(prefix); writer.print(" Op #"); writer.print(num);
- writer.print(": "); writer.print(cmdStr);
- writer.print(" "); writer.println(op.fragment);
+ writer.print(prefix);
+ writer.print(" Op #");
+ writer.print(num);
+ writer.print(": ");
+ writer.print(cmdStr);
+ writer.print(" ");
+ writer.println(op.fragment);
if (full) {
if (op.enterAnim != 0 || op.exitAnim != 0) {
- writer.print(innerPrefix); writer.print("enterAnim=#");
- writer.print(Integer.toHexString(op.enterAnim));
- writer.print(" exitAnim=#");
- writer.println(Integer.toHexString(op.exitAnim));
+ writer.print(innerPrefix);
+ writer.print("enterAnim=#");
+ writer.print(Integer.toHexString(op.enterAnim));
+ writer.print(" exitAnim=#");
+ writer.println(Integer.toHexString(op.exitAnim));
}
if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
- writer.print(innerPrefix); writer.print("popEnterAnim=#");
- writer.print(Integer.toHexString(op.popEnterAnim));
- writer.print(" popExitAnim=#");
- writer.println(Integer.toHexString(op.popExitAnim));
+ writer.print(innerPrefix);
+ writer.print("popEnterAnim=#");
+ writer.print(Integer.toHexString(op.popEnterAnim));
+ writer.print(" popExitAnim=#");
+ writer.println(Integer.toHexString(op.popExitAnim));
}
}
if (op.removed != null && op.removed.size() > 0) {
- for (int i=0; i<op.removed.size(); i++) {
+ for (int i = 0; i < op.removed.size(); i++) {
writer.print(innerPrefix);
if (op.removed.size() == 1) {
writer.print("Removed: ");
@@ -319,8 +395,10 @@
if (i == 0) {
writer.println("Removed:");
}
- writer.print(innerPrefix); writer.print(" #"); writer.print(i);
- writer.print(": ");
+ writer.print(innerPrefix);
+ writer.print(" #");
+ writer.print(i);
+ writer.print(": ");
}
writer.println(op.removed.get(i));
}
@@ -494,6 +572,51 @@
return this;
}
+ @Override
+ public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) {
+ mSceneRoot = sceneRootId;
+ mCustomTransition = transitionId;
+ return this;
+ }
+
+ @Override
+ public FragmentTransaction setSharedElement(View sharedElement, String name) {
+ String viewName = sharedElement.getViewName();
+ if (viewName == null) {
+ throw new IllegalArgumentException("Unique viewNames are required for all" +
+ " sharedElements");
+ }
+ mSharedElementSourceNames = new ArrayList<String>(1);
+ mSharedElementSourceNames.add(viewName);
+
+ mSharedElementTargetNames = new ArrayList<String>(1);
+ mSharedElementTargetNames.add(name);
+ return this;
+ }
+
+ @Override
+ public FragmentTransaction setSharedElements(Pair<View, String>... sharedElements) {
+ if (sharedElements == null || sharedElements.length == 0) {
+ mSharedElementSourceNames = null;
+ mSharedElementTargetNames = null;
+ } else {
+ ArrayList<String> sourceNames = new ArrayList<String>(sharedElements.length);
+ ArrayList<String> targetNames = new ArrayList<String>(sharedElements.length);
+ for (int i = 0; i < sharedElements.length; i++) {
+ String viewName = sharedElements[i].first.getViewName();
+ if (viewName == null) {
+ throw new IllegalArgumentException("Unique viewNames are required for all" +
+ " sharedElements");
+ }
+ sourceNames.add(viewName);
+ targetNames.add(sharedElements[i].second);
+ }
+ mSharedElementSourceNames = sourceNames;
+ mSharedElementTargetNames = targetNames;
+ }
+ return this;
+ }
+
public FragmentTransaction setTransitionStyle(int styleRes) {
mTransitionStyle = styleRes;
return this;
@@ -550,21 +673,27 @@
if (!mAddToBackStack) {
return;
}
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this
- + " by " + amt);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Bump nesting in " + this
+ + " by " + amt);
+ }
Op op = mHead;
while (op != null) {
if (op.fragment != null) {
op.fragment.mBackStackNesting += amt;
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
- + op.fragment + " to " + op.fragment.mBackStackNesting);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Bump nesting of "
+ + op.fragment + " to " + op.fragment.mBackStackNesting);
+ }
}
if (op.removed != null) {
- for (int i=op.removed.size()-1; i>=0; i--) {
+ for (int i = op.removed.size() - 1; i >= 0; i--) {
Fragment r = op.removed.get(i);
r.mBackStackNesting += amt;
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
- + r + " to " + r.mBackStackNesting);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Bump nesting of "
+ + r + " to " + r.mBackStackNesting);
+ }
}
}
op = op.next;
@@ -578,9 +707,11 @@
public int commitAllowingStateLoss() {
return commitInternal(true);
}
-
+
int commitInternal(boolean allowStateLoss) {
- if (mCommitted) throw new IllegalStateException("commit already called");
+ if (mCommitted) {
+ throw new IllegalStateException("commit already called");
+ }
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
@@ -597,9 +728,11 @@
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
-
+
public void run() {
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Run: " + this);
+ }
if (mAddToBackStack) {
if (mIndex < 0) {
@@ -609,6 +742,9 @@
bumpBackStackNesting(1);
+ TransitionState state = beginTransition(mSharedElementSourceNames,
+ mSharedElementTargetNames);
+
Op op = mHead;
while (op != null) {
switch (op.cmd) {
@@ -616,14 +752,17 @@
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
- } break;
+ }
+ break;
case OP_REPLACE: {
Fragment f = op.fragment;
if (mManager.mAdded != null) {
- for (int i=0; i<mManager.mAdded.size(); i++) {
+ for (int i = 0; i < mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
- if (FragmentManagerImpl.DEBUG) Log.v(TAG,
- "OP_REPLACE: adding=" + f + " old=" + old);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG,
+ "OP_REPLACE: adding=" + f + " old=" + old);
+ }
if (f == null || old.mContainerId == f.mContainerId) {
if (old == f) {
op.fragment = f = null;
@@ -635,8 +774,10 @@
old.mNextAnim = op.exitAnim;
if (mAddToBackStack) {
old.mBackStackNesting += 1;
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
- + old + " to " + old.mBackStackNesting);
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Bump nesting of "
+ + old + " to " + old.mBackStackNesting);
+ }
}
mManager.removeFragment(old, mTransition, mTransitionStyle);
}
@@ -647,32 +788,38 @@
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
}
- } break;
+ }
+ break;
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.removeFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
case OP_HIDE: {
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.hideFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
case OP_SHOW: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.showFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
case OP_DETACH: {
Fragment f = op.fragment;
f.mNextAnim = op.exitAnim;
mManager.detachFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
case OP_ATTACH: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.attachFragment(f, mTransition, mTransitionStyle);
- } break;
+ }
+ break;
default: {
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
@@ -687,9 +834,174 @@
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
+
+ if (state != null) {
+ updateTransitionEndState(state, mSharedElementTargetNames);
+ }
}
- public void popFromBackStack(boolean doStateMove) {
+ private TransitionState beginTransition(ArrayList<String> sourceNames,
+ ArrayList<String> targetNames) {
+ if (mCustomTransition <= 0 || mSceneRoot <= 0) {
+ return null;
+ }
+ View rootView = mManager.mContainer.findViewById(mSceneRoot);
+ if (!(rootView instanceof ViewGroup)) {
+ throw new IllegalArgumentException("SceneRoot is not a ViewGroup");
+ }
+ TransitionState state = new TransitionState();
+ // get Transition scene root and create Transitions
+ state.sceneRoot = (ViewGroup) rootView;
+ state.sceneRoot.captureTransitioningViews(state.transitioningViews);
+
+ state.exitTransition = TransitionInflater.from(mManager.mActivity)
+ .inflateTransition(mCustomTransition);
+ state.sharedElementTransition = TransitionInflater.from(mManager.mActivity)
+ .inflateTransition(mCustomTransition);
+ state.enterTransition = TransitionInflater.from(mManager.mActivity)
+ .inflateTransition(mCustomTransition);
+ // Adding a non-existent target view makes sure that the transitions don't target
+ // any views by default. They'll only target the views we tell add. If we don't
+ // add any, then no views will be targeted.
+ View nonExistentView = new View(mManager.mActivity);
+ state.enterTransition.addTarget(nonExistentView);
+ state.exitTransition.addTarget(nonExistentView);
+ state.sharedElementTransition.addTarget(nonExistentView);
+
+ setSharedElementEpicenter(state.enterTransition, state);
+
+ state.excludingTransition = new TransitionSet()
+ .addTransition(state.exitTransition)
+ .addTransition(state.enterTransition);
+
+ if (sourceNames != null) {
+ // Map shared elements.
+ state.sceneRoot.findNamedViews(state.namedViews);
+ state.namedViews.retainAll(sourceNames);
+ View epicenterView = state.namedViews.get(sourceNames.get(0));
+ if (epicenterView != null) {
+ // The epicenter is only the first shared element.
+ setEpicenter(state.exitTransition, epicenterView);
+ setEpicenter(state.sharedElementTransition, epicenterView);
+ }
+ state.transitioningViews.removeAll(state.namedViews.values());
+ state.excludingTransition.addTransition(state.sharedElementTransition);
+ addTransitioningViews(state.sharedElementTransition, state.namedViews.values());
+ }
+
+ // Adds the (maybe) exiting views, not including the shared element.
+ // If some stay, that's ok.
+ addTransitioningViews(state.exitTransition, state.transitioningViews);
+
+ // Prepare for shared element name mapping. This could be chained in the case
+ // of popping several back stack states.
+ state.excludingTransition.setNameOverrides(new ArrayMap<String, String>());
+ setNameOverrides(state, sourceNames, targetNames);
+
+ // Don't include any subtree in the views that are hidden when capturing the
+ // view hierarchy transitions. They should be as if not there.
+ excludeHiddenFragments(state, true);
+
+ TransitionManager.beginDelayedTransition(state.sceneRoot, state.excludingTransition);
+ return state;
+ }
+
+ private void updateTransitionEndState(TransitionState state, ArrayList<String> names) {
+ // Find all views that are entering.
+ ArrayList<View> enteringViews = new ArrayList<View>();
+ state.sceneRoot.captureTransitioningViews(enteringViews);
+ enteringViews.removeAll(state.transitioningViews);
+
+ if (names != null) {
+ // find all shared elements.
+ state.namedViews.clear();
+ state.sceneRoot.findNamedViews(state.namedViews);
+ state.namedViews.retainAll(names);
+ if (!state.namedViews.isEmpty()) {
+ enteringViews.removeAll(state.namedViews.values());
+ addTransitioningViews(state.sharedElementTransition, state.namedViews.values());
+ // now we know the epicenter of the entering transition.
+ state.mEnteringEpicenterView = state.namedViews.get(names.get(0));
+ }
+ }
+
+ // Add all entering views to the enter transition.
+ addTransitioningViews(state.enterTransition, enteringViews);
+
+ // Don't allow capturing state for the newly-hidden fragments.
+ excludeHiddenFragments(state, false);
+
+ // Allow capturing state for the newly-shown fragments
+ includeVisibleFragments(state.excludingTransition);
+ }
+
+ private void addTransitioningViews(Transition transition, Collection<View> views) {
+ if (views.isEmpty()) {
+ // Add a view so that we can modify the valid views at the end of the
+ // fragment transaction.
+ transition.addTarget(new View(mManager.mActivity));
+ } else {
+ for (View view : views) {
+ transition.addTarget(view);
+ }
+ }
+ }
+
+ private void excludeHiddenFragments(TransitionState state, boolean forceExclude) {
+ if (mManager.mAdded != null) {
+ for (int i = 0; i < mManager.mAdded.size(); i++) {
+ Fragment fragment = mManager.mAdded.get(i);
+ if (fragment.mView != null && fragment.mHidden
+ && (forceExclude || !state.hiddenViews.contains(fragment.mView))) {
+ state.excludingTransition.excludeTarget(fragment.mView, true);
+ state.hiddenViews.add(fragment.mView);
+ }
+ }
+ }
+ if (forceExclude && state.hiddenViews.isEmpty()) {
+ state.excludingTransition.excludeTarget(new View(mManager.mActivity), true);
+ }
+ }
+
+ private void includeVisibleFragments(Transition transition) {
+ if (mManager.mAdded != null) {
+ for (int i = 0; i < mManager.mAdded.size(); i++) {
+ Fragment fragment = mManager.mAdded.get(i);
+ if (fragment.mView != null && !fragment.mHidden) {
+ transition.excludeTarget(fragment.mView, false);
+ }
+ }
+ }
+ }
+
+ private static void setEpicenter(Transition transition, View view) {
+ final Rect epicenter = new Rect();
+ view.getBoundsOnScreen(epicenter);
+
+ transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return epicenter;
+ }
+ });
+ }
+
+ private void setSharedElementEpicenter(Transition transition, final TransitionState state) {
+ transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ private Rect mEpicenter;
+
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ if (mEpicenter == null && state.mEnteringEpicenterView != null) {
+ mEpicenter = new Rect();
+ state.mEnteringEpicenterView.getBoundsOnScreen(mEpicenter);
+ }
+ return mEpicenter;
+ }
+ });
+ }
+
+ public TransitionState popFromBackStack(boolean doStateMove, TransitionState state) {
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "popFromBackStack: " + this);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
@@ -698,6 +1010,12 @@
pw.flush();
}
+ if (state == null) {
+ state = beginTransition(mSharedElementTargetNames, mSharedElementSourceNames);
+ } else {
+ setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
+ }
+
bumpBackStackNesting(-1);
Op op = mTail;
@@ -709,7 +1027,8 @@
mManager.removeFragment(f,
FragmentManagerImpl.reverseTransit(mTransition),
mTransitionStyle);
- } break;
+ }
+ break;
case OP_REPLACE: {
Fragment f = op.fragment;
if (f != null) {
@@ -719,42 +1038,48 @@
mTransitionStyle);
}
if (op.removed != null) {
- for (int i=0; i<op.removed.size(); i++) {
+ for (int i = 0; i < op.removed.size(); i++) {
Fragment old = op.removed.get(i);
old.mNextAnim = op.popEnterAnim;
mManager.addFragment(old, false);
}
}
- } break;
+ }
+ break;
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = op.popEnterAnim;
mManager.addFragment(f, false);
- } break;
+ }
+ break;
case OP_HIDE: {
Fragment f = op.fragment;
f.mNextAnim = op.popEnterAnim;
mManager.showFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- } break;
+ }
+ break;
case OP_SHOW: {
Fragment f = op.fragment;
f.mNextAnim = op.popExitAnim;
mManager.hideFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- } break;
+ }
+ break;
case OP_DETACH: {
Fragment f = op.fragment;
f.mNextAnim = op.popEnterAnim;
mManager.attachFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- } break;
+ }
+ break;
case OP_ATTACH: {
Fragment f = op.fragment;
f.mNextAnim = op.popExitAnim;
mManager.detachFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- } break;
+ }
+ break;
default: {
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
@@ -766,12 +1091,39 @@
if (doStateMove) {
mManager.moveToState(mManager.mCurState,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
+ if (state != null) {
+ updateTransitionEndState(state, mSharedElementSourceNames);
+ state = null;
+ }
}
if (mIndex >= 0) {
mManager.freeBackStackIndex(mIndex);
mIndex = -1;
}
+ return state;
+ }
+
+ private static void setNameOverride(Transition transition, String source, String target) {
+ ArrayMap<String, String> overrides = transition.getNameOverrides();
+ for (int index = 0; index < overrides.size(); index++) {
+ if (source.equals(overrides.valueAt(index))) {
+ overrides.setValueAt(index, target);
+ return;
+ }
+ }
+ overrides.put(source, target);
+ }
+
+ private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
+ ArrayList<String> targetNames) {
+ if (sourceNames != null) {
+ for (int i = 0; i < sourceNames.size(); i++) {
+ String source = sourceNames.get(i);
+ String target = targetNames.get(i);
+ setNameOverride(state.excludingTransition, source, target);
+ }
+ }
}
public String getName() {
@@ -789,4 +1141,16 @@
public boolean isEmpty() {
return mNumOp == 0;
}
+
+ public class TransitionState {
+ public ArrayList<View> hiddenViews = new ArrayList<View>();
+ public ArrayList<View> transitioningViews = new ArrayList<View>();
+ public ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
+ public Transition exitTransition;
+ public Transition sharedElementTransition;
+ public Transition enterTransition;
+ public TransitionSet excludingTransition;
+ public ViewGroup sceneRoot;
+ public View mEnteringEpicenterView;
+ }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ab3bb49..425a140 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -115,9 +115,8 @@
import android.os.storage.StorageManager;
import android.print.IPrintManager;
import android.print.PrintManager;
+import android.service.fingerprint.IFingerprintService;
import android.service.fingerprint.FingerprintManager;
-import android.service.fingerprint.FingerprintManagerReceiver;
-import android.service.fingerprint.FingerprintService;
import android.telecomm.TelecommManager;
import android.telephony.TelephonyManager;
import android.content.ClipboardManager;
@@ -466,11 +465,6 @@
return new KeyguardManager();
}});
- registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
- public Object createService(ContextImpl ctx) {
- return new FingerprintManager(ctx);
- }});
-
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
@@ -519,6 +513,9 @@
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(POWER_SERVICE);
IPowerManager service = IPowerManager.Stub.asInterface(b);
+ if (service == null) {
+ Log.wtf(TAG, "Failed to get power manager service.");
+ }
return new PowerManager(ctx.getOuterContext(),
service, ctx.mMainThread.getHandler());
}});
@@ -690,6 +687,7 @@
return new MediaSessionManager(ctx);
}
});
+
registerService(TRUST_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(TRUST_SERVICE);
@@ -697,6 +695,14 @@
}
});
+ registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder binder = ServiceManager.getService(FINGERPRINT_SERVICE);
+ IFingerprintService service = IFingerprintService.Stub.asInterface(binder);
+ return new FingerprintManager(ctx.getOuterContext(), service);
+ }
+ });
+
registerService(TV_INPUT_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder iBinder = ServiceManager.getService(TV_INPUT_SERVICE);
@@ -2187,10 +2193,10 @@
|| overrideConfiguration != null
|| (compatInfo != null && compatInfo.applicationScale
!= resources.getCompatibilityInfo().applicationScale)) {
- resources = mResourcesManager.getTopLevelResources(
- packageInfo.getResDir(), packageInfo.getOverlayDirs(),
- packageInfo.getApplicationInfo().sharedLibraryFiles,
- displayId, overrideConfiguration, compatInfo, activityToken);
+ resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
+ packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
+ packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
+ overrideConfiguration, compatInfo, activityToken);
}
}
mResources = resources;
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index f54cb87..365cc8e 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -106,7 +106,16 @@
private void sendSharedElementDestination() {
ViewGroup decor = getDecor();
- if (!decor.isLayoutRequested()) {
+ boolean allReady = !decor.isLayoutRequested();
+ if (allReady) {
+ for (int i = 0; i < mSharedElements.size(); i++) {
+ if (mSharedElements.get(i).isLayoutRequested()) {
+ allReady = false;
+ break;
+ }
+ }
+ }
+ if (allReady) {
Bundle state = captureSharedElementState();
mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
} else {
@@ -115,10 +124,15 @@
@Override
public boolean onPreDraw() {
getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ Bundle state = captureSharedElementState();
+ mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
return true;
}
});
}
+ if (allowOverlappingTransitions()) {
+ startEnterTransitionOnly();
+ }
}
private static SharedElementListener getListener(Activity activity, boolean isReturning) {
@@ -207,24 +221,6 @@
}
}
- protected void onTakeSharedElements() {
- if (!mIsReadyForTransition || mSharedElementsBundle == null) {
- return;
- }
- final Bundle sharedElementState = mSharedElementsBundle;
- mSharedElementsBundle = null;
- getDecor().getViewTreeObserver()
- .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
- startSharedElementTransition(sharedElementState);
- return false;
- }
- });
- getDecor().invalidate();
- }
-
private void startSharedElementTransition(Bundle sharedElementState) {
setEpicenter();
// Remove rejected shared elements
@@ -242,7 +238,7 @@
setSharedElementState(sharedElementState, sharedElementSnapshots);
requestLayoutForSharedElements();
- boolean startEnterTransition = allowOverlappingTransitions();
+ boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
boolean startSharedElementTransition = true;
Transition transition = beginTransition(startEnterTransition, startSharedElementTransition);
@@ -258,6 +254,29 @@
mResultReceiver = null; // all done sending messages.
}
+ private void onTakeSharedElements() {
+ if (!mIsReadyForTransition || mSharedElementsBundle == null) {
+ return;
+ }
+ final Bundle sharedElementState = mSharedElementsBundle;
+ mSharedElementsBundle = null;
+ getDecor().getViewTreeObserver()
+ .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ startSharedElementTransition(sharedElementState);
+ }
+ });
+ return false;
+ }
+ });
+ getDecor().invalidate();
+ }
+
private void requestLayoutForSharedElements() {
int numSharedElements = mSharedElements.size();
for (int i = 0; i < numSharedElements; i++) {
@@ -282,9 +301,10 @@
if (transition == null) {
sharedElementTransitionStarted();
} else {
- transition.addListener(new Transition.TransitionListenerAdapter() {
+ transition.addListener(new ContinueTransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
+ super.onTransitionStart(transition);
transition.removeListener(this);
sharedElementTransitionStarted();
}
@@ -292,12 +312,17 @@
}
}
if (transition != null) {
+ if (sharedElementTransition == null) {
+ transition.addListener(new ContinueTransitionListener());
+ }
TransitionManager.beginDelayedTransition(getDecor(), transition);
if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
mSharedElements.get(0).invalidate();
} else if (startEnterTransition && !mTransitioningViews.isEmpty()) {
mTransitioningViews.get(0).invalidate();
}
+ } else {
+ transitionStarted();
}
return transition;
}
@@ -388,11 +413,21 @@
protected void onRemoteExitTransitionComplete() {
if (!allowOverlappingTransitions()) {
- boolean startEnterTransition = true;
- boolean startSharedElementTransition = false;
- Transition transition = beginTransition(startEnterTransition,
- startSharedElementTransition);
- startEnterTransition(transition);
+ startEnterTransitionOnly();
}
}
+
+ private void startEnterTransitionOnly() {
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ setEpicenter();
+ boolean startEnterTransition = true;
+ boolean startSharedElementTransition = false;
+ Transition transition = beginTransition(startEnterTransition,
+ startSharedElementTransition);
+ startEnterTransition(transition);
+ }
+ });
+ }
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 8d5b8317..9f3dbdc 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -19,6 +19,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Intent;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -27,6 +28,7 @@
import android.transition.Transition;
import android.transition.TransitionManager;
import android.view.View;
+import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
import java.util.ArrayList;
@@ -60,10 +62,10 @@
private boolean mIsHidden;
- private boolean mExitTransitionStarted;
-
private Bundle mExitSharedElementBundle;
+ private ArrayList<View> mSharedElementSnapshots;
+
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
@@ -106,30 +108,83 @@
break;
case MSG_SHARED_ELEMENT_DESTINATION:
mExitSharedElementBundle = resultData;
- if (mExitTransitionStarted) {
- startSharedElementExit();
- }
+ sharedElementExitBack();
break;
}
}
- private void startSharedElementExit() {
+ private void sharedElementExitBack() {
if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
- Transition transition = getSharedElementExitTransition();
- TransitionManager.beginDelayedTransition(getDecor(), transition);
- ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
- mSharedElementNames);
- setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
+ startTransition(new Runnable() {
+ public void run() {
+ startSharedElementExit();
+ }
+ });
}
}
+ private void startSharedElementExit() {
+ Transition transition = getSharedElementExitTransition();
+ final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
+ mSharedElementNames);
+ mSharedElementSnapshots = createSnapshots(mExitSharedElementBundle, mSharedElementNames);
+ transition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ setViewVisibility(mSharedElements, View.INVISIBLE);
+ ViewGroupOverlay overlay = getDecor().getOverlay();
+ if (mSharedElementSnapshots != null) {
+ for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
+ overlay.add(mSharedElementSnapshots.get(i));
+ }
+ }
+ }
+ });
+ getDecor().getViewTreeObserver()
+ .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
+ return true;
+ }
+ });
+ TransitionManager.beginDelayedTransition(getDecor(), transition);
+ getDecor().invalidate();
+ }
+
+ private static ArrayList<View> copySnapshots(ArrayList<View> snapshots) {
+ ArrayList<View> copy = new ArrayList<View>(snapshots.size());
+ for (int i = 0; i < snapshots.size(); i++) {
+ View view = snapshots.get(i);
+ View viewCopy = new View(view.getContext());
+ viewCopy.setBackground(view.getBackground());
+ copy.add(viewCopy);
+ }
+ return copy;
+ }
+
private void hideSharedElements() {
- setViewVisibility(mSharedElements, View.INVISIBLE);
+ if (!mIsHidden) {
+ setViewVisibility(mSharedElements, View.INVISIBLE);
+ }
+ if (mSharedElementSnapshots != null) {
+ ViewGroupOverlay overlay = getDecor().getOverlay();
+ for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
+ overlay.remove(mSharedElementSnapshots.get(i));
+ }
+ mSharedElementSnapshots = null;
+ }
finishIfNecessary();
}
public void startExit() {
- beginTransitions();
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ beginTransitions();
+ }
+ });
setViewVisibility(mTransitioningViews, View.INVISIBLE);
}
@@ -159,29 +214,25 @@
}
}
}, options);
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ startExitTransition();
+ }
+ });
+ }
+
+ private void startExitTransition() {
Transition sharedElementTransition = mSharedElements.isEmpty()
? null : getSharedElementTransition();
if (sharedElementTransition == null) {
sharedElementTransitionComplete();
}
- Transition transition = mergeTransitions(sharedElementTransition, getExitTransition());
- if (transition == null) {
- mExitTransitionStarted = true;
- } else {
+ Transition transition = mergeTransitions(sharedElementTransition,
+ getExitTransition());
+ if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
setViewVisibility(mTransitioningViews, View.INVISIBLE);
- getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
- mExitTransitionStarted = true;
- if (mExitSharedElementBundle != null) {
- startSharedElementExit();
- }
- notifyComplete();
- return true;
- }
- });
}
}
@@ -212,7 +263,7 @@
if (viewsTransition == null) {
exitTransitionComplete();
} else {
- viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
+ viewsTransition.addListener(new ContinueTransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
exitTransitionComplete();
@@ -238,7 +289,7 @@
if (sharedElementTransition == null) {
sharedElementTransitionComplete();
} else {
- sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
+ sharedElementTransition.addListener(new ContinueTransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
sharedElementTransitionComplete();
@@ -257,7 +308,6 @@
Transition viewsTransition = getExitTransition();
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
- mExitTransitionStarted = true;
if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
}
@@ -269,15 +319,31 @@
}
protected boolean isReadyToNotify() {
- return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady
- && mExitTransitionStarted;
+ return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
}
private void sharedElementTransitionComplete() {
- mSharedElementBundle = captureSharedElementState();
+ mSharedElementBundle = mExitSharedElementBundle == null
+ ? captureSharedElementState() : captureExitSharedElementsState();
notifyComplete();
}
+ private Bundle captureExitSharedElementsState() {
+ Bundle bundle = new Bundle();
+ Rect bounds = new Rect();
+ for (int i = 0; i < mSharedElementNames.size(); i++) {
+ String name = mSharedElementNames.get(i);
+ Bundle sharedElementState = mExitSharedElementBundle.getBundle(name);
+ if (sharedElementState != null) {
+ bundle.putBundle(name, sharedElementState);
+ } else {
+ View view = mSharedElements.get(i);
+ captureSharedElementState(view, name, bundle, bounds);
+ }
+ }
+ return bundle;
+ }
+
protected void notifyComplete() {
if (isReadyToNotify()) {
if (!mSharedElementNotified) {
@@ -294,8 +360,7 @@
}
private void finishIfNecessary() {
- if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty()
- || mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
+ if (mIsReturning && mExitNotified && mActivity != null && mSharedElementSnapshots == null) {
mActivity.finish();
mActivity.overridePendingTransition(0, 0);
mActivity = null;
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 76f9d97..b8f1962 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1494,7 +1494,7 @@
return false;
}
final BackStackRecord bss = mBackStack.remove(last);
- bss.popFromBackStack(true);
+ bss.popFromBackStack(true, null);
reportBackStackChanged();
} else {
int index = -1;
@@ -1538,9 +1538,10 @@
states.add(mBackStack.remove(i));
}
final int LAST = states.size()-1;
+ BackStackRecord.TransitionState state = null;
for (int i=0; i<=LAST; i++) {
if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
- states.get(i).popFromBackStack(i == LAST);
+ state = states.get(i).popFromBackStack(i == LAST, state);
}
reportBackStackChanged();
}
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 6e99899..7479ecd 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -1,5 +1,8 @@
package android.app;
+import android.util.Pair;
+import android.view.View;
+
/**
* API for performing a set of Fragment operations.
*
@@ -169,6 +172,36 @@
public abstract FragmentTransaction setTransition(int transit);
/**
+ * Set a {@link android.transition.Transition} resource id to use with this transaction.
+ * <var>transitionId</var> will be played for fragments when going forward and when popping
+ * the back stack.
+ * @param sceneRootId The ID of the element acting as the scene root for the transition.
+ * This should be a ViewGroup containing all Fragments in the transaction.
+ * @param transitionId The resource ID for the Transition used during the Fragment transaction.
+ */
+ public abstract FragmentTransaction setCustomTransition(int sceneRootId, int transitionId);
+
+ /**
+ * Used with {@link #setCustomTransition(int, int)} to map a View from a removed or hidden
+ * Fragment to a View from a shown or added Fragment.
+ * <var>sharedElement</var> must have a unique viewName in the View hierarchy.
+ * @param sharedElement A View in a disappearing Fragment to match with a View in an
+ * appearing Fragment.
+ * @param name The viewName for a View in an appearing Fragment to match to the shared
+ * element.
+ */
+ public abstract FragmentTransaction setSharedElement(View sharedElement, String name);
+
+ /**
+ * Used with {@link #setCustomTransition(int, int)} to map multiple Views from removed or hidden
+ * Fragments to a Views from a shown or added Fragments. Views in
+ * <var>sharedElements</var> must have unique viewNames in the View hierarchy.
+ * @param sharedElements Pairs of Views in disappearing Fragments to viewNames in
+ * appearing Fragments.
+ */
+ public abstract FragmentTransaction setSharedElements(Pair<View, String>... sharedElements);
+
+ /**
* Set a custom style resource that will be used for resolving transit
* animations.
*/
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 3ae8bfc..065e88d 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -16,8 +16,8 @@
package android.app;
+import android.text.TextUtils;
import android.util.ArrayMap;
-import com.android.internal.util.ArrayUtils;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -52,6 +52,8 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
final class IntentReceiverLeaked extends AndroidRuntimeException {
@@ -79,6 +81,8 @@
final String mPackageName;
private final String mAppDir;
private final String mResDir;
+ private final String[] mSplitAppDirs;
+ private final String[] mSplitResDirs;
private final String[] mOverlayDirs;
private final String[] mSharedLibraries;
private final String mDataDir;
@@ -116,13 +120,14 @@
public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
CompatibilityInfo compatInfo, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode) {
+ final int myUid = Process.myUid();
mActivityThread = activityThread;
mApplicationInfo = aInfo;
mPackageName = aInfo.packageName;
mAppDir = aInfo.sourceDir;
- final int myUid = Process.myUid();
- mResDir = aInfo.uid == myUid ? aInfo.sourceDir
- : aInfo.publicSourceDir;
+ mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
+ mSplitAppDirs = aInfo.splitSourceDirs;
+ mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
mOverlayDirs = aInfo.resourceDirs;
if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid),
@@ -149,6 +154,8 @@
mPackageName = "android";
mAppDir = null;
mResDir = null;
+ mSplitAppDirs = null;
+ mSplitResDirs = null;
mOverlayDirs = null;
mSharedLibraries = null;
mDataDir = null;
@@ -214,53 +221,6 @@
return ai.sharedLibraryFiles;
}
- /**
- * Combines two arrays (of library names) such that they are
- * concatenated in order but are devoid of duplicates. The
- * result is a single string with the names of the libraries
- * separated by colons, or <code>null</code> if both lists
- * were <code>null</code> or empty.
- *
- * @param list1 null-ok; the first list
- * @param list2 null-ok; the second list
- * @return null-ok; the combination
- */
- private static String combineLibs(String[] list1, String[] list2) {
- StringBuilder result = new StringBuilder(300);
- boolean first = true;
-
- if (list1 != null) {
- for (String s : list1) {
- if (first) {
- first = false;
- } else {
- result.append(':');
- }
- result.append(s);
- }
- }
-
- // Only need to check for duplicates if list1 was non-empty.
- boolean dupCheck = !first;
-
- if (list2 != null) {
- for (String s : list2) {
- if (dupCheck && ArrayUtils.contains(list1, s)) {
- continue;
- }
-
- if (first) {
- first = false;
- } else {
- result.append(':');
- }
- result.append(s);
- }
- }
-
- return result.toString();
- }
-
public ClassLoader getClassLoader() {
synchronized (this) {
if (mClassLoader != null) {
@@ -268,8 +228,15 @@
}
if (mIncludeCode && !mPackageName.equals("android")) {
- String zip = mAppDir;
- String libraryPath = mLibDir;
+ final ArrayList<String> zipPaths = new ArrayList<>();
+ final ArrayList<String> libPaths = new ArrayList<>();
+
+ zipPaths.add(mAppDir);
+ if (mSplitAppDirs != null) {
+ Collections.addAll(zipPaths, mSplitAppDirs);
+ }
+
+ libPaths.add(mLibDir);
/*
* The following is a bit of a hack to inject
@@ -280,50 +247,70 @@
* concatenation of both apps' shared library lists.
*/
- String instrumentationAppDir =
- mActivityThread.mInstrumentationAppDir;
- String instrumentationAppLibraryDir =
- mActivityThread.mInstrumentationAppLibraryDir;
- String instrumentationAppPackage =
- mActivityThread.mInstrumentationAppPackage;
- String instrumentedAppDir =
- mActivityThread.mInstrumentedAppDir;
- String instrumentedAppLibraryDir =
- mActivityThread.mInstrumentedAppLibraryDir;
+ String instrumentationPackageName = mActivityThread.mInstrumentationPackageName;
+ String instrumentationAppDir = mActivityThread.mInstrumentationAppDir;
+ String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs;
+ String instrumentationLibDir = mActivityThread.mInstrumentationLibDir;
+
+ String instrumentedAppDir = mActivityThread.mInstrumentedAppDir;
+ String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs;
+ String instrumentedLibDir = mActivityThread.mInstrumentedLibDir;
String[] instrumentationLibs = null;
if (mAppDir.equals(instrumentationAppDir)
|| mAppDir.equals(instrumentedAppDir)) {
- zip = instrumentationAppDir + ":" + instrumentedAppDir;
- libraryPath = instrumentationAppLibraryDir + ":" + instrumentedAppLibraryDir;
- if (! instrumentedAppDir.equals(instrumentationAppDir)) {
- instrumentationLibs =
- getLibrariesFor(instrumentationAppPackage);
+ zipPaths.clear();
+ zipPaths.add(instrumentationAppDir);
+ if (instrumentationSplitAppDirs != null) {
+ Collections.addAll(zipPaths, instrumentationSplitAppDirs);
+ }
+ zipPaths.add(instrumentedAppDir);
+ if (instrumentedSplitAppDirs != null) {
+ Collections.addAll(zipPaths, instrumentedSplitAppDirs);
+ }
+
+ libPaths.clear();
+ libPaths.add(instrumentationLibDir);
+ libPaths.add(instrumentedLibDir);
+
+ if (!instrumentedAppDir.equals(instrumentationAppDir)) {
+ instrumentationLibs = getLibrariesFor(instrumentationPackageName);
}
}
- if ((mSharedLibraries != null) ||
- (instrumentationLibs != null)) {
- zip =
- combineLibs(mSharedLibraries, instrumentationLibs)
- + ':' + zip;
+ if (mSharedLibraries != null) {
+ for (String lib : mSharedLibraries) {
+ if (!zipPaths.contains(lib)) {
+ zipPaths.add(0, lib);
+ }
+ }
}
+ if (instrumentationLibs != null) {
+ for (String lib : instrumentationLibs) {
+ if (!zipPaths.contains(lib)) {
+ zipPaths.add(0, lib);
+ }
+ }
+ }
+
+ final String zip = TextUtils.join(File.pathSeparator, zipPaths);
+ final String lib = TextUtils.join(File.pathSeparator, libPaths);
+
/*
* With all the combination done (if necessary, actually
* create the class loader.
*/
if (ActivityThread.localLOGV)
- Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + libraryPath);
+ Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + lib);
// Temporarily disable logging of disk reads on the Looper thread
// as this is early and necessary.
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- mClassLoader =
- ApplicationLoaders.getDefault().getClassLoader(
- zip, libraryPath, mBaseClassLoader);
+ mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib,
+ mBaseClassLoader);
initializeJavaContextClassLoader();
StrictMode.setThreadPolicy(oldPolicy);
@@ -469,6 +456,14 @@
return mResDir;
}
+ public String[] getSplitAppDirs() {
+ return mSplitAppDirs;
+ }
+
+ public String[] getSplitResDirs() {
+ return mSplitResDirs;
+ }
+
public String[] getOverlayDirs() {
return mOverlayDirs;
}
@@ -487,7 +482,7 @@
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
- mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs,
+ mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,
mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
}
return mResources;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 276f936..b94fd41 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2867,16 +2867,16 @@
/**
* Helper class for generating large-format notifications that include a large image attachment.
*
- * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
+ * Here's how you'd set the <code>BigPictureStyle</code> on a notification:
* <pre class="prettyprint">
- * Notification noti = new Notification.BigPictureStyle(
- * new Notification.Builder()
- * .setContentTitle("New photo from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_post)
- * .setLargeIcon(aBitmap))
- * .bigPicture(aBigBitmap)
- * .build();
+ * Notification notif = new Notification.Builder(mContext)
+ * .setContentTitle("New photo from " + sender.toString())
+ * .setContentText(subject)
+ * .setSmallIcon(R.drawable.new_post)
+ * .setLargeIcon(aBitmap)
+ * .setStyle(new Notification.BigPictureStyle()
+ * .bigPicture(aBigBitmap))
+ * .build();
* </pre>
*
* @see Notification#bigContentView
@@ -2963,16 +2963,16 @@
/**
* Helper class for generating large-format notifications that include a lot of text.
*
- * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
+ * Here's how you'd set the <code>BigTextStyle</code> on a notification:
* <pre class="prettyprint">
- * Notification noti = new Notification.BigTextStyle(
- * new Notification.Builder()
- * .setContentTitle("New mail from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail)
- * .setLargeIcon(aBitmap))
- * .bigText(aVeryLongString)
- * .build();
+ * Notification notif = new Notification.Builder(mContext)
+ * .setContentTitle("New mail from " + sender.toString())
+ * .setContentText(subject)
+ * .setSmallIcon(R.drawable.new_mail)
+ * .setLargeIcon(aBitmap)
+ * .setStyle(new Notification.BigTextStyle()
+ * .bigText(aVeryLongString))
+ * .build();
* </pre>
*
* @see Notification#bigContentView
@@ -3057,19 +3057,19 @@
/**
* Helper class for generating large-format notifications that include a list of (up to 5) strings.
*
- * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
+ * Here's how you'd set the <code>InboxStyle</code> on a notification:
* <pre class="prettyprint">
- * Notification noti = new Notification.InboxStyle(
- * new Notification.Builder()
- * .setContentTitle("5 New mails from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail)
- * .setLargeIcon(aBitmap))
- * .addLine(str1)
- * .addLine(str2)
- * .setContentTitle("")
- * .setSummaryText("+3 more")
- * .build();
+ * Notification notif = new Notification.Builder(mContext)
+ * .setContentTitle("5 New mails from " + sender.toString())
+ * .setContentText(subject)
+ * .setSmallIcon(R.drawable.new_mail)
+ * .setLargeIcon(aBitmap)
+ * .setStyle(new Notification.InboxStyle()
+ * .addLine(str1)
+ * .addLine(str2)
+ * .setContentTitle("")
+ * .setSummaryText("+3 more"))
+ * .build();
* </pre>
*
* @see Notification#bigContentView
@@ -3619,14 +3619,16 @@
* .build();</pre>
*
* <p>The activity to launch needs to allow embedding, must be exported, and
- * should have an empty task affinity.
+ * should have an empty task affinity. It is also recommended to use the device
+ * default light theme.
*
* <p>Example AndroidManifest.xml entry:
* <pre class="prettyprint">
* <activity android:name="com.example.MyDisplayActivity"
* android:exported="true"
* android:allowEmbedded="true"
- * android:taskAffinity="" /></pre>
+ * android:taskAffinity=""
+ * android:theme="@android:style/Theme.DeviceDefault.Light" /></pre>
*
* @param intent the {@link PendingIntent} for an activity
* @return this object for method chaining
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a67faa0..3c13115 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -149,9 +149,9 @@
* @param compatInfo the compability info. Must not be null.
* @param token the application token for determining stack bounds.
*/
- public Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs,
- int displayId, Configuration overrideConfiguration, CompatibilityInfo compatInfo,
- IBinder token) {
+ public Resources getTopLevelResources(String resDir, String[] splitResDirs,
+ String[] overlayDirs, String[] libDirs, int displayId,
+ Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
final float scale = compatInfo.applicationScale;
ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, token);
Resources r;
@@ -182,6 +182,14 @@
return null;
}
+ if (splitResDirs != null) {
+ for (String splitResDir : splitResDirs) {
+ if (assets.addAssetPath(splitResDir) == 0) {
+ return null;
+ }
+ }
+ }
+
if (overlayDirs != null) {
for (String idmapPath : overlayDirs) {
assets.addOverlayPath(idmapPath);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e80c761..ae1a4e7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -2340,15 +2341,20 @@
}
/**
- * Sets which components may enter lock task mode.
+ * Sets which packages may enter lock task mode.
+ *
+ * <p>Any packages that shares uid with an allowed package will also be allowed
+ * to activate lock task.
*
* This function can only be called by the device owner or the profile owner.
- * @param components The list of components allowed to enter lock task mode
+ * @param packages The list of packages allowed to enter lock task mode
+ *
+ * @see Activity#startLockTask()
*/
- public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+ public void setLockTaskPackages(String[] packages) throws SecurityException {
if (mService != null) {
try {
- mService.setLockTaskComponents(components);
+ mService.setLockTaskPackages(packages);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2356,13 +2362,13 @@
}
/**
- * This function returns the list of components allowed to start the lock task mode.
+ * This function returns the list of packages allowed to start the lock task mode.
* @hide
*/
- public ComponentName[] getLockTaskComponents() {
+ public String[] getLockTaskPackages() {
if (mService != null) {
try {
- return mService.getLockTaskComponents();
+ return mService.getLockTaskPackages();
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2373,12 +2379,12 @@
/**
* This function lets the caller know whether the given component is allowed to start the
* lock task mode.
- * @param component The component to check
+ * @param pkg The package to check
*/
- public boolean isLockTaskPermitted(ComponentName component) {
+ public boolean isLockTaskPermitted(String pkg) {
if (mService != null) {
try {
- return mService.isLockTaskPermitted(component);
+ return mService.isLockTaskPermitted(pkg);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a1caa21..8272c07 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -142,9 +142,9 @@
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 56a55fb..706ef04 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -228,19 +228,35 @@
}
/**
- * Get the package name of the next application with data in the backup store.
+ * Get the package name of the next application with data in the backup store, plus
+ * a description of the structure of the restored archive: either TYPE_KEY_VALUE for
+ * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream.
*
- * @return The name of one of the packages supplied to {@link #startRestore},
- * or "" (the empty string) if no more backup data is available,
- * or null if an error occurred (the restore should be aborted and rescheduled).
+ * <p>If the package name in the returned RestoreDescription object is the singleton
+ * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available
+ * in the current restore session: all packages described in startRestore() have been
+ * processed.
+ *
+ * <p>If this method returns {@code null}, it means that a transport-level error has
+ * occurred and the entire restore operation should be abandoned.
+ *
+ * @return A RestoreDescription object containing the name of one of the packages
+ * supplied to {@link #startRestore} plus an indicator of the data type of that
+ * restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
+ * no more packages can be restored in this session; or {@code null} to indicate
+ * a transport-level error.
*/
- public String nextRestorePackage() {
+ public RestoreDescription nextRestorePackage() {
return null;
}
/**
- * Get the data for the application returned by {@link #nextRestorePackage}.
- * @param data An open, writable file into which the backup data should be stored.
+ * Get the data for the application returned by {@link #nextRestorePackage}, if that
+ * method reported {@link RestoreDescription#TYPE_KEY_VALUE} as its delivery type.
+ * If the package has only TYPE_FULL_STREAM data, then this method will return an
+ * error.
+ *
+ * @param data An open, writable file into which the key/value backup data should be stored.
* @return the same error codes as {@link #startRestore}.
*/
public int getRestoreData(ParcelFileDescriptor outFd) {
@@ -332,32 +348,11 @@
// Full restore interfaces
/**
- * Ask the transport to set up to perform a full data restore of the given packages.
+ * Ask the transport to provide data for the "current" package being restored. This
+ * is the package that was just reported by {@link #nextRestorePackage()} as having
+ * {@link RestoreDescription#TYPE_FULL_STREAM} data.
*
- * @param token A backup token as returned by {@link #getAvailableRestoreSets}
- * or {@link #getCurrentRestoreSet}.
- * @param targetPackage The names of the packages whose data is being requested.
- * @return TRANSPORT_OK to indicate that the OS may proceed with requesting
- * restore data; TRANSPORT_ERROR to indicate a fatal error condition that precludes
- * performing any restore at this time.
- */
- public int prepareFullRestore(long token, String[] targetPackages) {
- return BackupTransport.TRANSPORT_OK;
- }
-
- /**
- * Ask the transport what package's full data will be restored next. When all apps'
- * data has been delivered, the transport should return {@code null} here.
- * @return The package name of the next application whose data will be restored, or
- * {@code null} if all available package has been delivered.
- */
- public String getNextFullRestorePackage() {
- return null;
- }
-
- /**
- * Ask the transport to provide data for the "current" package being restored. The
- * transport then writes some data to the socket supplied to this call, and returns
+ * The transport writes some data to the socket supplied to this call, and returns
* the number of bytes written. The system will then read that many bytes and
* stream them to the application's agent for restore, then will call this method again
* to receive the next chunk of the archive. This sequence will be repeated until the
@@ -369,10 +364,14 @@
* {@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 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 @@
}
@Override
- public String nextRestorePackage() throws RemoteException {
+ public RestoreDescription nextRestorePackage() throws RemoteException {
return BackupTransport.this.nextRestorePackage();
}
@@ -478,5 +495,15 @@
public int sendBackupData(int numBytes) throws RemoteException {
return BackupTransport.this.sendBackupData(numBytes);
}
+
+ @Override
+ public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
+ return BackupTransport.this.getNextFullRestoreDataChunk(socket);
+ }
+
+ @Override
+ public int abortFullRestore() {
+ return BackupTransport.this.abortFullRestore();
+ }
}
}
diff --git a/core/java/android/app/backup/RestoreDescription.aidl b/core/java/android/app/backup/RestoreDescription.aidl
new file mode 100644
index 0000000..9cbea78
--- /dev/null
+++ b/core/java/android/app/backup/RestoreDescription.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.backup;
+
+parcelable RestoreDescription;
diff --git a/core/java/android/app/backup/RestoreDescription.java b/core/java/android/app/backup/RestoreDescription.java
new file mode 100644
index 0000000..0fb4355
--- /dev/null
+++ b/core/java/android/app/backup/RestoreDescription.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.backup;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Description of the available restore data for a given package. Returned by a
+ * BackupTransport in response to a request about the next available restorable
+ * package.
+ *
+ * @see BackupTransport#nextRestorePackage()
+ *
+ * @hide
+ */
+public class RestoreDescription implements Parcelable {
+ private final String mPackageName;
+ private final int mDataType;
+
+ private static final String NO_MORE_PACKAGES_SENTINEL = "";
+
+ /**
+ * Return this constant RestoreDescription from BackupTransport.nextRestorePackage()
+ * to indicate that no more package data is available in the current restore operation.
+ */
+ public static final RestoreDescription NO_MORE_PACKAGES =
+ new RestoreDescription(NO_MORE_PACKAGES_SENTINEL, 0);
+
+ // ---------------------------------------
+ // Data type identifiers
+
+ /** This package's restore data is an original-style key/value dataset */
+ public static final int TYPE_KEY_VALUE = 1;
+
+ /** This package's restore data is a tarball-type full data stream */
+ public static final int TYPE_FULL_STREAM = 2;
+
+ // ---------------------------------------
+ // API
+
+ public RestoreDescription(String packageName, int dataType) {
+ mPackageName = packageName;
+ mDataType = dataType;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public int getDataType() {
+ return mDataType;
+ }
+
+ // ---------------------------------------
+ // Parcelable implementation - not used by transport
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mPackageName);
+ out.writeInt(mDataType);
+ }
+
+ public static final Parcelable.Creator<RestoreDescription> CREATOR
+ = new Parcelable.Creator<RestoreDescription>() {
+ public RestoreDescription createFromParcel(Parcel in) {
+ final RestoreDescription unparceled = new RestoreDescription(in);
+ return (NO_MORE_PACKAGES_SENTINEL.equals(unparceled.mPackageName))
+ ? NO_MORE_PACKAGES
+ : unparceled;
+ }
+
+ public RestoreDescription[] newArray(int size) {
+ return new RestoreDescription[size];
+ }
+ };
+
+ private RestoreDescription(Parcel in) {
+ mPackageName = in.readString();
+ mDataType = in.readInt();
+ }
+}
diff --git a/core/java/android/app/maintenance/IIdleCallback.aidl b/core/java/android/app/maintenance/IIdleCallback.aidl
deleted file mode 100644
index 582dede..0000000
--- a/core/java/android/app/maintenance/IIdleCallback.aidl
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.maintenance;
-
-import android.app.maintenance.IIdleService;
-
-/**
- * The server side of the idle maintenance IPC protocols. The app-side implementation
- * invokes on this interface to indicate completion of the (asynchronous) instructions
- * issued by the server.
- *
- * In all cases, the 'who' parameter is the caller's service binder, used to track
- * which idle service instance is reporting.
- *
- * {@hide}
- */
-interface IIdleCallback {
- /**
- * Acknowledge receipt and processing of the asynchronous "start idle work" incall.
- * 'result' is true if the app wants some time to perform ongoing background
- * idle-time work; or false if the app declares that it does not need any time
- * for such work.
- */
- void acknowledgeStart(int token, boolean result);
-
- /**
- * Acknowledge receipt and processing of the asynchronous "stop idle work" incall.
- */
- void acknowledgeStop(int token);
-
- /*
- * Tell the idle service manager that we're done with our idle maintenance, so that
- * it can go on to the next one and stop attributing wakelock time to us etc.
- *
- * @param opToken The identifier passed in the startIdleMaintenance() call that
- * indicated the beginning of this service's idle timeslice.
- */
- void idleFinished(int token);
-}
diff --git a/core/java/android/app/maintenance/IIdleService.aidl b/core/java/android/app/maintenance/IIdleService.aidl
deleted file mode 100644
index 54abccd..0000000
--- a/core/java/android/app/maintenance/IIdleService.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.maintenance;
-
-import android.app.maintenance.IIdleCallback;
-
-/**
- * Interface that the framework uses to communicate with application code
- * that implements an idle-time "maintenance" service. End user code does
- * not implement this interface directly; instead, the app's idle service
- * implementation will extend android.app.maintenance.IdleService.
- * {@hide}
- */
-oneway interface IIdleService {
- /**
- * Begin your idle-time work.
- */
- void startIdleMaintenance(IIdleCallback callbackBinder, int token);
- void stopIdleMaintenance(IIdleCallback callbackBinder, int token);
-}
diff --git a/core/java/android/app/maintenance/IdleService.java b/core/java/android/app/maintenance/IdleService.java
deleted file mode 100644
index 2331b81..0000000
--- a/core/java/android/app/maintenance/IdleService.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.maintenance;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-
-/**
- * Idle maintenance API. Full docs TBW (to be written).
- */
-public abstract class IdleService extends Service {
- private static final String TAG = "IdleService";
-
- static final int MSG_START = 1;
- static final int MSG_STOP = 2;
- static final int MSG_FINISH = 3;
-
- IdleHandler mHandler;
- IIdleCallback mCallbackBinder;
- int mToken;
- final Object mHandlerLock = new Object();
-
- void ensureHandler() {
- synchronized (mHandlerLock) {
- if (mHandler == null) {
- mHandler = new IdleHandler(getMainLooper());
- }
- }
- }
-
- /**
- * TBW: the idle service should supply an intent-filter handling this intent
- * <p>
- * <p class="note">The application must also protect the idle service with the
- * {@code "android.permission.BIND_IDLE_SERVICE"} permission to ensure that other
- * applications cannot maliciously bind to it. If an idle service's manifest
- * declaration does not require that permission, it will never be invoked.
- * </p>
- */
- @SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE =
- "android.service.idle.IdleService";
-
- /**
- * Idle services must be protected with this permission:
- *
- * <pre class="prettyprint">
- * <service android:name="MyIdleService"
- * android:permission="android.permission.BIND_IDLE_SERVICE" >
- * ...
- * </service>
- * </pre>
- *
- * <p>If an idle service is declared in the manifest but not protected with this
- * permission, that service will be ignored by the OS.
- */
- public static final String PERMISSION_BIND =
- "android.permission.BIND_IDLE_SERVICE";
-
- // Trampoline: the callbacks are always run on the main thread
- IIdleService mBinder = new IIdleService.Stub() {
- @Override
- public void startIdleMaintenance(IIdleCallback callbackBinder, int token)
- throws RemoteException {
- ensureHandler();
- Message msg = mHandler.obtainMessage(MSG_START, token, 0, callbackBinder);
- mHandler.sendMessage(msg);
- }
-
- @Override
- public void stopIdleMaintenance(IIdleCallback callbackBinder, int token)
- throws RemoteException {
- ensureHandler();
- Message msg = mHandler.obtainMessage(MSG_STOP, token, 0, callbackBinder);
- mHandler.sendMessage(msg);
- }
- };
-
- /**
- * Your application may begin doing "idle" maintenance work in the background.
- * <p>
- * Your application may continue to run in the background until it receives a call
- * to {@link #onIdleStop()}, at which point you <i>must</i> cease doing work. The
- * OS will hold a wakelock on your application's behalf from the time this method is
- * called until after the following call to {@link #onIdleStop()} returns.
- * </p>
- * <p>
- * Returning {@code false} from this method indicates that you have no ongoing work
- * to do at present. The OS will respond by immediately calling {@link #onIdleStop()}
- * and returning your application to its normal stopped state. Returning {@code true}
- * indicates that the application is indeed performing ongoing work, so the OS will
- * let your application run in this state until it's no longer appropriate.
- * </p>
- * <p>
- * You will always receive a matching call to {@link #onIdleStop()} even if your
- * application returns {@code false} from this method.
- *
- * @return {@code true} to indicate that the application wishes to perform some ongoing
- * background work; {@code false} to indicate that it does not need to perform such
- * work at present.
- */
- public abstract boolean onIdleStart();
-
- /**
- * Your app's maintenance opportunity is over. Once the application returns from
- * this method, the wakelock held by the OS on its behalf will be released.
- */
- public abstract void onIdleStop();
-
- /**
- * Tell the OS that you have finished your idle work. Calling this more than once,
- * or calling it when you have not received an {@link #onIdleStart()} callback, is
- * an error.
- *
- * <p>It is safe to call {@link #finishIdle()} from any thread.
- */
- public final void finishIdle() {
- ensureHandler();
- mHandler.sendEmptyMessage(MSG_FINISH);
- }
-
- class IdleHandler extends Handler {
- IdleHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START: {
- // Call the concrete onIdleStart(), reporting its return value back to
- // the OS. If onIdleStart() throws, report it as a 'false' return but
- // rethrow the exception at the offending app.
- boolean result = false;
- IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
- mCallbackBinder = callbackBinder;
- final int token = mToken = msg.arg1;
- try {
- result = IdleService.this.onIdleStart();
- } catch (Exception e) {
- Log.e(TAG, "Unable to start idle workload", e);
- throw new RuntimeException(e);
- } finally {
- // don't bother if the service already called finishIdle()
- if (mCallbackBinder != null) {
- try {
- callbackBinder.acknowledgeStart(token, result);
- } catch (RemoteException re) {
- Log.e(TAG, "System unreachable to start idle workload");
- }
- }
- }
- break;
- }
-
- case MSG_STOP: {
- // Structured just like MSG_START for the stop-idle bookend call.
- IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
- final int token = msg.arg1;
- try {
- IdleService.this.onIdleStop();
- } catch (Exception e) {
- Log.e(TAG, "Unable to stop idle workload", e);
- throw new RuntimeException(e);
- } finally {
- if (mCallbackBinder != null) {
- try {
- callbackBinder.acknowledgeStop(token);
- } catch (RemoteException re) {
- Log.e(TAG, "System unreachable to stop idle workload");
- }
- }
- }
- break;
- }
-
- case MSG_FINISH: {
- if (mCallbackBinder != null) {
- try {
- mCallbackBinder.idleFinished(mToken);
- } catch (RemoteException e) {
- Log.e(TAG, "System unreachable to finish idling");
- } finally {
- mCallbackBinder = null;
- }
- } else {
- Log.e(TAG, "finishIdle() called but the idle service is not started");
- }
- break;
- }
-
- default: {
- Slog.w(TAG, "Unknown message " + msg.what);
- }
- }
- }
- }
-
- /** @hide */
- @Override
- public final IBinder onBind(Intent intent) {
- return mBinder.asBinder();
- }
-
-}
diff --git a/core/java/android/app/maintenance/package.html b/core/java/android/app/maintenance/package.html
deleted file mode 100644
index 1c9bf9d..0000000
--- a/core/java/android/app/maintenance/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body>
- {@hide}
-</body>
-</html>
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6e53a6fb..3dfa78b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7375,6 +7375,7 @@
for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) {
out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx));
}
+ out.endTag(null, TAG_CATEGORIES);
}
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 791e5aa..abc8cde 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -104,6 +104,28 @@
public int documentLaunchMode;
/**
+ * Constant corresponding to <code>persistRootOnly</code> in
+ * the {@link android.R.attr#persistableMode} attribute.
+ */
+ public static final int PERSIST_ROOT_ONLY = 0;
+ /**
+ * Constant corresponding to <code>doNotPersist</code> in
+ * the {@link android.R.attr#persistableMode} attribute.
+ */
+ public static final int DO_NOT_PERSIST = 1;
+ /**
+ * Constant corresponding to <code>persistAcrossReboots</code> in
+ * the {@link android.R.attr#persistableMode} attribute.
+ */
+ public static final int PERSIST_ACROSS_REBOOTS = 2;
+ /**
+ * Value indicating how this activity is to be persisted across
+ * reboots for restoring in the Recents list.
+ * {@link android.R.attr#persistableMode}
+ */
+ public int persistableMode;
+
+ /**
* The maximum number of tasks rooted at this activity that can be in the recent task list.
* Refer to {@link android.R.attr#maxRecents}.
*/
@@ -230,12 +252,6 @@
*/
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 @@
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 @@
dest.writeInt(softInputMode);
dest.writeInt(uiOptions);
dest.writeString(parentActivityName);
+ dest.writeInt(persistableMode);
}
public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -713,5 +740,6 @@
softInputMode = source.readInt();
uiOptions = source.readInt();
parentActivityName = source.readString();
+ persistableMode = source.readInt();
}
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6b44a11..06f4019 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -23,8 +23,12 @@
import android.os.Parcelable;
import android.util.Printer;
+import com.android.internal.util.ArrayUtils;
+
import java.text.Collator;
+import java.util.Arrays;
import java.util.Comparator;
+import java.util.Objects;
/**
* Information you can retrieve about a particular application. This
@@ -398,17 +402,30 @@
public int largestWidthLimitDp = 0;
/**
- * Full path to the location of this package.
+ * Full path to the base APK for this application.
*/
public String sourceDir;
/**
- * Full path to the location of the publicly available parts of this
- * package (i.e. the primary resource package and manifest). For
- * non-forward-locked apps this will be the same as {@link #sourceDir).
+ * Full path to the publicly available parts of {@link #sourceDir},
+ * including resources and manifest. This may be different from
+ * {@link #sourceDir} if an application is forward locked.
*/
public String publicSourceDir;
-
+
+ /**
+ * Full paths to zero or more split APKs that, when combined with the base
+ * APK defined in {@link #sourceDir}, form a complete application.
+ */
+ public String[] splitSourceDirs;
+
+ /**
+ * Full path to the publicly available parts of {@link #splitSourceDirs},
+ * including resources and manifest. This may be different from
+ * {@link #splitSourceDirs} if an application is forward locked.
+ */
+ public String[] splitPublicSourceDirs;
+
/**
* Full paths to the locations of extra resource packages this application
* uses. This field is only used if there are extra resource packages,
@@ -512,13 +529,16 @@
+ " compatibleWidthLimitDp=" + compatibleWidthLimitDp
+ " largestWidthLimitDp=" + largestWidthLimitDp);
pw.println(prefix + "sourceDir=" + sourceDir);
- if (sourceDir == null) {
- if (publicSourceDir != null) {
- pw.println(prefix + "publicSourceDir=" + publicSourceDir);
- }
- } else if (!sourceDir.equals(publicSourceDir)) {
+ if (!Objects.equals(sourceDir, publicSourceDir)) {
pw.println(prefix + "publicSourceDir=" + publicSourceDir);
}
+ if (!ArrayUtils.isEmpty(splitSourceDirs)) {
+ pw.println(prefix + "splitSourceDirs=" + Arrays.toString(splitSourceDirs));
+ }
+ if (!ArrayUtils.isEmpty(splitPublicSourceDirs)
+ && !Arrays.equals(splitSourceDirs, splitPublicSourceDirs)) {
+ pw.println(prefix + "splitPublicSourceDirs=" + Arrays.toString(splitPublicSourceDirs));
+ }
if (resourceDirs != null) {
pw.println(prefix + "resourceDirs=" + resourceDirs);
}
@@ -591,6 +611,8 @@
largestWidthLimitDp = orig.largestWidthLimitDp;
sourceDir = orig.sourceDir;
publicSourceDir = orig.publicSourceDir;
+ splitSourceDirs = orig.splitSourceDirs;
+ splitPublicSourceDirs = orig.splitPublicSourceDirs;
nativeLibraryDir = orig.nativeLibraryDir;
cpuAbi = orig.cpuAbi;
resourceDirs = orig.resourceDirs;
@@ -633,6 +655,8 @@
dest.writeInt(largestWidthLimitDp);
dest.writeString(sourceDir);
dest.writeString(publicSourceDir);
+ dest.writeStringArray(splitSourceDirs);
+ dest.writeStringArray(splitPublicSourceDirs);
dest.writeString(nativeLibraryDir);
dest.writeString(cpuAbi);
dest.writeStringArray(resourceDirs);
@@ -674,6 +698,8 @@
largestWidthLimitDp = source.readInt();
sourceDir = source.readString();
publicSourceDir = source.readString();
+ splitSourceDirs = source.readStringArray();
+ splitPublicSourceDirs = source.readStringArray();
nativeLibraryDir = source.readString();
cpuAbi = source.readString();
resourceDirs = source.readStringArray();
diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java
index a977e41..dab0caf 100644
--- a/core/java/android/content/pm/InstrumentationInfo.java
+++ b/core/java/android/content/pm/InstrumentationInfo.java
@@ -30,17 +30,32 @@
* "package" attribute.
*/
public String targetPackage;
-
+
/**
- * Full path to the location of this package.
+ * Full path to the base APK for this application.
*/
public String sourceDir;
-
+
/**
- * Full path to the location of the publicly available parts of this package (i.e. the resources
- * and manifest). For non-forward-locked apps this will be the same as {@link #sourceDir).
+ * Full path to the publicly available parts of {@link #sourceDir},
+ * including resources and manifest. This may be different from
+ * {@link #sourceDir} if an application is forward locked.
*/
public String publicSourceDir;
+
+ /**
+ * Full paths to zero or more split APKs that, when combined with the base
+ * APK defined in {@link #sourceDir}, form a complete application.
+ */
+ public String[] splitSourceDirs;
+
+ /**
+ * Full path to the publicly available parts of {@link #splitSourceDirs},
+ * including resources and manifest. This may be different from
+ * {@link #splitSourceDirs} if an application is forward locked.
+ */
+ public String[] splitPublicSourceDirs;
+
/**
* Full path to a directory assigned to the package for its persistent
* data.
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 69fa408..6c10bb8 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -57,6 +57,65 @@
private List<OnAppsChangedListener> mListeners
= new ArrayList<OnAppsChangedListener>();
+ private List<OnAppsChangedCallback> mCallbacks
+ = new ArrayList<OnAppsChangedCallback>();
+
+ /**
+ * Callbacks for package changes to this and related managed profiles.
+ */
+ public static abstract class OnAppsChangedCallback {
+ /**
+ * Indicates that a package was removed from the specified profile.
+ *
+ * @param packageName The name of the package that was removed.
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ abstract public void onPackageRemoved(String packageName, UserHandle user);
+
+ /**
+ * Indicates that a package was added to the specified profile.
+ *
+ * @param packageName The name of the package that was added.
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ abstract public void onPackageAdded(String packageName, UserHandle user);
+
+ /**
+ * Indicates that a package was modified in the specified profile.
+ *
+ * @param packageName The name of the package that has changed.
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ abstract public void onPackageChanged(String packageName, UserHandle user);
+
+ /**
+ * Indicates that one or more packages have become available. For
+ * example, this can happen when a removable storage card has
+ * reappeared.
+ *
+ * @param packageNames The names of the packages that have become
+ * available.
+ * @param user The UserHandle of the profile that generated the change.
+ * @param replacing Indicates whether these packages are replacing
+ * existing ones.
+ */
+ abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
+ boolean replacing);
+
+ /**
+ * Indicates that one or more packages have become unavailable. For
+ * example, this can happen when a removable storage card has been
+ * removed.
+ *
+ * @param packageNames The names of the packages that have become
+ * unavailable.
+ * @param user The UserHandle of the profile that generated the change.
+ * @param replacing Indicates whether the packages are about to be
+ * replaced with new versions.
+ */
+ abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+ boolean replacing);
+ }
/**
* Callbacks for package changes to this and related managed profiles.
@@ -270,7 +329,7 @@
synchronized (this) {
if (listener != null && !mListeners.contains(listener)) {
mListeners.add(listener);
- if (mListeners.size() == 1) {
+ if (mListeners.size() == 1 && mCallbacks.size() == 0) {
try {
mService.addOnAppsChangedListener(mAppsChangedListener);
} catch (RemoteException re) {
@@ -289,7 +348,44 @@
public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
synchronized (this) {
mListeners.remove(listener);
- if (mListeners.size() == 0) {
+ if (mListeners.size() == 0 && mCallbacks.size() == 0) {
+ try {
+ mService.removeOnAppsChangedListener(mAppsChangedListener);
+ } catch (RemoteException re) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a callback for changes to packages in current and managed profiles.
+ *
+ * @param callback The callback to add.
+ */
+ public void addOnAppsChangedCallback(OnAppsChangedCallback callback) {
+ synchronized (this) {
+ if (callback != null && !mCallbacks.contains(callback)) {
+ mCallbacks.add(callback);
+ if (mCallbacks.size() == 1 && mListeners.size() == 0) {
+ try {
+ mService.addOnAppsChangedListener(mAppsChangedListener);
+ } catch (RemoteException re) {
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a callback that was previously added.
+ *
+ * @param callback The callback to remove.
+ * @see #addOnAppsChangedListener(OnAppsChangedCallback)
+ */
+ public void removeOnAppsChangedCallback(OnAppsChangedCallback callback) {
+ synchronized (this) {
+ mListeners.remove(callback);
+ if (mListeners.size() == 0 && mCallbacks.size() == 0) {
try {
mService.removeOnAppsChangedListener(mAppsChangedListener);
} catch (RemoteException re) {
@@ -309,6 +405,9 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackageRemoved(user, packageName);
}
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackageRemoved(packageName, user);
+ }
}
}
@@ -321,6 +420,9 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackageChanged(user, packageName);
}
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackageChanged(packageName, user);
+ }
}
}
@@ -333,6 +435,9 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackageAdded(user, packageName);
}
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackageAdded(packageName, user);
+ }
}
}
@@ -346,6 +451,9 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackagesAvailable(user, packageNames, replacing);
}
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackagesAvailable(packageNames, user, replacing);
+ }
}
}
@@ -359,7 +467,10 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackagesUnavailable(user, packageNames, replacing);
}
- }
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackagesUnavailable(packageNames, user, replacing);
+ }
+ }
}
};
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 91895ff..618c2bd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2607,10 +2607,9 @@
com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode,
0);
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) {
- a.info.flags |= ActivityInfo.FLAG_PERSISTABLE;
- }
+ a.info.persistableMode = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifestActivity_persistableMode,
+ ActivityInfo.PERSIST_ROOT_ONLY);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded,
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index 4c50dda..31896f5 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -69,4 +69,9 @@
* 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 0901562..73188ff 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -19,8 +19,10 @@
import android.content.Context;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
+import android.hardware.CameraInfo;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.CameraDeviceUserShim;
+import android.hardware.camera2.legacy.LegacyMetadataMapper;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.hardware.camera2.utils.BinderHolder;
@@ -57,6 +59,10 @@
private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
private static final int USE_CALLING_UID = -1;
+ @SuppressWarnings("unused")
+ private static final int API_VERSION_1 = 1;
+ private static final int API_VERSION_2 = 2;
+
private final ICameraService mCameraService;
private ArrayList<String> mDeviceIdList;
@@ -142,6 +148,9 @@
synchronized (mLock) {
mListenerMap.put(listener, handler);
+
+ // TODO: fire the current oldest known state when adding a new listener
+ // (must be done while holding lock)
}
}
@@ -185,16 +194,46 @@
}
}
- CameraMetadataNative info = new CameraMetadataNative();
- try {
- mCameraService.getCameraCharacteristics(Integer.valueOf(cameraId), info);
- } catch(CameraRuntimeException e) {
- throw e.asChecked();
- } catch(RemoteException e) {
- // impossible
- return null;
+ int id = Integer.valueOf(cameraId);
+
+ /*
+ * Get the camera characteristics from the camera service directly if it supports it,
+ * otherwise get them from the legacy shim instead.
+ */
+
+ if (!supportsCamera2Api(cameraId)) {
+ // Legacy backwards compatibility path; build static info from the camera parameters
+ String[] outParameters = new String[1];
+ try {
+ mCameraService.getLegacyParameters(id, /*out*/outParameters);
+ String parameters = outParameters[0];
+
+ CameraInfo info = new CameraInfo();
+ mCameraService.getCameraInfo(id, /*out*/info);
+
+ return LegacyMetadataMapper.createCharacteristics(parameters, info);
+ } catch (RemoteException e) {
+ // Impossible
+ return null;
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ }
+
+ } else {
+ // Normal path: Get the camera characteristics directly from the camera service
+ CameraMetadataNative info = new CameraMetadataNative();
+
+ try {
+ mCameraService.getCameraCharacteristics(id, info);
+ } catch(CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch(RemoteException e) {
+ // impossible
+ return null;
+ }
+
+ return new CameraCharacteristics(info);
}
- return new CameraCharacteristics(info);
}
/**
@@ -456,6 +495,53 @@
}
}
+ /**
+ * Queries the camera service if it supports the camera2 api directly, or needs a shim.
+ *
+ * @param cameraId a non-{@code null} camera identifier
+ * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
+ */
+ private boolean supportsCamera2Api(String cameraId) {
+ return supportsCameraApi(cameraId, API_VERSION_2);
+ }
+
+ /**
+ * Queries the camera service if it supports a camera api directly, or needs a shim.
+ *
+ * @param cameraId a non-{@code null} camera identifier
+ * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
+ * @return {@code true} if connecting will work for that device version.
+ */
+ private boolean supportsCameraApi(String cameraId, int apiVersion) {
+ int id = Integer.parseInt(cameraId);
+
+ /*
+ * Possible return values:
+ * - NO_ERROR => Camera2 API is supported
+ * - CAMERA_DEPRECATED_HAL => Camera2 API is *not* supported (thrown as an exception)
+ *
+ * Anything else is an unexpected error we don't want to recover from.
+ */
+
+ try {
+ int res = mCameraService.supportsCameraApi(id, apiVersion);
+
+ if (res != CameraBinderDecorator.NO_ERROR) {
+ throw new AssertionError("Unexpected value " + res);
+ }
+
+ return true;
+ } catch (CameraRuntimeException e) {
+ if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+ return false;
+ } else {
+ throw e;
+ }
+ } catch (RemoteException e) {
+ throw new AssertionError("Camera service unreachable", e);
+ }
+ }
+
// TODO: this class needs unit tests
// TODO: extract class into top level
private class CameraServiceListener extends ICameraServiceListener.Stub {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 22ff9c6..ab7e844 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -34,6 +34,7 @@
* <li>{@code CONFIGURING -> IDLE}</li>
* <li>{@code IDLE -> CONFIGURING}</li>
* <li>{@code IDLE -> CAPTURING}</li>
+ * <li>{@code IDLE -> IDLE}</li>
* <li>{@code CAPTURING -> IDLE}</li>
* <li>{@code ANY -> ERROR}</li>
* </ul>
@@ -216,12 +217,17 @@
mCurrentState = STATE_CONFIGURING;
break;
case STATE_IDLE:
+ if (mCurrentState == STATE_IDLE) {
+ break;
+ }
+
if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
doStateTransition(STATE_ERROR);
break;
}
+
if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
mCurrentListener != null) {
mCurrentHandler.post(new Runnable() {
diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
index 3fd2309..0382557 100644
--- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
@@ -148,6 +148,12 @@
Handler handler = mGLHandlerThread.getHandler();
handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
mGLHandlerThread.quitSafely();
+ try {
+ mGLHandlerThread.join();
+ } catch (InterruptedException e) {
+ Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+ mGLHandlerThread.getName(), mGLHandlerThread.getId()));
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index f9cf905..c34a34d 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -48,7 +48,6 @@
*/
public class LegacyCameraDevice implements AutoCloseable {
public static final String DEBUG_PROP = "HAL1ShimLogging";
-
private final String TAG;
private final int mCameraId;
@@ -56,10 +55,11 @@
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 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 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 @@
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 void close() {
mRequestThreadManager.quit();
mCallbackHandlerThread.quitSafely();
+ mResultThread.quitSafely();
+
+ try {
+ mCallbackHandlerThread.join();
+ } catch (InterruptedException e) {
+ Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+ mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
+ }
+
+ try {
+ mResultThread.join();
+ } catch (InterruptedException e) {
+ Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+ mResultThread.getName(), mResultThread.getId()));
+ }
+
// TODO: throw IllegalStateException in every method after close has been called
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
new file mode 100644
index 0000000..8bb066f
--- /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 c4669f5..7b522ff 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.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.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 @@
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 @@
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 @@
}
}
+
+ /**
+ * 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 @@
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 @@
startPreview();
}
+
private void configureOutputs(Collection<Surface> outputs) throws IOException {
stopPreview();
if (mGLThreadManager != null) {
@@ -261,6 +300,7 @@
mInFlightPreview = null;
mInFlightJpeg = null;
+
for (Surface s : outputs) {
int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
switch (format) {
@@ -273,6 +313,52 @@
}
}
+ 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 @@
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 @@
// 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 @@
}
};
+ 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 @@
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 2f0f6bc..49f419f 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -431,6 +431,11 @@
public void configureSurfaces(Collection<Surface> surfaces) {
releaseEGLContext();
+ if (surfaces == null || surfaces.size() == 0) {
+ Log.w(TAG, "No output surfaces configured for GL drawing.");
+ return;
+ }
+
for (Surface s : surfaces) {
// If pixel conversions aren't handled by egl, use a pbuffer
if (LegacyCameraDevice.needsConversion(s)) {
diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
index 40cda08..898c746 100644
--- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
+++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
@@ -47,6 +47,8 @@
* - POLICY_PROHIBITS
* - RESOURCE_BUSY
* - NO_SUCH_DEVICE
+ * - NOT_SUPPORTED
+ * - TOO_MANY_USERS
*/
public static final int EACCES = -13;
public static final int EBUSY = -16;
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index 8ad9463..d86dd5e 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -185,6 +185,7 @@
public static final int RESULT_TARGET_NOT_AVAILABLE = 3;
public static final int RESULT_ALREADY_IN_PROGRESS = 4;
public static final int RESULT_EXCEPTION = 5;
+ public static final int RESULT_INCORRECT_MODE = 6;
private static final int[] ADDRESS_TO_TYPE = {
DEVICE_TV, // ADDR_TV
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index dd9c39f..b7af374 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -17,6 +17,7 @@
package android.net;
import android.net.LinkAddress;
+import android.net.RouteInfo;
/**
* Callback class for receiving events from an INetworkManagementService
@@ -98,4 +99,14 @@
* @param servers The IP addresses of the DNS servers.
*/
void interfaceDnsServerInfo(String iface, long lifetime, in String[] servers);
+
+ /**
+ * A route has been added or updated.
+ */
+ void routeUpdated(in RouteInfo route);
+
+ /**
+ * A route has been removed.
+ */
+ void routeRemoved(in RouteInfo route);
}
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index a14d13f..f1fa3eb 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Pair;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -46,9 +47,18 @@
private final byte[] address; // network byte order
private final int prefixLength;
+ private void checkAndMaskAddressAndPrefixLength() {
+ if (address.length != 4 && address.length != 16) {
+ throw new IllegalArgumentException(
+ "IpPrefix has " + address.length + " bytes which is neither 4 nor 16");
+ }
+ NetworkUtils.maskRawAddress(address, prefixLength);
+ }
+
/**
* Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in
- * network byte order and a prefix length.
+ * network byte order and a prefix length. Silently truncates the address to the prefix length,
+ * so for example {@code 192.0.2.1/24} is silently converted to {@code 192.0.2.0/24}.
*
* @param address the IP address. Must be non-null and exactly 4 or 16 bytes long.
* @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
@@ -56,24 +66,46 @@
* @hide
*/
public IpPrefix(byte[] address, int prefixLength) {
- if (address.length != 4 && address.length != 16) {
- throw new IllegalArgumentException(
- "IpPrefix has " + address.length + " bytes which is neither 4 nor 16");
- }
- if (prefixLength < 0 || prefixLength > (address.length * 8)) {
- throw new IllegalArgumentException("IpPrefix with " + address.length +
- " bytes has invalid prefix length " + prefixLength);
- }
this.address = address.clone();
this.prefixLength = prefixLength;
- // TODO: Validate that the non-prefix bits are zero
+ checkAndMaskAddressAndPrefixLength();
}
/**
+ * Constructs a new {@code IpPrefix} from an IPv4 or IPv6 address and a prefix length. Silently
+ * truncates the address to the prefix length, so for example {@code 192.0.2.1/24} is silently
+ * converted to {@code 192.0.2.0/24}.
+ *
+ * @param address the IP address. Must be non-null.
+ * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
* @hide
*/
public IpPrefix(InetAddress address, int prefixLength) {
- this(address.getAddress(), prefixLength);
+ // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
+ // which is unnecessary because getAddress() already returns a clone.
+ this.address = address.getAddress();
+ this.prefixLength = prefixLength;
+ checkAndMaskAddressAndPrefixLength();
+ }
+
+ /**
+ * Constructs a new IpPrefix from a string such as "192.0.2.1/24" or "2001:db8::1/64".
+ * Silently truncates the address to the prefix length, so for example {@code 192.0.2.1/24}
+ * is silently converted to {@code 192.0.2.0/24}.
+ *
+ * @param prefix the prefix to parse
+ *
+ * @hide
+ */
+ public IpPrefix(String prefix) {
+ // We don't reuse the (InetAddress, int) constructor because "error: call to this must be
+ // first statement in constructor". We could factor out setting the member variables to an
+ // init() method, but if we did, then we'd have to make the members non-final, or "error:
+ // cannot assign a value to final variable address". So we just duplicate the code here.
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
+ this.address = ipAndMask.first.getAddress();
+ this.prefixLength = ipAndMask.second;
+ checkAndMaskAddressAndPrefixLength();
}
/**
@@ -129,7 +161,7 @@
}
/**
- * Returns the prefix length of this {@code IpAddress}.
+ * Returns the prefix length of this {@code IpPrefix}.
*
* @return the prefix length.
*/
@@ -138,6 +170,20 @@
}
/**
+ * Returns a string representation of this {@code IpPrefix}.
+ *
+ * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::"}.
+ */
+ public String toString() {
+ try {
+ return InetAddress.getByAddress(address).getHostAddress() + "/" + prefixLength;
+ } catch(UnknownHostException e) {
+ // Cosmic rays?
+ throw new IllegalStateException("IpPrefix with invalid address! Shouldn't happen.", e);
+ }
+ }
+
+ /**
* Implement the Parcelable interface.
*/
public int describeContents() {
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 5246078..f9a25f9 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Pair;
import java.net.Inet4Address;
import java.net.InetAddress;
@@ -166,23 +167,9 @@
* @hide
*/
public LinkAddress(String address, int flags, int scope) {
- InetAddress inetAddress = null;
- int prefixLength = -1;
- try {
- String [] pieces = address.split("/", 2);
- prefixLength = Integer.parseInt(pieces[1]);
- inetAddress = InetAddress.parseNumericAddress(pieces[0]);
- } catch (NullPointerException e) { // Null string.
- } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
- } catch (NumberFormatException e) { // Non-numeric prefix.
- } catch (IllegalArgumentException e) { // Invalid IP address.
- }
-
- if (inetAddress == null || prefixLength == -1) {
- throw new IllegalArgumentException("Bad LinkAddress params " + address);
- }
-
- init(inetAddress, prefixLength, flags, scope);
+ // This may throw an IllegalArgumentException; catching it is the caller's responsibility.
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
+ init(ipAndMask.first, ipAndMask.second, flags, scope);
}
/**
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 8eefa0f..e7184ed 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -31,6 +31,7 @@
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
+import java.util.Objects;
/**
* Describes the properties of a network link.
@@ -334,15 +335,17 @@
}
/**
- * Adds a {@link RouteInfo} to this {@code LinkProperties}. If the {@link RouteInfo}
- * had an interface name set and that differs from the interface set for this
- * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The
- * proper course is to add either un-named or properly named {@link RouteInfo}.
+ * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
+ * {@link RouteInfo} had an interface name set and that differs from the interface set for this
+ * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The proper
+ * course is to add either un-named or properly named {@link RouteInfo}.
*
* @param route A {@link RouteInfo} to add to this object.
+ * @return {@code false} if the route was already present, {@code true} if it was added.
+ *
* @hide
*/
- public void addRoute(RouteInfo route) {
+ public boolean addRoute(RouteInfo route) {
if (route != null) {
String routeIface = route.getInterface();
if (routeIface != null && !routeIface.equals(mIfaceName)) {
@@ -350,8 +353,28 @@
"Route added with non-matching interface: " + routeIface +
" vs. " + mIfaceName);
}
- mRoutes.add(routeWithInterface(route));
+ route = routeWithInterface(route);
+ if (!mRoutes.contains(route)) {
+ mRoutes.add(route);
+ return true;
+ }
}
+ return false;
+ }
+
+ /**
+ * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must
+ * specify an interface and the interface must match the interface of this
+ * {@code LinkProperties}, or it will not be removed.
+ *
+ * @return {@code true} if the route was removed, {@code false} if it was not present.
+ *
+ * @hide
+ */
+ public boolean removeRoute(RouteInfo route) {
+ return route != null &&
+ Objects.equals(mIfaceName, route.getInterface()) &&
+ mRoutes.remove(route);
}
/**
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b02f88e..15c0a71 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -24,6 +24,8 @@
import java.util.Locale;
import android.util.Log;
+import android.util.Pair;
+
/**
* Native methods for managing network interfaces.
@@ -218,24 +220,17 @@
}
/**
- * Get InetAddress masked with prefixLength. Will never return null.
- * @param IP address which will be masked with specified prefixLength
- * @param prefixLength the prefixLength used to mask the IP
+ * Masks a raw IP address byte array with the specified prefix length.
*/
- public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
- if (address == null) {
- throw new RuntimeException("getNetworkPart doesn't accept null address");
- }
-
- byte[] array = address.getAddress();
-
+ public static void maskRawAddress(byte[] array, int prefixLength) {
if (prefixLength < 0 || prefixLength > array.length * 8) {
- throw new RuntimeException("getNetworkPart - bad prefixLength");
+ throw new RuntimeException("IP address with " + array.length +
+ " bytes has invalid prefix length " + prefixLength);
}
int offset = prefixLength / 8;
- int reminder = prefixLength % 8;
- byte mask = (byte)(0xFF << (8 - reminder));
+ int remainder = prefixLength % 8;
+ byte mask = (byte)(0xFF << (8 - remainder));
if (offset < array.length) array[offset] = (byte)(array[offset] & mask);
@@ -244,6 +239,16 @@
for (; offset < array.length; offset++) {
array[offset] = 0;
}
+ }
+
+ /**
+ * Get InetAddress masked with prefixLength. Will never return null.
+ * @param address the IP address to mask with
+ * @param prefixLength the prefixLength used to mask the IP
+ */
+ public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
+ byte[] array = address.getAddress();
+ maskRawAddress(array, prefixLength);
InetAddress netPart = null;
try {
@@ -255,6 +260,30 @@
}
/**
+ * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
+ * @hide
+ */
+ public static Pair<InetAddress, Integer> parseIpAndMask(String ipAndMaskString) {
+ InetAddress address = null;
+ int prefixLength = -1;
+ try {
+ String[] pieces = ipAndMaskString.split("/", 2);
+ prefixLength = Integer.parseInt(pieces[1]);
+ address = InetAddress.parseNumericAddress(pieces[0]);
+ } catch (NullPointerException e) { // Null string.
+ } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
+ } catch (NumberFormatException e) { // Non-numeric prefix.
+ } catch (IllegalArgumentException e) { // Invalid IP address.
+ }
+
+ if (address == null || prefixLength == -1) {
+ throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString);
+ }
+
+ return new Pair<InetAddress, Integer>(address, prefixLength);
+ }
+
+ /**
* Check if IP address type is consistent between two InetAddress.
* @return true if both are the same type. False otherwise.
*/
diff --git a/core/java/android/net/PSKKeyManager.java b/core/java/android/net/PSKKeyManager.java
new file mode 100644
index 0000000..92dd141
--- /dev/null
+++ b/core/java/android/net/PSKKeyManager.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import java.net.Socket;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Provider of key material for pre-shared key (PSK) key exchange used in TLS-PSK cipher suites.
+ *
+ * <h3>Overview of TLS-PSK</h3>
+ *
+ * <p>TLS-PSK is a set of TLS/SSL cipher suites which rely on a symmetric pre-shared key (PSK) to
+ * secure the TLS/SSL connection and mutually authenticate its peers. These cipher suites may be
+ * a more natural fit compared to conventional public key based cipher suites in some scenarios
+ * where communication between peers is bootstrapped via a separate step (for example, a pairing
+ * step) and requires both peers to authenticate each other. In such scenarios a symmetric key (PSK)
+ * can be exchanged during the bootstrapping step, removing the need to generate and exchange public
+ * key pairs and X.509 certificates.</p>
+ *
+ * <p>When a TLS-PSK cipher suite is used, both peers have to use the same key for the TLS/SSL
+ * handshake to succeed. Thus, both peers are implicitly authenticated by a successful handshake.
+ * This removes the need to use a {@code TrustManager} in conjunction with this {@code KeyManager}.
+ * </p>
+ *
+ * <h3>Supporting multiple keys</h3>
+ *
+ * <p>A peer may have multiple keys to choose from. To help choose the right key, during the handshake
+ * the server can provide a <em>PSK identity hint</em> to the client, and the client can provide a
+ * <em>PSK identity</em> to the server. The contents of these two pieces of information are specific
+ * to application-level protocols.</p>
+ *
+ * <p><em>NOTE: Both the PSK identity hint and the PSK identity are transmitted in cleartext.
+ * Moreover, these data are received and processed prior to peer having been authenticated. Thus,
+ * they must not contain or leak key material or other sensitive information, and should be
+ * treated (e.g., parsed) with caution, as untrusted data.</em></p>
+ *
+ * <p>The high-level flow leading to peers choosing a key during TLS/SSL handshake is as follows:
+ * <ol>
+ * <li>Server receives a handshake request from client.
+ * <li>Server replies, optionally providing a PSK identity hint to client.</li>
+ * <li>Client chooses the key.</li>
+ * <li>Client provides a PSK identity of the chosen key to server.</li>
+ * <li>Server chooses the key.</li>
+ * </ol></p>
+ *
+ * <p>In the flow above, either peer can signal that they do not have a suitable key, in which case
+ * the the handshake will be aborted immediately. This may enable a network attacker who does not
+ * know the key to learn which PSK identity hints or PSK identities are supported. If this is a
+ * concern then a randomly generated key should be used in the scenario where no key is available.
+ * This will lead to the handshake aborting later, due to key mismatch -- same as in the scenario
+ * where a key is available -- making it appear to the attacker that all PSK identity hints and PSK
+ * identities are supported.</p>
+ *
+ * <h3>Maximum sizes</h3>
+ *
+ * <p>The maximum supported sizes are as follows:
+ * <ul>
+ * <li>256 bytes for keys (see {@link #MAX_KEY_LENGTH_BYTES}),</li>
+ * <li>128 bytes for PSK identity and PSK identity hint (in modified UTF-8 representation) (see
+ * {@link #MAX_IDENTITY_LENGTH_BYTES} and {@link #MAX_IDENTITY_HINT_LENGTH_BYTES}).</li>
+ * </ul></p>
+ *
+ * <h3>Example</h3>
+ * The following example illustrates how to create an {@code SSLContext} which enables the use of
+ * TLS-PSK in {@code SSLSocket}, {@code SSLServerSocket} and {@code SSLEngine} instances obtained
+ * from it.
+ * <pre> {@code
+ * PSKKeyManager myPskKeyManager = ...;
+ *
+ * SSLContext sslContext = SSLContext.getInstance("TLS");
+ * sslContext.init(
+ * new KeyManager[] {myPskKeyManager},
+ * new TrustManager[0], // No TrustManagers needed in TLS-PSK
+ * null // Use the default source of entropy
+ * );
+ *
+ * SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(...);
+ * // Enable a TLS-PSK cipher suite (no TLS-PSK cipher suites are enabled by default)
+ * sslSocket.setEnabledCipherSuites(new String[] {"TLS_PSK_WITH_AES_128_CBC_SHA"});
+ * sslSocket.startHandshake();
+ * }</pre>
+ */
+public interface PSKKeyManager extends com.android.org.conscrypt.PSKKeyManager {
+ // IMPLEMENTATION DETAILS: This class exists only because the default implemenetation of the
+ // TLS/SSL JSSE provider (currently Conscrypt) cannot depend on Android framework classes.
+ // As a result, this framework class simply extends the PSKKeyManager interface from Conscrypt
+ // without adding any new methods or fields. Moreover, for technical reasons (Conscrypt classes
+ // are "hidden") this class replaces the Javadoc of Conscrypt's PSKKeyManager.
+
+ /**
+ * Maximum supported length (in bytes) for PSK identity hint (in modified UTF-8 representation).
+ */
+ int MAX_IDENTITY_HINT_LENGTH_BYTES =
+ com.android.org.conscrypt.PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES;
+
+ /** Maximum supported length (in bytes) for PSK identity (in modified UTF-8 representation). */
+ int MAX_IDENTITY_LENGTH_BYTES =
+ com.android.org.conscrypt.PSKKeyManager.MAX_IDENTITY_LENGTH_BYTES;
+
+ /** Maximum supported length (in bytes) for PSK. */
+ int MAX_KEY_LENGTH_BYTES = com.android.org.conscrypt.PSKKeyManager.MAX_KEY_LENGTH_BYTES;
+
+ /**
+ * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
+ * socket.
+ *
+ * @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
+ */
+ @Override
+ String chooseServerKeyIdentityHint(Socket socket);
+
+ /**
+ * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
+ * engine.
+ *
+ * @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
+ */
+ @Override
+ String chooseServerKeyIdentityHint(SSLEngine engine);
+
+ /**
+ * Gets the PSK identity to report to the server to help agree on the PSK for the provided
+ * socket.
+ *
+ * @param identityHint identity hint provided by the server or {@code null} if none provided.
+ *
+ * @return PSK identity to provide to the server. {@code null} is permitted but will be
+ * converted into an empty string.
+ */
+ @Override
+ String chooseClientKeyIdentity(String identityHint, Socket socket);
+
+ /**
+ * Gets the PSK identity to report to the server to help agree on the PSK for the provided
+ * engine.
+ *
+ * @param identityHint identity hint provided by the server or {@code null} if none provided.
+ *
+ * @return PSK identity to provide to the server. {@code null} is permitted but will be
+ * converted into an empty string.
+ */
+ @Override
+ String chooseClientKeyIdentity(String identityHint, SSLEngine engine);
+
+ /**
+ * Gets the PSK to use for the provided socket.
+ *
+ * @param identityHint identity hint provided by the server to help select the key or
+ * {@code null} if none provided.
+ * @param identity identity provided by the client to help select the key.
+ *
+ * @return key or {@code null} to signal to peer that no suitable key is available and to abort
+ * the handshake.
+ */
+ @Override
+ SecretKey getKey(String identityHint, String identity, Socket socket);
+
+ /**
+ * Gets the PSK to use for the provided engine.
+ *
+ * @param identityHint identity hint provided by the server to help select the key or
+ * {@code null} if none provided.
+ * @param identity identity provided by the client to help select the key.
+ *
+ * @return key or {@code null} to signal to peer that no suitable key is available and to abort
+ * the handshake.
+ */
+ @Override
+ SecretKey getKey(String identityHint, String identity, SSLEngine engine);
+}
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
new file mode 100644
index 0000000..f3a95b9
--- /dev/null
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Battery manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class BatteryManagerInternal {
+ /**
+ * Returns true if the device is plugged into any of the specified plug types.
+ */
+ public abstract boolean isPowered(int plugTypeSet);
+
+ /**
+ * Returns the current plug type.
+ */
+ public abstract int getPlugType();
+
+ /**
+ * Returns battery level as a percentage.
+ */
+ public abstract int getBatteryLevel();
+
+ /**
+ * Returns whether we currently consider the battery level to be low.
+ */
+ public abstract boolean getBatteryLevelLow();
+
+ /**
+ * Returns a non-zero value if an unsupported charger is attached.
+ */
+ public abstract int getInvalidCharger();
+}
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 69b828f..08a15eb 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -16,8 +16,6 @@
package android.os;
-import android.view.WindowManagerPolicy;
-
/**
* Power manager local system service interface.
*
@@ -57,12 +55,9 @@
public abstract boolean getLowPowerModeEnabled();
+ public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
+
public interface LowPowerModeListener {
public void onLowPowerModeChanged(boolean enabled);
}
-
- public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
-
- // TODO: Remove this and retrieve as a local service instead.
- public abstract void setPolicy(WindowManagerPolicy policy);
}
diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java
index e32890d..67f6409 100644
--- a/core/java/android/preference/SeekBarPreference.java
+++ b/core/java/android/preference/SeekBarPreference.java
@@ -40,11 +40,19 @@
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 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 ba66e65..1e7d7f1 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -7963,8 +7963,7 @@
actualContext = ((ContextWrapper) actualContext).getBaseContext();
}
final int intentFlags = (actualContext instanceof Activity)
- ? Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
- : Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK;
+ ? 0 : Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK;
// Launch pivot dialog through intent for now
final Intent intent = new Intent(ACTION_QUICK_CONTACT).addFlags(intentFlags);
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
index 2fcec52..5fd597b 100644
--- a/core/java/android/service/fingerprint/FingerprintManager.java
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -22,12 +22,14 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
+import android.util.Slog;
/**
* A class that coordinates access to the fingerprint hardware.
@@ -36,31 +38,40 @@
public class FingerprintManager {
private static final String TAG = "FingerprintManager";
private static final boolean DEBUG = true;
- private static final String FINGERPRINT_SERVICE_PACKAGE = "com.android.service.fingerprint";
- private static final String FINGERPRINT_SERVICE_CLASS =
- "com.android.service.fingerprint.FingerprintService";
private static final int MSG_ENROLL_RESULT = 100;
- private static final int MSG_SCANNED = 101;
- private static final int MSG_ERROR = 102;
- private static final int MSG_REMOVED = 103;
+ private static final int MSG_ACQUIRED = 101;
+ private static final int MSG_PROCESSED = 102;
+ private static final int MSG_ERROR = 103;
+ private static final int MSG_REMOVED = 104;
+ // Errors generated by layers above HAL
public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10;
- public static final int FINGERPRINT_ERROR = -1; // One of the error messages below.
- // Progress messages.
- public static final int FINGERPRINT_SCANNED = 1;
- public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2;
+ // Message types. Must agree with HAL (fingerprint.h)
+ public static final int FINGERPRINT_ERROR = -1;
+ public static final int FINGERPRINT_ACQUIRED = 1;
+ public static final int FINGERPRINT_PROCESSED = 2;
+ public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
- // Error messages. Must agree with fingerprint HAL definitions.
+ // Error messages. Must agree with HAL (fingerprint.h)
public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
- public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2;
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+ // FINGERPRINT_ACQUIRED messages. Must agree with HAL (fingerprint.h)
+ public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+ public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+ public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+ public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4;
+ public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8;
+ public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16;
+
private IFingerprintService mService;
private FingerprintManagerReceiver mClientReceiver;
private Context mContext;
+ private IBinder mToken = new Binder();
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
@@ -69,8 +80,11 @@
case MSG_ENROLL_RESULT:
mClientReceiver.onEnrollResult(msg.arg1, msg.arg2);
break;
- case MSG_SCANNED:
- mClientReceiver.onScanned(msg.arg1, msg.arg2);
+ case MSG_ACQUIRED:
+ mClientReceiver.onAcquired(msg.arg1);
+ break;
+ case MSG_PROCESSED:
+ mClientReceiver.onProcessed(msg.arg1);
break;
case MSG_ERROR:
mClientReceiver.onError(msg.arg1);
@@ -82,45 +96,29 @@
}
};
- public FingerprintManager(Context context) {
+ /**
+ * @hide
+ */
+ public FingerprintManager(Context context, IFingerprintService service) {
mContext = context;
- // Connect to service...
- Intent intent = new Intent();
- intent.setClassName(FINGERPRINT_SERVICE_PACKAGE, FINGERPRINT_SERVICE_CLASS);
- if (!context.bindServiceAsUser(intent, mFingerprintConnection,
- Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF)) {
- if (DEBUG) Log.v(TAG, "Can't bind to " + FINGERPRINT_SERVICE_CLASS);
+ mService = service;
+ if (mService == null) {
+ Slog.v(TAG, "FingerprintManagerService was null");
}
}
- private final ServiceConnection mFingerprintConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) Log.v(TAG, "Connected to FingerprintService");
- mService = IFingerprintService.Stub.asInterface(service);
- try {
- mService.startListening(mServiceReceiver, getCurrentUserId());
- } catch (RemoteException e) {
- if (DEBUG) Log.v(TAG, "Failed to set callback", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Log.v(TAG, "Disconnected from FingerprintService");
- mService = null;
- }
- };
-
private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
public void onEnrollResult(int fingerprintId, int remaining) {
mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget();
}
- public void onScanned(int fingerprintId, int confidence) {
- mHandler.obtainMessage(MSG_SCANNED, fingerprintId, confidence)
- .sendToTarget();;
+ public void onAcquired(int acquireInfo) {
+ mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0).sendToTarget();
+ }
+
+ public void onProcessed(int fingerprintId) {
+ mHandler.obtainMessage(MSG_PROCESSED, fingerprintId, 0).sendToTarget();
}
public void onError(int error) {
@@ -151,12 +149,14 @@
*/
public void enroll(long timeout) {
if (mServiceReceiver == null) {
- throw new IllegalStateException("enroll: Call registerCallback() first");
+ sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
+ return;
}
if (mService != null) try {
- mService.enroll(timeout, getCurrentUserId());
+ mService.enroll(mToken, timeout, getCurrentUserId());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception while enrolling: ", e);
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
}
}
@@ -166,10 +166,19 @@
* @param fingerprintId
*/
public void remove(int fingerprintId) {
- if (mService != null) try {
- mService.remove(fingerprintId, getCurrentUserId());
- } catch (RemoteException e) {
- Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
+ if (mServiceReceiver == null) {
+ sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
+ return;
+ }
+ if (mService != null) {
+ try {
+ mService.remove(mToken, fingerprintId, getCurrentUserId());
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
+ }
+ } else {
+ Log.w(TAG, "remove(): Service not connected!");
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
}
}
@@ -181,10 +190,13 @@
mClientReceiver = receiver;
if (mService != null) {
try {
- mService.startListening(mServiceReceiver, getCurrentUserId());
+ mService.startListening(mToken, mServiceReceiver, getCurrentUserId());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in startListening(): ", e);
}
+ } else {
+ Log.w(TAG, "startListening(): Service not connected!");
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
}
}
@@ -201,15 +213,38 @@
* Stops the client from listening to fingerprint events.
*/
public void stopListening() {
- mClientReceiver = null;
if (mService != null) {
try {
- mService.stopListening(getCurrentUserId());
+ mService.stopListening(mToken, getCurrentUserId());
+ mClientReceiver = null;
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in stopListening(): ", e);
}
} else {
Log.w(TAG, "stopListening(): Service not connected!");
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
}
}
+
+ public void enrollCancel() {
+ if (mServiceReceiver == null) {
+ sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
+ return;
+ }
+ if (mService != null) {
+ try {
+ mService.enrollCancel(mToken, getCurrentUserId());
+ mClientReceiver = null;
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception in enrollCancel(): ", e);
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
+ }
+ } else {
+ Log.w(TAG, "enrollCancel(): Service not connected!");
+ }
+ }
+
+ private void sendError(int msg, int arg1, int arg2) {
+ mHandler.obtainMessage(msg, arg1, arg2);
+ }
}
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
index 34f1655..e5193f5 100644
--- a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
+++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
@@ -30,18 +30,32 @@
public void onEnrollResult(int fingerprintId, int remaining) { }
/**
- * Fingerprint scan detected. Most clients will use this function to detect a fingerprint
+ * Fingerprint touch detected, but not processed yet. Clients will use this message to
+ * determine a good or bad scan before the fingerprint is processed. This is meant for the
+ * client to provide feedback about the scan or alert the user that recognition is to follow.
*
- * @param fingerprintId is the finger the hardware has detected.
- * @param confidence from 0 (no confidence) to 65535 (high confidence). Fingerprint 0 has
- * special meaning - the finger wasn't recognized.
+ * @param acquiredInfo one of:
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_GOOD},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_PARTIAL},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_INSUFFICIENT},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_IMAGER_DIRTY},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_SLOW},
+ * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_FAST}
*/
- public void onScanned(int fingerprintId, int confidence) { }
+ public void onAcquired(int acquiredInfo) { }
+
+ /**
+ * Fingerprint has been detected and processed. A non-zero return indicates a valid
+ * fingerprint was detected.
+ *
+ * @param fingerprintId the finger id, or 0 if not recognized.
+ */
+ public void onProcessed(int fingerprintId) { }
/**
* An error was detected during scan or enrollment. One of
* {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE},
- * {@link FingerprintManager#FINGERPRINT_ERROR_BAD_CAPTURE} or
+ * {@link FingerprintManager#FINGERPRINT_ERROR_UNABLE_TO_PROCESS} or
* {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT}
* {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE}
*
diff --git a/core/java/android/service/fingerprint/FingerprintService.java b/core/java/android/service/fingerprint/FingerprintService.java
deleted file mode 100644
index c7fa7cd..0000000
--- a/core/java/android/service/fingerprint/FingerprintService.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.fingerprint;
-
-import android.app.Service;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.util.Slog;
-
-import java.io.PrintWriter;
-import java.util.HashMap;
-
-/**
- * A service to manage multiple clients that want to access the fingerprint HAL API.
- * The service is responsible for maintaining a list of clients and dispatching all
- * fingerprint -related events.
- *
- * @hide
- */
-public class FingerprintService extends Service {
- private final String TAG = FingerprintService.class.getSimpleName() +
- "[" + getClass().getSimpleName() + "]";
- private static final boolean DEBUG = true;
- HashMap<IFingerprintServiceReceiver, ClientData> mClients =
- new HashMap<IFingerprintServiceReceiver, ClientData>();
-
- private static final int MSG_NOTIFY = 10;
-
- Handler mHandler = new Handler() {
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case MSG_NOTIFY:
- handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj);
- break;
-
- default:
- Slog.w(TAG, "Unknown message:" + msg.what);
- }
- }
- };
-
- private static final int STATE_IDLE = 0;
- private static final int STATE_LISTENING = 1;
- private static final int STATE_ENROLLING = 2;
- private static final int STATE_DELETING = 3;
- private static final long MS_PER_SEC = 1000;
-
- private static final class ClientData {
- public IFingerprintServiceReceiver receiver;
- int state;
- int userId;
- }
-
- @Override
- public final IBinder onBind(Intent intent) {
- if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
- return new FingerprintServiceWrapper();
- }
-
- // JNI methods to communicate from FingerprintManagerService to HAL
- native int nativeEnroll(int timeout);
- native int nativeRemove(int fingerprintId);
-
- // JNI methods for communicating from HAL to clients
- void notify(int msg, int arg1, int arg2) {
- mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget();
- }
-
- void handleNotify(int msg, int arg1, int arg2) {
- for (int i = 0; i < mClients.size(); i++) {
- ClientData clientData = mClients.get(i);
- switch (msg) {
- case FingerprintManager.FINGERPRINT_ERROR: {
- if (clientData.state != STATE_IDLE) {
- // FINGERPRINT_ERROR_HW_UNAVAILABLE
- // FINGERPRINT_ERROR_BAD_CAPTURE
- // FINGERPRINT_ERROR_TIMEOUT
- // FINGERPRINT_ERROR_NO_SPACE
- final int error = arg1;
- clientData.state = STATE_IDLE;
- if (clientData.receiver != null) {
- try {
- clientData.receiver.onError(error);
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to client. Did it die?", e);
- }
- }
- }
- }
- break;
- case FingerprintManager.FINGERPRINT_SCANNED: {
- final int fingerId = arg1;
- final int confidence = arg2;
- if (clientData.state == STATE_LISTENING && clientData.receiver != null) {
- try {
- clientData.receiver.onScanned(fingerId, confidence);
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to client. Did it die?", e);
- }
- }
- break;
- }
- case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
- if (clientData.state == STATE_ENROLLING) {
- final int fingerId = arg1;
- final int remaining = arg2;
- if (remaining == 0) {
- FingerprintUtils.addFingerprintIdForUser(fingerId,
- getContentResolver(), clientData.userId);
- clientData.state = STATE_IDLE; // Nothing left to do
- }
- if (clientData.receiver != null) {
- try {
- clientData.receiver.onEnrollResult(fingerId, remaining);
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to client. Did it die?", e);
- }
- }
- }
- break;
- }
- case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
- int fingerId = arg1;
- if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
- if (clientData.state == STATE_DELETING) {
- FingerprintUtils.removeFingerprintIdForUser(fingerId, getContentResolver(),
- clientData.userId);
- if (clientData.receiver != null) {
- try {
- clientData.receiver.onRemoved(fingerId);
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to client. Did it die?", e);
- }
- }
- }
- }
- break;
- }
- }
- }
-
- int enroll(IFingerprintServiceReceiver receiver, long timeout, int userId) {
- ClientData clientData = mClients.get(receiver);
- if (clientData != null) {
- if (clientData.userId != userId) throw new IllegalStateException("Bad user");
- clientData.state = STATE_ENROLLING;
- return nativeEnroll((int) (timeout / MS_PER_SEC));
- }
- return -1;
- }
-
- int remove(IFingerprintServiceReceiver receiver, int fingerId, int userId) {
- ClientData clientData = mClients.get(receiver);
- if (clientData != null) {
- if (clientData.userId != userId) throw new IllegalStateException("Bad user");
- clientData.state = STATE_DELETING;
- // The fingerprint id will be removed when we get confirmation from the HAL
- return nativeRemove(fingerId);
- }
- return -1;
- }
-
- void startListening(IFingerprintServiceReceiver receiver, int userId) {
- ClientData clientData = new ClientData();
- clientData.state = STATE_LISTENING;
- clientData.receiver = receiver;
- clientData.userId = userId;
- mClients.put(receiver, clientData);
- }
-
- void stopListening(IFingerprintServiceReceiver receiver, int userId) {
- ClientData clientData = mClients.get(receiver);
- if (clientData != null) {
- clientData.state = STATE_IDLE;
- clientData.userId = -1;
- clientData.receiver = null;
- }
- mClients.remove(receiver);
- }
-
- private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
- IFingerprintServiceReceiver mReceiver;
- public int enroll(long timeout, int userId) {
- return mReceiver != null ? FingerprintService.this.enroll(mReceiver, timeout, userId)
- : FingerprintManager.FINGERPRINT_ERROR_NO_RECEIVER;
- }
-
- public int remove(int fingerprintId, int userId) {
- return FingerprintService.this.remove(mReceiver, fingerprintId, userId);
- }
-
- public void startListening(IFingerprintServiceReceiver receiver, int userId) {
- mReceiver = receiver;
- FingerprintService.this.startListening(receiver, userId);
- }
-
- public void stopListening(int userId) {
- FingerprintService.this.stopListening(mReceiver, userId);
- }
- }
-}
diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java
index 81a2aac..f4b5526 100644
--- a/core/java/android/service/fingerprint/FingerprintUtils.java
+++ b/core/java/android/service/fingerprint/FingerprintUtils.java
@@ -18,10 +18,12 @@
import android.content.ContentResolver;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import java.util.Arrays;
+public
class FingerprintUtils {
private static final boolean DEBUG = true;
private static final String TAG = "FingerprintUtils";
@@ -30,13 +32,16 @@
String fingerIdsRaw = Settings.Secure.getStringForUser(res,
Settings.Secure.USER_FINGERPRINT_IDS, userId);
- String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", ");
- int result[] = new int[fingerStringIds.length];
- for (int i = 0; i < result.length; i++) {
- try {
- result[i] = Integer.decode(fingerStringIds[i]);
- } catch (NumberFormatException e) {
- if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]);
+ int result[] = {};
+ if (!TextUtils.isEmpty(fingerIdsRaw)) {
+ String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", ");
+ result = new int[fingerStringIds.length];
+ for (int i = 0; i < result.length; i++) {
+ try {
+ result[i] = Integer.decode(fingerStringIds[i]);
+ } catch (NumberFormatException e) {
+ if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]);
+ }
}
}
return result;
diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl
index e92c20c..43d5e9a 100644
--- a/core/java/android/service/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/service/fingerprint/IFingerprintService.aidl
@@ -22,17 +22,20 @@
* Communication channel from client to the fingerprint service.
* @hide
*/
-interface IFingerprintService {
- // Returns 0 if successfully started, -1 otherwise
- int enroll(long timeout, int userId);
+oneway interface IFingerprintService {
+ // Any errors resulting from this call will be returned to the listener
+ void enroll(IBinder token, long timeout, int userId);
+
+ // Any errors resulting from this call will be returned to the listener
+ void enrollCancel(IBinder token, int userId);
- // Returns 0 if fingerprintId's template can be removed, -1 otherwise
- int remove(int fingerprintId, int userId);
+ // Any errors resulting from this call will be returned to the listener
+ void remove(IBinder token, int fingerprintId, int userId);
// Start listening for fingerprint events. This has the side effect of starting
// the hardware if not already started.
- oneway void startListening(IFingerprintServiceReceiver receiver, int userId);
+ void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId);
// Stops listening for fingerprints
- oneway void stopListening(int userId);
+ void stopListening(IBinder token, int userId);
}
diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
index 4826b59..af4128f 100644
--- a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
@@ -24,7 +24,8 @@
*/
oneway interface IFingerprintServiceReceiver {
void onEnrollResult(int fingerprintId, int remaining);
- void onScanned(int fingerprintId, int confidence);
+ void onAcquired(int acquiredInfo);
+ void onProcessed(int fingerprintId);
void onError(int error);
void onRemoved(int fingerprintId);
}
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 0a4f641..508769d 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -206,6 +206,10 @@
// like CircularPropagation
EpicenterCallback mEpicenterCallback;
+ // For Fragment shared element transitions, linking views explicitly by mismatching
+ // viewNames.
+ ArrayMap<String, String> mNameOverrides;
+
/**
* Constructs a Transition object with no target objects. A transition with
* no targets defaults to running on all target objects in the scene hierarchy
@@ -1347,6 +1351,21 @@
} else {
captureHierarchy(sceneRoot, start);
}
+ if (!start && mNameOverrides != null) {
+ int numOverrides = mNameOverrides.size();
+ ArrayList<View> overriddenViews = new ArrayList<View>(numOverrides);
+ for (int i = 0; i < numOverrides; i++) {
+ String fromName = mNameOverrides.keyAt(i);
+ overriddenViews.add(mStartValues.nameValues.remove(fromName));
+ }
+ for (int i = 0; i < numOverrides; i++) {
+ View view = overriddenViews.get(i);
+ if (view != null) {
+ String toName = mNameOverrides.valueAt(i);
+ mStartValues.nameValues.put(toName, view);
+ }
+ }
+ }
}
static void addViewValues(TransitionValuesMaps transitionValuesMaps,
@@ -1400,10 +1419,12 @@
mStartValues.viewValues.clear();
mStartValues.idValues.clear();
mStartValues.itemIdValues.clear();
+ mStartValues.nameValues.clear();
} else {
mEndValues.viewValues.clear();
mEndValues.idValues.clear();
mEndValues.itemIdValues.clear();
+ mEndValues.nameValues.clear();
}
}
@@ -1866,6 +1887,20 @@
return mCanRemoveViews;
}
+ /**
+ * Sets the shared element names -- a mapping from a name at the start state to
+ * a different name at the end state.
+ * @hide
+ */
+ public void setNameOverrides(ArrayMap<String, String> overrides) {
+ mNameOverrides = overrides;
+ }
+
+ /** @hide */
+ public ArrayMap<String, String> getNameOverrides() {
+ return mNameOverrides;
+ }
+
@Override
public String toString() {
return toString("");
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index 698b563..c04be4f 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -168,30 +168,106 @@
@Override
public TransitionSet addTarget(View target) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).addTarget(target);
+ }
return (TransitionSet) super.addTarget(target);
}
@Override
public TransitionSet addTarget(int targetId) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).addTarget(targetId);
+ }
return (TransitionSet) super.addTarget(targetId);
}
@Override
+ public TransitionSet addTarget(String targetName) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).addTarget(targetName);
+ }
+ return (TransitionSet) super.addTarget(targetName);
+ }
+
+ @Override
+ public TransitionSet addTarget(Class targetType) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).addTarget(targetType);
+ }
+ return (TransitionSet) super.addTarget(targetType);
+ }
+
+ @Override
public TransitionSet addListener(TransitionListener listener) {
return (TransitionSet) super.addListener(listener);
}
@Override
public TransitionSet removeTarget(int targetId) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).removeTarget(targetId);
+ }
return (TransitionSet) super.removeTarget(targetId);
}
@Override
public TransitionSet removeTarget(View target) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).removeTarget(target);
+ }
return (TransitionSet) super.removeTarget(target);
}
@Override
+ public TransitionSet removeTarget(Class target) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).removeTarget(target);
+ }
+ return (TransitionSet) super.removeTarget(target);
+ }
+
+ @Override
+ public TransitionSet removeTarget(String target) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).removeTarget(target);
+ }
+ return (TransitionSet) super.removeTarget(target);
+ }
+
+ @Override
+ public Transition excludeTarget(View target, boolean exclude) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).excludeTarget(target, exclude);
+ }
+ return super.excludeTarget(target, exclude);
+ }
+
+ @Override
+ public Transition excludeTarget(String targetViewName, boolean exclude) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).excludeTarget(targetViewName, exclude);
+ }
+ return super.excludeTarget(targetViewName, exclude);
+ }
+
+ @Override
+ public Transition excludeTarget(int targetId, boolean exclude) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).excludeTarget(targetId, exclude);
+ }
+ return super.excludeTarget(targetId, exclude);
+ }
+
+ @Override
+ public Transition excludeTarget(Class type, boolean exclude) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ mTransitions.get(i).excludeTarget(type, exclude);
+ }
+ return super.excludeTarget(type, exclude);
+ }
+
+ @Override
public TransitionSet removeListener(TransitionListener listener) {
return (TransitionSet) super.removeListener(listener);
}
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 0f7638b..c6c8337 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -18,6 +18,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
import android.view.View;
import android.view.ViewGroup;
@@ -272,15 +275,23 @@
if (startView.getParent() == null) {
// no parent - safe to use
overlayView = startView;
- } else if (startView.getParent() instanceof View &&
- startView.getParent().getParent() == null) {
+ } else if (startView.getParent() instanceof View) {
View startParent = (View) startView.getParent();
- int id = startParent.getId();
- if (id != View.NO_ID && sceneRoot.findViewById(id) != null && mCanRemoveViews) {
- // no parent, but its parent is unparented but the parent
- // hierarchy has been replaced by a new hierarchy with the same id
- // and it is safe to un-parent startView
- overlayView = startView;
+ if (!isValidTarget(startParent)) {
+ if (startView.isAttachedToWindow()) {
+ overlayView = copyViewImage(startView);
+ } else {
+ overlayView = startView;
+ }
+ } else if (startParent.getParent() == null) {
+ int id = startParent.getId();
+ if (id != View.NO_ID && sceneRoot.findViewById(id) != null
+ && mCanRemoveViews) {
+ // no parent, but its parent is unparented but the parent
+ // hierarchy has been replaced by a new hierarchy with the same id
+ // and it is safe to un-parent startView
+ overlayView = startView;
+ }
}
}
}
@@ -378,6 +389,26 @@
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 d7a913d..181f77e 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -599,6 +599,42 @@
}
/**
+ * 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 b0fe0fa..98696c7 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -180,6 +180,20 @@
public float physicalYDpi;
/**
+ * This is a positive value indicating the phase offset of the VSYNC events provided by
+ * Choreographer relative to the display refresh. For example, if Choreographer reports
+ * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos).
+ */
+ public long appVsyncOffsetNanos;
+
+ /**
+ * This is how far in advance a buffer must be queued for presentation at
+ * a given time. If you want a buffer to appear on the screen at
+ * time N, you must submit the buffer before (N - bufferDeadlineNanos).
+ */
+ public long presentationDeadlineNanos;
+
+ /**
* The state of the display, such as {@link android.view.Display#STATE_ON}.
*/
public int state;
@@ -253,6 +267,8 @@
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
&& physicalYDpi == other.physicalYDpi
+ && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+ && presentationDeadlineNanos == other.presentationDeadlineNanos
&& state == other.state
&& ownerUid == other.ownerUid
&& Objects.equal(ownerPackageName, other.ownerPackageName);
@@ -286,6 +302,8 @@
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
physicalYDpi = other.physicalYDpi;
+ appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+ presentationDeadlineNanos = other.presentationDeadlineNanos;
state = other.state;
ownerUid = other.ownerUid;
ownerPackageName = other.ownerPackageName;
@@ -314,6 +332,8 @@
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
physicalYDpi = source.readFloat();
+ appVsyncOffsetNanos = source.readLong();
+ presentationDeadlineNanos = source.readLong();
state = source.readInt();
ownerUid = source.readInt();
ownerPackageName = source.readString();
@@ -343,6 +363,8 @@
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
dest.writeFloat(physicalYDpi);
+ dest.writeLong(appVsyncOffsetNanos);
+ dest.writeLong(presentationDeadlineNanos);
dest.writeInt(state);
dest.writeInt(ownerUid);
dest.writeString(ownerPackageName);
@@ -450,6 +472,10 @@
sb.append(physicalYDpi);
sb.append(") dpi, layerStack ");
sb.append(layerStack);
+ sb.append(", appVsyncOff ");
+ sb.append(appVsyncOffsetNanos);
+ sb.append(", presDeadline ");
+ sb.append(presentationDeadlineNanos);
sb.append(", type ");
sb.append(Display.typeToString(type));
if (address != null) {
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 832d67a..446bbc9 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -404,6 +404,9 @@
@Override
public void scale(float sx, float sy) {
+ // TODO: remove
+ if (sx > 1000000 || sy > 1000000) throw new IllegalArgumentException("invalid scales passed " + sx + ", " + sy);
+
nScale(mRenderer, sx, sy);
}
@@ -544,9 +547,9 @@
///////////////////////////////////////////////////////////////////////////
@Override
- public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
- Paint paint) {
- nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
+ public void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, boolean useCenter, Paint paint) {
+ nDrawArc(mRenderer, left, top, right, bottom,
startAngle, sweepAngle, useCenter, paint.mNativePaint);
}
@@ -771,8 +774,8 @@
}
@Override
- public void drawOval(RectF oval, Paint paint) {
- nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
+ public void drawOval(float left, float top, float right, float bottom, Paint paint) {
+ nDrawOval(mRenderer, left, top, right, bottom, paint.mNativePaint);
}
private static native void nDrawOval(long renderer, float left, float top,
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 79f19b5..94d8f70 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -458,6 +458,8 @@
public float xDpi;
public float yDpi;
public boolean secure;
+ public long appVsyncOffsetNanos;
+ public long presentationDeadlineNanos;
public PhysicalDisplayInfo() {
}
@@ -479,7 +481,9 @@
&& density == other.density
&& xDpi == other.xDpi
&& yDpi == other.yDpi
- && secure == other.secure;
+ && secure == other.secure
+ && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+ && presentationDeadlineNanos == other.presentationDeadlineNanos;
}
@Override
@@ -495,6 +499,8 @@
xDpi = other.xDpi;
yDpi = other.yDpi;
secure = other.secure;
+ appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+ presentationDeadlineNanos = other.presentationDeadlineNanos;
}
// For debugging purposes
@@ -502,7 +508,8 @@
public String toString() {
return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
+ "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure
- + "}";
+ + ", appVsyncOffset " + appVsyncOffsetNanos
+ + ", bufferDeadline " + presentationDeadlineNanos + "}";
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b9e56f3..9156216 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25,6 +25,7 @@
import android.annotation.Nullable;
import android.content.ClipData;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -44,6 +45,7 @@
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 @@
@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 @@
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;
}
}
@@ -13432,8 +13447,6 @@
// Destroy any previous software drawing cache if needed
if (mLayerType == LAYER_TYPE_SOFTWARE) {
destroyDrawingCache();
- invalidateParentCaches();
- invalidate(true);
}
mLayerType = layerType;
@@ -13441,13 +13454,10 @@
mLayerPaint = layerDisabled ? null : (paint == null ? new Paint() : paint);
mRenderNode.setLayerPaint(mLayerPaint);
- if (mLayerType == LAYER_TYPE_SOFTWARE) {
- // LAYER_TYPE_SOFTWARE is handled by View:draw(), so need to invalidate()
- invalidateParentCaches();
- invalidate(true);
- } else {
- invalidateViewProperty(false, false);
- }
+ // draw() behaves differently if we are on a layer, so we need to
+ // invalidate() here
+ invalidateParentCaches();
+ invalidate(true);
}
/**
@@ -15703,7 +15713,7 @@
return;
}
- Drawable d= null;
+ Drawable d = null;
if (resid != 0) {
d = mContext.getDrawable(resid);
}
@@ -15779,8 +15789,9 @@
// Compare the minimum sizes of the old Drawable and the new. If there isn't an old or
// if it has a different minimum size, we should layout again
- if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() ||
- mBackground.getMinimumWidth() != background.getMinimumWidth()) {
+ if (mBackground == null
+ || mBackground.getMinimumHeight() != background.getMinimumHeight()
+ || mBackground.getMinimumWidth() != background.getMinimumWidth()) {
requestLayout = true;
}
@@ -15791,6 +15802,8 @@
background.setVisible(getVisibility() == VISIBLE, false);
mBackground = background;
+ applyBackgroundTint();
+
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
mPrivateFlags |= PFLAG_ONLY_DRAWS_BACKGROUND;
@@ -15846,6 +15859,88 @@
}
/**
+ * Applies a tint to the background drawable.
+ * <p>
+ * Subsequent calls to {@link #setBackground(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#View_backgroundTint
+ * @attr ref android.R.styleable#View_backgroundTintMode
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ private void setBackgroundTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mBackgroundTint = tint;
+ mBackgroundTintMode = tintMode;
+ mHasBackgroundTint = true;
+
+ applyBackgroundTint();
+ }
+
+ /**
+ * Applies a tint to the background drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setBackground(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#View_backgroundTint
+ * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setBackgroundTint(@Nullable ColorStateList tint) {
+ setBackgroundTint(tint, mBackgroundTintMode);
+ }
+
+ /**
+ * @return the tint applied to the background drawable
+ * @attr ref android.R.styleable#View_backgroundTint
+ * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getBackgroundTint() {
+ return mBackgroundTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setBackgroundTint(ColorStateList)}} to the background drawable.
+ * The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#View_backgroundTintMode
+ * @see #setBackgroundTint(ColorStateList)
+ */
+ public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setBackgroundTint(mBackgroundTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the background drawable
+ * @attr ref android.R.styleable#View_backgroundTintMode
+ * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getBackgroundTintMode() {
+ return mBackgroundTintMode;
+ }
+
+ private void applyBackgroundTint() {
+ if (mBackground != null && mHasBackgroundTint) {
+ mBackground = mBackground.mutate();
+ mBackground.setTint(mBackgroundTint, mBackgroundTintMode);
+ }
+ }
+
+ /**
* Sets the padding. The view may add on the space required to display
* the scrollbars, depending on the style and visibility of the scrollbars.
* So the values returned from {@link #getPaddingLeft}, {@link #getPaddingTop},
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dfd5cdf..82e5ddd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -656,7 +656,7 @@
}
public boolean invokeFunctor(long functor, boolean waitForCompletion) {
- if (mAttachInfo.mHardwareRenderer == null || !mAttachInfo.mHardwareRenderer.isEnabled()) {
+ if (mAttachInfo.mHardwareRenderer == null) {
return false;
}
mAttachInfo.mHardwareRenderer.invokeFunctor(functor, waitForCompletion);
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index abed082..321d9d3 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -17,6 +17,7 @@
package android.webkit;
import android.net.WebAddress;
+import android.webkit.ValueCallback;
/**
* Manages the cookies used by an application's {@link WebView} instances.
@@ -72,7 +73,7 @@
* path and name will be replaced with the new cookie. The cookie being set
* will be ignored if it is expired.
*
- * @param url the URL for which the cookie is set
+ * @param url the URL for which the cookie is to be set
* @param value the cookie as a string, using the format of the 'Set-Cookie'
* HTTP response header
*/
@@ -81,6 +82,29 @@
}
/**
+ * Sets a cookie for the given URL. Any existing cookie with the same host,
+ * path and name will be replaced with the new cookie. The cookie being set
+ * will be ignored if it is expired.
+ * <p>
+ * This method is asynchronous.
+ * If a {@link ValueCallback} is provided,
+ * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
+ * thread's {@link android.os.Looper} once the operation is complete.
+ * The value provided to the callback indicates whether the cookie was set successfully.
+ * You can pass {@code null} as the callback if you don't need to know when the operation
+ * completes or whether it succeeded, and in this case it is safe to call the method from a
+ * thread without a Looper.
+ *
+ * @param url the URL for which the cookie is to be set
+ * @param value the cookie as a string, using the format of the 'Set-Cookie'
+ * HTTP response header
+ * @param callback a callback to be executed when the cookie has been set
+ */
+ public void setCookie(String url, String value, ValueCallback<Boolean> callback) {
+ throw new MustOverrideException();
+ }
+
+ /**
* Gets the cookies for the given URL.
*
* @param url the URL for which the cookies are requested
@@ -120,19 +144,57 @@
/**
* Removes all session cookies, which are cookies without an expiration
* date.
+ * @deprecated use {@link #removeSessionCookies(ValueCallback)} instead.
*/
public void removeSessionCookie() {
throw new MustOverrideException();
}
/**
- * Removes all cookies.
+ * Removes all session cookies, which are cookies without an expiration
+ * date.
+ * <p>
+ * This method is asynchronous.
+ * If a {@link ValueCallback} is provided,
+ * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
+ * thread's {@link android.os.Looper} once the operation is complete.
+ * The value provided to the callback indicates whether any cookies were removed.
+ * You can pass {@code null} as the callback if you don't need to know when the operation
+ * completes or whether any cookie were removed, and in this case it is safe to call the
+ * method from a thread without a Looper.
+ * @param callback a callback which is executed when the session cookies have been removed
*/
+ public void removeSessionCookies(ValueCallback<Boolean> callback) {
+ throw new MustOverrideException();
+ }
+
+ /**
+ * Removes all cookies.
+ * @deprecated Use {@link #removeAllCookies(ValueCallback)} instead.
+ */
+ @Deprecated
public void removeAllCookie() {
throw new MustOverrideException();
}
/**
+ * Removes all cookies.
+ * <p>
+ * This method is asynchronous.
+ * If a {@link ValueCallback} is provided,
+ * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
+ * thread's {@link android.os.Looper} once the operation is complete.
+ * The value provided to the callback indicates whether any cookies were removed.
+ * You can pass {@code null} as the callback if you don't need to know when the operation
+ * completes or whether any cookies were removed, and in this case it is safe to call the
+ * method from a thread without a Looper.
+ * @param callback a callback which is executed when the cookies have been removed
+ */
+ public void removeAllCookies(ValueCallback<Boolean> callback) {
+ throw new MustOverrideException();
+ }
+
+ /**
* Gets whether there are stored cookies.
*
* @return true if there are stored cookies
@@ -153,7 +215,9 @@
/**
* Removes all expired cookies.
+ * @deprecated The WebView handles removing expired cookies automatically.
*/
+ @Deprecated
public void removeExpiredCookie() {
throw new MustOverrideException();
}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 08931fe..eb3f882 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.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 @@
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 @@
* @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 @@
} else {
needUpdate = false;
}
+
if (thumb != null) {
thumb.setCallback(this);
if (canResolveLayoutDirection()) {
@@ -136,8 +156,12 @@
requestLayout();
}
}
+
mThumb = thumb;
+
+ applyThumbTint();
invalidate();
+
if (needUpdate) {
updateThumbAndTrackPos(getWidth(), getHeight());
if (thumb != null && thumb.isStateful()) {
@@ -160,6 +184,88 @@
}
/**
+ * Applies a tint to the thumb drawable.
+ * <p>
+ * Subsequent calls to {@link #setThumb(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#SeekBar_thumbTint
+ * @attr ref android.R.styleable#SeekBar_thumbTintMode
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ private void setThumbTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mThumbTint = tint;
+ mThumbTintMode = tintMode;
+ mHasThumbTint = true;
+
+ applyThumbTint();
+ }
+
+ /**
+ * Applies a tint to the thumb drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setThumb(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#SeekBar_thumbTint
+ * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setThumbTint(@Nullable ColorStateList tint) {
+ setThumbTint(tint, mThumbTintMode);
+ }
+
+ /**
+ * @return the tint applied to the thumb drawable
+ * @attr ref android.R.styleable#SeekBar_thumbTint
+ * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getThumbTint() {
+ return mThumbTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setThumbTint(ColorStateList)}} to the thumb drawable. The
+ * default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#SeekBar_thumbTintMode
+ * @see #setThumbTint(ColorStateList)
+ */
+ public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setThumbTint(mThumbTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the thumb drawable
+ * @attr ref android.R.styleable#SeekBar_thumbTintMode
+ * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getThumbTintMode() {
+ return mThumbTintMode;
+ }
+
+ private void applyThumbTint() {
+ if (mThumb != null && mHasThumbTint) {
+ mThumb = mThumb.mutate();
+ mThumb.setTint(mThumbTint, mThumbTintMode);
+ }
+ }
+
+ /**
* @see #setThumbOffset(int)
*/
public int getThumbOffset() {
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index c934ad7..747d2b1 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 @@
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 @@
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 @@
}
/**
- * 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 @@
}
/**
- * Set the background to a given Drawable
+ * Set the button graphic to a given Drawable
*
- * @param d The Drawable to use as the background
+ * @param d The Drawable to use as the button graphic
*/
public void setButtonDrawable(Drawable d) {
- if (d != null) {
+ if (mButtonDrawable != d) {
if (mButtonDrawable != null) {
mButtonDrawable.setCallback(null);
unscheduleDrawable(mButtonDrawable);
}
- d.setCallback(this);
- d.setVisible(getVisibility() == VISIBLE, false);
- mButtonDrawable = d;
- setMinHeight(mButtonDrawable.getIntrinsicHeight());
- }
- refreshDrawableState();
+ mButtonDrawable = d;
+
+ if (d != null) {
+ d.setCallback(this);
+ d.setLayoutDirection(getLayoutDirection());
+ if (d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ d.setVisible(getVisibility() == VISIBLE, false);
+ setMinHeight(d.getIntrinsicHeight());
+ applyButtonTint();
+ }
+ }
+ }
+
+ /**
+ * Applies a tint to the button drawable.
+ * <p>
+ * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#CompoundButton_buttonTint
+ * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setButtonTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mButtonTint = tint;
+ mButtonTintMode = tintMode;
+ mHasButtonTint = true;
+
+ applyButtonTint();
+ }
+
+ /**
+ * Applies a tint to the button drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#CompoundButton_buttonTint
+ * @see #setButtonTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ public void setButtonTint(@Nullable ColorStateList tint) {
+ setButtonTint(tint, mButtonTintMode);
+ }
+
+ /**
+ * @return the tint applied to the button drawable
+ * @attr ref android.R.styleable#CompoundButton_buttonTint
+ * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getButtonTint() {
+ return mButtonTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setButtonTint(ColorStateList)}} to the button drawable. The
+ * default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+ * @see #setButtonTint(ColorStateList)
+ */
+ public void setButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setButtonTint(mButtonTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the button drawable
+ * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+ * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getButtonTintMode() {
+ return mButtonTintMode;
+ }
+
+ private void applyButtonTint() {
+ if (mButtonDrawable != null && mHasButtonTint) {
+ mButtonDrawable = mButtonDrawable.mutate();
+ mButtonDrawable.setTint(mButtonTint, mButtonTintMode);
+ }
}
@Override
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 01a6b8a..5a14929 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -18,11 +18,15 @@
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.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 @@
@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 @@
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 @@
* 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 @@
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 a40b85e..399e087 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.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.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.R;
+
import java.io.IOException;
import java.io.InputStream;
@@ -75,13 +80,18 @@
private int mMaxHeight = Integer.MAX_VALUE;
// these are applied to the drawable
- private ColorFilter mColorFilter;
+ private ColorFilter mColorFilter = null;
+ private boolean mHasColorFilter = false;
private Xfermode mXfermode;
private int mAlpha = 255;
private int mViewAlphaScale = 256;
private boolean mColorMod = false;
private Drawable mDrawable = null;
+ private ColorStateList mDrawableTint = null;
+ private PorterDuff.Mode mDrawableTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasDrawableTint = false;
+
private int[] mState = null;
private boolean mMergeState = false;
private int mLevel = 0;
@@ -154,17 +164,21 @@
setMaxHeight(a.getDimensionPixelSize(
com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
- int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
+ final int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
if (index >= 0) {
setScaleType(sScaleTypeArray[index]);
}
- int tint = a.getInt(com.android.internal.R.styleable.ImageView_tint, 0);
- if (tint != 0) {
- setColorFilter(tint);
+ if (a.hasValue(R.styleable.ImageView_tint)) {
+ mDrawableTint = a.getColorStateList(R.styleable.ImageView_tint);
+ mDrawableTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
+ mHasDrawableTint = true;
+
+ applyDrawableTint();
}
-
- int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255);
+
+ final int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255);
if (alpha != 255) {
setAlpha(alpha);
}
@@ -435,6 +449,88 @@
}
/**
+ * Applies a tint to the image drawable.
+ * <p>
+ * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ImageView_tint
+ * @attr ref android.R.styleable#ImageView_tintMode
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ private void setTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mDrawableTint = tint;
+ mDrawableTintMode = tintMode;
+ mHasDrawableTint = true;
+
+ applyDrawableTint();
+ }
+
+ /**
+ * Applies a tint to the image drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ImageView_tint
+ * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setTint(@Nullable ColorStateList tint) {
+ setTint(tint, mDrawableTintMode);
+ }
+
+ /**
+ * @return the tint applied to the image drawable
+ * @attr ref android.R.styleable#ImageView_tint
+ * @see #setTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getTint() {
+ return mDrawableTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setTint(ColorStateList)}} to the image drawable. The default
+ * mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ImageView_tintMode
+ * @see #setTint(ColorStateList)
+ */
+ public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setTint(mDrawableTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the image drawable
+ * @attr ref android.R.styleable#ImageView_tintMode
+ * @see #setTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getTintMode() {
+ return mDrawableTintMode;
+ }
+
+ private void applyDrawableTint() {
+ if (mDrawable != null && mHasDrawableTint) {
+ mDrawable = mDrawable.mutate();
+ mDrawable.setTint(mDrawableTint, mDrawableTintMode);
+ }
+ }
+
+ /**
* Sets a Bitmap as the content of this ImageView.
*
* @param bm The bitmap to set
@@ -709,17 +805,20 @@
mDrawable.setCallback(null);
unscheduleDrawable(mDrawable);
}
+
mDrawable = d;
+
if (d != null) {
d.setCallback(this);
+ d.setLayoutDirection(getLayoutDirection());
if (d.isStateful()) {
d.setState(getDrawableState());
}
- d.setLevel(mLevel);
- d.setLayoutDirection(getLayoutDirection());
d.setVisible(getVisibility() == VISIBLE, true);
+ d.setLevel(mLevel);
mDrawableWidth = d.getIntrinsicWidth();
mDrawableHeight = d.getIntrinsicHeight();
+ applyDrawableTint();
applyColorMod();
configureBounds();
} else {
@@ -1177,6 +1276,7 @@
public void setColorFilter(ColorFilter cf) {
if (mColorFilter != cf) {
mColorFilter = cf;
+ mHasColorFilter = true;
mColorMod = true;
applyColorMod();
invalidate();
@@ -1231,7 +1331,9 @@
// re-applied if the Drawable is changed.
if (mDrawable != null && mColorMod) {
mDrawable = mDrawable.mutate();
- mDrawable.setColorFilter(mColorFilter);
+ if (mHasColorFilter) {
+ mDrawable.setColorFilter(mColorFilter);
+ }
mDrawable.setXfermode(mXfermode);
mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
}
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index af32f1c..62a8bec 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -16,13 +16,17 @@
package android.widget;
+import android.annotation.Nullable;
+import android.graphics.PorterDuff;
import com.android.internal.R;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
+import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Animatable;
@@ -210,8 +214,26 @@
private Transformation mTransformation;
private AlphaAnimation mAnimation;
private boolean mHasAnimation;
+
private Drawable mIndeterminateDrawable;
+ private ColorStateList mIndeterminateTint = null;
+ private PorterDuff.Mode mIndeterminateTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasIndeterminateTint = false;
+
private Drawable mProgressDrawable;
+
+ private ColorStateList mProgressTint = null;
+ private PorterDuff.Mode mProgressTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasProgressTint = false;
+
+ private ColorStateList mProgressBackgroundTint = null;
+ private PorterDuff.Mode mProgressBackgroundTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasProgressBackgroundTint = false;
+
+ private ColorStateList mSecondaryProgressTint = null;
+ private PorterDuff.Mode mSecondaryProgressTintMode = PorterDuff.Mode.SRC_ATOP;
+ private boolean mHasSecondaryProgressTint = false;
+
private Drawable mCurrentDrawable;
Bitmap mSampleTile;
private boolean mNoInvalidate;
@@ -257,11 +279,11 @@
mNoInvalidate = true;
- Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
- if (drawable != null) {
+ final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
+ if (progressDrawable != null) {
// Calling this method can set mMaxHeight, make sure the corresponding
// XML attribute for mMaxHeight is read after calling this method
- setProgressDrawableTiled(drawable);
+ setProgressDrawableTiled(progressDrawable);
}
@@ -288,9 +310,10 @@
setSecondaryProgress(
a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
- drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable);
- if (drawable != null) {
- setIndeterminateDrawableTiled(drawable);
+ final Drawable indeterminateDrawable = a.getDrawable(
+ R.styleable.ProgressBar_indeterminateDrawable);
+ if (indeterminateDrawable != null) {
+ setIndeterminateDrawableTiled(indeterminateDrawable);
}
mOnlyIndeterminate = a.getBoolean(
@@ -303,6 +326,53 @@
mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);
+ if (a.hasValue(R.styleable.ProgressBar_progressTint)) {
+ mProgressTint = a.getColorStateList(
+ R.styleable.ProgressBar_progressTint);
+ mProgressTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_progressBackgroundTintMode, -1),
+ mProgressTintMode);
+ mHasProgressTint = true;
+
+ applyProgressLayerTint(R.id.progress, mProgressTint,
+ mProgressTintMode, true);
+ }
+
+ if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) {
+ mProgressBackgroundTint = a.getColorStateList(
+ R.styleable.ProgressBar_progressBackgroundTint);
+ mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_progressTintMode, -1),
+ mProgressBackgroundTintMode);
+ mHasProgressBackgroundTint = true;
+
+ applyProgressLayerTint(R.id.background, mProgressBackgroundTint,
+ mProgressBackgroundTintMode, false);
+ }
+
+ if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) {
+ mSecondaryProgressTint = a.getColorStateList(
+ R.styleable.ProgressBar_secondaryProgressTint);
+ mSecondaryProgressTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_secondaryProgressTintMode, -1),
+ mSecondaryProgressTintMode);
+ mHasSecondaryProgressTint = true;
+
+ applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint,
+ mSecondaryProgressTintMode, false);
+ }
+
+ if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) {
+ mIndeterminateTint = a.getColorStateList(
+ R.styleable.ProgressBar_indeterminateTint);
+ mIndeterminateTintMode = Drawable.parseTintMode(a.getInt(
+ R.styleable.ProgressBar_indeterminateTintMode, -1),
+ mIndeterminateTintMode);
+ mHasIndeterminateTint = true;
+
+ applyIndeterminateTint();
+ }
+
a.recycle();
// If not explicitly specified this view is important for accessibility.
@@ -479,16 +549,111 @@
* @see #setIndeterminate(boolean)
*/
public void setIndeterminateDrawable(Drawable d) {
- if (d != null) {
- d.setCallback(this);
+ if (mIndeterminateDrawable != d) {
+ if (mIndeterminateDrawable != null) {
+ mIndeterminateDrawable.setCallback(null);
+ unscheduleDrawable(mIndeterminateDrawable);
+ }
+
+ mIndeterminateDrawable = d;
+
+ if (d != null) {
+ d.setCallback(this);
+ d.setLayoutDirection(getLayoutDirection());
+ if (d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ applyIndeterminateTint();
+ }
+
+ if (mIndeterminate) {
+ mCurrentDrawable = d;
+ postInvalidate();
+ }
}
- mIndeterminateDrawable = d;
- if (mIndeterminateDrawable != null && canResolveLayoutDirection()) {
- mIndeterminateDrawable.setLayoutDirection(getLayoutDirection());
- }
- if (mIndeterminate) {
- mCurrentDrawable = d;
- postInvalidate();
+ }
+
+ /**
+ * Applies a tint to the indeterminate drawable.
+ * <p>
+ * Subsequent calls to {@link #setVisibilminateDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and
+ * tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTint
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setIndeterminateTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mIndeterminateTint = tint;
+ mIndeterminateTintMode = tintMode;
+ mHasIndeterminateTint = true;
+
+ applyIndeterminateTint();
+ }
+
+ /**
+ * Applies a tint to the indeterminate drawable. Does not modify the
+ * current tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * Subsequent calls to {@link #setIndeterminateDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and
+ * tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTint
+ * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setIndeterminateTint(@Nullable ColorStateList tint) {
+ setIndeterminateTint(tint, mIndeterminateTintMode);
+ }
+
+ /**
+ * @return the tint applied to the indeterminate drawable
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTint
+ * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getIndeterminateTint() {
+ return mIndeterminateTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setIndeterminateTint(ColorStateList)} to the indeterminate
+ * drawable. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
+ * @see #setIndeterminateTint(ColorStateList)
+ */
+ public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setIndeterminateTint(mIndeterminateTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the indeterminate drawable
+ * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
+ * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getIndeterminateTintMode() {
+ return mIndeterminateTintMode;
+ }
+
+ private void applyIndeterminateTint() {
+ if (mIndeterminateDrawable != null && mHasIndeterminateTint) {
+ mIndeterminateDrawable = mIndeterminateDrawable.mutate();
+ mIndeterminateDrawable.setTint(mIndeterminateTint, mIndeterminateTintMode);
}
}
@@ -532,42 +697,340 @@
* @see #setIndeterminate(boolean)
*/
public void setProgressDrawable(Drawable d) {
- boolean needUpdate;
- if (mProgressDrawable != null && d != mProgressDrawable) {
- mProgressDrawable.setCallback(null);
- needUpdate = true;
- } else {
- needUpdate = false;
- }
+ if (mProgressDrawable != d) {
+ if (mProgressDrawable != null) {
+ mProgressDrawable.setCallback(null);
+ unscheduleDrawable(mProgressDrawable);
+ }
- if (d != null) {
- d.setCallback(this);
- if (canResolveLayoutDirection()) {
+ mProgressDrawable = d;
+
+ if (d != null) {
+ d.setCallback(this);
d.setLayoutDirection(getLayoutDirection());
+ if (d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+
+ // Make sure the ProgressBar is always tall enough
+ int drawableHeight = d.getMinimumHeight();
+ if (mMaxHeight < drawableHeight) {
+ mMaxHeight = drawableHeight;
+ requestLayout();
+ }
+
+ if (mHasProgressTint) {
+ applyProgressLayerTint(R.id.progress, mProgressTint, mProgressTintMode, true);
+ }
+
+ if (mHasProgressBackgroundTint) {
+ applyProgressLayerTint(R.id.background, mProgressBackgroundTint,
+ mProgressBackgroundTintMode, false);
+ }
+
+ if (mHasSecondaryProgressTint) {
+ applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint,
+ mSecondaryProgressTintMode, false);
+ }
}
- // Make sure the ProgressBar is always tall enough
- int drawableHeight = d.getMinimumHeight();
- if (mMaxHeight < drawableHeight) {
- mMaxHeight = drawableHeight;
- requestLayout();
+ if (!mIndeterminate) {
+ mCurrentDrawable = d;
+ postInvalidate();
}
- }
- mProgressDrawable = d;
- if (!mIndeterminate) {
- mCurrentDrawable = d;
- postInvalidate();
- }
- if (needUpdate) {
updateDrawableBounds(getWidth(), getHeight());
updateDrawableState();
+
doRefreshProgress(R.id.progress, mProgress, false, false);
doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false);
}
}
/**
+ * Applies a tint to the progress indicator, if one exists, or to the
+ * entire progress drawable otherwise.
+ * <p>
+ * The progress indicator should be specified as a layer with
+ * id {@link android.R.id#progress} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and
+ * tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_progressTint
+ * @attr ref android.R.styleable#ProgressBar_progressTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setProgressTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mProgressTint = tint;
+ mProgressTintMode = tintMode;
+ mHasProgressTint = true;
+
+ applyProgressLayerTint(R.id.progress, tint, tintMode, true);
+ }
+
+ /**
+ * Applies a tint to the progress indicator, if one exists, or to the
+ * entire progress drawable otherwise. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * The progress indicator should be specified as a layer with
+ * id {@link android.R.id#progress} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and
+ * tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_progressTint
+ * @see #setProgressTint(ColorStateList)
+ */
+ public void setProgressTint(@Nullable ColorStateList tint) {
+ setProgressTint(tint, mProgressTintMode);
+ }
+
+ /**
+ * @return the tint applied to the progress drawable
+ * @attr ref android.R.styleable#ProgressBar_progressTint
+ * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getProgressTint() {
+ return mProgressTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setProgressTint(ColorStateList)}} to the progress
+ * indicator. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ProgressBar_progressTintMode
+ * @see #setProgressTint(ColorStateList)
+ */
+ public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setProgressTint(mProgressTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the progress drawable
+ * @attr ref android.R.styleable#ProgressBar_progressTintMode
+ * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getProgressTintMode() {
+ return mProgressTintMode;
+ }
+
+ /**
+ * Applies a tint to the progress background, if one exists.
+ * <p>
+ * The progress background must be specified as a layer with
+ * id {@link android.R.id#background} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+ * drawable contains a progress background will automatically mutate the
+ * drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setProgressBackgroundTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mProgressBackgroundTint = tint;
+ mProgressBackgroundTintMode = tintMode;
+ mHasProgressBackgroundTint = true;
+
+ applyProgressLayerTint(R.id.background, tint, tintMode, false);
+ }
+
+ /**
+ * Applies a tint to the progress background, if one exists. Does not
+ * modify the current tint mode, which is
+ * {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * The progress background must be specified as a layer with
+ * id {@link android.R.id#background} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+ * drawable contains a progress background will automatically mutate the
+ * drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
+ * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setProgressBackgroundTint(@Nullable ColorStateList tint) {
+ setProgressBackgroundTint(tint, mProgressBackgroundTintMode);
+ }
+
+ /**
+ * @return the tint applied to the progress background
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
+ * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getProgressBackgroundTint() {
+ return mProgressBackgroundTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setProgressBackgroundTint(ColorStateList)}} to the progress
+ * background. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
+ * @see #setProgressBackgroundTint(ColorStateList)
+ */
+ public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setProgressBackgroundTint(mProgressBackgroundTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the progress
+ * background
+ * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
+ * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getProgressBackgroundTintMode() {
+ return mProgressBackgroundTintMode;
+ }
+
+ /**
+ * Applies a tint to the secondary progress indicator, if one exists.
+ * <p>
+ * The secondary progress indicator must be specified as a layer with
+ * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+ * drawable contains a secondary progress indicator will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
+ * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+ */
+ private void setSecondaryProgressTint(@Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode) {
+ mSecondaryProgressTint = tint;
+ mSecondaryProgressTintMode = tintMode;
+ mHasSecondaryProgressTint = true;
+
+ applyProgressLayerTint(R.id.secondaryProgress, tint, tintMode, false);
+ }
+
+ /**
+ * Applies a tint to the secondary progress indicator, if one exists.
+ * Does not modify the current tint mode, which is
+ * {@link PorterDuff.Mode#SRC_ATOP} by default.
+ * <p>
+ * The secondary progress indicator must be specified as a layer with
+ * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable}
+ * used as the progress drawable.
+ * <p>
+ * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+ * drawable contains a secondary progress indicator will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
+ * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ public void setSecondaryProgressTint(@Nullable ColorStateList tint) {
+ setSecondaryProgressTint(tint, mSecondaryProgressTintMode);
+ }
+
+ /**
+ * @return the tint applied to the secondary progress drawable
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
+ * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public ColorStateList getSecondaryProgressTint() {
+ return mSecondaryProgressTint;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setSecondaryProgressTint(ColorStateList)}} to the secondary
+ * progress indicator. The default mode is
+ * {@link PorterDuff.Mode#SRC_ATOP}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
+ * @see #setSecondaryProgressTint(ColorStateList)
+ */
+ public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
+ setSecondaryProgressTint(mSecondaryProgressTint, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the secondary
+ * progress drawable
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
+ * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getSecondaryProgressTintMode() {
+ return mSecondaryProgressTintMode;
+ }
+
+ private void applyProgressLayerTint(int layerId, @Nullable ColorStateList tint,
+ @Nullable PorterDuff.Mode tintMode, boolean shouldFallback) {
+ final Drawable d = mProgressDrawable;
+ if (d != null) {
+ mProgressDrawable = d.mutate();
+
+ Drawable layer = null;
+ if (d instanceof LayerDrawable) {
+ layer = ((LayerDrawable) d).findDrawableByLayerId(layerId);
+ }
+
+ if (shouldFallback && layer == null) {
+ layer = d;
+ }
+
+ if (layer != null) {
+ layer.setTint(tint, tintMode);
+ }
+ }
+ }
+
+ /**
* Define the tileable drawable used to draw the progress bar in
* progress mode.
* <p>
@@ -670,6 +1133,22 @@
}
}
+ private void setDrawableTint(int id, ColorStateList tint, Mode tintMode, boolean fallback) {
+ Drawable layer = null;
+
+ // We expect a layer drawable, so try to find the target ID.
+ final Drawable d = mCurrentDrawable;
+ if (d instanceof LayerDrawable) {
+ layer = ((LayerDrawable) d).findDrawableByLayerId(id);
+ }
+
+ if (fallback && layer == null) {
+ layer = d;
+ }
+
+ layer.mutate().setTint(tint, tintMode);
+ }
+
private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
boolean callBackToApp) {
float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 9601d4a..9914800 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -25,6 +25,7 @@
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -427,9 +428,15 @@
* {@link Adapter#getItemViewType(int) getItemViewType(int)} on the object
* returned from {@link #getAdapter()} will always return 0. Calling
* {@link Adapter#getViewTypeCount() getViewTypeCount()} will always return
- * 1.
+ * 1. On API {@link Build.VERSION_CODES#L} and above, attempting to set an
+ * adapter with more than one view type will throw an
+ * {@link IllegalArgumentException}.
+ *
+ * @param adapter the adapter to set
*
* @see AbsSpinner#setAdapter(SpinnerAdapter)
+ * @throws IllegalArgumentException if the adapter has more than one view
+ * type
*/
@Override
public void setAdapter(SpinnerAdapter adapter) {
@@ -437,6 +444,12 @@
mRecycler.clear();
+ final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+ if (targetSdkVersion >= Build.VERSION_CODES.L
+ && adapter != null && adapter.getViewTypeCount() != 1) {
+ throw new IllegalArgumentException("Spinner adapter view type count must be 1");
+ }
+
if (mPopup != null) {
mPopup.setAdapter(new DropDownAdapter(adapter));
} else {
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 960fa49..643225d 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -16,6 +16,7 @@
package com.android.internal.backup;
+import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -168,12 +169,25 @@
int startRestore(long token, in PackageInfo[] packages);
/**
- * Get the package name of the next application with data in the backup store.
- * @return The name of one of the packages supplied to {@link #startRestore},
- * or "" (the empty string) if no more backup data is available,
- * or null if an error occurred (the restore should be aborted and rescheduled).
+ * Get the package name of the next application with data in the backup store, plus
+ * a description of the structure of the restored archive: either TYPE_KEY_VALUE for
+ * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream.
+ *
+ * <p>If the package name in the returned RestoreDescription object is the singleton
+ * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available
+ * in the current restore session: all packages described in startRestore() have been
+ * processed.
+ *
+ * <p>If this method returns {@code null}, it means that a transport-level error has
+ * occurred and the entire restore operation should be abandoned.
+ *
+ * @return A RestoreDescription object containing the name of one of the packages
+ * supplied to {@link #startRestore} plus an indicator of the data type of that
+ * restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
+ * no more packages can be restored in this session; or {@code null} to indicate
+ * a transport-level error.
*/
- String nextRestorePackage();
+ RestoreDescription nextRestorePackage();
/**
* Get the data for the application returned by {@link #nextRestorePackage}.
@@ -188,7 +202,58 @@
*/
void finishRestore();
+ // full backup stuff
+
long requestFullBackupTime();
int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket);
int sendBackupData(int numBytes);
+
+ // full restore stuff
+
+ /**
+ * Ask the transport to provide data for the "current" package being restored. This
+ * is the package that was just reported by {@link #nextRestorePackage()} as having
+ * {@link RestoreDescription#TYPE_FULL_STREAM} data.
+ *
+ * The transport writes some data to the socket supplied to this call, and returns
+ * the number of bytes written. The system will then read that many bytes and
+ * stream them to the application's agent for restore, then will call this method again
+ * to receive the next chunk of the archive. This sequence will be repeated until the
+ * transport returns zero indicating that all of the package's data has been delivered
+ * (or returns a negative value indicating some sort of hard error condition at the
+ * transport level).
+ *
+ * <p>After this method returns zero, the system will then call
+ * {@link #getNextFullRestorePackage()} to begin the restore process for the next
+ * application, and the sequence begins again.
+ *
+ * <p>The transport should always close this socket when returning from this method.
+ * Do not cache this socket across multiple calls or you may leak file descriptors.
+ *
+ * @param socket The file descriptor that the transport will use for delivering the
+ * streamed archive. The transport must close this socket in all cases when returning
+ * from this method.
+ * @return 0 when no more data for the current package is available. A positive value
+ * indicates the presence of that many bytes to be delivered to the app. Any negative
+ * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
+ * indicating a fatal error condition that precludes further restore operations
+ * on the current dataset.
+ */
+ int getNextFullRestoreDataChunk(in ParcelFileDescriptor socket);
+
+ /**
+ * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
+ * data for restore, it will invoke this method to tell the transport that it should
+ * abandon the data download for the current package. The OS will then either call
+ * {@link #nextRestorePackage()} again to move on to restoring the next package in the
+ * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
+ * operation.
+ *
+ * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
+ * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
+ * transport-level failure. If the transport reports an error here, the entire restore
+ * operation will immediately be finished with no further attempts to restore app data.
+ */
+ int abortFullRestore();
+
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index c9d621d..ff0ee65 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -19,6 +19,7 @@
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
+import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.content.ComponentName;
import android.content.Context;
@@ -63,18 +64,24 @@
private static final String TRANSPORT_DESTINATION_STRING
= "Backing up to debug-only private cache";
+ private static final String INCREMENTAL_DIR = "_delta";
+ private static final String FULL_DATA_DIR = "_full";
+
// The currently-active restore set always has the same (nonzero!) token
private static final long CURRENT_SET_TOKEN = 1;
private Context mContext;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
- private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, "_delta");
- private File mCurrentSetFullDir = new File(mCurrentSetDir, "_full");
+ private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR);
+ private File mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR);
private PackageInfo[] mRestorePackages = null;
private int mRestorePackage = -1; // Index into mRestorePackages
- private File mRestoreDataDir;
+ private int mRestoreType;
+ private File mRestoreSetDir;
+ private File mRestoreSetIncrementalDir;
+ private File mRestoreSetFullDir;
private long mRestoreToken;
// Additional bookkeeping for full backup
@@ -361,30 +368,55 @@
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 @@
// 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 017801b..bc1f002 100644
--- a/core/java/com/android/internal/policy/IFaceLockInterface.aidl
+++ b/core/java/com/android/internal/policy/IFaceLockInterface.aidl
@@ -23,6 +23,7 @@
void startUi(IBinder containingWindowToken, int x, int y, int width, int height,
boolean useLiveliness);
void stopUi();
+ void startWithoutUi();
void registerCallback(IFaceLockCallback cb);
void unregisterCallback(IFaceLockCallback cb);
}
diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java
index 87a50a9..fda6479 100644
--- a/core/java/com/android/server/SystemServiceManager.java
+++ b/core/java/com/android/server/SystemServiceManager.java
@@ -50,8 +50,19 @@
* @return The service instance.
*/
@SuppressWarnings("unchecked")
- public SystemService startService(String className) throws ClassNotFoundException {
- return startService((Class<SystemService>) Class.forName(className));
+ public SystemService startService(String className) {
+ final Class<SystemService> serviceClass;
+ try {
+ serviceClass = (Class<SystemService>)Class.forName(className);
+ } catch (ClassNotFoundException ex) {
+ Slog.i(TAG, "Starting " + className);
+ throw new RuntimeException("Failed to create service " + className
+ + ": service class not found, usually indicates that the caller should "
+ + "have called PackageManager.hasSystemFeature() to check whether the "
+ + "feature is available on this device before trying to start the "
+ + "services that implement it", ex);
+ }
+ return startService(serviceClass);
}
/**
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 430dd63..3d9fb5c 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -18,6 +18,7 @@
import android.net.INetworkManagementEventObserver;
import android.net.LinkAddress;
+import android.net.RouteInfo;
/**
* Base {@link INetworkManagementEventObserver} that provides no-op
@@ -70,4 +71,14 @@
public void interfaceDnsServerInfo(String iface, long lifetime, String[] servers) {
// default no-op
}
+
+ @Override
+ public void routeUpdated(RouteInfo route) {
+ // default no-op
+ }
+
+ @Override
+ public void routeRemoved(RouteInfo route) {
+ // default no-op
+ }
}
diff --git a/core/java/com/android/server/net/NetlinkTracker.java b/core/java/com/android/server/net/NetlinkTracker.java
new file mode 100644
index 0000000..7dd8dd8
--- /dev/null
+++ b/core/java/com/android/server/net/NetlinkTracker.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.RouteInfo;
+import android.util.Log;
+
+/**
+ * Keeps track of link configuration received from Netlink.
+ *
+ * Instances of this class are expected to be owned by subsystems such as Wi-Fi
+ * or Ethernet that manage one or more network interfaces. Each interface to be
+ * tracked needs its own {@code NetlinkTracker}.
+ *
+ * An instance of this class is constructed by passing in an interface name and
+ * a callback. The owner is then responsible for registering the tracker with
+ * NetworkManagementService. When the class receives update notifications from
+ * the NetworkManagementService notification threads, it applies the update to
+ * its local LinkProperties, and if something has changed, notifies its owner of
+ * the update via the callback.
+ *
+ * The owner can then call {@code getLinkProperties()} in order to find out
+ * what changed. If in the meantime the LinkProperties stored here have changed,
+ * this class will return the current LinkProperties. Because each change
+ * triggers an update callback after the change is made, the owner may get more
+ * callbacks than strictly necessary (some of which may be no-ops), but will not
+ * be out of sync once all callbacks have been processed.
+ *
+ * Threading model:
+ *
+ * - The owner of this class is expected to create it, register it, and call
+ * getLinkProperties or clearLinkProperties on its thread.
+ * - Most of the methods in the class are inherited from BaseNetworkObserver
+ * and are called by NetworkManagementService notification threads.
+ * - All accesses to mLinkProperties must be synchronized(this). All the other
+ * member variables are immutable once the object is constructed.
+ *
+ * This class currently tracks IPv4 and IPv6 addresses. In the future it will
+ * track routes and DNS servers.
+ *
+ * @hide
+ */
+public class NetlinkTracker extends BaseNetworkObserver {
+
+ private final String TAG;
+
+ public interface Callback {
+ public void update();
+ }
+
+ private final String mInterfaceName;
+ private final Callback mCallback;
+ private final LinkProperties mLinkProperties;
+
+ private static final boolean DBG = true;
+
+ public NetlinkTracker(String iface, Callback callback) {
+ TAG = "NetlinkTracker/" + iface;
+ mInterfaceName = iface;
+ mCallback = callback;
+ mLinkProperties = new LinkProperties();
+ mLinkProperties.setInterfaceName(mInterfaceName);
+ }
+
+ private void maybeLog(String operation, String iface, LinkAddress address) {
+ if (DBG) {
+ Log.d(TAG, operation + ": " + address + " on " + iface +
+ " flags " + address.getFlags() + " scope " + address.getScope());
+ }
+ }
+
+ private void maybeLog(String operation, Object o) {
+ if (DBG) {
+ Log.d(TAG, operation + ": " + o.toString());
+ }
+ }
+
+ @Override
+ public void addressUpdated(String iface, LinkAddress address) {
+ if (mInterfaceName.equals(iface)) {
+ maybeLog("addressUpdated", iface, address);
+ boolean changed;
+ synchronized (this) {
+ changed = mLinkProperties.addLinkAddress(address);
+ }
+ if (changed) {
+ mCallback.update();
+ }
+ }
+ }
+
+ @Override
+ public void addressRemoved(String iface, LinkAddress address) {
+ if (mInterfaceName.equals(iface)) {
+ maybeLog("addressRemoved", iface, address);
+ boolean changed;
+ synchronized (this) {
+ changed = mLinkProperties.removeLinkAddress(address);
+ }
+ if (changed) {
+ mCallback.update();
+ }
+ }
+ }
+
+ @Override
+ public void routeUpdated(RouteInfo route) {
+ if (mInterfaceName.equals(route.getInterface())) {
+ maybeLog("routeUpdated", route);
+ boolean changed;
+ synchronized (this) {
+ changed = mLinkProperties.addRoute(route);
+ }
+ if (changed) {
+ mCallback.update();
+ }
+ }
+ }
+
+ @Override
+ public void routeRemoved(RouteInfo route) {
+ if (mInterfaceName.equals(route.getInterface())) {
+ maybeLog("routeRemoved", route);
+ boolean changed;
+ synchronized (this) {
+ changed = mLinkProperties.removeRoute(route);
+ }
+ if (changed) {
+ mCallback.update();
+ }
+ }
+ }
+
+ /**
+ * Returns a copy of this object's LinkProperties.
+ */
+ public synchronized LinkProperties getLinkProperties() {
+ return new LinkProperties(mLinkProperties);
+ }
+
+ public synchronized void clearLinkProperties() {
+ mLinkProperties.clear();
+ mLinkProperties.setInterfaceName(mInterfaceName);
+ }
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f2b9bac..a7a1faad 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -159,6 +159,7 @@
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_AndroidBidi(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
+extern int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
extern int register_android_server_Watchdog(JNIEnv* env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
@@ -1328,6 +1329,7 @@
REG_JNI(register_android_media_ToneGenerator),
REG_JNI(register_android_opengl_classes),
+ REG_JNI(register_android_server_fingerprint_FingerprintService),
REG_JNI(register_android_server_NetworkManagementSocketTagger),
REG_JNI(register_android_server_Watchdog),
REG_JNI(register_android_ddm_DdmHandleNativeHeap),
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index a5964c8..4584c46 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -569,12 +569,11 @@
canvas->drawRectCoords(left, top, right, bottom, *paint);
}
- static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jobject joval,
- jlong paintHandle) {
+ static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jlong paintHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect oval;
- GraphicsJNI::jrectf_to_rect(env, joval, &oval);
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
canvas->drawOval(oval, *paint);
}
@@ -585,13 +584,12 @@
canvas->drawCircle(cx, cy, radius, *paint);
}
- static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jobject joval,
- jfloat startAngle, jfloat sweepAngle,
- jboolean useCenter, jlong paintHandle) {
+ static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, jboolean useCenter,
+ jlong paintHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect oval;
- GraphicsJNI::jrectf_to_rect(env, joval, &oval);
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
canvas->drawArc(oval, startAngle, sweepAngle, useCenter, *paint);
}
@@ -1241,11 +1239,9 @@
{"native_drawLines", "(J[FIIJ)V", (void*) SkCanvasGlue::drawLines},
{"native_drawLine","(JFFFFJ)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
{"native_drawRect","(JFFFFJ)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
- {"native_drawOval","(JLandroid/graphics/RectF;J)V",
- (void*) SkCanvasGlue::drawOval},
+ {"native_drawOval","(JFFFFJ)V", (void*) SkCanvasGlue::drawOval},
{"native_drawCircle","(JFFFJ)V", (void*) SkCanvasGlue::drawCircle},
- {"native_drawArc","(JLandroid/graphics/RectF;FFZJ)V",
- (void*) SkCanvasGlue::drawArc},
+ {"native_drawArc","(JFFFFFFZJ)V", (void*) SkCanvasGlue::drawArc},
{"native_drawRoundRect","(JFFFFFFJ)V",
(void*) SkCanvasGlue::drawRoundRect},
{"native_drawPath","(JJJ)V", (void*) SkCanvasGlue::drawPath},
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 1e40d94..d07b154 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -936,32 +936,62 @@
return paint->getLooper() && paint->getLooper()->asABlurShadow(NULL);
}
- static int breakText(JNIEnv* env, SkPaint& paint, const jchar text[],
+ static int breakText(JNIEnv* env, const SkPaint& paint, TypefaceImpl* typeface, const jchar text[],
int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
- SkPaint::TextBufferDirection tbd) {
+ SkPaint::TextBufferDirection textBufferDirection) {
+ size_t measuredCount = 0;
+ float measured = 0;
+
+#ifdef USE_MINIKIN
+ Layout layout;
+ std::string css = MinikinUtils::setLayoutProperties(&layout, &paint, bidiFlags, typeface);
+ layout.doLayout(text, 0, count, count, css);
+ float* advances = new float[count];
+ layout.getAdvances(advances);
+ const bool forwardScan = (textBufferDirection == SkPaint::kForward_TextBufferDirection);
+ for (int i = 0; i < count; i++) {
+ // traverse in the given direction
+ int index = forwardScan ? i : (count - i - 1);
+ float width = advances[index];
+ if (measured + width > maxWidth) {
+ break;
+ }
+ // properly handle clusters when scanning backwards
+ if (forwardScan || width != 0.0f) {
+ measuredCount = i + 1;
+ }
+ measured += width;
+ }
+ delete[] advances;
+#else
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
text, 0, count, count, bidiFlags);
if (value == NULL) {
return 0;
}
- SkScalar measured;
- size_t bytes = paint.breakText(value->getGlyphs(), value->getGlyphsCount() << 1,
- maxWidth, &measured, tbd);
+ SkScalar m;
+ size_t bytes = paint.breakText(value->getGlyphs(), value->getGlyphsCount() << 1,
+ maxWidth, &m, textBufferDirection);
SkASSERT((bytes & 1) == 0);
+ measuredCount = bytes >> 1;
+ measured = SkScalarToFloat(m);
+#endif
if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
jfloat* array = autoMeasured.ptr();
- array[0] = SkScalarToFloat(measured);
+ array[0] = measured;
}
- return bytes >> 1;
+ return measuredCount;
}
- static jint breakTextC(JNIEnv* env, jobject jpaint, jcharArray jtext,
+ static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray jtext,
jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
- NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, jtext);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
SkPaint::TextBufferDirection tbd;
if (count < 0) {
tbd = SkPaint::kBackward_TextBufferDirection;
@@ -976,28 +1006,28 @@
return 0;
}
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
const jchar* text = env->GetCharArrayElements(jtext, NULL);
- count = breakText(env, *paint, text + index, count, maxWidth,
+ count = breakText(env, *paint, typeface, text + index, count, maxWidth,
bidiFlags, jmeasuredWidth, tbd);
env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
JNI_ABORT);
return count;
}
- static jint breakTextS(JNIEnv* env, jobject jpaint, jstring jtext,
+ static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring jtext,
jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
- NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, jtext);
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
SkPaint::TextBufferDirection tbd = forwards ?
SkPaint::kForward_TextBufferDirection :
SkPaint::kBackward_TextBufferDirection;
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
int count = env->GetStringLength(jtext);
const jchar* text = env->GetStringChars(jtext, NULL);
- count = breakText(env, *paint, text, count, maxWidth, bidiFlags, jmeasuredWidth, tbd);
+ count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, tbd);
env->ReleaseStringChars(jtext, text);
return count;
}
@@ -1108,8 +1138,8 @@
{"native_measureText","([CIII)F", (void*) SkPaintGlue::measureText_CIII},
{"native_measureText","(Ljava/lang/String;I)F", (void*) SkPaintGlue::measureText_StringI},
{"native_measureText","(Ljava/lang/String;III)F", (void*) SkPaintGlue::measureText_StringIII},
- {"native_breakText","([CIIFI[F)I", (void*) SkPaintGlue::breakTextC},
- {"native_breakText","(Ljava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS},
+ {"native_breakText","(JJ[CIIFI[F)I", (void*) SkPaintGlue::breakTextC},
+ {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS},
{"native_getTextWidths","(JJ[CIII[F)I", (void*) SkPaintGlue::getTextWidths___CIII_F},
{"native_getTextWidths","(JJLjava/lang/String;III[F)I", (void*) SkPaintGlue::getTextWidths__StringIII_F},
{"native_getTextRunAdvances","(JJ[CIIIIZ[FI)F",
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index 420a17f..6ef1d2c 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -2,22 +2,22 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
// This file was generated from the C++ include file: SkPath.h
// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
+// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
// or one of the auxilary file specifications in device/tools/gluemaker.
#include "jni.h"
@@ -92,7 +92,7 @@
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
return obj->isEmpty();
}
-
+
static jboolean isRect(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect) {
SkRect rect;
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
@@ -100,65 +100,66 @@
GraphicsJNI::rect_to_jrectf(rect, env, jrect);
return result;
}
-
+
static void computeBounds(JNIEnv* env, jobject clazz, jlong objHandle, jobject jbounds) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
const SkRect& bounds = obj->getBounds();
GraphicsJNI::rect_to_jrectf(bounds, env, jbounds);
}
-
+
static void incReserve(JNIEnv* env, jobject clazz, jlong objHandle, jint extraPtCount) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->incReserve(extraPtCount);
}
-
+
static void moveTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->moveTo(x, y);
}
-
+
static void rMoveTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->rMoveTo(dx, dy);
}
-
+
static void lineTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->lineTo(x, y);
}
-
+
static void rLineTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->rLineTo(dx, dy);
}
-
+
static void quadTo__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->quadTo(x1, y1, x2, y2);
}
-
+
static void rQuadTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->rQuadTo(dx1, dy1, dx2, dy2);
}
-
+
static void cubicTo__FFFFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->cubicTo(x1, y1, x2, y2, x3, y3);
}
-
+
static void rCubicTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->rCubicTo(x1, y1, x2, y2, x3, y3);
}
-
- static void arcTo(JNIEnv* env, jobject clazz, jlong objHandle, jobject oval, jfloat startAngle, jfloat sweepAngle, jboolean forceMoveTo) {
+
+ static void arcTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
+ jboolean forceMoveTo) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
- SkRect oval_;
- GraphicsJNI::jrectf_to_rect(env, oval, &oval_);
- obj->arcTo(oval_, startAngle, sweepAngle, forceMoveTo);
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+ obj->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
}
-
+
static void close(JNIEnv* env, jobject clazz, jlong objHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->close();
@@ -185,28 +186,26 @@
obj->addCircle(x, y, radius, dir);
}
- static void addArc(JNIEnv* env, jobject clazz, jlong objHandle, jobject oval, jfloat startAngle, jfloat sweepAngle) {
- SkRect oval_;
+ static void addArc(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle) {
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
- GraphicsJNI::jrectf_to_rect(env, oval, &oval_);
- obj->addArc(oval_, startAngle, sweepAngle);
+ obj->addArc(oval, startAngle, sweepAngle);
}
- static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect,
- jfloat rx, jfloat ry, jint dirHandle) {
- SkRect rect;
+ static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloat rx, jfloat ry, jint dirHandle) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
- GraphicsJNI::jrectf_to_rect(env, jrect, &rect);
obj->addRoundRect(rect, rx, ry, dir);
}
-
- static void addRoundRect8(JNIEnv* env, jobject, jlong objHandle, jobject jrect,
- jfloatArray array, jint dirHandle) {
- SkRect rect;
+
+ static void addRoundRect8(JNIEnv* env, jobject, jlong objHandle, jfloat left, jfloat top,
+ jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
- GraphicsJNI::jrectf_to_rect(env, jrect, &rect);
AutoJavaFloatArray afa(env, array, 8);
#ifdef SK_SCALAR_IS_FLOAT
const float* src = afa.ptr();
@@ -215,32 +214,32 @@
#endif
obj->addRoundRect(rect, src, dir);
}
-
+
static void addPath__PathFF(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jfloat dx, jfloat dy) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
obj->addPath(*src, dx, dy);
}
-
+
static void addPath__Path(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
obj->addPath(*src);
}
-
+
static void addPath__PathMatrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jlong matrixHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
obj->addPath(*src, *matrix);
}
-
+
static void offset__FFPath(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy, jlong dstHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
obj->offset(dx, dy, dst);
}
-
+
static void offset__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->offset(dx, dy);
@@ -250,14 +249,14 @@
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->setLastPt(dx, dy);
}
-
+
static void transform__MatrixPath(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle, jlong dstHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
obj->transform(*matrix, dst);
}
-
+
static void transform__Matrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
@@ -487,14 +486,14 @@
{"native_rQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo},
{"native_cubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF},
{"native_rCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
- {"native_arcTo","(JLandroid/graphics/RectF;FFZ)V", (void*) SkPathGlue::arcTo},
+ {"native_arcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo},
{"native_close","(J)V", (void*) SkPathGlue::close},
{"native_addRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
{"native_addOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
{"native_addCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
- {"native_addArc","(JLandroid/graphics/RectF;FF)V", (void*) SkPathGlue::addArc},
- {"native_addRoundRect","(JLandroid/graphics/RectF;FFI)V", (void*) SkPathGlue::addRoundRectXY},
- {"native_addRoundRect","(JLandroid/graphics/RectF;[FI)V", (void*) SkPathGlue::addRoundRect8},
+ {"native_addArc","(JFFFFFF)V", (void*) SkPathGlue::addArc},
+ {"native_addRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY},
+ {"native_addRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8},
{"native_addPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF},
{"native_addPath","(JJ)V", (void*) SkPathGlue::addPath__Path},
{"native_addPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix},
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 86207f0..87ee618 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -174,21 +174,21 @@
ssize_t pss = memtrack_proc_graphics_pss(p);
if (pss < 0) {
- ALOGW("failed to get graphics pss: %d", pss);
+ ALOGW("failed to get graphics pss: %zd", pss);
return pss;
}
graphics_mem->graphics = pss / 1024;
pss = memtrack_proc_gl_pss(p);
if (pss < 0) {
- ALOGW("failed to get gl pss: %d", pss);
+ ALOGW("failed to get gl pss: %zd", pss);
return pss;
}
graphics_mem->gl = pss / 1024;
pss = memtrack_proc_other_pss(p);
if (pss < 0) {
- ALOGW("failed to get other pss: %d", pss);
+ ALOGW("failed to get other pss: %zd", pss);
return pss;
}
graphics_mem->other = pss / 1024;
@@ -231,9 +231,9 @@
unsigned referenced = 0;
unsigned temp;
- unsigned long int start;
- unsigned long int end = 0;
- unsigned long int prevEnd = 0;
+ uint64_t start;
+ uint64_t end = 0;
+ uint64_t prevEnd = 0;
char* name;
int name_pos;
@@ -255,7 +255,7 @@
if (len < 1) return;
line[--len] = 0;
- if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
+ if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
skip = true;
} else {
while (isspace(line[name_pos])) {
@@ -371,7 +371,7 @@
referenced = temp;
} else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
swapped_out = temp;
- } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') {
+ } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
// looks like a new mapping
// example: "10000000-10001000 ---p 10000000 00:00 0"
break;
diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp
index f8a1fd9..ad36843c 100644
--- a/core/jni/android_server_FingerprintManager.cpp
+++ b/core/jni/android_server_FingerprintManager.cpp
@@ -20,30 +20,10 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
#include <utils/Log.h>
-namespace android {
-
-static struct {
- jclass clazz;
- jmethodID notify;
-} gFingerprintManagerClassInfo;
-
-static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
- return -1; // TODO
-}
-
-static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) {
- return -1; // TODO
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod g_methods[] = {
- { "nativeEnroll", "(I)I", (void*)nativeEnroll },
- { "nativeRemove", "(I)I", (void*)nativeRemove },
-};
-
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className); \
@@ -61,13 +41,174 @@
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
-int register_android_server_FingerprintManager(JNIEnv* env) {
- FIND_CLASS(gFingerprintManagerClassInfo.clazz,
- "android/service/fingerprint/FingerprintManager");
- GET_METHOD_ID(gFingerprintManagerClassInfo.notify, gFingerprintManagerClassInfo.clazz,
- "notify", "(III)V");
- return AndroidRuntime::registerNativeMethods(
- env, "com/android/service/fingerprint/FingerprintManager", g_methods, NELEM(g_methods));
+namespace android {
+
+static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(1, 0);
+
+static const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService";
+static struct {
+ jclass clazz;
+ jmethodID notify;
+ jobject callbackObject;
+} gFingerprintServiceClassInfo;
+
+static struct {
+ fingerprint_module_t const* module;
+ fingerprint_device_t *device;
+} gContext;
+
+// TODO: remove after driver update to use new HAL
+fingerprint_msg_type_t hackTilFpDriverUpdate(fingerprint_msg_type_t t) {
+ switch(static_cast<int>(t)) {
+ case 1: return FINGERPRINT_PROCESSED;
+ case 2: return FINGERPRINT_TEMPLATE_ENROLLING;
+ default: return t;
+ }
+}
+
+// Called by the HAL to notify us of fingerprint events
+static void hal_notify_callback(fingerprint_msg_t msg) {
+ uint32_t arg1 = 0;
+ uint32_t arg2 = 0;
+ uint32_t arg3 = 0; // TODO
+ msg.type = hackTilFpDriverUpdate(msg.type);
+ switch (msg.type) {
+ case FINGERPRINT_ERROR:
+ arg1 = msg.data.error;
+ break;
+ case FINGERPRINT_ACQUIRED:
+ arg1 = msg.data.acquired.acquired_info;
+ break;
+ case FINGERPRINT_PROCESSED:
+ arg1 = msg.data.processed.id;
+ break;
+ case FINGERPRINT_TEMPLATE_ENROLLING:
+ arg1 = msg.data.enroll.id;
+ arg2 = msg.data.enroll.samples_remaining;
+ arg3 = msg.data.enroll.data_collected_bmp;
+ break;
+ case FINGERPRINT_TEMPLATE_REMOVED:
+ arg1 = msg.data.removed.id;
+ break;
+ default:
+ ALOGE("fingerprint: invalid msg: %d", msg.type);
+ return;
+ }
+ //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2);
+
+ // TODO: fix gross hack to attach JNI to calling thread
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ int result = vm->AttachCurrentThread(&env, (void*) &args);
+ if (result != JNI_OK) {
+ ALOGE("Can't call JNI method: attach failed: %#x", result);
+ return;
+ }
+ }
+ env->CallVoidMethod(gFingerprintServiceClassInfo.callbackObject,
+ gFingerprintServiceClassInfo.notify, msg.type, arg1, arg2);
+}
+
+static void nativeInit(JNIEnv *env, jobject clazz, jobject callbackObj) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeInit()\n");
+ FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
+ GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz,
+ "notify", "(III)V");
+ gFingerprintServiceClassInfo.callbackObject = env->NewGlobalRef(callbackObj);
+}
+
+static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll()\n");
+ int ret = gContext.device->enroll(gContext.device, timeout);
+ return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeEnrollCancel(JNIEnv* env, jobject clazz) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnrollCancel()\n");
+ int ret = gContext.device->enroll_cancel(gContext.device);
+ return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeRemove(%d)\n", fingerprintId);
+ int ret = gContext.device->remove(gContext.device, fingerprintId);
+ return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeOpenHal(JNIEnv* env, jobject clazz) {
+ ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
+ int err;
+ const hw_module_t *hw_module = NULL;
+ if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
+ ALOGE("Can't open fingerprint HW Module, error: %d", err);
+ return 0;
+ }
+ if (NULL == hw_module) {
+ ALOGE("No valid fingerprint module");
+ return 0;
+ }
+
+ gContext.module = reinterpret_cast<const fingerprint_module_t*>(hw_module);
+
+ if (gContext.module->common.methods->open == NULL) {
+ ALOGE("No valid open method");
+ return 0;
+ }
+
+ hw_device_t *device = NULL;
+
+ if (0 != (err = gContext.module->common.methods->open(hw_module, NULL, &device))) {
+ ALOGE("Can't open fingerprint methods, error: %d", err);
+ return 0;
+ }
+
+ if (kVersion != device->version) {
+ ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
+ // return 0; // FIXME
+ }
+
+ gContext.device = reinterpret_cast<fingerprint_device_t*>(device);
+ err = gContext.device->set_notify(gContext.device, hal_notify_callback);
+ if (err < 0) {
+ ALOGE("Failed in call to set_notify(), err=%d", err);
+ return 0;
+ }
+
+ // Sanity check - remove
+ if (gContext.device->notify != hal_notify_callback) {
+ ALOGE("NOTIFY not set properly: %p != %p", gContext.device->notify, hal_notify_callback);
+ }
+
+ ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
+ return reinterpret_cast<jlong>(gContext.device);
+}
+
+static jint nativeCloseHal(JNIEnv* env, jobject clazz) {
+ return -ENOSYS; // TODO
+}
+
+// ----------------------------------------------------------------------------
+
+// TODO: clean up void methods
+static const JNINativeMethod g_methods[] = {
+ { "nativeEnroll", "(I)I", (void*)nativeEnroll },
+ { "nativeEnrollCancel", "()I", (void*)nativeEnroll },
+ { "nativeRemove", "(I)I", (void*)nativeRemove },
+ { "nativeOpenHal", "()I", (void*)nativeOpenHal },
+ { "nativeCloseHal", "()I", (void*)nativeCloseHal },
+ { "nativeInit", "(Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit }
+};
+
+int register_android_server_fingerprint_FingerprintService(JNIEnv* env) {
+ FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
+ GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz, "notify",
+ "(III)V");
+ int result = AndroidRuntime::registerNativeMethods(
+ env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods));
+ ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n");
+ return result;
}
} // namespace android
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index c0d5221..cfc8eb8 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -61,6 +61,8 @@
jfieldID xDpi;
jfieldID yDpi;
jfieldID secure;
+ jfieldID appVsyncOffsetNanos;
+ jfieldID presentationDeadlineNanos;
} gPhysicalDisplayInfoClassInfo;
static struct {
@@ -392,6 +394,10 @@
env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi);
env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi);
env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure);
+ env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos,
+ info.appVsyncOffset);
+ env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos,
+ info.presentationDeadline);
env->SetObjectArrayElement(configArray, static_cast<jsize>(c), infoObj);
env->DeleteLocalRef(infoObj);
}
@@ -648,6 +654,10 @@
gPhysicalDisplayInfoClassInfo.xDpi = env->GetFieldID(clazz, "xDpi", "F");
gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F");
gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z");
+ gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos = env->GetFieldID(clazz,
+ "appVsyncOffsetNanos", "J");
+ gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos = env->GetFieldID(clazz,
+ "presentationDeadlineNanos", "J");
jclass rectClazz = env->FindClass("android/graphics/Rect");
gRectClassInfo.bottom = env->GetFieldID(rectClazz, "bottom", "I");
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index edca101..8b5dff0 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/ic_corp_badge.png b/core/res/res/drawable-hdpi/ic_corp_badge.png
new file mode 100644
index 0000000..c79ce92
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_corp_badge.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_corp_icon_badge.png b/core/res/res/drawable-hdpi/ic_corp_icon_badge.png
new file mode 100644
index 0000000..0059e09
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_corp_icon_badge.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_corp_badge.png b/core/res/res/drawable-mdpi/ic_corp_badge.png
new file mode 100644
index 0000000..c1447fe
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_corp_badge.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_corp_icon_badge.png b/core/res/res/drawable-mdpi/ic_corp_icon_badge.png
new file mode 100644
index 0000000..5ff8c5d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_corp_icon_badge.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_corp_badge.png b/core/res/res/drawable-xhdpi/ic_corp_badge.png
new file mode 100644
index 0000000..2d3d748
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_corp_badge.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png
new file mode 100644
index 0000000..dc5716d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_corp_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_badge.png
new file mode 100644
index 0000000..430e63b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_corp_badge.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png
new file mode 100644
index 0000000..cc00dd8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png
Binary files differ
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 5325712..0000000
--- 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 7bfab4c..0000000
--- 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 0da9a81..68e3f1e 100644
--- a/core/res/res/drawable/view_accessibility_focused.xml
+++ b/core/res/res/drawable/view_accessibility_focused.xml
@@ -17,9 +17,11 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke
- android:width="2dp"
- android:color="@color/accessibility_focus_highlight" />
+ android:width="4dp"
+ android:color="@color/accessibility_focus_highlight"
+ android:dashWidth="4dp"
+ android:dashGap="2dp" />
- <corners android:radius="2dp"/>
+ <corners android:radius="2dp" />
</shape>
diff --git a/core/res/res/layout/preference_widget_seekbar.xml b/core/res/res/layout/preference_widget_seekbar.xml
index c427965..05daa1a 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 0000000..f70a472
--- /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 21fd37d..72b0909 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 abeadfa..da89136 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 aa47774..626858f 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 a8ceac6..ca71ce3 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 f6aaf73d..e1eed49 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 d18c107..cf06dc1 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 8695451..0aa2a36 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 bffbd1e..d557d37 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 59e9085..8205c60 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 c7d6149..a0c35c8 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 c7d6149..a0c35c8 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 8db1114..b6a7667 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 5ad1cd3..1dcbf62 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 7a4659b..60d036e 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 e9628ec..b4727bb 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 09f1f45..02c6da4 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 5982bca..b1292b3 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 89ef90c..f511df0 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 056f63f..470ec41 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 8dbe791..7dc8e19 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 cbe36be..2582737 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 17779b7..f0bee2b 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 2fb9f8a..9dca8a5 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 8a9ff58..0ea6f6e 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 8de9971..19fb05a 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 3fde38a..6ba99bf 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 01a7194..879d00f 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 62b2e8a..daf004c 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 88327ca..43d1b25 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 44f5e0d..e386a24 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 09fbdfb..be7e571 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 2cb5735..b421da4 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 b030060..803128f 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 9ad8f37..1d1a75b 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 1ae5c5e..31fa11d 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 e42d714..a655f36 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 64609df..c30ad74 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 059a85f..2e4e423 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 ec805f5..3df7ecb 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 ba25b52..ff26d8f 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 07c32fb..110b243 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 a7ca57b..12d1895 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 5631559..ab596c5 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 a51e909..cc06fd3 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 4b2aa24..099e6ef 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 f9cd57a..46bb563 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 9573e15..595c807 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 68d5442..feabf4f 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 5c926ba..13b629e 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 3583fb0..0c19b03 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 fdb40b5..0d56975b 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 022efcc..dd13b45 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 f2090a2..3a07366 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 3ebf416..8e290b5 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 85e47ac..6ab2b48 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 639d400..07238c3 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 5213896..8d8f69e 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 814d8fc..a8735cb 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.
+ <!-- Define how an activity persist across reboots. Activities defined as "never" will not
+ be persisted. Those defined as "always" will be persisted. Those defined as "taskOnly"
+ will persist the root activity of the task only. See below for more detail as to
+ what gets persisted. -->
+ <attr name="persistableMode">
+ <!-- The default. If this activity forms the root of a task then that task will be
+ persisted across reboots but only the launching intent will be used. All
+ activities above this activity in the task will not be persisted. In addition
+ this activity will not be passed a PersistableBundle into which it could have
+ stored its state. -->
+ <enum name="persistRootOnly" value="0" />
+ <!-- If this activity forms the root of a task then that task will not be persisted
+ across reboots -->
+ <enum name="doNotPersist" value="1" />
+ <!-- If this activity forms the root of a task then the task and this activity will
+ be persisted across reboots. If the activity above this activity is also
+ tagged with the attribute <code>"persist"</code> then it will be persisted as well.
+ And so on up the task stack until either an activity without the
+ <code>persistableMode="persistAcrossReboots"</code> attribute or one that was launched
+ with the flag Intent.FLAG_CLEAR_TASK_WHEN_RESET is encountered.
- <p>Activities that are declared with the persistable attribute will be provided with a
- forced-persistable Bundle in onCreate() and onSavedInstanceState(), and must only
- be passed a persistable Bundle in their Intent.extras. -->
- <attr name="persistable" format="boolean" />
+ <p>Activities that are declared with the persistAcrossReboots attribute will be
+ provided with a PersistableBundle in onSavedInstanceState(), These activities may
+ use this PeristableBundle to save their state. Then, following a reboot, that
+ PersistableBundle will be provided back to the activity in its onCreate() method. -->
+ <enum name="persistAcrossReboots" value="2" />
+ </attr>
<!-- This attribute specifies that an activity shall become the root activity of a
new task each time it is launched. Using this attribute permits the user to
@@ -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 9f6c7ad..f304514 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -143,7 +143,7 @@
<color name="keyguard_avatar_nick_color">#ffffffff</color>
<color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color>
- <color name="accessibility_focus_highlight">#80ffff00</color>
+ <color name="accessibility_focus_highlight">#bf39b500</color>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 202c127..c5ce229 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 6e1b5ef..e31dbaf 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 5bd6122..d6be3133 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -980,6 +980,10 @@
<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 @@
<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 84dbc79..84d38ce 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -254,6 +254,7 @@
<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 0c95149..25307b9 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -64,6 +64,10 @@
<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 d69f60a..5f4553b 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 cb5cb0c..5eec197 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -321,6 +321,7 @@
<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 @@
<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 @@
<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 27c8754..8e83e48 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -160,6 +160,7 @@
<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 @@
<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 769c8a1..77e4307 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -282,6 +282,7 @@
<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 @@
<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/net/IpPrefixTest.java b/core/tests/coretests/src/android/net/IpPrefixTest.java
new file mode 100644
index 0000000..cf278fb
--- /dev/null
+++ b/core/tests/coretests/src/android/net/IpPrefixTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.IpPrefix;
+import android.os.Parcel;
+import static android.test.MoreAsserts.assertNotEqual;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.junit.Assert.assertArrayEquals;
+import java.net.InetAddress;
+import java.util.Random;
+import junit.framework.TestCase;
+
+
+public class IpPrefixTest extends TestCase {
+
+ // Explicitly cast everything to byte because "error: possible loss of precision".
+ private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4};
+ private static final byte[] IPV6_BYTES = {
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
+ (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0
+ };
+
+ @SmallTest
+ public void testConstructor() {
+ IpPrefix p;
+ try {
+ p = new IpPrefix((byte[]) null, 9);
+ fail("Expected NullPointerException: null byte array");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix((InetAddress) null, 10);
+ fail("Expected NullPointerException: null InetAddress");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix((String) null);
+ fail("Expected NullPointerException: null String");
+ } catch(RuntimeException expected) {}
+
+
+ try {
+ byte[] b2 = {1, 2, 3, 4, 5};
+ p = new IpPrefix(b2, 29);
+ fail("Expected IllegalArgumentException: invalid array length");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1.2.3.4");
+ fail("Expected IllegalArgumentException: no prefix length");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1.2.3.4/");
+ fail("Expected IllegalArgumentException: empty prefix length");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("foo/32");
+ fail("Expected IllegalArgumentException: invalid address");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1/32");
+ fail("Expected IllegalArgumentException: deprecated IPv4 format");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("1.2.3.256/32");
+ fail("Expected IllegalArgumentException: invalid IPv4 address");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("foo/32");
+ fail("Expected IllegalArgumentException: non-address");
+ } catch(IllegalArgumentException expected) {}
+
+ try {
+ p = new IpPrefix("f00:::/32");
+ fail("Expected IllegalArgumentException: invalid IPv6 address");
+ } catch(IllegalArgumentException expected) {}
+ }
+
+ public void testTruncation() {
+ IpPrefix p;
+
+ p = new IpPrefix(IPV4_BYTES, 32);
+ assertEquals("192.0.2.4/32", p.toString());
+
+ p = new IpPrefix(IPV4_BYTES, 29);
+ assertEquals("192.0.2.0/29", p.toString());
+
+ p = new IpPrefix(IPV4_BYTES, 8);
+ assertEquals("192.0.0.0/8", p.toString());
+
+ p = new IpPrefix(IPV4_BYTES, 0);
+ assertEquals("0.0.0.0/0", p.toString());
+
+ try {
+ p = new IpPrefix(IPV4_BYTES, 33);
+ fail("Expected IllegalArgumentException: invalid prefix length");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix(IPV4_BYTES, 128);
+ fail("Expected IllegalArgumentException: invalid prefix length");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix(IPV4_BYTES, -1);
+ fail("Expected IllegalArgumentException: negative prefix length");
+ } catch(RuntimeException expected) {}
+
+ p = new IpPrefix(IPV6_BYTES, 128);
+ assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 122);
+ assertEquals("2001:db8:dead:beef:f00::80/122", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 64);
+ assertEquals("2001:db8:dead:beef::/64", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 3);
+ assertEquals("2000::/3", p.toString());
+
+ p = new IpPrefix(IPV6_BYTES, 0);
+ assertEquals("::/0", p.toString());
+
+ try {
+ p = new IpPrefix(IPV6_BYTES, -1);
+ fail("Expected IllegalArgumentException: negative prefix length");
+ } catch(RuntimeException expected) {}
+
+ try {
+ p = new IpPrefix(IPV6_BYTES, 129);
+ fail("Expected IllegalArgumentException: negative prefix length");
+ } catch(RuntimeException expected) {}
+
+ }
+
+ private void assertAreEqual(Object o1, Object o2) {
+ assertTrue(o1.equals(o2));
+ assertTrue(o2.equals(o1));
+ }
+
+ private void assertAreNotEqual(Object o1, Object o2) {
+ assertFalse(o1.equals(o2));
+ assertFalse(o2.equals(o1));
+ }
+
+ @SmallTest
+ public void testEquals() {
+ IpPrefix p1, p2;
+
+ p1 = new IpPrefix("192.0.2.251/23");
+ p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23);
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("192.0.2.5/23");
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("192.0.2.5/24");
+ assertAreNotEqual(p1, p2);
+
+ p1 = new IpPrefix("192.0.4.5/23");
+ assertAreNotEqual(p1, p2);
+
+
+ p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122");
+ p2 = new IpPrefix(IPV6_BYTES, 122);
+ assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString());
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122");
+ assertAreEqual(p1, p2);
+
+ p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123");
+ assertAreNotEqual(p1, p2);
+
+ p1 = new IpPrefix("2001:db8:dead:beef::/122");
+ assertAreNotEqual(p1, p2);
+
+ // 192.0.2.4/32 != c000:0204::/32.
+ byte[] ipv6bytes = new byte[16];
+ System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length);
+ p1 = new IpPrefix(ipv6bytes, 32);
+ assertAreEqual(p1, new IpPrefix("c000:0204::/32"));
+
+ p2 = new IpPrefix(IPV4_BYTES, 32);
+ assertAreNotEqual(p1, p2);
+ }
+
+ @SmallTest
+ public void testHashCode() {
+ IpPrefix p;
+ int oldCode = -1;
+ Random random = new Random();
+ for (int i = 0; i < 100; i++) {
+ if (random.nextBoolean()) {
+ // IPv4.
+ byte[] b = new byte[4];
+ random.nextBytes(b);
+ p = new IpPrefix(b, random.nextInt(33));
+ assertNotEqual(oldCode, p.hashCode());
+ oldCode = p.hashCode();
+ } else {
+ // IPv6.
+ byte[] b = new byte[16];
+ random.nextBytes(b);
+ p = new IpPrefix(b, random.nextInt(129));
+ assertNotEqual(oldCode, p.hashCode());
+ oldCode = p.hashCode();
+ }
+ }
+ }
+
+ @SmallTest
+ public void testMappedAddressesAreBroken() {
+ // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress,
+ // we are unable to comprehend that.
+ byte[] ipv6bytes = {
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff,
+ (byte) 192, (byte) 0, (byte) 2, (byte) 0};
+ IpPrefix p = new IpPrefix(ipv6bytes, 120);
+ assertEquals(16, p.getRawAddress().length); // Fine.
+ assertArrayEquals(ipv6bytes, p.getRawAddress()); // Fine.
+
+ // Broken.
+ assertEquals("192.0.2.0/120", p.toString());
+ assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress());
+ }
+
+ public IpPrefix passThroughParcel(IpPrefix p) {
+ Parcel parcel = Parcel.obtain();
+ IpPrefix p2 = null;
+ try {
+ p.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ p2 = IpPrefix.CREATOR.createFromParcel(parcel);
+ } finally {
+ parcel.recycle();
+ }
+ assertNotNull(p2);
+ return p2;
+ }
+
+ public void assertParcelingIsLossless(IpPrefix p) {
+ IpPrefix p2 = passThroughParcel(p);
+ assertEquals(p, p2);
+ }
+
+ public void testParceling() {
+ IpPrefix p;
+
+ p = new IpPrefix("2001:4860:db8::/64");
+ assertParcelingIsLossless(p);
+
+ p = new IpPrefix("192.0.2.0/25");
+ assertParcelingIsLossless(p);
+ }
+}
diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java
index c80d0bf..af6a32b 100644
--- a/core/tests/coretests/src/android/net/RouteInfoTest.java
+++ b/core/tests/coretests/src/android/net/RouteInfoTest.java
@@ -19,7 +19,7 @@
import java.lang.reflect.Method;
import java.net.InetAddress;
-import android.net.LinkAddress;
+import android.net.IpPrefix;
import android.net.RouteInfo;
import android.os.Parcel;
@@ -32,9 +32,8 @@
return InetAddress.parseNumericAddress(addr);
}
- private LinkAddress Prefix(String prefix) {
- String[] parts = prefix.split("/");
- return new LinkAddress(Address(parts[0]), Integer.parseInt(parts[1]));
+ private IpPrefix Prefix(String prefix) {
+ return new IpPrefix(prefix);
}
@SmallTest
@@ -43,17 +42,17 @@
// Invalid input.
try {
- r = new RouteInfo((LinkAddress) null, null, "rmnet0");
+ r = new RouteInfo((IpPrefix) null, null, "rmnet0");
fail("Expected RuntimeException: destination and gateway null");
} catch(RuntimeException e) {}
// Null destination is default route.
- r = new RouteInfo((LinkAddress) null, Address("2001:db8::1"), null);
+ r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null);
assertEquals(Prefix("::/0"), r.getDestination());
assertEquals(Address("2001:db8::1"), r.getGateway());
assertNull(r.getInterface());
- r = new RouteInfo((LinkAddress) null, Address("192.0.2.1"), "wlan0");
+ r = new RouteInfo((IpPrefix) null, Address("192.0.2.1"), "wlan0");
assertEquals(Prefix("0.0.0.0/0"), r.getDestination());
assertEquals(Address("192.0.2.1"), r.getGateway());
assertEquals("wlan0", r.getInterface());
@@ -74,7 +73,7 @@
class PatchedRouteInfo {
private final RouteInfo mRouteInfo;
- public PatchedRouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
+ public PatchedRouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
mRouteInfo = new RouteInfo(destination, gateway, iface);
}
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
index 58b7db9..42de9ea 100644
--- a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
@@ -36,8 +36,7 @@
}
}
- private void setLocale(String code) {
- Locale locale = new Locale(code);
+ private void setLocale(Locale locale) {
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
@@ -268,193 +267,193 @@
*/
public void testMatrix100000() throws Throwable {
final int resId = R.integer.matrix_100000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 100);
}
public void testMatrix100001() throws Throwable {
final int resId = R.integer.matrix_100001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
public void testMatrix100010() throws Throwable {
final int resId = R.integer.matrix_100010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 500);
}
public void testMatrix100011() throws Throwable {
final int resId = R.integer.matrix_100011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
public void testMatrix100100() throws Throwable {
final int resId = R.integer.matrix_100100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix100101() throws Throwable {
final int resId = R.integer.matrix_100101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix100110() throws Throwable {
final int resId = R.integer.matrix_100110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix100111() throws Throwable {
final int resId = R.integer.matrix_100111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix101000() throws Throwable {
final int resId = R.integer.matrix_101000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 300);
}
public void testMatrix101001() throws Throwable {
final int resId = R.integer.matrix_101001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
public void testMatrix101010() throws Throwable {
final int resId = R.integer.matrix_101010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 500);
}
public void testMatrix101011() throws Throwable {
final int resId = R.integer.matrix_101011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
public void testMatrix101100() throws Throwable {
final int resId = R.integer.matrix_101100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix101101() throws Throwable {
final int resId = R.integer.matrix_101101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix101110() throws Throwable {
final int resId = R.integer.matrix_101110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
public void testMatrix101111() throws Throwable {
final int resId = R.integer.matrix_101111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
public void testMatrix110000() throws Throwable {
final int resId = R.integer.matrix_110000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix110001() throws Throwable {
final int resId = R.integer.matrix_110001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix110010() throws Throwable {
final int resId = R.integer.matrix_110010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix110011() throws Throwable {
final int resId = R.integer.matrix_110011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix110100() throws Throwable {
final int resId = R.integer.matrix_110100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix110101() throws Throwable {
final int resId = R.integer.matrix_110101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
public void testMatrix110110() throws Throwable {
final int resId = R.integer.matrix_110110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix110111() throws Throwable {
final int resId = R.integer.matrix_110111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
public void testMatrix111000() throws Throwable {
final int resId = R.integer.matrix_111000;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix111001() throws Throwable {
final int resId = R.integer.matrix_111001;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix111010() throws Throwable {
final int resId = R.integer.matrix_111010;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
public void testMatrix111011() throws Throwable {
final int resId = R.integer.matrix_111011;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
public void testMatrix111100() throws Throwable {
final int resId = R.integer.matrix_111100;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix111101() throws Throwable {
final int resId = R.integer.matrix_111101;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
public void testMatrix111110() throws Throwable {
final int resId = R.integer.matrix_111110;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
public void testMatrix111111() throws Throwable {
final int resId = R.integer.matrix_111111;
- setLocale("sv_SE");
+ setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
}
diff --git a/docs/html/google/auth/api-client.jd b/docs/html/google/auth/api-client.jd
index 402a95f..5331d1e 100644
--- a/docs/html/google/auth/api-client.jd
+++ b/docs/html/google/auth/api-client.jd
@@ -1,4 +1,4 @@
-page.title=Accessing Google Play Services APIs
+page.title=Accessing Google APIs
page.tags="oauth 2.0","GoogleAuthUtil"
trainingnavtop=true
diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs
index 6ff00c0..b4028bd 100644
--- a/docs/html/google/google_toc.cs
+++ b/docs/html/google/google_toc.cs
@@ -3,11 +3,11 @@
######## ATTENTION ###############
######## ###############
#########################################################
-
+
IF YOU MAKE CHANGES TO THIS FILE, YOU MUST GENERATE THE
GMS REFERENCE DOCS, BECAUSE THEY ARE NOT INCLUDED IN THE
DOCS BUILD RULE.
-
+
#########################################################
#########################################################
?>
@@ -73,11 +73,11 @@
</div>
<ul>
<li><a href="<?cs var:toroot?>google/play-services/setup.html">
- <span class="en">Setup</span></a>
+ <span class="en">Setting Up Google Play Services</span></a>
</li>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot?>google/auth/api-client.html">
- <span class="en">Accessing Google Play Services APIs</span></a>
+ <span class="en">Accessing Google APIs</span></a>
</div>
<ul>
<li>
diff --git a/docs/html/google/play-services/games.jd b/docs/html/google/play-services/games.jd
index 94f6715..a73f688 100644
--- a/docs/html/google/play-services/games.jd
+++ b/docs/html/google/play-services/games.jd
@@ -1,4 +1,5 @@
page.title=Google Play Game Services
+page.tags="games"
header.hide=1
@jd:body
diff --git a/docs/html/google/play-services/setup.jd b/docs/html/google/play-services/setup.jd
index 744e191..4332953 100644
--- a/docs/html/google/play-services/setup.jd
+++ b/docs/html/google/play-services/setup.jd
@@ -1,4 +1,4 @@
-page.title=Set Up Google Play Services SDK
+page.title=Setting Up Google Play Services
@jd:body
@@ -8,8 +8,7 @@
<h2>In this document</h2>
<ol>
- <li><a href="#Install">Install the Google Play Services SDK</a></li>
- <li><a href="#Setup">Set Up a Project that Uses Google Play Services</a></li>
+ <li><a href="#Setup">Add Google Play Services to Your Project</a></li>
<li><a href="#Proguard">Create a Proguard Exception</a></li>
<li><a href="#ensure">Ensure Devices Have the Google Play services APK</a></li>
</ol>
@@ -18,12 +17,17 @@
</div>
</div>
+<script>
+$(document).ready(function() {
+ setupIdeDocToggle();
+});
+</script>
-
+
<p>To develop an app using the <a href="{@docRoot}reference/gms-packages.html">Google
-Play services APIs</a>, you must download the Google Play services SDK
-from the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a>.
-The download includes the client library and code samples.</p>
+Play services APIs</a>, you need to set up your project with the Google Play services SDK.
+<p>If you haven't installed the Google Play services SDK yet, go get it now by following the guide
+to <a href="{@docRoot}sdk/installing/adding-packages.html">Adding SDK Packages</a>.</p>
<p>To test your app when using the Google Play services SDK, you must use either:</p>
<ul>
@@ -33,68 +37,25 @@
that runs the Google APIs platform based on Android 4.2.2 or higher.</li>
</ul>
-<p>Ideally, you should develop and test your app on a variety of devices, including
-both phones and tablets.</p>
-<h2 id="Install">Install the Google Play Services SDK</h2>
+<h2 id="Setup">Add Google Play Services to Your Project</h2>
-<p>To install the Google Play services SDK for development:</p>
+<p>
+<select class="ide">
+ <option value="eclipse">Using Eclipse with ADT</option>
+ <option value="studio">Using Android Studio</option>
+ <option value="other">Using something else</option>
+</select>
+</p>
+
+
+<div class="select-ide studio">
<ol>
- <li>Launch the SDK Manager in one of the following ways:
- <ul>
- <li>In Android Studio, click <strong>SDK Manager</strong>
-<img src="{@docRoot}images/tools/sdk-manager-studio.png" style="vertical-align:bottom;margin:0;height:19px" />
-in the toolbar.</li>
- <li>In Eclipse (with <a href="{@docRoot}tools/help/adt.html">ADT</a>),
- select <strong>Window</strong> > <strong>Android SDK Manager</strong>.</li>
- <li>On Windows, double-click the <code>SDK Manager.exe</code> file at the root of the Android
- SDK directory.</li>
- <li>On Mac or Linux, open a terminal and navigate to the <code>tools/</code> directory in the
- Android SDK, then execute <code>android sdk</code>.</li>
- </ul>
- </li>
- <li>Install the Google Play services SDK.
- <p>Scroll to the bottom of the package list, expand <b>Extras</b>, select
- <b>Google Play services</b>, and install it. If you're using Android Studio, also install
- <b>Google Repository</b> (it provides the Maven repository used for Gradle builds).</p>
- <p>The Google Play services SDK is saved in your Android SDK environment at
- <code><android-sdk>/extras/google/google_play_services/</code>.</p>
-
-<p class="note"><strong>Note:</strong> Google Play services 4.0.30 (released
-November 2013) and newer versions require Android 2.3 or higher. If your app supports Android 2.2,
-you can continue development with the Google Play services SDK, but must instead install
-<b>Google Play services for Froyo</b> from the SDK Manager.</p>
-
- </li>
- <li>Install a compatible version of the Google APIs platform.
- <p>If you want to test your app on the emulator, expand the directory for <b>Android 4.2.2
- (API 17)</b> or a higher version, select <b>Google APIs</b>, and install it. Then create a
- new <a href="{@docRoot}tools/devices/index.html">AVD</a> with Google APIs as
- the platform target.</p>
- </li>
- <li>Make a copy of the Google Play services library project.
- <p class="note"><strong>Note:</strong> If you are using Android Studio, skip this step.</p>
- <p>Copy the library project at
- <code><android-sdk>/extras/google/google_play_services/libproject/google-play-services_lib/</code>
- to the location where you maintain your Android app projects.
- <p>If you are using Eclipse, import the library project into your workspace.
- Click <b>File > Import</b>, select <b>Android > Existing
- Android Code into Workspace</b>, and browse to the copy of the library project to import it.</p>
- </li>
-</ol>
-
-
-
-<h2 id="Setup">Set Up a Project that Uses Google Play Services</h2>
-
-<p><b>Using Android Studio:</b></p>
-
-<ol>
- <li>Open the <code>build.gradle</code> file inside your application module directory.
- <p class="note"><strong>Note:</strong> Android Studio projects contain a top-level
- <code>build.gradle</code> file and a <code>build.gradle</code> file for each module.
+ <li>Open the <code>build.gradle</code> file inside your application module directory.
+ <p class="note"><strong>Note:</strong> Android Studio projects contain a top-level
+ <code>build.gradle</code> file and a <code>build.gradle</code> file for each module.
Be sure to edit the file for your application module. See
<a href="{@docRoot}sdk/installing/studio-build.html">Building Your Project with
Gradle</a> for more information about Gradle.</p></li>
@@ -128,8 +89,11 @@
<p>You can now begin developing features with the
<a href="{@docRoot}reference/gms-packages.html">Google Play services APIs</a>.</p>
+</div><!-- end studio -->
-<p><b>Using Eclipse or another IDE:</b></p>
+
+
+<div class="select-ide eclipse other">
<p>To make the Google Play services APIs available to your app, you must reference the library
project you created in step 4 of the <a href="#Install">installation instructions</a>.</p>
@@ -156,6 +120,9 @@
you can begin developing features with the
<a href="{@docRoot}reference/gms-packages.html">Google Play services APIs</a>.</p>
+</div><!-- end eclipse and other -->
+
+
<h2 id="Proguard">Create a Proguard Exception</h2>
@@ -236,5 +203,6 @@
that takes the user to Google Play Store to install the update.</p>
-<p>To then begin a connection to Google Play services, read <a
-href="{@docRoot}google/auth/api-client.html">Accessing Google Play Services APIs</a>.</p>
+<p>To then begin a connection to Google Play services (required by most Google APIs such
+as Google Drive, Google+, and Games), read <a
+href="{@docRoot}google/auth/api-client.html">Accessing Google APIs</a>.</p>
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 606baf8..de2980b 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 40618a3..2fa029f 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -15,7 +15,9 @@
<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 @@
<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 @@
</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 @@
<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 @@
</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 @@
</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>
+notifications look right with the new color scheme:</p>
-<p>The system will ignore all non-alpha channels in action icons and the main
-notification icon, so you should assume that these icons will be alpha-only.
-</p>
+<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 @@
<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 @@
<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 @@
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="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
-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>
+<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>{@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>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 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. To set the metadata, call the following methods in
+{@code android.app.Notification.Builder} when you construct the
+notification:</p>
+
+<ul>
+<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 @@
<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 @@
<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 @@
</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 @@
{@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 @@
<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 @@
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 @@
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 @@
You must add the {@code android.permission.BLUETOOTH_ADMIN} permission in your
manifest in order for your app to use the new advertising and scanning features.</a>
-<p>To begin Bluetooth LE advertising so that other devices can discover the
-device running your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvisertising()} and pass in an implementation of the
-{@code android.bluetooth.le.AdvertiseCallback} class to report the success
-or failure of the advertising operation.</p>
+<p>To begin Bluetooth LE advertising so that other devices can discover
+your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvisertising()}
+and pass in an implementation of the
+{@code android.bluetooth.le.AdvertiseCallback} class. The callback object
+receives a report of the success or failure of the advertising operation.</p>
-<p>Conversely, if you want to scan for Bluetooth LE devices nearby, call
-{@code android.bluetooth.le.BluetoothLeScanner.startScan()} and pass in an
+<p> The L Developer Preview introduces the {@code
+android.bluetooth.le.ScanFilter} class so that your app can scan for only the
+specific types of devices it is interested in. To begin scanning for Bluetooth
+LE devices, call {@code android.bluetooth.le.BluetoothLeScanner.startScan()} and
+pass in a list of filters. In the method call, you must also provide an
implementation of {@code android.bluetooth.le.ScanCallback} to report if a
-Bluetooth LE advertisement is found. Optionally, you can pass in filters to scan
-for a specific type of device.</p>
+Bluetooth LE advertisement is found. </p>
<h3 id="NFCEnhancements">NFC enhancements</h3>
<p>The L Developer Preview adds these enhancements to enable wider and more
@@ -446,13 +569,12 @@
<ul>
<li>Android Beam is now available in the share menu.
-<li>Support for the <a href="http://www.wi-fi.org/discover-wi-fi/wi-fi-direct">Wi-fi Direct standard</a>.
<li>Your app can invoke the Android Beam on the user’s device to share data by
calling {@code android.nfc.NfcAdapter.invokeBeam()}. This avoids the need for
the user to manually tap the device against another NFC-capable device to
complete the data transfer.
-<li>Use the new {@code android.nfc.NdefRecord.createTextRecord()} method if
- you want to create an NDEF record containing UTF-8 text data.
+<li>You can use the new {@code android.nfc.NdefRecord.createTextRecord()} method
+to create an NDEF record containing UTF-8 text data.
<li>If you are developing a payment app, you now have the ability to
register an NFC application ID (AID) dynamically by calling
{@code android.nfc.cardemulation.CardEmulation.registerAidsForService()}.
@@ -466,18 +588,31 @@
<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 @@
</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 @@
</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 @@
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
new file mode 100644
index 0000000..5b0db74
--- /dev/null
+++ b/docs/html/preview/images/battery_historian.png
Binary files differ
diff --git a/docs/html/preview/images/battery_historian@2x.png b/docs/html/preview/images/battery_historian@2x.png
new file mode 100644
index 0000000..dbb5d5e
--- /dev/null
+++ b/docs/html/preview/images/battery_historian@2x.png
Binary files differ
diff --git a/docs/html/preview/images/managed_apps_launcher.png b/docs/html/preview/images/managed_apps_launcher.png
new file mode 100644
index 0000000..983d904
--- /dev/null
+++ b/docs/html/preview/images/managed_apps_launcher.png
Binary files differ
diff --git a/docs/html/preview/images/managed_apps_launcher@2.png b/docs/html/preview/images/managed_apps_launcher@2.png
new file mode 100644
index 0000000..d298fd2
--- /dev/null
+++ b/docs/html/preview/images/managed_apps_launcher@2.png
Binary files differ
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
index 84a2d85..e44e9f3 100644
--- a/docs/html/preview/index.jd
+++ b/docs/html/preview/index.jd
@@ -3,7 +3,8 @@
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 @@
}
</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 @@
<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 @@
</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 @@
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 @@
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 @@
<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 @@
<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 @@
<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 @@
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 8297c65..cee782a 100644
--- a/docs/html/preview/material/animations.jd
+++ b/docs/html/preview/material/animations.jd
@@ -2,9 +2,22 @@
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#touch">Touch Feedback</a></li>
+ <li><a href="#reveal">Reveal Effect</a></li>
+ <li><a href="#transitions">Activity Transitions</a></li>
+ <li><a href="#curvedmotion">Curved Motion</a></li>
+ <li><a href="#viewstate">View State Changes</a></li>
+ <li><a href="#drawabletint">Drawable Tinting</a></li>
+</ol>
+</div>
+</div>
<p>Animations in material design give users feedback on their actions and provide visual
-continuity as users interact with your app. The Material theme provides some default animations
+continuity as users interact with your app. The material theme provides some default animations
for buttons and activity transitions, and the Android L Developer Preview provides additional
APIs that let you customize these animations and create new ones:</p>
@@ -17,7 +30,7 @@
</ul>
-<h2 style="margin-top:35px">Touch Feedback</h2>
+<h2 id="touch">Touch Feedback</h2>
<p>In the Android L Developer Preview the default touch feedback animations for buttons use the new
<code>RippleDrawable</code> class, which transitions between different states with a ripple
@@ -28,7 +41,7 @@
using the <code>ripple</code> element.</p>
-<h2 style="margin-top:35px">Reveal Effect</h2>
+<h2 id="reveal">Reveal Effect</h2>
<p>The <code>View.createRevealAnimator</code> method enables you to animate a clipping circle
to reveal or hide a view.</p>
@@ -82,7 +95,7 @@
</pre>
-<h2 style="margin-top:35px">Activity Transitions</h2>
+<h2 id="transitions">Activity Transitions</h2>
<p>The Android L Developer Preview enables your app to customize the default animations for
activity transitions. You can specify custom animations for enter and exit transitions and for
@@ -109,10 +122,10 @@
<strong>Figure 1</strong> - A scene transition with one shared element.
</p>
-<h3 style="margin-top:30px">Specify custom transitions</h3>
+<h3>Specify custom transitions</h3>
<p>First, enable window content transitions with the <code>android:windowContentTransitions</code>
-attribute when you define a style that inherits from the Material theme:</p>
+attribute when you define a style that inherits from the material theme:</p>
<pre>
<style name="BaseAppTheme" parent="android:Theme.Material">
@@ -174,14 +187,14 @@
<li><code>Window.setSharedElementExitTransition</code></li>
</ul>
-<h3 style="margin-top:30px">Start an activity using transitions</h3>
+<h3>Start an activity using transitions</h3>
<p>If you enable transitions and set an exit transition for an activity, the transition is activated
when you launch another activity with the <code>startActivity</code> method. If you have set an
enter transition for the second activity, the transition is also activated when the activity
starts.</p>
-<h3 style="margin-top:30px">Shared elements transitions</h3>
+<h3>Shared elements transitions</h3>
<p>To make a screne transition animation between two activities that have a shared element:</p>
@@ -219,7 +232,7 @@
<p>For shared dynamic views that you generate in your code, use the <code>View.setViewName</code>
method to specify a common element name in both activities.</p>
-<h3 style="margin-top:30px">Multiple shared elements</h3>
+<h3>Multiple shared elements</h3>
<p>To make a scene transition animation between two activities that have more than one shared
element, define the shared elements in both layouts with the <code>android:viewName</code>
@@ -237,7 +250,7 @@
</pre>
-<h2 style="margin-top:35px">Curved Motion</h2>
+<h2 id="curvedmotion">Curved Motion</h2>
<p>Animations in material design rely on curves for time interpolation and spatial movement
patterns. The Android L Developer Preview provides new APIs that enable you to define custom
@@ -280,7 +293,7 @@
</pre>
-<h2 style="margin-top:35px">View State Changes</h2>
+<h2 id="viewstate">View State Changes</h2>
<p>The new <code>StateListAnimator</code> class lets you define animators that run when the state
of a view changes. The following example shows how to define an <code>StateListAnimator</code> as
@@ -343,7 +356,7 @@
</pre>
-<h2 style="margin-top:35px">Drawable Tinting</h2>
+<h2 id="drawabletint">Drawable Tinting</h2>
<p>The Android L Developer Preview enables you to define bitmaps as an alpha mask and to tint
them using a color resource or a theme attribute that resolves to a color resource. You can
diff --git a/docs/html/preview/material/compatibility.jd b/docs/html/preview/material/compatibility.jd
index b5555ad..ce04e9e 100644
--- a/docs/html/preview/material/compatibility.jd
+++ b/docs/html/preview/material/compatibility.jd
@@ -2,22 +2,34 @@
@jd:body
-<p>The new material design features (like the Material theme and custom animations) are only
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#materialtheme">Material Theme</a></li>
+ <li><a href="#layouts">Layouts</a></li>
+ <li><a href="#widgets">UI Widgets</a></li>
+ <li><a href="#animation">Animation APIs</a></li>
+</ol>
+</div>
+</div>
+
+<p>The new material design features (like the material theme and custom animations) are only
available in the Android L Developer Preview. However, you can design your apps to make use of
these features when running on devices with the Android L Developer Preview and still be
compatible with previous releases of Android.</p>
-<h2 style="margin-top:35px">Material Theme</h2>
+<h2 id="materialtheme">Material Theme</h2>
-<p>The Material theme is only available in the Android L Developer Preview. To configure your
-app to use the Material theme on devices running the Android L Developer Preview and an older
+<p>The material theme is only available in the Android L Developer Preview. To configure your
+app to use the material theme on devices running the Android L Developer Preview and an older
theme on devices running earlier versions of Android:</p>
<ol>
<li>Define a theme that inherits from an older theme (like Holo) in
<code>res/values/styles.xml</code>.</li>
-<li>Define a theme with the same name that inherits from the Material theme in
+<li>Define a theme with the same name that inherits from the material theme in
<code>res/values-v21/styles.xml</code>.</li>
<li>Set this theme as your app's theme in the manifest file.</li>
</ol>
@@ -26,7 +38,7 @@
your app will not run on earlier versions of Android.</p>
-<h2 style="margin-top:35px">Layouts</h2>
+<h2 id="layouts">Layouts</h2>
<p>If the layouts that you design according to the material design guidelines do not use any
of the new XML attributes from the Android L Developer Preview, they will work on previous
@@ -38,13 +50,13 @@
Alternative layouts have the same file name.</p>
-<h2 style="margin-top:35px">UI Widgets</h2>
+<h2 id="widgets">UI Widgets</h2>
<p>The <code>RecyclerView</code> and <code>CardView</code> widgets are included in the Android L
Developer Preview Support Library, so they are available in earlier versions of Android.</p>
-<h2 style="margin-top:35px">Animation APIs</h2>
+<h2 id="animation">Animation APIs</h2>
<p>The new APIs for custom animations are only available in the Android L Developer Preview. To
preserve compatibility with earlier verisons of Android, check the system version at runtime before
diff --git a/docs/html/preview/material/get-started.jd b/docs/html/preview/material/get-started.jd
index c527550..9c0e55d 100644
--- a/docs/html/preview/material/get-started.jd
+++ b/docs/html/preview/material/get-started.jd
@@ -2,135 +2,145 @@
@jd:body
-<p>To create material design apps on Android:</p>
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#applytheme">Apply the Material Theme</a></li>
+ <li><a href="#layouts">Design Your Layouts</a></li>
+ <li><a href="#depth">Specify Depth in Your Views</a></li>
+ <li><a href="#widgets">Use the New UI Widgets</a></li>
+ <li><a href="#apis">Use the New APIs</a></li>
+</ol>
+</div>
+</div>
+
+<p>To create apps with material design:</p>
<ol>
- <li>Take a look at the <a href="">material design specification</a>.</li>
- <li>Apply the Material <strong>theme</strong> to your app.</li>
- <li>Define additional <strong>styles</strong> to customize the Material theme.</li>
- <li>Create your <strong>layouts</strong> following material design guidelines.</li>
- <li>Specify the <strong>depth</strong> of each component in your layouts to cast appropriate shadows.</li>
- <li>Use the new <strong>widgets</strong> for complex views, such as lists and cards.</li>
- <li>Use the new <strong>APIs</strong> to customize 3D views and animations in your app.</li>
+ <li style="margin-bottom:10px">
+ Take a look at the <a href="">material design specification</a>.</li>
+ <li style="margin-bottom:10px">
+ Apply the material <strong>theme</strong> to your app.</li>
+ <li style="margin-bottom:10px">
+ Define additional <strong>styles</strong> to customize the material theme.</li>
+ <li style="margin-bottom:10px">
+ Create your <strong>layouts</strong> following material design guidelines.</li>
+ <li style="margin-bottom:10px">
+ Specify the <strong>depth</strong> for views to cast appropriate shadows.</li>
+ <li style="margin-bottom:10px">
+ Use the new <strong>widgets</strong> for complex views, such as lists and cards.</li>
+ <li style="margin-bottom:10px">
+ Use the new <strong>APIs</strong> to customize the animations in your app.</li>
</ol>
-<h3 style="margin-top:25px;">Update Your App for the Android L Developer Preview</h3>
+<h3>Update Your App for the Android L Developer Preview</h3>
<p>To update an existing app for the Android L Developer Preview, design new layouts following
material design guidelines and consider how you can improve the user experience for your app by
incorporating depth, touch feedback and animations in your UI.</p>
-<h3 style="margin-top:25px;">Create New Apps for the Android L Developer Preview</h3>
+<h3>Create New Apps for the Android L Developer Preview</h3>
<p>If you are creating a new app for the Android L Developer Preview, the material design
guidelines provide you with a solid design framework for your app. Follow these guidelines and
use the new functionality in the Android framework to design and develop your app.</p>
-<h2 style="margin-top:35px">Material Theme</h2>
+<h2 id="applytheme">Apply the Material Theme</h2>
-<div style="float:right;margin-left:25px">
-<img src="{@docRoot}preview/material/images/ThemeColors.png" style="width:250px"/>
-<p class="img-caption"><strong>Figure 1.</strong> Customizing the Material theme.</p>
-</div>
-
-<p>The new Material theme provides:</p>
-
-<ul>
- <li>System widgets that let you set their color palette</li>
- <li>Touch feedback animations for the system widgets</li>
- <li>Activity transition animations</li>
-</ul>
-
-<p>The Android L Developer Preview lets you easily customize the look of the Material theme
-according to your brand identity with a color palette you control. You can tint the app bar and
-the status bar using theme attributes, as shown in Figure 1.</p>
-
-<p>The system widgets have a new design and touch feedback animations. Activity transitions help
-users navigate your app by providing visual continuity. You can customize the color palette,
-the touch feedback animations, and the activity transitions for your app.</p>
-
-<p>The Material theme is defined as:</p>
-
-<ul>
- <li><code>@android:style/Theme.Material</code> (dark version)</li>
- <li><code>@android:style/Theme.Material.Light</code> (light version)</li>
- <li><code>@android:style/Theme.Material.Light.DarkActionBar</code></li>
-</ul>
-
-<p>For a list of material styles that you can use, see the API reference for
-<code>android.R.styles</code>.</p>
-
-<p class="note">
-<strong>Note:</strong> The Material theme is only available in the Android L Developer Preview.
-For more information, see <a href="{@docRoot}preview/material/compatibility.html">Compatibility</a>.
-</p>
-
-<h3 style="margin-top:25px;">Theme Inheritance</h3>
-
-<p>In the Android L Developer Preview, elements in XML layout definitions can specify the
-<code>android:theme</code> attribute, which references a theme resource. This attribute modifies
-the theme for the element and any elements inflated below it, which is useful to alter theme
-color palettes in a specific portion of an interface.</p>
-
-<h3 style="margin-top:25px;">Customize the Status Bar</h3>
-
-<p>The Material theme lets you easily customize the status bar, so you can specify a
-color which fits your brand and provides enough contrast to show the white status icons. To
-set a custom color for the status bar, use the <code>android:statusBarColor</code> attribute when
-you extend the Material theme.</p>
-
-<p>To handle the color of the status bar yourself (for example, by adding a gradient in the
-background), set the <code>android:statusBarColor</code> attribute to
-<code>@android:color/transparent</code>. You can also use the
-<code>Window.setStatusBarColor</code> method for animations or fading.</p>
-
-
-<h2 style="margin-top:35px">Material Design</h2>
-
-<p>In addition to applying the Material theme, you also have to:</p>
-
-<ul>
- <li>Customize the theme's base colors to fit your brand.</li>
- <li>Design your layouts according to material design guidelines.</li>
-</ul>
-
-<p>The Android L Developer Preview provides new attributes to make it easy to customize the
-Material theme:</p>
+<p>To apply the material theme in your app, specify a style that inherits from
+<code>android:theme.Material</code>:</p>
<pre>
+<!-- res/values/styles.xml -->
<resources>
- <!-- inherit from the Material theme -->
- <style name="BaseAppTheme" parent="android:Theme.Material">
- <!-- Main theme colors -->
- <!-- your app's branding color (for the app bar) -->
- <item name="android:colorPrimary">@color/primary</item>
- <!-- darker variant of colorPrimary (for contextual app bars) -->
- <item name="android:colorPrimaryDark">@color/primary_dark</item>
-
- <!-- other theme colors -->
- <item name="android:colorBackground">@color/background</item>
- <item name="android:colorAccent">@color/accent</item>
- <item name="android:colorButtonNormal">@color/button_normal</item>
- <item name="android:colorControlHighlight">@color/button_chigh</item>
- <item name="android:windowBackground">@color/wbackground</item>
+ <!-- your app's theme inherits from the Material theme -->
+ <style name="AppTheme" parent="android:Theme.Material">
+ <!-- theme customizations -->
</style>
</resources>
</pre>
-<p>Ensure that you follow material design guidelines when choosing colors for your app.</p>
+<p>The material theme provides new system widgets that let you set their color palette and default
+animations for touch feedback and activity transitions. For more details, see
+<a href="{@docRoot}preview/material/theme.html">Material Theme</a>.</p>
-<p>Design your layouts according to the material design specification. In particular, pay
-attention to:</p>
+
+<h2 id="layouts">Design Your Layouts</h2>
+
+<p>In addition to applying and customizing the material theme, your layouts should conform to
+the material design guidelines. When you design your layouts, pay special attention to the
+following:</p>
<ul>
- <li>Baseline grids</li>
- <li>Keylines</li>
- <li>Spacing</li>
- <li>Touch target size</li>
- <li>Layout structure</li>
+<li>Baseline grids</li>
+<li>Keylines</li>
+<li>Spacing</li>
+<li>Touch target size</li>
+<li>Layout structure</li>
</ul>
<p>You still define layouts inside XML files using the standard tools from the Android framework.
-To specify the depth level of each view in your layout, use the <code>android:elevation</code>
-attribute.</p>
\ No newline at end of file
+For details on the material design guidelines, see the <a href="">material design
+specification</a>.</p>
+
+
+<h2 id="depth">Specify Depth in Your Views</h2>
+
+<p>In the Android L Developer Preview, views can cast shadows. The elevation value of a view
+determines the size of its shadow. To set the elevation of a view, use the
+<code>android:elevation</code> attribute in your layouts:</p>
+
+<pre>
+<Button
+ android:id="@+id/my_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/next"
+ <strong>android:elevation</strong>="10dp" />
+</pre>
+
+<p>For more details, see <a href="{@docRoot}preview/material/views-shadows.html">Views and
+Shadows</a>.</p>
+
+
+<h2 id="widgets">Use the New UI Widgets</h2>
+
+<p>The Android L Developer Preview includes two new UI widgets for complex views,
+<code>RecyclerView</code> and <code>CardView</code>. <code>RecyclerView</code> is a more advanced
+version of <code>ListView</code> that provides performance improvements and is easier to use.
+<code>CardView</code> lets you show pieces of information inside cards with a consistent look
+across apps. To include a <code>CardView</code> in your layout:</p>
+
+<pre>
+<android.support.v7.widget.CardView
+ android:id="@+id/card_view"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ card_view:cardCornerRadius="3dp">
+ ...
+</android.support.v7.widget.CardView>
+</pre>
+
+<p>For more information, see <a href="{@docRoot}preview/material/ui-widgets.html">UI Widgets</a>.</p>
+
+
+<h2 id="apis">Use the APIs to Customize Your Animations</h2>
+
+<p>The Android L Developer Preview includes new APIs to create custom animations in your app.
+For example, you can enable activity transitions and define an exit transition inside an
+activity:</p>
+
+<pre>
+// inside your activity
+getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+
+// set an exit transition
+getWindow().setExitTransition(new Explode());
+</pre>
+
+<p>When you start another activity from this activity, the exit transition is activated.</p>
+
+<p>To learn about all the features in the new APIs, see <a
+href="{@docRoot}preview/material/animations.html">Animations</a>.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/index.jd b/docs/html/preview/material/index.jd
index 468e4bd..b7abcb4 100644
--- a/docs/html/preview/material/index.jd
+++ b/docs/html/preview/material/index.jd
@@ -19,9 +19,9 @@
</ul>
-<h3 style="margin-top:30px">Material Theme</h3>
+<h3>Material Theme</h3>
-<p>The Material theme provides a new style for your app, system widgets that let you set
+<p>The material theme provides a new style for your app, system widgets that let you set
their color palette, and default animations for touch feedback and activity transitions.</p>
<!-- two columns -->
@@ -42,7 +42,7 @@
</div>
-<h3 style="margin-top:30px">New Widgets</h3>
+<h3>New Widgets</h3>
<p>The Android L Developer Preview includes two new widgets for displaying complex views:</p>
@@ -50,8 +50,8 @@
<div style="width:700px;margin-top:25px;margin-bottom:20px">
<div style="float:left;width:250px;margin-left:40px;margin-right:60px;">
<img src="{@docRoot}preview/material/images/list_mail.png" style="width:250px;"/>
- <p>The new <code>RecyclerView</code> widget is a container for large sets of views that can be
- recycled and scrolled very efficiently.</p>
+ <p>The new <code>RecyclerView</code> widget is a more advanced version of <code>ListView</code>
+ provides performance improvements for dynamic views and is easier to use.</p>
</div>
<div style="float:left;width:250px;margin-right:0px;">
<img src="{@docRoot}preview/material/images/card_travel.png" style="width:250px;"/>
@@ -62,21 +62,13 @@
</div>
-<h3 style="margin-top:30px">3D Views and Shadows</h3>
+<h3>View Shadows</h3>
<p>In addition to the X and Y components, views in the Android L Developer Preview have a Z
component. This new component represents the elevation of a view, which determines the size of
its shadow: views with higher Z values cast bigger shadows.</p>
-
-<h3 style="margin-top:30px">Animations</h3>
-
-<p>The Android L Developer Preview provides new APIs that let you create custom animations for
-touch feedback in UI controls, view state changes, and activity transitions.</p>
-
-<!-- two columns -->
-<div style="width:700px;margin-left:12px;margin-top:25px;margin-bottom:5px">
-<div style="float:left;width:340px;margin-left:0px;margin-right:0px;">
+<div style="width:290px;margin-left:35px;float:right">
<div class="framed-nexus5-port-span-5">
<video class="play-on-hover" autoplay>
<source src="/preview/material/videos/ContactsAnim.mp4"/>
@@ -84,52 +76,41 @@
<source src="/preview/material/videos/ContactsAnim.ogv"/>
</video>
</div>
-</div>
-<div style="float:left;width:340px;margin-right:0px;">
- <div class="framed-nexus5-port-span-5">
- <video class="play-on-hover" autoplay>
- <source src="/preview/material/videos/Dial.mp4"/>
- <source src="/preview/material/videos/Dial.webm"/>
- <source src="/preview/material/videos/Dial.ogv"/>
- </video>
+ <div style="font-size:10pt;margin-left:20px;margin-bottom:30px">
+ <em>Click on the device screen to replay the movie</em>
</div>
</div>
-<br style="clear:left"/>
-</div>
-<div style="text-align:center;font-size:10pt;margin-right:35px">
-<em>Click on the device screen to replay the movie</em>
-</div>
+<h3>Animations</h3>
-<!-- three columns -->
-<div style="width:700px;margin-top:25px;margin-bottom:0px">
-<div style="float:left;width:200px;margin-left:0px;margin-right:0px;">
- <p>Respond to touch events in your views with <strong>touch feedback</strong> animations.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Hide and show views with <strong>reveal effect</strong> animations.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Switch between activities with custom <strong>activity transition</strong> animations.</p>
-</div>
-<br style="clear:left"/>
-</div>
-<!-- three columns -->
-<div style="width:700px;margin-top:0px;margin-bottom:20px">
-<div style="float:left;width:200px;margin-left:0px;margin-right:0px;">
- <p>Create custom animation patterns with <strong>curved motion</strong>.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Animate changes in one or more view properties with <strong>view state change</strong> animations.</p>
-</div>
-<div style="float:left;margin-left:25px;width:200px;margin-right:0px;">
- <p>Show animations in <strong>state list drawables</strong> between view state changes.</p>
-</div>
-<br style="clear:left"/>
-</div>
+<p>The Android L Developer Preview provides new APIs that let you create custom animations for
+touch feedback in UI controls, view state changes, and activity transitions.</p>
+
+<p>The new animation APIs in the Android L Developer Preview let you:</p>
+
+<ul>
+<li style="margin-bottom:15px">
+Respond to touch events in your views with <strong>touch feedback</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Hide and show views with <strong>reveal effect</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Switch between activities with custom <strong>activity transition</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Create custom animation patterns with <strong>curved motion</strong>.
+</li>
+<li style="margin-bottom:15px">
+Animate changes in one or more view properties with <strong>view state change</strong> animations.
+</li>
+<li style="margin-bottom:15px">
+Show animations in <strong>state list drawables</strong> between view state changes.
+</li>
+</ul>
-<h3 style="margin-top:30px">New Capabilities for Drawables</h3>
+<h3>New Capabilities for Drawables</h3>
<p>The Android L Developer Preview supports <strong>drawable tinting</strong>: you can define
bitmaps as an alpha mask and tint them using a color resource. You can create these assets only
diff --git a/docs/html/preview/material/theme.jd b/docs/html/preview/material/theme.jd
new file mode 100644
index 0000000..b954960
--- /dev/null
+++ b/docs/html/preview/material/theme.jd
@@ -0,0 +1,100 @@
+page.title=Material Theme
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#colorpalette">Customize the Colot Palette</a></li>
+ <li><a href="#statusbar">Customize the Status Bar</a></li>
+ <li><a href="#inheritance">Theme Inheritance</a></li>
+</ol>
+</div>
+</div>
+
+<p>The new material theme provides:</p>
+
+<ul>
+ <li>System widgets that let you set their color palette</li>
+ <li>Touch feedback animations for the system widgets</li>
+ <li>Activity transition animations</li>
+</ul>
+
+<p>The Android L Developer Preview lets you easily customize the look of the material theme
+according to your brand identity with a color palette you control. You can tint the app bar and
+the status bar using theme attributes, as shown in Figure 1.</p>
+
+<div style="float:right;margin-left:25px;margin-top:-25px">
+<img src="{@docRoot}preview/material/images/ThemeColors.png" style="width:250px"/>
+<p class="img-caption"><strong>Figure 1.</strong> Customizing the material theme.</p>
+</div>
+
+<p>The system widgets have a new design and touch feedback animations. Activity transitions help
+users navigate your app by providing visual continuity. You can customize the color palette,
+the touch feedback animations, and the activity transitions for your app.</p>
+
+<p>The material theme is defined as:</p>
+
+<ul>
+ <li><code>@android:style/Theme.Material</code> (dark version)</li>
+ <li><code>@android:style/Theme.Material.Light</code> (light version)</li>
+ <li><code>@android:style/Theme.Material.Light.DarkActionBar</code></li>
+</ul>
+
+<p>For a list of material styles that you can use, see the API reference for
+<code>android.R.styles</code>.</p>
+
+<p class="note">
+<strong>Note:</strong> The material theme is only available in the Android L Developer Preview.
+For more information, see <a href="{@docRoot}preview/material/compatibility.html">Compatibility</a>.
+</p>
+
+
+<h2 id="colorpalette">Customize the Color Palette</h2>
+
+<p>To customize the theme's base colors to fit your brand, define your custom colors using
+theme attributes when you inherit from the material theme:</p>
+
+<pre>
+<resources>
+ <!-- inherit from the material theme -->
+ <style name="AppTheme" parent="android:Theme.Material">
+ <!-- Main theme colors -->
+ <!-- your app's branding color (for the app bar) -->
+ <item name="android:colorPrimary">@color/primary</item>
+ <!-- darker variant of colorPrimary (for contextual app bars) -->
+ <item name="android:colorPrimaryDark">@color/primary_dark</item>
+
+ <!-- other theme colors -->
+ <item name="android:colorButtonNormal">@color/button_normal</item>
+ <item name="android:windowBackground">@color/wbackground</item>
+ </style>
+</resources>
+</pre>
+
+
+<h2 id="statusbar">Customize the Status Bar</h2>
+
+<p>The material theme lets you easily customize the status bar, so you can specify a
+color which fits your brand and provides enough contrast to show the white status icons. To
+set a custom color for the status bar, use the <code>android:statusBarColor</code> attribute when
+you extend the material theme.</p>
+
+<p>To handle the color of the status bar yourself (for example, by adding a gradient in the
+background), set the <code>android:statusBarColor</code> attribute to
+<code>@android:color/transparent</code>. You can also use the
+<code>Window.setStatusBarColor</code> method for animations or fading.</p>
+
+<p class="note"><strong>Note:</strong>
+The status bar should almost always have a clear delineation from the primary toolbar, except for
+full-bleed imagery cases and when you use a gradient as a protection.
+</p>
+
+
+<h2 id="inheritance">Theme Inheritance</h3>
+
+<p>In the Android L Developer Preview, elements in XML layout definitions can specify the
+<code>android:theme</code> attribute, which references a theme resource. This attribute modifies
+the theme for the element and any elements inflated below it, which is useful to alter theme
+color palettes in a specific portion of an interface.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/ui-widgets.jd b/docs/html/preview/material/ui-widgets.jd
index 5c12a1a..f18bff9 100644
--- a/docs/html/preview/material/ui-widgets.jd
+++ b/docs/html/preview/material/ui-widgets.jd
@@ -2,13 +2,22 @@
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#recyclerview">RecyclerView</a></li>
+ <li><a href="#cardview">CardView</a></li>
+</ol>
+</div>
+</div>
<p>The support library in the Android L Developer Preview contains two new widgets,
<code>RecyclerView</code> and <code>CardView</code>. Use these widgets to show complex lists
and cards in your app. These widgets have material design styles and animations by default.</p>
-<h2 style="margin-top:35px">RecyclerView</h2>
+<h2 id="recyclerview">RecyclerView</h2>
<p><code>RecyclerView</code> is a more advanced version of <code>ListView</code>. This widget is
a container for large sets of views that can be recycled and scrolled very efficiently. Use the
@@ -62,7 +71,7 @@
<p>To create a custom layout, you extend the <code>RecyclerView.LayoutManager</code> class.</p>
-<h3 style="margin-top:30px">Examples</h3>
+<h3>Examples</h3>
<p>To include a <code>RecyclerView</code> in your layout:</p>
@@ -155,7 +164,7 @@
</pre>
-<h2 style="margin-top:35px">CardView</h2>
+<h2 id="cardview">CardView</h2>
<p><code>CardView</code> extends the <code>FrameLayout</code> class and lets you show information
inside a card with optional rounded corners:</p>
diff --git a/docs/html/preview/material/videos/Dial.mp4 b/docs/html/preview/material/videos/Dial.mp4
deleted file mode 100644
index cd5a6a2..0000000
--- a/docs/html/preview/material/videos/Dial.mp4
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/material/videos/Dial.ogv b/docs/html/preview/material/videos/Dial.ogv
deleted file mode 100644
index b7b29d0..0000000
--- a/docs/html/preview/material/videos/Dial.ogv
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/material/videos/Dial.webm b/docs/html/preview/material/videos/Dial.webm
deleted file mode 100644
index e30d2a5..0000000
--- a/docs/html/preview/material/videos/Dial.webm
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/material/views-shadows.jd b/docs/html/preview/material/views-shadows.jd
index 52fe83c..c5884d6 100644
--- a/docs/html/preview/material/views-shadows.jd
+++ b/docs/html/preview/material/views-shadows.jd
@@ -2,14 +2,24 @@
@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+ <li><a href="#elevation">View Elevation</a></li>
+ <li><a href="#shadows">Shadows and Outlines</a></li>
+ <li><a href="#clip">Clipping Views</a></li>
+</ol>
+</div>
+</div>
-<p>In material design apps, depth has meaning. You should assign higher elevation values to more
+<p>In apps with material design, depth has meaning. You should assign higher elevation values to more
important UI elements in your app. The elevation value of a view determines the size of its
shadow: views with higher Z values cast bigger shadows. Views only cast shadows on the Z=0 plane
under an orthographic projection (the views do not scale for different values of Z).</p>
-<h2 style="margin-top:35px">View Elevation</h2>
+<h2 id="elevation">View Elevation</h2>
<p>The Z value for a view has two components, elevation and translation. The elevation is the
static component, and the translation is used for animations:</p>
@@ -29,7 +39,7 @@
<code>px</code>).</p>
-<h2 style="margin-top:35px">Shadows and Outlines</h2>
+<h2 id="shadows">Shadows and Outlines</h2>
<p>The bounds of a view's background drawable determine the default shape of its shadow. To define
a custom shape for a shadow, such as an oval, use the <code>View.setOutline</code> method:</p>
@@ -55,7 +65,7 @@
<p>To prevent a view from casting a shadow, set its outline to <code>null</code>.</p>
-<h2 style="margin-top:35px">Clipping Views</h2>
+<h2 id="clip">Clipping Views</h2>
<p>The Android L Developer Preview lets you clip a view to its outline area using the
<code>View.setClipToOutline</code> method. Only rectangle, circle, and round rectangle outlines
diff --git a/docs/html/preview/preview_toc.cs b/docs/html/preview/preview_toc.cs
index 8f6f8c1..5920ecc 100644
--- a/docs/html/preview/preview_toc.cs
+++ b/docs/html/preview/preview_toc.cs
@@ -19,6 +19,7 @@
</a></div>
<ul>
<li><a href="<?cs var:toroot ?>preview/material/get-started.html">Get Started</a></li>
+ <li><a href="<?cs var:toroot ?>preview/material/theme.html">Material Theme</a></li>
<li><a href="<?cs var:toroot ?>preview/material/ui-widgets.html">UI Widgets</a></li>
<li><a href="<?cs var:toroot ?>preview/material/views-shadows.html">Views and Shadows</a></li>
<li><a href="<?cs var:toroot ?>preview/material/animations.html">Animations</a></li>
@@ -75,7 +76,12 @@
</li>
<li class="nav-section">
<div class="nav-section-header empty">
- <a href="<?cs var:toroot ?>preview/feedback-support.html">Feedback and Support</a>
+ <a href="<?cs var:toroot ?>preview/support.html">Support</a>
</div>
</li>
-</ul>
\ No newline at end of file
+ <li class="nav-section">
+ <div class="nav-section-header empty">
+ <a href="<?cs var:toroot ?>preview/tos.html">Terms of Service</a>
+ </div>
+ </li>
+</ul>
diff --git a/docs/html/preview/samples.jd b/docs/html/preview/samples.jd
new file mode 100644
index 0000000..67404b6
--- /dev/null
+++ b/docs/html/preview/samples.jd
@@ -0,0 +1,16 @@
+page.title=Samples
+
+@jd:body
+
+<p>The code samples for the L Developer Preview are available in the Android SDK Manager under the
+L Preview section. Here is a summary of everything that is available:</p>
+
+<ul>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ul>
diff --git a/docs/html/preview/setup-devices.jd b/docs/html/preview/setup-devices.jd
index 0654685..86e4845 100644
--- a/docs/html/preview/setup-devices.jd
+++ b/docs/html/preview/setup-devices.jd
@@ -26,12 +26,6 @@
can cause your phone and installed services and applications to stop working.
</p>
-<p><!-- Will this link change before we publish (to a clean version of the doc)?
- Or will we scrub the doc's comments & revision history? -->
-<a href="https://docs.google.com/a/google.com/document/d/1OixnM1Q890ExOzDB3Z-FDD6Sb2kF4uZQiMxsYVII8F0/edit?usp=sharing">L
-Preview Terms of Service</a>
-</p>
-
<ol>
<li>Download and extract the Android Developer Preview package to a directory
diff --git a/docs/html/preview/support.jd b/docs/html/preview/support.jd
new file mode 100644
index 0000000..23ce6ff
--- /dev/null
+++ b/docs/html/preview/support.jd
@@ -0,0 +1,22 @@
+page.title=Support
+
+@jd:body
+
+<p>If you've encountered bugs or have feedback about the L Developer Preview, create
+an issue on our bug tracker</p>
+
+<p>Go to the Bug Tracker</p>
+
+<h2>Release Notes</h2>
+
+<p>June 25, 2014 - Initial Release of the L Developer Preview</p>
+
+<ul>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ul>
diff --git a/docs/html/preview/tos.jd b/docs/html/preview/tos.jd
new file mode 100644
index 0000000..602439f
--- /dev/null
+++ b/docs/html/preview/tos.jd
@@ -0,0 +1,9 @@
+page.title=License Agreement
+
+@jd:body
+
+<p><!-- Will this link change before we publish (to a clean version of the doc)?
+ Or will we scrub the doc's comments & revision history? -->
+<a href="https://docs.google.com/a/google.com/document/d/1OixnM1Q890ExOzDB3Z-FDD6Sb2kF4uZQiMxsYVII8F0/edit?usp=sharing">L
+Preview Terms of Service</a>
+</p>
\ No newline at end of file
diff --git a/docs/html/preview/tv/adt-1/index.jd b/docs/html/preview/tv/adt-1/index.jd
deleted file mode 100644
index 062968e..0000000
--- a/docs/html/preview/tv/adt-1/index.jd
+++ /dev/null
@@ -1,319 +0,0 @@
-page.title=ADT-1 Developer Kit
-page.tags="emote","e-mote","adt"
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#faq">ADT-1 Frequently Asked Questions</a></li>
- <li><a href="#regulatory">Regulatory Disclosures</a></li>
- <li><a href="#safety">Important Safety Instructions</a></li>
- </ol>
-</div>
-</div>
-
-<p class="note">
- <strong>!FIX: FOR REVIEW ONLY:</strong> link to <a href="request.html">ADT-1 Request</a> page.
-</p>
-
-
-<p>The ADT-1 Developer Kit is streaming media player and game controller designed for running
-and testing app built for Android TV. The kit is provided to a limited number of developers
-who are interested in building new apps or extending their existing apps to run on the Android TV
-platform.</p>
-
-<p class="note">
- <strong>Note:</strong> The ADT-1 kit <em>is not required</em> for building and testing apps
- for Android TV. You can build apps for TV and test them using an emulator for TV devices. The
- L-Preview SDK includes all the software needed to build TV apps and an emulator for running and
- testing them. For more information, see the
- <a href="{@docRoot}preview/tv/start/index.html">Get Started</a> guide for TV apps.
-</p>
-
-<h2 id="faq">ADT-1 Frequently Asked Questions</h2>
-
-<p>
- <strong>How do I put the gamepad that came with my ADT-1 into pairing mode?</strong>
-</p>
-<p>Press and hold the Back and Home buttons together for about three seconds, until all four
- blue LEDs flash together. When the LEDs are flashing, the gamepad is in pairing mode.</p>
-
-<p>
- <strong>How do I use the gamepad with the on-screen keyboard?</strong>
-</p>
-<p>Use the D-pad or left joystick to move the cursor, and press A to select. Press X to delete a
- character, and press Y to insert a space. Also, you can press the right joystick to toggle caps
- lock, and press the left joystick to show additional symbols.</p>
-
-<p>
- <strong>How do I perform a hard reset of ADT-1?</strong>
-</p>
-<p>Unplug the power cable from the back of ADT-1. Press and hold the small, round button on the
- back of ADT-1 as you re-insert the power cable, and continue to hold the small round button. The
- LED will begin flashing red for a few seconds, then change to multi-color cycle. When the LED
- starts the multi-color cycle, release the small, round button, and ADT-1 will boot. Note: this is
- a factory data reset, thus all downloaded apps, system and app data, and account settings will be
- lost.</p>
-
-<p>
- <strong>How do I do a soft reset?</strong>
-</p>
-<p>Go to Settings - Device - Factory data reset, and select ‘Reset device’. Note: this is a
- factory data reset, thus all downloaded apps, system and app data, and account settings will be
- lost.</p>
-
-<p>
- <strong>How do I turn my device on?</strong>
-</p>
-<p>Plug in the included power cable into the back of ADT-1. There is no on/off switch.</p>
-
-<p>
- <strong>How do I completely turn my device off? </strong>
-</p>
-<p>Unplug in the included power cable from the back of ADT-1. There is no on/off switch.
- However, ADT-1 will begin sleeping (daydream) based on user settings in Display -> Daydream.</p>
-
-<p>
- <strong>How do I connect to the network?</strong>
-</p>
-<p>ADT-1 has both Wi-Fi and Ethernet for connecting to your network. To change your Wi-Fi
- network, go to Settings -> Wi-Fi. To use an Ethernet network connection, simply plug in an
- Ethernet cable (that is connected to your network) into the port on the back of ADT-1.</p>
-
-<p>
- <strong>How do I use the developer cable?</strong>
-</p>
-<p>The developer cable has three connectors: a small, male power connector that plugs into the
- power port on the back of ADT-1, a standard male USB-A connector that connects your PC, and a
- small, female power connector that the included power supply plugs into.</p>
-
-<p>
- <strong>Is there an app for phone and tablet that I can use to control ADT-1?</strong>
-</p>
-<p>Yes, you can download the remote control app from Android phones and tablets here.</p>
-
-<p>
- <strong>Can I connect a USB keyboard/mouse to ADT-1?</strong>
-</p>
-<p>Yes, you can connect a USB keyboard/mouse to the USB port on the back of ADT-1. Note: not all
- manufacturers/models are guaranteed to work.</p>
-<hr />
-<p>
- Press the small, round button on the back of ADT-1 to make it search for Bluetooth devices in
- pairing mode. If multiple accessories are found, press the small, round button to select the
- device you want to pair. Pairing will happen automatically after a few seconds. To pair Bluetooth
- devices to ADT-1 from the UI, go to <strong>Settings > Remote & Accessories >
- Add accessory</strong>.
-</p>
-
-
-
-<h2 id="regulatory">Regulatory Disclosures</h2>
-
-
-<p>Model: W2</p>
-<p>FCC ID: A4R-W2</p>
-<p>IC: 10395A-W2</p>
-<p>U.S. Federal Communications Commission Notices</p>
-<p>To satisfy FCC and IC exposure requirements, a separation distance of at least 20 cm should
- be maintained between the antenna of this device and persons during device operation. Operations
- at closer than this distance are not recommended.</p>
-<p>The antenna used for this transmitter must not be co-located in conjunction with any other
- antenna or transmitter.</p>
-<p>This equipment has been tested and found to comply with the limits for a Class B digital
- device, pursuant to part 15 of the FCC Rules. These limits are designed to provide reasonable
- protection against harmful interference in a residential installation. This equipment generates,
- uses and can radiate radio frequency energy and, if not installed and used in accordance with the
- instructions, may cause harmful interference to radio communications. However, there is no
- guarantee that interference will not occur in a particular installation. If this equipment does
- cause harmful interference to radio or television reception, which can be determined by turning
- the equipment off and on, the user is encouraged to try to correct the interference by one or more
- of the following measures:</p>
-<p>—Reorient or relocate the receiving antenna.</p>
-<p>—Increase the separation between the equipment and receiver.</p>
-<p>—Connect the equipment into an outlet on a circuit different from that to which the receiver
- is connected.</p>
-<p>—Consult the dealer or an experienced radio/ TV technician for help.</p>
-<p>This device complies with part 15 of the FCC Rules. Operation is subject to the following two
- conditions: (1) This device may not cause harmful interference, and (2) this device must accept
- any interference received, including interference that may cause undesired operation.</p>
-<p>Changes or modifications not expressly approved by Google Inc. could void the user's
- authority to operate the equipment.</p>
-<p>Industry Canada Notices</p>
-<p>This device complies with Industry Canada licence-exempt RSS standard(s). Operation is
- subject to the following two conditions: (1) this device may not cause interference, and (2) this
- device must accept any interference, including interference that may cause undesired operation of
- the device.</p>
-<p>Under Industry Canada regulations, this radio transmitter may only operate using an antenna
- of a type and maximum (or lesser) gain approved for the transmitter by Industry Canada. To reduce
- potential radio interference to other users, the antenna type and its gain should be so chosen
- that the equivalent isotropically radiated power (e.i.r.p.) is not more than that necessary for
- successful communication.</p>
-<p>The radiated output power of the Wireless Device is below the Industry Canada (IC) radio
- frequency exposure limits. The Wireless Device should be used in such a manner such that the
- potential for human contact during normal operation is minimized.</p>
-
-<hr />
-
-<p>CAN ICES-3 (B)/NMB-3(B)</p>
-<p>
- <u>Avis d’<em>Industrie Canada</em></u>
-</p>
-<p>
- Le présent appareil est conforme aux <em>CNR</em> d'Industrie Canada applicables aux appareils
- radio exempts de licence. L'exploitation est autorisée aux deux conditions suivantes : (1)
- l'appareil ne doit pas produire de brouillage, et (2) l'appareil doit accepter tout brouillage
- radioélectrique subi, même si le brouillage est susceptible d'en compromettre le fonctionnement.
-</p>
-<p>
- En vertu de la règlementation d’<em>Industrie Canada</em>, cet émetteur radio peut
- fonctionner avec une antenne d'un type et d'un gain maximal (ou inférieur) approuvé pour
- l'émetteur par <em>Industrie Canada</em>. Dans le but de réduire les risques de brouillage
- radioélectrique à l'intention des autres utilisateurs, il faut choisir le type d'antenne et son
- gain de sorte que la puissance isotrope rayonnée équivalente (p.i.r.e.) ne dépasse pas l'intensité
- nécessaire à l'établissement d'une communication satisfaisante.
-</p>
-<p>
- La puissance rayonnée en sortie de l'appareil sans fil est inférieure aux limites fixées par
- <em>Industrie Canada</em> en matière d'exposition aux radiofréquences. L'appareil sans fil
- doit être utilisé de sorte que la possibilité d'un contact humain pendant le fonctionnement
- normal soit limitée.
-</p>
-
-
-<h2 id="safety">Important Safety Instructions</h2>
-
-<p>
- <strong>WARNING:</strong> Read all safety information below before using this device to avoid
- injury.
-</p>
-<ul>
- <li><p>Do not install near heat sources, such as heaters and other devices.</p></li>
- <li><p>Use in a well-ventilated area and plug power adapter into an easily accessible
- outlet. Only use this device with the provided power adapter.</p></li>
- <li><p>The device has no on/off switch. To disconnect from power, you must unplug the
- power adapter.</p></li>
- <li><p>Only use indoors and do not expose to rain, liquid, moisture, excessive heat, or
- naked flame.</p></li>
- <li><p>Clean only with a dry cloth.</p></li>
-</ul>
-<p>
- <strong>WARNING:</strong> Playing video games has been linked to injuries in some
- users. Read all safety and health information below before using the gamepad to avoid possible
- injury.
-</p>
-
-<p><u>Photosensitive Seizures</u></p>
-
-<p>
- A very small percentage of people may experience a seizure when exposed to certain visual images,
- including flashing lights or patterns that may appear in some video games, even people who have no
- history of seizures or epilepsy. These seizures have a variety of symptoms, including
- lightheadedness, altered vision, disorientation, loss of awareness, involuntary movements, loss of
- consciousness, or convulsions. If you experience any of these symptoms, <u>stop gaming
- immediately and consult your doctor</u>.
-</p>
-
-<p><u>Ergonomics</u></p>
-
-<p>Long periods of repetitive motion using incorrect body positioning may be associated with
- physical discomfort and injuries to nerves, tendons, and muscles. If during or after gaming you
- feel pain, numbness, weakness, swelling, burning, cramping, or stiffness,<u>stop gaming
- and consult your doctor</u>.
-
-<p>
- <strong>Healthy Gaming</strong>
-</p>
-
-<p>To reduce risk of seizures or injury, take the following precautions:</p>
-
-<ul>
- <li><p>Sit as far away from the TV screen as possible.</p></li>
- <li><p>Play in a well-lit room.</p></li>
- <li><p>Do not play when you are drowsy or fatigued.</p></li>
- <li><p>Take 10-15 minute breaks every hour if playing video games and avoid prolonged
- gaming.</p></li>
-</ul>
-
-<p>
- <strong>Do Not Attempt Repairs Yourself</strong>
-</p>
-
-<p>There are no user-serviceable parts inside. Do not attempt to open or disassemble.</p>
-
-<p>Failure to follow these safety instructions could result in fire, electric shock, damage to
- the device or other property, or personal injury.</p>
-
-<hr />
-
-<p>
- <strong>Importantes instructions concernant la sécurité</strong>
-</p>
-
-<p>
- <strong>ATTENTION:</strong> Veuillez lire toutes les informations de sécurité énoncées ci-bas
- avant d’utiliser l’appareil pour éviter des blessures.
-</p>
-
-<ul>
- <li><p>Ne pas installer à proximité d’une source de chaleur telle une chaufferette ou un
- autre appareil similaire.</p></li>
- <li><p>Utiliser dans un endroit bien aéré et brancher l’adaptateur électrique dans une
- prise de courant facilement accessible.</p></li>
- <li><p>L’appareil ne possède aucun interrupteur marché/arrêt. Pour mettre l’appareil hors
- tension, il faut débrancher l’appareil de la prise de courant.</p></li>
- <li><p>Utiliser l’appareil uniquement à l’intérieur et ne pas l’exposer à la pluie, à des
- substances liquides, à l’humidité, à la chaleur excessive ou à une flamme.</p></li>
- <li><p>Nettoyer uniquement avec un linge sec.</p></li>
-</ul>
-
-<p>
- <strong>ATTENTION:</strong> Le fait de jouer à des jeux vidéo a été relié à des blessures chez certains
- utilisateurs. Afin d’éviter de possibles blessures, veuillez lire toutes les informations
- concernant la sécurité et la santé énoncées ci-bas avant d’utiliser la tablette de jeu.
-</p>
-
-<p><u>Épilepsie photosensible</u></p>
-
-<p>L’exposition à certaines images visuelles, incluant les lumières ou motifs clignotants qui
- peuvent apparaître dans certains jeux vidéo, peut provoquer chez un très faible pourcentage de
- personnes une crise d’épilepsie, et ce, même si ces personnes n’ont aucun historique de crises ou
- d’épilepsie. Ces crises comportent divers symptômes tels que des étourdissements, une vision
- altérée, un sentiment de désorientation, la perte de conscience, des mouvements involontaires, la
- perte de connaissance ou de conscience ou des convulsions. Si vous ressentez quelconque de ces
- symptômes, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
-
-<p><u>Ergonomie</u></p>
-
-<p>Les longues périodes de mouvements répétitifs effectués dans une position corporelle
- inadéquate peuvent mener à un inconfort physique et à des blessures aux nerfs, tendons et muscles.
- Si durant ou après avoir joué à des jeux vidéo, vous ressentez de la douleur, de
- l’engourdissement, une faiblesse, de l’inflammation, une sensation de brûlure, des crampes ou de
- la rigidité, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
-
-<p>
- <strong>Le jeu sécuritaire</strong>
-</p>
-
-<p>Afin de réduire les risques de crises d’épilepsie ou de blessures, veuillez prendre les
- précautions suivantes :</p>
-
-<ul>
- <li>Asseyez-vous aussi loin de l’écran de télévision que possible.</li>
- <li>Jouez dans une pièce munie d’un éclairage adéquat.</li>
- <li>Ne jouez pas lorsque vous êtes étourdi ou fatigué.</li>
- <li>Prenez 10 à 15 minutes de pause après chaque heure de jeu et évitez les périodes de jeu
- prolongées.</li>
-</ul>
-
-<p>
- <strong>Ne pas tenter d’effectuer des réparations par vous-même</strong>
-</p>
-
-<p>L’Appareil ne contient aucune pièce pouvant être réparée par l’utilisateur. Ne pas tenter
- d’ouvrir ou de désassembler l’Appareil.</p>
-
-<p>Le défaut de suivre ces instructions de sécurité pourrait provoquer un feu, un choc
- électrique, un dommage à l’Appareil ou à d’autres objets ou des lésions corporelles.</p>
diff --git a/docs/html/preview/tv/adt-1/request.jd b/docs/html/preview/tv/adt-1/request.jd
deleted file mode 100644
index 69e3e4e..0000000
--- a/docs/html/preview/tv/adt-1/request.jd
+++ /dev/null
@@ -1,47 +0,0 @@
-page.title=Request ADT-1 Developer Kit
-
-@jd:body
-
-
-<p>The ADT-1 Developer Kit is streaming media player and game controller designed for running
-and testing app built for Android TV. The kit is offered to developers who are interested in
-building new apps or extending their existing apps to run on the Android TV platform before
-the commercial release of Android TV devices. Supplies of the ADT-1 kit are limited and
-requesting one not guarantee it will be delivered to you.</p>
-
-<p class="note">
- <strong>Note:</strong> The ADT-1 kit <em>is not required</em> for building and testing apps
- for Android TV. You can build apps for TV and test them using an emulator for TV devices. The
- L-Preview SDK includes all the software needed to build TV apps and an emulator for running and
- testing them. For more information, see the
- <a href="{@docRoot}preview/tv/start/index.html">Get Started</a> guide for TV apps.
- For more information about the ADT-1 kit, see the
- <a href="{@docRoot}preview/tv/adt-1/index.html">ADT-1 Developer Kit</a> information page.
-</p>
-
-<div class="sdk-terms" style="width:678px" onfocus="this.blur()">
-<div class="sdk-terms-padding">
-Android ADT-1 Developer Kit Request Agreement.
-
-1. These are the terms.
-
-2. You must agree to the terms.
-
-3. Otherwise you have not agreed to the terms.
-
-4. And then we don't want to talk to you.
-</div>
-</div>
-
-
-<p class="caution">
- <strong>Important:</strong> The email address your provide in this form is used to verify
- you as an Android developer. Please provide a Google account email address that is associated
- with the Google Play app you enter. We may also use your email address to provide you with
- updates about the ADT-1 Developer Kit and Android TV.
-</p>
-
-<iframe src="https://docs.google.com/a/google.com/forms/d/1MLhC39rf3aAJw-KhZw9cyjT1dWuz_k3_iC5QXpC4Cbw/viewform?embedded=true"
- width="100%" height="540" frameborder="0" marginheight="0" marginwidth="0"
- id="signupform">Loading...</iframe>
-
diff --git a/docs/html/preview/tv/games/index.jd b/docs/html/preview/tv/games/index.jd
deleted file mode 100644
index b9de3a4..0000000
--- a/docs/html/preview/tv/games/index.jd
+++ /dev/null
@@ -1,70 +0,0 @@
-page.title=Games on TV
-page.tags="controller"
-
-@jd:body
-
-<p>This section complements the [larger best-practices guidance for designing for Android TV](TODO, use formal name of referenced doc, and add link). It assumes that you have read that guidance, and seeks to minimize repetition.</p>
-
-<h2>Overview</h2>
-<p>Because of factors including its large size, its control scheme, and its nature as a shared display, the television screen presents a number of considerations that may be new to mobile developers. This document breaks these considerations down into five sections:</p>
-<ul>
-<li>Display</li>
-<li>Control</li>
-<li>Manifest</li>
-<li>Google Play Game Services</li>
-<li>Web</li>
-</ul>
-<h2>Display</h2>
-<p>Large and centrally situated, the television screen imposes limitations, but also opens up new opportunities for immersive gameplay.</p>
-<h3>A shared display</h3>
-<p>A living-room TV poses design challenges for multiplayer games, in that all players can see everything. This issue is especially germane to games (such as card games or strategy games) that rely on each player’s possession of hidden information.</p>
-<p>Some mechanisms you can implement to address the problem of one player’s “eavesdropping” on another’s information are:</p>
-<ul>
-<li>A player might place a "blinder" on the screen to help conceal information. For example, in a turn-based game like a word or card game, one player at a time might view the display. When the player finishes a move, the game allows him or her to cover the screen with a “blinder” that blocks anyone from viewing secret information. When the next player begins a turn, the blinder opens to reveal his or her own information.</li>
-<li>A second screen, such as a handset or larger device, can enable a player to conceal information. For information on implementing second-screen support, see <a href="http://developer.android.com/reference/android/app/Presentation.html">Presentation</a> on the Android developer site.</li>
-</ul>
-<h3>No touch interface</h3>
-<p>A television does not have a touch interface. Your game design, therefore, need not take into account the possibility that a player’s controlling fingers might block the on-screen action. You can assume constant visibility of the entire viewing area.</p>
-<p>See the <a href=#control>Control</a> section in this document and in [Design for TV](TODO, use formal name of referenced doc, and add link) for more implications of the lack of touch interface.</p>
-<h3>Landscape display</h3>
-<p>In mobile-device terms, a TV is always “sideways.” You can’t turn it, and there is no portrait orientation. You should always be designing your TV games to be displayed in landscape mode.</p>
-<a id=control><h2>Control</h2>
-<p>Without a touch interface, it's even more important than usual to get your controls right, so that players find them intuitive and fun to use. The separation of controller from device also introduces some other issues to pay attention to, like keeping track of multiple players' controllers, and handling disconnects gracefully.</p>
-<h3>D-pad</h3>
-<p>Because of the lack of touch interface, you should be planning your control scheme based on a D-pad. Some key points to keep in mind include:</p>
-<p>The player needs to use the gamepad in all aspects of the game–not just controlling core gameplay, but also navigating menus and ads. For this reason, you should also ensure that your Android TV game does not refer to a touch interface: for example, an Android TV game cannot tell a player to "Tap to skip".</p>
-<p>You can avoid unhappy surprises (and resulting low ratings) by using your Play Store description to communicate to the player any expectations about controllers. If a game is better suited to a gamepad with a joystick than one with only a D-pad, you should make this clear. A player who uses an ill-suited controller for a game is likely to have a subpar experience–and penalize your game in the ratings.</p>
-<p>You can also help ensure a good player experience by ensuring that button mapping is intuitive and flexible. For example, you can adhere to accepted custom by using the A button to <code>Accept</code>, and the B button to <code>Cancel</code>. You can also offer flexibility in the form of remappability. For more information on button mapping, see <a href="http://developer.android.com/training/game-controllers/controller-input.html">Handling Controller Actions</a>.</p>
-<p>Your game can also contribute to a good match between controller and game by querying the controller about its capabilities. For example, you may intend for a player to steer an object by waving the controller in the air. If a player's controller lacks accelerometer and gyroscope hardware, however, waving will not work. But when your game queries the controller and discovers that motion detection is not supported, it can switch over to an alternative, available control scheme.</p>
-<p>For more information on querying controller capabilities, see <a href="http://developer.android.com/training/game-controllers/compatibility.html">Supporting Controllers Across Android Versions</a>.</p>
-<h3>Back-button behavior</h3>
-<p>The Back button should never act as a toggle. For example, do not use it to both open and close a menu. Its behavior should only be linear. For example: Game play > Game pause screen > Game main screen > Android home screen.</p>
-<p>With this principle of "linear navigation" in mind, you <b>may</b> use the back button to leave an in-game menu (opened by a different button) and return to gameplay.</p>
-<h3>Handling multiple controllers</h3>
-<p>When multiple players are playing a game, each with his or her own controller, it is important to map each player-controller pair. For information on how to implement controller-number identification, see <a href="http://developer.android.com/reference/android/view/InputDevice.html#getControllerNumber(">Input Devices</a>) on the Android developer site.</p>
-<h3>Handling disconnects</h3>
-<p>When a controller is disconnected in the middle of gameplay, the game should pause, and a dialog should appear prompting the disconnected player to reconnect his or her controller.</p>
-<p>The dialog should also offer troubleshooting tips (e.g., "Check your Bluetooth connection").</p>
-<h2>Manifest</h2>
-<p>Games are displayed in a separate row from regular apps in the launcher. Android TV uses the <code>android:isGame</code> flag to differentiate games from non-game apps. You can assign it a value of either <code>true</code> or <code>false</code>. For example:</p>
-<pre class="fragment"><application>
- . . .
- <meta-data android:name="isGame" android:value=["true" | "false"]/>
-android:isGame=["true" | "false"] >
- . . .
-</application>
-</pre><h2>Google Play Game Services</h2>
-<p>If your game integrates Google Play Game Services, you should keep in mind a number of considerations pertaining to achievements, sign-on, saving games, and multiplayer play.</p>
-<h3>Achievements</h3>
-<p>Your game should include at least five (earnable) achievements. Only a user controlling gameplay from a supported input device should be able to earn achievements.</p>
-<h3>Sign-on</h3>
-<p>Your game should attempt to sign the user in on launch. If the player declines sign-in several times in a row, your game should stop asking.</p>
-<h3>Saving</h3>
-<p>We highly recommend using Play Services cloud save to store your game save. Your game should bind game saves to a specific Google account, so as to be uniquely identifiable even across devices: Whether the player is using a handset or a TV, the game should be able to pull the same game-save information from his or her account.</p>
-<p>You should also provide an option in your game's UI to prompt the player to destroy save data. You might put the option in the game's <code>Settings</code> screen.</p>
-<h3>Multiplayer experience</h3>
-<p>A game offering a multiplayer experience must allow at least two players to enter a room.</p>
-<h2>Web</h2>
-<p>Android TV games do not support a full web browser. You should therefore avoid using generic URLs in your game.</p>
-<p>Webviews will work for logins to services like Google+ and Facebook. </p>
-
diff --git a/docs/html/preview/tv/images/atv.png b/docs/html/preview/tv/images/atv.png
deleted file mode 100644
index cd96164..0000000
--- a/docs/html/preview/tv/images/atv.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/images/browsefragment.png b/docs/html/preview/tv/images/browsefragment.png
deleted file mode 100644
index 8998b13..0000000
--- a/docs/html/preview/tv/images/browsefragment.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/images/detailsfragment.png b/docs/html/preview/tv/images/detailsfragment.png
deleted file mode 100644
index 014ab23..0000000
--- a/docs/html/preview/tv/images/detailsfragment.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/images/home-recommendations.png b/docs/html/preview/tv/images/home-recommendations.png
deleted file mode 100644
index 2c18827..0000000
--- a/docs/html/preview/tv/images/home-recommendations.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/tv/index.jd b/docs/html/preview/tv/index.jd
deleted file mode 100644
index da40985..0000000
--- a/docs/html/preview/tv/index.jd
+++ /dev/null
@@ -1,12 +0,0 @@
-page.title=Android on TV
-
-@jd:body
-
-<img src="{@docRoot}preview/tv/images/atv.png" align="middle"/>
-
-<p>Android offers a rich user experience that's optimized for apps running on large screen
- devices, such as high-definition televisions. Apps on TV offer new opportunities to
- delight your users from the comfort of their couch.</p>
-
-<a href="{@docRoot}preview/tv/start/index.html">Get Started ></a>
-
diff --git a/docs/html/preview/tv/start/hardware-features.jd b/docs/html/preview/tv/start/hardware-features.jd
deleted file mode 100644
index f3b51bb..0000000
--- a/docs/html/preview/tv/start/hardware-features.jd
+++ /dev/null
@@ -1,174 +0,0 @@
-page.title=Hardware Features on TV
-page.tags="unsupported"
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#unsupported-features">Unsupported Hardware Features</a></li>
- <li><a href="#workaround-features">Handling Unsupported Features</a></li>
- <li><a href="#check-features">Checking Available Features</a>
- <ol>
- <li><a href="#no-touchscreen">Touch screen</a></li>
- <li><a href="#no-camera">Camera</a></li>
- <li><a href="#no-gps">GPS</a></li>
- </ol>
-
- </li>
- </ol>
-</div>
-</div>
-
-<p>TVs do not have some of the hardware features found on other Android devices.
-Touch screens, cameras and GPS receivers are some of the most commonly used hardware features
-which are typically not available on a TV. When you build an app for TV, you must carefully
-consider if your app can handle not having these features and, if necessary, work around them.</p>
-
-<p>This guide discusses the hardware features not available on TV devices and shows you how to
-work around those limitations in your app.</p>
-
-
-<h2 id="unsupported-features">Unsupported Hardware Features</h2>
-
-<p>TVs have a different purpose from other devices, and so they do not have hardware
-features that other Android-powered devices often have. For this reason, the Android system
-does not support the following features for a TV device:
-
-<table>
-<tr>
-<th>Hardware</th>
-<th>Android feature descriptor</th>
-</tr>
-<tr>
-<td>Camera</td>
-<td>android.hardware.camera</td>
-</tr>
-<tr>
-<td>GPS</td>
-<td>android.hardware.location.gps</td>
-</tr>
-<tr>
-<td>Microphone</td>
-<td>android.hardware.microphone</td>
-</tr>
-<tr>
-<td>Near Field Communications (NFC)</td>
-<td>android.hardware.nfc</td>
-</tr>
-<tr>
-<td>Telephony</td>
-<td>android.hardware.telephony</td>
-</tr>
-<tr>
-<td>Touchscreen</td>
-<td>android.hardware.touchscreen</td>
-</tr>
-</table>
-</p>
-
-
-<h2 id="check-features">Checking Available Features</h2>
-
-<p>To check if a feature is available at runtime, call {@link
- android.content.pm.PackageManager#hasSystemFeature(String)}. This method takes a single string
- argument that specifies the feature you want to check. For example, to check for a touch screen,
- use {@link android.content.pm.PackageManager#hasSystemFeature(String)} with the argument
- {@link android.content.pm.PackageManager#FEATURE_TOUCHSCREEN}.</p>
-
-<p>The following code example demonstrates how to detect the availability of a hardware features
- at runtime:</p>
-
-<pre>
-// Check if the telephony hardware feature is available.
-if (getPackageManager().hasSystemFeature("android.hardware.telephony")) {
- Log.d("Mobile Test", "Running on phone");
-// Check if android.hardware.touchscreen feature is available.
-} else if (getPackageManager().hasSystemFeature("android.hardware.touchscreen")) {
- Log.d("Tablet Test", "Running on devices that don't support telephony but "+
- "do have a touch screen.");
-} else {
- Log.d("TV Test", "Running on a TV!");
-}
-</pre>
-
-
-<h2 id="workaround-features">Handling Unsupported Features</h2>
-
-<p>Depending on the design and functionality of your app, you may be able to work around certain
- hardware features being unavailable. This section discusses how to workaround specific hardware
- features.</p>
-
-
-<h3 id="no-touchscreen">Touch screen</h3>
-
-<p>Android doesn't support touch screen interaction for TV devices, since most TVs don't have touch
- screens, and using a touch screen is not consistent with a viewing environment where the user is
- seated 10 feet away from the display.</p>
-
-<p>On TV devices, you should workaround this limitation by supporting navigation using a directional
- pad (D-pad) on TV remote control. For more information on properly supporting navigation using
- TV-friendly controls, see <a href="{@docRoot}preview/tv/ui/navigation.html">Navigation for
- TV</a>.</p>
-
-<p>You can explicitly declare if your application requires (or does not require) a touch screen
- by including the following entry in your manifest:</p>
-
-<pre>
-<uses-feature android:name="android.hardware.touchscreen"
- android:required="false"/>
-</pre>
-
-
-<h3 id="no-camera">Camera</h3>
-
-<p>Although a TV typically does not have a camera, you can still provide a photography-related
- application on a TV. For example, if you have an app that takes, views and edits photos, you can
- disable its picture-taking functionality for TVs and still allow users to view and even edit
- photos. If you decide that you want to enable your camera-related application to work on a
- TV device without a camera, you can add an attribute to your app manifest declaring that
- a camera is not required by your app:</p>
-
-<pre>
-<uses-feature android:name="android.hardware.camera" android:required="false" />
-</pre>
-
-<p>If you enable your application to run without a camera, you should add code to your application
-that detects if the camera feature is available and make adjustments to the operation of your app.
-The following code example demonstrates how to detect the presence of a camera:</p>
-
-<pre>
-// Check if the camera hardware feature is available.
-if (getPackageManager().hasSystemFeature("android.hardware.camera")) {
- Log.d("Camera test", "Camera available!");
-} else {
- Log.d("Camera test", "No camera available. View and edit features only.");
-}
-</pre>
-
-
-<h3 id="no-gps">GPS</h3>
-
-<p>TVs are stationary, indoor devices, and do not have built-in global positioning system (GPS)
- receivers. If your application uses location information, you can still allow users to search
- for a location or use a static location provider such as a zip code configured during the
- TV device setup.</p>
-
-<pre>
-LocationManager locationManager = (LocationManager) this.getSystemService(
- Context.LOCATION_SERVICE);
-Location location = locationManager.getLastKnownLocation("static");
-Geocoder geocoder = new Geocoder(this);
-Address address = null;
-
-try {
- address = geocoder.getFromLocation(location.getLatitude(),
- location.getLongitude(), 1).get(0);
- Log.d("Zip code", address.getPostalCode());
-
-} catch (IOException e) {
- Log.e(TAG, "Geocoder error", e);
-}
-</pre>
-
diff --git a/docs/html/preview/tv/start/index.jd b/docs/html/preview/tv/start/index.jd
deleted file mode 100644
index 7f726bd..0000000
--- a/docs/html/preview/tv/start/index.jd
+++ /dev/null
@@ -1,193 +0,0 @@
-page.title=Get Started with TV Apps
-page.tags="leanback","recyclerview","launcher"
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#prerequisites">Prerequisites</a></li>
- <li><a href="#dev-project">Setup a TV Project</a>
- <ul>
- <li><a href="#tv-activity">Create a TV Activity</a></li>
- <li><a href="#tv-libraries">Add TV Support Libraries</a></li>
- </ul>
- </li>
- <li><a href="#build-it">Build TV Apps</a></li>
- </ol>
-</div>
-</div>
-
-<p>This guide describes how to prepare your development environment and projects for building
- TV apps, including updating your existing app to run on TV devices.</p>
-
-
-<h2 id="prerequisites">Prerequisites</h2>
-
-<p>Before you begin setting up to build apps for TV, you must:</p>
-
-<ul>
- <li><strong><a href="{@docRoot}preview/setup-sdk.html">
- Setup the Preview SDK</a></strong>
- <br>
- The preview SDK provides the developer tools needed to build and test apps for TV.
- </li>
- <li><strong><a href="{@docRoot}preview/setup-sdk.html#project">
- Create a Preview SDK Project</a></strong>
- <br>
- In order to access new APIs for TV devices, you must create a project that targets the preview
- release level or modify an existing project to target the preview release.
- </li>
-</ul>
-
-
-<h2 id="dev-project">Setup a TV Project</h2>
-
-<p>TV apps use the same structure as those for phones and tablets. This means you can modify
- your existing apps to also run on TV devices or create new apps based on what you already know
- about building apps for Android. This section discusses how to modify an existing app or create a
- new app that runs on TV devices.</p>
-
-<p>There are the main steps to creating an app that runs on TV devices, only the first of these
- is required:</p>
-
-<ul>
- <li><strong>Activity for TV</strong> - (Required) Your application must have an activity that is
- is declared to run on TV devices through an app manifest entry.</li>
- <li><strong>TV Support Libraries</strong> - (Optional) There are several Support Libraries
- available for TV devices that provide user interface widgets for building user interfaces
- for use on TV.</li>
-</ul>
-
-
-<h3 id="tv-activity">Create a TV Activity</h3>
-
-<p>Applications that are intended to run on TV devices must declare a launcher activity for TV
- in their manifest using a the {@code android.intent.category.LEANBACK_LAUNCHER} intent filter.
- This filter identifies your app as being built for TV, enabling your app to be displayed in the
- Google Play app running on TV devices. Declaring this intent also identifies which activity in
- your app should be launched when a user selects your app icon on a TV.</p>
-
-<p class="caution">
- <strong>Caution:</strong> If you do not include the LEANBACK_LAUNCHER intent filter in your app,
- it will not be visible to users running the Google Play store on TV devices. If your load an app
- without this intent filter onto a TV device using developer tools, the app does not appear in
- the TV user interface.
-</p>
-
-<p>The following code snippet shows how to include this intent filter in your manifest:</p>
-
-<pre>
-<application>
- ...
- <activity
- android:name="com.example.android.MainActivity"
- android:label="@string/app_name" >
-
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
-
- <activity
- android:name="com.example.android.<strong>TvActivity</strong>"
- android:label="@string/app_name"
- android:theme="@android:style/Theme.NoTitleBar">
-
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="<strong>android.intent.category.LEANBACK_LAUNCHER</strong>" />
- </intent-filter>
-
- </activity>
-</application>
-</pre>
-
-<p>The second activity manifest entry in the example above specifies that it should be used as
- the main activity when your app launched on an TV device.</p>
-
-<p>If you have an existing app that you are modifying for TV use, your app should not use the same
- activity layout for TV that you do for phones and tablets. The user interface of your TV app (or
- TV portion of your existing app) should provide a simpler interface that can be easily navigated
- using a remote control from a couch. For guidelines on designing an app for TV, see the
- <a href="{@docRoot}design/tv/index.html">TV Design</a> guide. For more instructions on
- developing a user interface that is appropriate to TV, see the
- <a href="{@docRoot}preview/tv/ui/index.html">TV User Interface</a> guide.
-</p>
-
-
-<h3 id="tv-libraries">Add TV Support Libraries</h3>
-
-<p>The Preview SDK includes support libraries that are intended for use with TV apps. These
- libraries provide APIs and user interface widgets for use on TV devices. The libraries are
- located in the {@code <sdk>/extras/android/support/} directory where you installed the
- Preview SDK. Here is a list of the libraries and their general purpose:</p>
-
-<ul>
- <li><strong>v17 leanback library</strong> - Provides user interface widgets for TV, including
- {@code BrowseFragment}, {@code DetailsFragment}, and {@code SearchFragment}.
- <ul>
- <li>SDK location: {@code <sdk>/extras/android/support/v17/leanback}</li>
- <li>Gradle dependency: {@code com.android.support:leanback-v17:20.0.+}</li>
- <li>Contains resources: Yes</li>
- </ul>
- </li>
- <li><strong>v7 recyclerview library</strong> - Provides classes for managing display of long
- lists in a memory efficient manner. Several classes in the v17 leanback library depend on the
- classes in this library.
- <ul>
- <li>SDK location: {@code <sdk>/extras/android/support/v7/recyclerview}</li>
- <li>Gradle dependency: {@code com.android.support:recyclerview-v7:20.0.+}</li>
- <li>Contains resources: No</li>
- </ul>
- </li>
-</ul>
-
-<p class="note">
- <strong>Note:</strong> You are not required to use these support libraries for your TV app.
- However, we strongly recommend using them, particularly for apps that provide a media catalog
- browsing interface.
-</p>
-
-<p>If you decide to use the v17 leanback library for your application, you should note that it is
- dependent on the <a href="{@docRoot}tools/support-library/features.html#v7-appcompat">v7
- appcompat library</a>, which is, in turn, dependent on the
- <a href="{@docRoot}tools/support-library/features.html#v4">v4 support library</a>. This means
- that apps that use the leanback support library should include all of these support
- libraries:</p>
-
-<ul>
- <li>v17 leanback support library</li>
- <li>v7 recyclerview support library</li>
- <li>v7 appcompat support library</li>
- <li>v4 support library</li>
-</ul>
-
-<p>Two of these libraries (v17 leanback and v7 appcompat) contain resources, which require
- you to take specific steps to include them in app projects. For instructions on
- importing a support library with resources, see
- <a href="http://developer.android.com/tools/support-library/setup.html#libs-with-res">
- Support Library Setup</a>.
-</p>
-
-
-<h2 id="build-it">Build TV Apps</h2>
-
-<p>After you have completed the steps described above, it's time to start building apps for
- the big screen! Check out these additional topics to help you build your app for TV:
-
-<ul>
- <li><a href="{@docRoot}preview/tv/ui/index.html">User Interface</a> - The user interface of
- TV devices is different from those of other Android devices. See this topic to find out how
- to build TV user interfaces and the widgets provided to make it easier to build them.
- </li>
- <li><a href="{@docRoot}preview/tv/games/index.html">Games for TV</a> - TV devices are great
- platforms for games. See this topic for information on building great game experiences for
- TV.</li>
- <li><a href="{@docRoot}preview/tv/start/hardware-features.html">Hardware features</a> - TV
- devices do not contain hardware features normally found on other Android devices. See this
- topic for information on unsupported hardware features and what to do about them.
- </li>
-</ul>
diff --git a/docs/html/preview/tv/ui/browse.jd b/docs/html/preview/tv/ui/browse.jd
deleted file mode 100644
index 9d878b0..0000000
--- a/docs/html/preview/tv/ui/browse.jd
+++ /dev/null
@@ -1,217 +0,0 @@
-page.title=BrowseFragment
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-next.title=DetailsFragment
-next.link=details.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#layout">Media Browse Layout</a></li>
- <li><a href="#lists">Displaying Media Lists</a></li>
- <li><a href="#background">Updating the Background</a></li>
- </ol>
-
-</div>
-</div>
-
-<p>The <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
- provides several APIs for displaying and browsing media catalogs
- on the TV devices. This guide discusses how to use the classes provided by this library to
- implement a user interface for browsing music or videos from your app's media catalog.</p>
-
-
-<h2 id="layout">Media Browse Layout</h2>
-
-<p>The BrowseFragment class in the Leanback support library allows you to create a primary
- layout for browsing categories and rows of media items with a minimum of code. The following
- example layout shows how to create a layout that contains a BrowseFragment:</p>
-
-<pre>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
-
- <fragment
- <strong>android:name="android.support.v17.leanback.app.BrowseFragment"</strong>
- android:id="@+id/browse_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
-</LinearLayout>
-</pre>
-
-<p>In order to work with this layout in an activity, retrieve the BrowseFragment element from
- the layout. Use the BrowseFragment.Params class to set display parameters such as the icon, title
- and whether category headers are enabled. The following code sample demonstrates how to set the
- layout parameters for a BrowseFragment in a layout:</p>
-
-<pre>
-public class BrowseMediaActivity extends Activity {
-
- public static final String TAG ="BrowseActivity";
-
- protected BrowseFragment mBrowseFragment;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.browse_fragment);
-
- final FragmentManager fragmentManager = getFragmentManager();
- <strong>mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById(
- R.id.browse_fragment);</strong>
-
- // Set display parameters for the BrowseFragment
- BrowseFragment.Params params = mBrowseFragment.getBrowseParams();
- if (params == null) {
- params = new BrowseFragment.Params();
- }
- params.setHeadersState(BrowseFragment.HEADERS_ENABLED);
- params.setTitle(getString(R.string.app_name));
- params.setBadgeImage(getResources().getDrawable(R.drawable.ic_launcher));
- mBrowseFragment.setBrowseParams(params);
-
- }
-}
-</pre>
-
-
-<h2 id="lists">Displaying Media Lists</h2>
-
-<p>The BrowseFragment allows you to define and display browseable media content categories and
- media items from a media catalog using adapters and presenters. Adapters enable you to connect to
- local or online data sources that contain your media catalog information. Presenter classes hold
- data about media items and provide layout information for displaying an item on screen.</p>
-
-<p>The following example code shows an implementation of a presenter for displaying string
- data:</p>
-
-<pre>
-public class StringPresenter extends Presenter {
- private static final String TAG = "StringPresenter";
-
- public ViewHolder onCreateViewHolder(ViewGroup parent) {
- TextView textView = new TextView(parent.getContext());
- textView.setFocusable(true);
- textView.setFocusableInTouchMode(true);
- textView.setBackground(
- parent.getContext().getResources().getDrawable(R.drawable.text_bg));
- return new ViewHolder(textView);
- }
-
- public void onBindViewHolder(ViewHolder viewHolder, Object item) {
- ((TextView) viewHolder.view).setText(item.toString());
- }
-
- public void onUnbindViewHolder(ViewHolder viewHolder) {
- Log.d(TAG, "onUnbindViewHolder");
- }
-}
-</pre>
-
-<p>Once you have constructed a presenter class for your media items, you can build and attach an
- adapter to the BrowseFragment to display those items on screen for browsing by the user. The
- following example code demonstrates how to construct an adapter to display categories and items
- in those categories using the StringPresenter class shown in the previous code example:</p>
-
-<pre>
-private ArrayObjectAdapter mRowsAdapter;
-private static final int NUM_ROWS = 4;
-
-@Override
-protected void onCreate(Bundle savedInstanceState) {
- ...
-
- buildRowsAdapter();
-}
-
-private void buildRowsAdapter() {
- mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
-
- for (int i = 0; i < NUM_ROWS; ++i) {
- ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
- new StringPresenter());
- listRowAdapter.add("Media Item 1");
- listRowAdapter.add("Media Item 2");
- listRowAdapter.add("Media Item 3");
- HeaderItem header = new HeaderItem(i, "Category " + i, null);
- mRowsAdapter.add(new ListRow(header, listRowAdapter));
- }
-
- mBrowseFragment.setAdapter(mRowsAdapter);
-}
-</pre>
-
-<p>This example shows a static implementation of the adapters. A typical media browsing
- application uses data from an online database or web service. For an example of a browsing
- application that uses data retrieved from the web, see the <a href="">!FIX</a> code sample.</p>
-
-<p>The following screenshot shows the output of this code on an Android TV device:</p>
-
-<img src="{@docRoot}preview/tv/images/browsefragment.png" alt="" height="XXX" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> Display layout example based on the
- {@link android.support.v17.leanback.app.BrowseFragment} and {@code StringPresenter}
- classes.
-</p>
-
-
-<h2 id="background">Updating the Background</h2>
-
-<p>In order to add visual interest to a media browsing app on TV, you can update the background
- image as users browse through content. This technique can make interaction with your app feel more
- cinematic and enjoyable for users.</p>
-
-<p>The Leanback support library provides a {@link
- android.support.v17.leanback.app.BackgroundManager} class for changing the background of your TV
- app activity. The following example shows how to create a simple method for updating the
- background:</p>
-
-<pre>
-protected void updateBackground(Drawable drawable) {
- BackgroundManager.getInstance(this).setDrawable(drawable);
-}
-</pre>
-
-<p>Many of the existing media browse apps automatically update the background as the user
- navigates through media listings. In order to do this, you can set up a selection listener to
- automatically update the background based on the user's current selection. The following example
- shows you how to set up an {@link android.support.v17.leanback.widget.OnItemSelectedListener}
- class to catch selection events and update the background:</p>
-
-<pre>
-protected void clearBackground() {
- BackgroundManager.getInstance(this).setDrawable(mDefaultBackground);
-}
-
-protected OnItemSelectedListener getDefaultItemSelectedListener() {
- return new OnItemSelectedListener() {
- @Override
- public void onItemSelected(Object item, Row row) {
- if (item instanceof Movie ) {
- URI uri = ((Movie)item).getBackdropURI();
- updateBackground(uri);
- } else {
- clearBackground();
- }
- }
- };
-}
-</pre>
-
-<p class="note">
- <strong>Note:</strong> The implementation above is a simple example shown for purposes of
- illustration. When creating this function in your own app, you should consider running the
- background update action in a separate thread for better performance. In addition, if you are
- planning on updating the background in response to user's scrolling through items, consider adding
- a time to delay a background image update until the user settles on item, to avoid excessive
- background image updates.
-</p>
diff --git a/docs/html/preview/tv/ui/details.jd b/docs/html/preview/tv/ui/details.jd
deleted file mode 100644
index 7da3b5d..0000000
--- a/docs/html/preview/tv/ui/details.jd
+++ /dev/null
@@ -1,230 +0,0 @@
-page.title=DetailFragment
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=BrowseFragment
-previous.link=browse.html
-next.title=Searching in TV Apps
-next.link=in-app-search.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#details-presenter">Build a Details Presenter</a></li>
- <li><a href="#details-fragment">Extend the Details Fragment</a>
- <li><a href="#activity">Creating a Details Activity</a></li>
- <li><a href="#item-listener">Listener for Clicked Items</a></li>
- </li>
- </ol>
-</div>
-</div>
-
-<p>The media browsing interface classes provided by the
- <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
- include classes for displaying additional information about a media item, such as a description
- or reviews, and taking action on that item, such as purchasing it or playing its content. This
- section discusses how to create a presenter class for media item details and extend the
- {@code DetailsFragment} class to implement a details view for a media item when it
- is selected by a user.
-</p>
-
-<p class="note">
- <strong>Note:</strong> The implementation example shown here uses an additional activity to
- contain the {@code DetailsFragment}. However, it is possible to avoid creating a second activity
- by replacing the current {@code BrowseFragment} with a {@code DetailsFragment} within the <em>same</em>
- activity using fragment transactions. For more information on using fragment transactions, see the
- <a href="{@docRoot}training/basics/fragments/fragment-ui.html#Replace">Building a Dynamic
- UI with Fragments</a> training.
-</p>
-
-
-<h2 id="details-presenter">Build a Details Presenter</h2>
-
-<p>In the media browsing framework provided for by the leanback support library, you use
- presenter objects to control the display of data on screen, including media item details. The
- framework provides the {@code AbstractDetailsDescriptionPresenter} class for this purpose, which
- is a nearly complete implementation of the presenter for media item details. All you have to do is
- implement the {@code onBindDescription()} method to bind the view fields to your data objects, as shown in
- the following code sample:</p>
-
-<pre>
-public class DetailsDescriptionPresenter
- extends AbstractDetailsDescriptionPresenter {
-
- @Override
- protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
- MyMediaItemDetails details = (MyMediaItemDetails) itemData;
- // In a production app, the itemData object contains the information
- // needed to display details for the media item:
- // viewHolder.getTitle().setText(details.getShortTitle());
-
- // Here we provide static data for testing purposes:
- viewHolder.getTitle().setText(itemData.toString());
- viewHolder.getSubtitle().setText("2014 Drama TV-14");
- viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur "
- + "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
- + " et dolore magna aliqua. Ut enim ad minim veniam, quis "
- + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
- + "commodo consequat.");
- }
-}
-</pre>
-
-
-<h2 id="details-fragment">Extend the Details Fragment</h2>
-
-<p>When you use the {@code DetailsFragment} class for displaying your media item details, you
- extend that class to provide additional content such as a preview image and actions for the media
- item. You can also provide additional content, such as a list of related media items.</p>
-
-<p>The following example code demonstrates how to use the presenter class you created in the
- previous section, add a preview image and actions for the media item being viewed. This example
- also shows the addition of a related media items row, which appears below the details listing.</p>
-
-<pre>
-public class MediaItemDetailsFragment extends DetailsFragment {
- private static final String TAG = "MediaItemDetailsFragment";
- private ArrayObjectAdapter mRowsAdapter;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Log.i(TAG, "onCreate");
- super.onCreate(savedInstanceState);
-
- buildDetails();
- }
-
- private void buildDetails() {
- ClassPresenterSelector selector = new ClassPresenterSelector();
- // Attach your media item details presenter to the row presenter:
- DetailsOverviewRowPresenter rowPresenter =
- new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
-
- selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
- selector.addClassPresenter(ListRow.class,
- new ListRowPresenter());
- mRowsAdapter = new ArrayObjectAdapter(selector);
-
- Resources res = getActivity().getResources();
- DetailsOverviewRow detailsOverview = new DetailsOverviewRow(
- "Media Item Details");
-
- // Add images and action buttons to the details view
- detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans));
- detailsOverview.addAction(new Action(1, "Buy $9.99"));
- detailsOverview.addAction(new Action(2, "Rent $2.99"));
- mRowsAdapter.add(detailsOverview);
-
- // Add a Related items row
- ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
- new StringPresenter());
- listRowAdapter.add("Media Item 1");
- listRowAdapter.add("Media Item 2");
- listRowAdapter.add("Media Item 3");
- HeaderItem header = new HeaderItem(0, "Related Items", null);
- mRowsAdapter.add(new ListRow(header, listRowAdapter));
-
- setAdapter(mRowsAdapter);
- }
-}
-</pre>
-
-<p>The following screenshot shows the output of this code on a TV device:</p>
-
-<img src="{@docRoot}preview/tv/images/detailsfragment.png" alt="" height="XXX" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> Display layout example based on {@code DetailsFragment}, using a
- {@code DetailsOverviewRow} and a {@code ListRow} for related items.
-</p>
-
-
-<h3 id="activity">Creating a Details Activity</h3>
-
-<p>Fragments such as the {@code DetailsFragment} must be contained within an activity in order
- to be used for display. Creating an activity for your details view, separate from the browse
- activity, enables you to invoke your details view using an Intent. This section explains how to
- build an activity to contain your implementation of the detail view for your media items.</p>
-
-<p>Start creating the details activity by building a layout that references your implementation
- of the {@code DetailsFragment}:</p>
-
-<pre>
-<!-- file: res/layout/details.xml -->
-
-<fragment xmlns:android="http://schemas.android.com/apk/res/android"
- <strong>android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"</strong>
- android:id="@+id/details_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
-/>
-</pre>
-
-<p>Next, create an activity class that uses the layout shown in the previous code example:</p>
-
-<pre>
-public class DetailsActivity extends Activity
-{
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- <strong>setContentView(R.layout.details);</strong>
- }
-}
-</pre>
-
-<p>Finally, add this new activity to the manifest. Remember to apply the Leanback theme to
- ensure that the user interface is consistent with the media browse activity:</p>
-
-<pre>
-<application>
- ...
-
- <activity android:name=".DetailsActivity"
- android:exported="true"
- <strong>android:theme="@style/Theme.Leanback"/></strong>
-
-</application>
-</pre>
-
-
-<h3 id="item-listener">Listener for Clicked Items</h3>
-
-<p>After you have implemented the {@code DetailsFragment}, you must modify your main media
- browsing view to move to your details view when a user clicks on a media item. In order to enable
- this behavior, add an {@code OnItemClickedListener} object to the BrowseFragment that fires an
- intent to start the item details activity.</p>
-
-<p>The following example shows how to implement a listener to start the details view when a user
- clicks a media item in the main media browsing activity:</p>
-
-<pre>
-public class BrowseMediaActivity extends Activity {
- ...
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- ...
-
- // create the media item rows
- buildRowsAdapter();
-
- // add a listener for selected items
- mBrowseFragment.setOnItemClickedListener(
- new OnItemClickedListener() {
- @Override
- public void onItemClicked(Object item, Row row) {
- System.out.println("Media Item clicked: " + item.toString());
- Intent intent = new Intent(BrowseMediaActivity.this,
- DetailsActivity.class);
- // pass the item information
- intent.getExtras().putLong("id", item.getId());
- startActivity(intent);
- }
- });
- }
-}
-</pre>
diff --git a/docs/html/preview/tv/ui/in-app-search.jd b/docs/html/preview/tv/ui/in-app-search.jd
deleted file mode 100644
index b372254..0000000
--- a/docs/html/preview/tv/ui/in-app-search.jd
+++ /dev/null
@@ -1,119 +0,0 @@
-page.title=Searching in TV Apps
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=DetailsFragment
-previous.link=details.html
-next.title=Recommendations
-next.link=recommendations.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#add-search-ui">Add Search User Interface</a></li>
- </ol>
-
-</div>
-</div>
-
-
-<p>Users frequently have specific content in mind when using a media app. A search interface can
- help your users get to the content they want faster than browsing. The Leanback library provides a
- set of classes to enable a standard search interface within your app that is consistent with other
- search functions on TV and provides features such as voice input.</p>
-
-<h2 id="add-search-ui">Add Search User Interface</h2>
-<p>When you use the BrowseFragment class for your media browsing interface, you can enable the
- search icon by setting an OnClickListener to the browse fragment object. The following sample code
- demonstrates this technique.</p>
-
-<pre>
-@Override
-public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.browse_activity);
-
- mBrowseFragment = (BrowseFragment)
- getFragmentManager().findFragmentById(R.id.browse_fragment);
-
- ...
-
- mBrowseFragment.setOnSearchClickedListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(BrowseActivity.this, SearchActivity.class);
- startActivity(intent);
- }
- });
-
- mBrowseFragment.setAdapter(buildAdapter());
-}
-</pre>
-
-<p class="note">
- <strong>Note:</strong> You can set the color of the search icon using the
- {@code setSearchAffordanceColor()} method of {@code BrowseFragment}.
-</p>
-
-<p>When a user selects the search icon, the system invokes a search activity via the defined
- Intent. Your search activity should use a linear layout containing a SearchFragment. This fragment
- must also implement the SearchFragment.SearchResultProvider interface in order to display the
- results of a search. The following code sample shows how to extend the SearchFragment class to
- provide a search interface and results:</p>
-
-<pre>
-public class MySearchFragment extends SearchFragment
- implements SearchFragment.SearchResultProvider {
-
- private static final int SEARCH_DELAY_MS = 300;
- private ArrayObjectAdapter mRowsAdapter;
- private Handler mHandler = new Handler();
- private SearchRunnable mDelayedLoad;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
- setSearchResultProvider(this);
- setOnItemClickedListener(getDefaultItemClickedListener());
- mDelayedLoad = new SearchRunnable();
- }
-
- @Override
- public ObjectAdapter getResultsAdapter() {
- return mRowsAdapter;
- }
-
- @Override
- public boolean onQueryTextChange(String newQuery) {
- mRowsAdapter.clear();
- if (!TextUtils.isEmpty(newQuery)) {
- mDelayedLoad.setSearchQuery(newQuery);
- mHandler.removeCallbacks(mDelayedLoad);
- mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
- }
- return true;
- }
-
- @Override
- public boolean onQueryTextSubmit(String query) {
- mRowsAdapter.clear();
- if (!TextUtils.isEmpty(query)) {
- mDelayedLoad.setSearchQuery(query);
- mHandler.removeCallbacks(mDelayedLoad);
- mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
- }
- return true;
- }
-}
-</pre>
-
-<p>This example code shown above is meant to be used with a separate SearchRunnable class, that
- runs the search query on a separate thread. This technique keeps potentially slow-running queries
- from interfering with the main user interface thread.</p>
-
diff --git a/docs/html/preview/tv/ui/index.jd b/docs/html/preview/tv/ui/index.jd
deleted file mode 100644
index 9513bc6..0000000
--- a/docs/html/preview/tv/ui/index.jd
+++ /dev/null
@@ -1,44 +0,0 @@
-page.title=User Interfaces for TV
-page.tags="input","screens"
-
-trainingnavtop=true
-startpage=true
-
-@jd:body
-
-
-<p>
- Building an effective and engaging for TV devices requires a firm understanding what works well
- in the context of a living room. Imagine a large screen that can be seen by many people at the
- same time, controlled a few buttons by users with limited attention and you start to see the
- challenges and opportunity of building an app for TV. Building apps for this environment
- requires a different approach and different tools.</p>
-
-<p>This section discusses how to build a living room experience with your app, including
- implementation instructions and user interface widgets built for TV. Also check out the
- <a href="{@docRoot}design/tv/index.html">Design for TV</a> for information and inspiration
- on creating engaging user interfaces for TV devices.</p>
-
-<h2>Topics</h2>
-
-<dl>
- <dt><b><a href="layouts.html">Layouts</a></b></dt>
- <dd>Learn how to build app layouts for TV screens.</dd>
-
- <dt><b><a href="navigation.html">Navigation</a></b></dt>
- <dd>Learn how to build navigation for TV devices.</dd>
-
- <dt><b><a href="browse.html">BrowseFragment</a></b></dt>
- <dd>Learn how to use this fragment to build a browsing interface for media catalogs.</dd>
-
- <dt><b><a href="details.html">DetailsFragment</a></b></dt>
- <dd>Learn how to use this fragment to build a details page for media items.</dd>
-
- <dt><b><a href="search.html">In-App Search</a></b></dt>
- <dd>Learn how to use a built-for-TV user interface for searching within your app.</dd>
-
- <dt><b><a href="recommendations.html">Recommendations</a></b></dt>
- <dd>Learn how to contribute watch next suggestions and get your content noticed by users.</dd>
-</dl>
-
-
diff --git a/docs/html/preview/tv/ui/layouts.jd b/docs/html/preview/tv/ui/layouts.jd
deleted file mode 100644
index 5655152..0000000
--- a/docs/html/preview/tv/ui/layouts.jd
+++ /dev/null
@@ -1,298 +0,0 @@
-page.title=Layouts for TV
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-next.title=Navigation for TV
-next.link=navigation.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#themes">Themes</a>
- <ol>
- <li><a href="#leanback-theme">Leanback Theme</a></li>
- </ol>
- </li>
- <li><a href="#structure">Layout Structure</a>
- <ol>
- <li><a href="#overscan">Overscan</a></li>
- </ol>
- </li>
- <li><a href="#visibility">Text and Controls Visibility</a></li>
- <li><a href="#density-resources">Screen Density and Image Resources</a></li>
- <li><a href="#anti-patterns">Layout Anti-Patterns</a></li>
- <li><a href="#large-bitmaps">Handling Large Bitmaps</a></li>
- </ol>
-
-</div>
-</div>
-
-<p>
- A TV screen is typically viewed from about 10 feet away, and while it is much larger than most
- other Android device displays, this type of screen does not provide the same level of precise
- detail and color as a smaller device. These factors require that you create app layouts with
- TV devices in mind in order to create a useful and enjoyable user experience.</p>
-
-<p>This guide provides guidance and implementation details for building effective layouts for
- TV apps.</p>
-
-
-<h2 id="themes">Themes</h2>
-
-<p>Android <a href="{@docRoot}guide/topics/ui/themes.html">Themes</a> can provide a basis for
- layouts in your apps. On TV devices, you should use a theme to suppress the title bar from
- your app activities that are meant for TV. The title bar is a standard user interface element
- for Android apps on phones and tablets, but it is not appropriate for TV apps. The following
- code example from TV app manifest demonstrates how to apply this theme:
-</p>
-
-<pre>
-<application>
- ...
-
- <activity
- android:name="com.example.android.TvActivity"
- android:label="@string/app_name"
- <strong>android:theme="@android:style/Theme.NoTitleBar"</strong>>
- ...
-
- </activity>
-</application>
-
-</pre>
-
-
-<h3 id="leanback-theme">Leanback Theme</h3>
-
-<p>The Android framework provides a standard theme for TV activities, called {@code
- Leanback.Theme}, which establishes a consistent visual style for TV apps. Use of this theme is
- recommended for most apps. The following code sample shows how to apply this theme to a given
- activity within an app:</p>
-
-<pre>
-<activity
- android:name="com.example.android.TvActivity"
- android:label="@string/app_name"
- <strong>android:theme="@android:style/Theme.Leanback"</strong>>
-</pre>
-
-
-<h2 id="structure">Layout Structure</h2>
-
-<p>Layouts for TV devices should follow some basic guidelines to ensure they are useable and
- effective on large screens. Follow these tips to build landscape layouts optimized for TV screens:
-</p>
-
-<ul>
- <li>Build layouts with a landscape orientation. TV screens are always displayed in this
- orientation.</li>
- <li>Put on-screen navigation controls on the left or right side of the screen and save the
- vertical space for content.</li>
- <li>Create UIs that are divided into sections, using <a
- href="{@docRoot}guide/components/fragments.html"
- >Fragments</a> and use view groups like {@link android.widget.GridView} instead of {@link
- android.widget.ListView} to make better use of the horizontal screen space.
- </li>
- <li>Use view groups such as {@link android.widget.RelativeLayout} or {@link
- android.widget.LinearLayout} to arrange views. This approach allows the system to adjust the
- position of the views to the size, alignment, aspect ratio, and pixel density of a TV screen.</li>
- <li>Add sufficient margins between layout controls to avoid a cluttered UI.</li>
-</ul>
-
-
-<h3 id="overscan">Overscan</h3>
-
-<p>Layouts for TV have some unique requirements due to the evolution of TV standards and the
- desire to always present a full screen picture to viewers. For this reason, TV devices may
- clip the outside edge of an app layout in order to ensure a that the entire display is filled.
- This behavior is generally referred to as Overscan.</p>
-
-<p>In order to account for the impact of overscan and make sure that all the user interface
- elements you place in a layout are actually shown on screen, you should incorporate a 10% margin
- on all sides of your layout. This translates into a 27dp margin on the left and right edges and
- a 48dp margin on the top and bottom of your base layouts for activities. The following
- example layout demonstrates how to set these margins in the root layout for a TV app:
-</p>
-
-<pre>
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/base_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:layout_marginTop="27dp"
- android:layout_marginLeft="48dp"
- android:layout_marginRight="48dp"
- android:layout_marginBottom="27dp" >
-</LinearLayout>
-</pre>
-
-<p class="caution">
- <strong>Caution:</strong> Do not apply overscan margins to your layout if you are using the
- Leanback Support Library {@code BrowseFragment} or related widgets, as those layouts already
- incorporate overscan-safe margins.
-</p>
-
-
-<h2 id="visibility">Text and Controls Visibility</h2>
-
-<p>
-The text and controls in a TV app layout should be easily visible and navigable from a distance.
-Follow these tips to make them easier to see from a distance :
-</p>
-
-<ul>
- <li>Break text into small chunks that users can quickly scan.</li>
- <li>Use light text on a dark background. This style is easier to read on a TV.</li>
- <li>Avoid lightweight fonts or fonts that have both very narrow and very broad strokes.
- Use simple sans-serif fonts and anti-aliasing to increase readability.</li>
- <li>Use Android's standard font sizes:
-<pre>
-<TextView
- android:id="@+id/atext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
-</pre>
- </li>
- <li>Ensure that all your view widgets are large enough to be clearly visible to someone
- sitting 10 feet away from the screen (this distance is greater for very large screens). The
- best way to do this is to use layout-relative sizing rather than absolute sizing, and
- density-independent pixel units instead of absolute pixel units. For example, to set the
- width of a widget, use wrap_content instead of a pixel measurement, and to set the margin
- for a widget, use dip instead of px values.</li>
-</ul>
-
-
-<h2 id="density-resources">Screen Density and Image Resources</h2>
-
-<p>The common high-definition TV display resolutions are 720p, 1080i, and 1080p.
- Your TV layout should target a screen size of 1920 x 1080 pixels, and then allow the Android
- system to downscale your layout elements to 720p if necessary. In general, downscaling
- (removing pixels) does not degrade your layout presentation quality. However, upscaling can
- cause display artifacts that degrade the quality of your layout and have a negative impact on
- the user experience of your app.</p>
-
-<p>
- To get the best scaling results for images, provide them as
- <a href="{@docRoot}tools/help/draw9patch.html">9-patch image</a> elements if possible. If you
- provide low quality or small images in your layouts, they will appear pixelated, fuzzy, or
- grainy. This is not a good experience for the user. Instead, use high-quality images.
-</p>
-
-<p>
- For more information on optimizing layouts and resources for large screens see
- <a href="{@docRoot}training/multiscreen/index.html">Designing for multiple screens</a>.
-</p>
-
-
-<h2 id="anti-patterns">Layout Anti-Patterns</h2>
-
-<p>There are a few approaches to building layouts for TV that you should avoid because they do not
-work well and lead to bad user experiences. Here are some user interface approaches you
-should specifically <em>not</em> use when developing a layout for TV.
-</p>
-
-<ul>
- <li><strong>Re-using phone or tablet layouts</strong> - Do not reuse layouts from a phone or
- tablet app without modification. Layouts built for other Android device form factors are not
- well suited for TV devices and should be simplified for operation on a TV.</li>
- <li><strong>ActionBar</strong> - While this user interface convention is recommended for use
- on phones and tablets, it is not appropriate for a TV interface. In particular, using an
- antion bar options menu (or any pull-down menu for that matter) is strongly discouraged, due
- to the difficulty in navigating such a menu with a remote control.</li>
- <li><strong>ViewPager</strong> - Sliding between screens can work great on a phone or tablet,
- but don't try this on a TV!</li>
-
-</ul>
-
-<p>For more information on designing layouts that are appropriate to TV, see the
- <a href="{@docRoot}design/tv/index.html">TV Design</a> guide.</p>
-
-
-<h2 id="large-bitmaps">Handling Large Bitmaps</h2>
-
-<p>TV devices, like any other Android device, have a limited amount of memory. If you build your
- app layout with very high-resolution images or use many high-resolutions images in the operation
- of your app, it can quickly run into memory limits and cause out of memory errors.
- To avoid these types of problems, follow these tips:</p>
-
-<ul>
- <li>Load images only when they're displayed on the screen. For example, when displaying multiple images in
- a {@link android.widget.GridView} or
- {@link android.widget.Gallery}, only load an image when
- {@link android.widget.Adapter#getView(int, View, ViewGroup) getView()}
- is called on the View's {@link android.widget.Adapter}.
- </li>
- <li>Call {@link android.graphics.Bitmap#recycle()} on
- {@link android.graphics.Bitmap} views that are no longer needed.
- </li>
- <li>Use {@link java.lang.ref.WeakReference} for storing references
- to {@link android.graphics.Bitmap} objects in an in-memory
- {@link java.util.Collection}.</li>
- <li>If you fetch images from the network, use {@link android.os.AsyncTask}
- to fetch them and store them on the SD card for faster access.
- Never do network transactions on the application's UI thread.
- </li>
- <li>Scale down large images to a more appropriate size as you download them;
- otherwise, downloading the image itself may cause an out of memory exception.
- The following sample code demonstrates how to scale down images while downloading:
-<pre>
- // Get the source image's dimensions
- BitmapFactory.Options options = new BitmapFactory.Options();
- // This does not download the actual image, just downloads headers.
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
- // The actual width of the image.
- int srcWidth = options.outWidth;
- // The actual height of the image.
- int srcHeight = options.outHeight;
-
- // Only scale if the source is bigger than the width of the destination view.
- if(desiredWidth > srcWidth)
- desiredWidth = srcWidth;
-
- // Calculate the correct inSampleSize/scale value. This approach helps reduce
- // memory use. This value should be a power of 2.
- int inSampleSize = 1;
- while(srcWidth / 2 > desiredWidth){
- srcWidth /= 2;
- srcHeight /= 2;
- inSampleSize *= 2;
- }
-
- float desiredScale = (float) desiredWidth / srcWidth;
-
- // Decode with inSampleSize
- options.inJustDecodeBounds = false;
- options.inDither = false;
- options.inSampleSize = inSampleSize;
- options.inScaled = false;
- // Ensures the image stays as a 32-bit ARGB_8888 image.
- // This preserves image quality.
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-
- Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
-
- // Resize
- Matrix matrix = new Matrix();
- matrix.postScale(desiredScale, desiredScale);
- Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0,
- sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
- sampledSrcBitmap = null;
-
- // Save
- FileOutputStream out = new FileOutputStream(LOCAL_PATH_TO_STORE_IMAGE);
- scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
- scaledBitmap = null;
-</pre>
- </li>
-</ul>
-
diff --git a/docs/html/preview/tv/ui/navigation.jd b/docs/html/preview/tv/ui/navigation.jd
deleted file mode 100644
index 3041e58..0000000
--- a/docs/html/preview/tv/ui/navigation.jd
+++ /dev/null
@@ -1,144 +0,0 @@
-page.title=Navigation for TV
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=Layouts for TV
-previous.link=layouts.html
-next.title=BrowseFragment
-next.link=browse.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#d-pad-navigation">D-pad Navigation</a></li>
- <li><a href="#focus-selection">Focus and Selection</a></li>
- </ol>
-
-</div>
-</div>
-
-<p>TV devices provide a limited set of navigation controls for apps. Creating an effective
- navigation scheme for your TV app depends on understanding these limited controls and the limits
- of users perception while operating your app. As you build your Android application for TVs,
- you should pay special attention to how the user actually navigates around your application
- when using remote control buttons instead of a touch screen.</p>
-
-<p>This guide shows you how to build an effective navigation scheme for your TV app.</p>
-
-
-<h2 id="d-pad-navigation">D-pad Navigation</h2>
-
-<p>On a TV device, users navigate with controls on a remote control device, using either a
- directional pad (D-pad) or arrow keys. This type of control limits movement to up, down, left,
- and right. To build a great TV-optimized app, you must provide a navigation scheme where
- the user can quickly learn how to navigate your app using these limited controls.</p>
-
-<p>Follow these guidelines to build navigation system that works well with a D-pad on a TV device:
-</p>
-
-<ul>
- <li>Ensure that the D-pad can navigate to all the visible controls on the screen.</li>
- <li>For scrolling lists with focus, D-pad up/down keys scroll the list and Enter key selects
- an item in the list. Ensure that users can select an element in the list and that the list still
- scrolls when an element is selected.</li>
- <li>Ensure that movement between controls is straightforward and predictable.</li>
-</ul>
-
-<p>The Android framework handles directional navigation between layout elements automatically, so
- you typically do not need to do anything extra for your app. However, you should thoroughly test
- navigation with a D-pad control to discover any navigation problems. If you discover that your
- screen layout makes navigation difficult, or if you want users to move through the layout in a
- specific way, you can set up explicit directional navigation for your controls. The following
- code sample shows how to define the next control to receive focus for a
- {@link android.widget.TextView} layout object:</p>
-
-<pre>
-<TextView android:id="@+id/Category1"
- android:nextFocusDown="@+id/Category2"\>
-</pre>
-
-<p>The following table lists all of the available navigation attributes for Android user interface
-widgets:</p>
-
-<table>
- <tr>
- <th>Attribute</th>
- <th>Function</th>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusDown}</td>
- <td>Defines the next view to receive focus when the user navigates down.</td>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusLeft}</td>
- <td>Defines the next view to receive focus when the user navigates left.</td>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusRight}</td>
- <td>Defines the next view to receive focus when the user navigates right.</td>
- </tr>
- <tr>
- <td>{@link android.R.attr#nextFocusUp}</td>
- <td>Defines the next view to receive focus when the user navigates up.</td>
- </tr>
-</table>
-
-<p>To use one of these explicit navigation attributes, set the value to the ID ({@code android:id}
- value) of another widget in the layout. You should set up the navigation order as a loop, so that
- the last control directs focus back to the first one.</p>
-
-<p class="note">
- <strong>Note:</strong> You should only use these attributes to modify the navigation order if the
- default order that the system applies does not work well.
-</p>
-
-
-<h2 id="focus-selection">Focus and Selection</h2>
-
-<p>The success of a navigation scheme on TV devices is strongly dependent on how easy it is for a
- user to determine what user interface element is in focus on screen. If you do not provide clear
- indications of what is in focus on screen (and therefore what item they can take action on),
- users can quickly become frustrated and exit your app. By the same token, it is important
- to always have an item in focus that a user can take action on immediately after your app starts,
- and any time your app is not playing content.</p>
-
-<p>Your app layout and implementation should use color, size, animation or a combination of
- these attributes to help users easily determine what actions they can take next. Use a uniform
- scheme for indicating focus across your application.</p>
-
-<p>Android provides <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">
-Drawable State List Resources</a> to implement highlights for selected and focused controls. The
-following code example demonstates how to apply selection indication for a button object:
-</p>
-
-<pre>
-<!-- res/drawable/button.xml -->
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"
- android:drawable="@drawable/button_pressed" /> <!-- pressed -->
- <item android:state_focused="true"
- android:drawable="@drawable/button_focused" /> <!-- focused -->
- <item android:state_hovered="true"
- android:drawable="@drawable/button_focused" /> <!-- hovered -->
- <item android:drawable="@drawable/button_normal" /> <!-- default -->
-</selector>
-</pre>
-
-<p>
-This layout XML applies the above state list drawable to a {@link android.widget.Button}:
-</p>
-<pre>
-<Button
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:background="@drawable/button" />
-</pre>
-
-<p>Make sure to provide sufficient padding within the focusable and selectable controls so that
- the highlights around them are clearly visible.</p>
-
diff --git a/docs/html/preview/tv/ui/recommendations.jd b/docs/html/preview/tv/ui/recommendations.jd
deleted file mode 100644
index 52ac2e5..0000000
--- a/docs/html/preview/tv/ui/recommendations.jd
+++ /dev/null
@@ -1,234 +0,0 @@
-page.title=Making Recommendations
-parent.title=User Interfaces for TV
-parent.link=index.html
-
-trainingnavtop=true
-previous.title=Searching in TV Apps
-previous.link=in-app-search.html
-
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#service">Create a Recommendations Service</a></li>
- <li><a href="#build">Build Recommendations</a></li>
- <li><a href="#run-service">Run Recommendations Service</a></li>
- <li><a href="#DesignLandscapeLayouts">Design Landscape Layouts</a></li>
- </ol>
-
-</div>
-</div>
-
-
-<p>Content recommendations appear as the first row of the TV launch screen after the first use
- of the device. This row is intended to help users quickly find content they enjoy. Contributing
- recommendations from your apps content catalog can help bring users back to your app.</p>
-
-
-<img src="{@docRoot}preview/tv/images/home-recommendations.png" alt="" height="XXX" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> The first row after the search widget is the system-wide
- recommendations.
-</p>
-
-
-<h2 id="service">Create a Recommendations Service</h2>
-
-<p>Content recommendations are created with background processing. In order for your application
- to contribute to recommendations, you create a service that periodically adds listings from your
- app's catalog to the system list of recommendations.</p>
-
-<p>The following code example illustrates how to extend the {@link android.app.IntentService} to
- create a recommendation service for your application.</p>
-
-<pre>
-public class RecommendationsService extends IntentService {
-
- ...
-
- public Notification buildRecommendation(Context context, Movie movie)
- throws IOException {
-
- if (mNotificationManager == null) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- Bundle extras = new Bundle();
- if (mBackgroundUri != movie.getBackgroundUri()) {
- extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
- }
-
- // build the recommendation as a Notification object
- Notification notification = new NotificationCompat.BigPictureStyle(
- new NotificationCompat.Builder(context)
- .setContentTitle(movie.getTitle())
- .setContentText(movie.getDescription())
- .setPriority(movie.getPriority())
- .setOngoing(true)
- .setCategory("recommendation")
- .setLargeIcon(movie.getImage())
- .setSmallIcon(movie.getSmallIcon())
- .setContentIntent(buildPendingIntent(movie.getId()))
- .setExtras(extras))
- .build();
-
- // post the recommendation to the NotificationManager
- mNotificationManager.notify(movie.getId(), notification);
- mNotificationManager = null;
- return notification;
- }
-
- private PendingIntent buildPendingIntent(long id) {
- Intent detailsIntent = new Intent(this, DetailsActivity.class);
- detailsIntent.putExtra("id", id);
-
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
- stackBuilder.addParentStack(DetailsActivity.class);
- stackBuilder.addNextIntent(detailsIntent);
- // Ensure each PendingIntent is unique
- detailsIntent.setAction(Long.toString(id));
-
- PendingIntent intent = stackBuilder.getPendingIntent(
- 0, PendingIntent.FLAG_UPDATE_CURRENT);
- return intent;
- }
-}
-</pre>
-
-<p>In order for this class to be recognized and run as a service, you must register this service
- using your app manifest. The following code snippet illustrates how to add this class as a
- service:</p>
-
-<pre>
-<manifest ... >
- <application ... >
- ...
-
- <service android:name=".UpdateRecommendationsService"
- android:enabled="true" android:exported="true"/>
- </application>
-</manifest>
-</pre>
-
-<h2 id="build">Build Recommendations</h2>
-
-<p>Once it starts running, your service must create recommendations and pass them to the Android
- framework. The framework receives the recommendations as {@link android.app.Notification} objects
- that use a specific style and are marked with a specific category.</p>
-
-<p>The following code example demonstrates how to get an instance of the {@link
- android.app.NotificationManager}, build a recommendation and post it to the manager:</p>
-
-<pre>
-public class RecommendationsService extends IntentService {
-
- ...
-
- public Notification buildRecommendation(Context context, Movie movie)
- throws IOException {
-
- if (mNotificationManager == null) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- Bundle extras = new Bundle();
- if (mBackgroundUri != movie.getBackgroundUri()) {
- extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
- }
-
- // build the recommendation as a Notification object
- Notification notification = new NotificationCompat.BigPictureStyle(
- new NotificationCompat.Builder(context)
- .setContentTitle(movie.getTitle())
- .setContentText(movie.getDescription())
- .setPriority(movie.getPriority())
- .setOngoing(true)
- .setCategory("recommendation")
- .setLargeIcon(movie.getImage())
- .setSmallIcon(movie.getSmallIcon())
- .setContentIntent(buildPendingIntent(movie.getId()))
- .setExtras(extras))
- .build();
-
- // post the recommendation to the NotificationManager
- mNotificationManager.notify(movie.getId(), notification);
- mNotificationManager = null;
- return notification;
- }
-
- private PendingIntent buildPendingIntent(long id) {
- Intent detailsIntent = new Intent(this, DetailsActivity.class);
- detailsIntent.putExtra("id", id);
-
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
- stackBuilder.addParentStack(DetailsActivity.class);
- stackBuilder.addNextIntent(detailsIntent);
- // Ensure each PendingIntent is unique
- detailsIntent.setAction(Long.toString(id));
-
- PendingIntent intent = stackBuilder.getPendingIntent(
- 0, PendingIntent.FLAG_UPDATE_CURRENT);
- return intent;
- }
-}
-</pre>
-
-
-<h3 id="run-service">Run Recommendations Service</h3>
-
-<p>Your app's recommendation service must run periodically in order to create current
- recommendations. In order to run your service, you should create a class that runs a timer and
- invokes it at regular intervals. The following code example extends the {@link
- android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
- every 30 minutes:</p>
-
-<pre>
-public class BootupActivity extends BroadcastReceiver {
- private static final String TAG = "BootupActivity";
-
- private static final long INITIAL_DELAY = 5000;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
- scheduleRecommendationUpdate(context);
- }
- }
-
- private void scheduleRecommendationUpdate(Context context) {
- AlarmManager alarmManager = (AlarmManager)context.getSystemService(
- Context.ALARM_SERVICE);
- Intent recommendationIntent = new Intent(context,
- UpdateRecommendationsService.class);
- PendingIntent alarmIntent = PendingIntent.getService(context, 0,
- recommendationIntent, 0);
-
- alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- INITIAL_DELAY,
- AlarmManager.INTERVAL_HALF_HOUR,
- alarmIntent);
- }
-}
-</pre>
-
-<p>In order for the {@link android.content.BroadcastReceiver} class to execute after an TV
- device starts up, you must register this class in your app manifest and attach an intent filter
- for the completion of the device boot process. This sample code demonstrates how to add this
- configuration to the manifest:</p>
-
-<pre>
-<manifest ... >
- <application ... >
- <receiver android:name=".BootupActivity" android:enabled="true"
- android:exported="false">
- <intent-filter>
- <action android:name="android.intent.action.BOOT_COMPLETED"/>
- </intent-filter>
- </receiver>
- </application>
-</manifest>
-</pre>
diff --git a/docs/html/sdk/installing/adding-packages.jd b/docs/html/sdk/installing/adding-packages.jd
index c38c927..e6c0118 100644
--- a/docs/html/sdk/installing/adding-packages.jd
+++ b/docs/html/sdk/installing/adding-packages.jd
@@ -14,7 +14,7 @@
background:#eee;
}
ol.large > li:nth-child(odd) {
-}
+}
ol.large > li:before {
display:inline;
left:-40px;
@@ -74,9 +74,14 @@
</li>
</ul>
+<p>When you open the SDK Manager for the first time, several packages will be selected by
+default. Leave these selected, but be sure you have everything you need
+to get started by following these steps:</p>
+
+
<ol class="large">
<li>
- <h2 class="norule">Get the latest SDK tools</h2>
+ <h2 id="GetTools" class="norule">Get the latest SDK tools</h2>
<img src="/images/sdk_manager_packages.png" alt="" width="350" style="float:right;margin-left:20px" />
@@ -87,7 +92,7 @@
<ul>
<li><strong>Android SDK Tools</strong></li>
<li><strong>Android SDK Platform-tools</strong></li>
- <li><strong>Android SDK Build-tools</strong></li>
+ <li><strong>Android SDK Build-tools</strong> (highest version)</li>
</ul>
</li>
<li>Open the first Android X.X folder (the latest version) and select:
@@ -97,16 +102,13 @@
<strong>ARM EABI v7a System Image</strong></li>
</ul>
</li>
- <li>Click <strong>Install</strong>.</li>
</ol>
</li>
<li>
- <h2 class="norule">Get the support library for additional APIs</h2>
+ <h2 id="GetSupportLib" class="norule">Get the support library for additional APIs</h2>
<div class="sidebox">
- <h3>Why use the support library?</h3>
-
<p>The support library is required for:</p>
<ul>
<li><a href="{@docRoot}wear/index.html">Android Wear</a></li>
@@ -114,7 +116,7 @@
<li><a href="{@docRoot}google/play-services/cast.html">Google Cast</a></li>
</ul>
- <p>The support library also provides these popular APIs:</p>
+ <p>It also provides these popular APIs:</p>
<ul>
<li><a href="{@docRoot}reference/android/support/v4/widget/DrawerLayout.html">Navigation
drawer</a></li>
@@ -127,16 +129,11 @@
<p>The <a href="{@docRoot}tools/support-library/features.html">Android Support Library</a>
provides an extended set of APIs that are compatible with most versions of Android.</p>
- <p>To download the support library:</p>
- <ol>
- <li>Open the <strong>Extras</strong> directory and select:
+ <p>Open the <strong>Extras</strong> directory and select:</p>
<ul>
<li><strong>Android Support Repository</strong></li>
<li><strong>Android Support Library</strong></li>
</ul>
- </li>
- <li>Click <strong>Install</strong>.</li>
- </ol>
<p> </p>
<p> </p>
@@ -145,10 +142,9 @@
<li>
- <h2 class="norule">Get Google Play services for even more APIs</h2>
+ <h2 id="GetGoogle" class="norule">Get Google Play services for even more APIs</h2>
<div class="sidebox">
- <h3>Why use Google Play services?</h3>
<p>The Google Play services APIs provide a variety of features and services for your Android
apps, such as:</p>
@@ -163,15 +159,11 @@
</div>
<p>To develop with Google APIs, you need the Google Play services package:</p>
- <ol>
- <li>Open the <strong>Extras</strong> directory and select:
+ <p>Open the <strong>Extras</strong> directory and select:</p>
<ul>
<li><strong>Google Repository</strong></li>
<li><strong>Google Play services</strong></li>
</ul>
- </li>
- <li>Click <strong>Install</strong>.</li>
- </ol>
<p class="note"><strong>Note:</strong> Google Play services APIs are not available on all
Android-powered devices, but are available on all devices with Google Play Store. To use these
@@ -180,9 +172,21 @@
</li>
+<li>
+ <h2 id="Install" class="norule">Install the packages</h2>
+ <p>Once you've selected all the desired packages, continue to install:</p>
+ <ol>
+ <li>Click <strong>Install X packages</strong>.</li>
+ <li>In the next window, double-click each package name on the left
+ to accept the license agreement for each.</li>
+ <li>Click <strong>Install</strong>.</li>
+ </ol>
+ <p>The download progress is shown at the bottom of the SDK Manager window.
+ <strong>Do not exit the SDK Manager</strong> or it will cancel the download.</p>
+</li>
<li>
- <h2 class="norule">Build something!</h2>
+ <h2 id="Build" class="norule">Build something!</h2>
<p>With the above packages now in your Android SDK, you're ready to build apps
for Android. As new tools and other APIs become available, simply launch the SDK Manager
@@ -208,8 +212,8 @@
<h3>Use Google APIs</h3>
<p>To start using Google APIs, such as Maps or
Play Game services, see the guide to
-<strong><a href="{@docRoot}google/auth/api-client.html">Accessing Google Play Services
-APIs</a></strong>.</p>
+<strong><a href="{@docRoot}google/play-services/setup.html">Setting Up Google Play
+Services</a></strong>.</p>
</div>
</div><!-- end cols -->
diff --git a/docs/html/sdk/installing/index.jd b/docs/html/sdk/installing/index.jd
index b6929bd..3671726 100644
--- a/docs/html/sdk/installing/index.jd
+++ b/docs/html/sdk/installing/index.jd
@@ -108,13 +108,13 @@
your JDK folder, for example <code>C:\Program Files\Java\jdk1.7.0_21</code>.</p>
</p>
</li>
-
+
</ol>
<p>The individual tools and
other SDK packages are saved within the Android Studio application directory.
-To access the tools directly, use a terminal to navigate into the application and locate
+If you need to access the tools directly, use a terminal to navigate into the application and locate
the {@code sdk/} directory. For example:</p>
<p><code>\Users\<user>\AppData\Local\Android\android-studio\sdk\</code></p>
@@ -140,7 +140,7 @@
<p>The individual tools and
other SDK packages are saved within the Android Studio application directory.
-To access the tools directly, use a terminal to navigate into the application and locate
+If you need access the tools directly, use a terminal to navigate into the application and locate
the {@code sdk/} directory. For example:</p>
<p><code>/Applications/Android\ Studio.app/sdk/</code></p>
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index af6bd75..8ac6163 100644
--- a/docs/html/sdk/installing/studio.jd
+++ b/docs/html/sdk/installing/studio.jd
@@ -339,18 +339,12 @@
</tr>
<tr>
- <td>APK signing</td>
+ <td>APK signing and keystore management</td>
<td class="yes">Yes</td>
<td class="yes">Yes</td>
</tr>
<tr>
- <td>Keystore management</td>
- <td class="no">Coming soon</td>
- <td class="yes">Yes</td>
- </tr>
-
- <tr>
<td>NDK support</td>
<td class="no">Coming soon</td>
<td class="yes">Yes</td>
@@ -619,7 +613,7 @@
function onDownloadForRealz(link) {
if ($("input#agree").is(':checked')) {
- $("h1").text('Now downloading Android Studio...');
+ $("h1").text('Now redirecting to the install instructions...');
$("#tos").slideUp();
$("#jd-content .jd-descr").fadeOut('slow', function() {
setTimeout(function() {
diff --git a/docs/html/tv/images/hero.jpg b/docs/html/tv/images/hero.jpg
index c42a436..e951167 100644
--- a/docs/html/tv/images/hero.jpg
+++ b/docs/html/tv/images/hero.jpg
Binary files differ
diff --git a/docs/html/tv/index.jd b/docs/html/tv/index.jd
index e1cae8c..3e7652c 100644
--- a/docs/html/tv/index.jd
+++ b/docs/html/tv/index.jd
@@ -4,7 +4,6 @@
no_footer_links=true
page.type=about
-
@jd:body
<style>
@@ -14,17 +13,9 @@
}
</style>
-<style>
-#footer {
- display: none;
-}
-.content-footer {
- display: none;
-}
-</style>
-
<div class="landing-body-content">
+
<div class="landing-hero-container">
<div class="landing-section tv-hero">
@@ -42,9 +33,11 @@
Put your app on TV and bring everyone into
the action.</p>
</div>
+ </div>
<div class="landing-body">
- <a href="{@docRoot}preview/tv/index.html" class="landing-button landing-primary" style="margin-top: 40px;">
+ <a href="{@docRoot}preview/tv/start/index.html" class="landing-button
+ landing-primary" style="margin-top: 40px;">
Get Started
</a>
</div>
@@ -58,11 +51,10 @@
</a>
</div>
</div> <!-- end .landing-section .landing-hero -->
- </div> <!-- end .landing-hero-container -->
<div class="landing-rest-of-page">
- <div class="landing-section landing-gray-background" id="reimagine-your-app">
+ <div class="landing-section" style="background-color:#f5f5f5" id="reimagine-your-app">
<div class="wrap">
<div class="landing-section-header">
<div class="landing-h1">Reimagine Your App</div>
@@ -71,7 +63,6 @@
</div>
</div>
-
<div class="landing-body">
<div class="landing-breakout cols">
@@ -119,13 +110,13 @@
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
- <div class="landing-section" style="background-color:#f5f5f5">
+ <div class="landing-section landing-gray-background">
<div class="wrap">
<div class="landing-section-header">
<div class="landing-h1">Build to Entertain</div>
<div class="landing-subhead">
- Android TV let's you engage your users in a new, shared environment.<br>
- Find out how to get your app ready for it's big screen debut.
+ Android TV lets you engage your users in a new, shared environment.<br>
+ Find out how to get your app ready for its big-screen debut.
</div>
</div>
@@ -142,7 +133,7 @@
catalogs.
</p>
<p class="landing-small">
- <a href="{@docRoot}design/tv/index.html">Learn pre-built fragments</a>
+ <a href="{@docRoot}preview/tv/ui/browse.html">Learn pre-built fragments</a>
</p>
</div>
@@ -151,11 +142,10 @@
<p>Get Found</p>
<p class="landing-small">
- Give your content the attention it deserves by including it in Android TV's global
- search results.
+ Help users find your content quickly with in-app searching.
</p>
<p class="landing-small">
- <a href="{@docRoot}design/tv/index.html">Learn about TV design</a>
+ <a href="{@docRoot}preview/tv/ui/in-app-search.html">Learn about app search</a>
</p>
</div>
@@ -167,7 +157,8 @@
Suggest content from your app to keep your users coming back.
</p>
<p class="landing-small">
- <a href="{@docRoot}design/tv/index.html">Learn about design for TV</a>
+ <a href="{@docRoot}preview/tv/ui/recommendations.html">Learn about
+ recommendations</a>
</p>
</div>
@@ -182,28 +173,26 @@
<div class="landing-section-header">
<div class="landing-h1 landing-align-left">Get Started with Android TV</div>
<div class="landing-body">
- <p>You can begin building apps right away using these developer resources.</p>
+ <p>Begin building TV apps right away using these developer resources:</p>
</div>
</div>
<div class="landing-body">
<div class="landing-breakout cols">
- <div class="col-8">
- <p>Preview SDK</p>
+ <div class="col-8" style="margin-left: -8px;">
+ <p style="font-size: 24px;">L-Preview SDK</p>
<p>
- Get started building for Android TV using the Android L-preview SDK. The preview
- SDK includes the Android TV emulator so you can start building your TV app right
- away.
+ The preview SDK includes all the tools you need to build and test apps for TV.
+ Download it and start creating your big-screen app.
</p>
</div>
<div class="col-8">
- <p>ADT-1 Developer Kit</p>
+ <p style="font-size: 24px;">ADT-1 Developer Kit</p>
<p>
- While supplies last, developers can request an ADT-1 Developer Kit, a compact and
- powerful streaming media player and gamepad, ideal for developing apps for Android
- TV.
+ Request an ADT-1 Developer Kit, a compact and powerful streaming media player
+ and gamepad, ideal for developing and testing apps for TV.
</p>
</div>
@@ -215,15 +204,16 @@
<div class="landing-breakout cols">
<div class="col-8">
- <a href="{@docRoot}preview/download.html" class="landing-button landing-secondary">
+ <a href="{@docRoot}preview/setup-sdk.html" class="landing-button landing-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 @@
</div> <!-- end .landing-rest-of-page -->
-
- <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
- <div class="layout-content-col col-16" style="padding-top:4px">
- <style>#___plusone_0 {float:right !important;}</style>
- <div class="g-plusone" data-size="medium"></div>
+ <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement"
+ style="border-top: none;">
+ <div class="layout-content-col col-16" style="padding-top:4px">
+ <style>#___plusone_0 {float:right !important;}</style>
+ <div class="g-plusone" data-size="medium"></div>
+ </div>
</div>
- </div>
- <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
- <div id="copyright">
- Except as noted, this content is
- licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
- Creative Commons Attribution 2.5</a>. For details and
- restrictions, see the <a href="/license.html">Content
- License</a>.
+ <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
+ <div id="copyright">
+ Except as noted, this content is
+ licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+ Creative Commons Attribution 2.5</a>. For details and
+ restrictions, see the <a href="/license.html">Content
+ License</a>.
+ </div>
</div>
- </div>
-
- </div> <!-- end landing-body-content -->
+ </div> <!-- end .landing-hero-container -->
<script>
$("a.landing-down-arrow").on("click", function(e) {
$("body").animate({
- scrollTop: $(".wear-hero").height() + 76
+ scrollTop: $(".tv-hero").height() + 120
}, 1000, "easeOutQuint");
e.preventDefault();
});
</script>
+
+</div> <!-- end landing-body-content -->
+
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index 026698c..0d9325d 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -51,7 +51,7 @@
</div>
<div class="landing-body">
- <a href="/training/wearables/index.html" class="landing-button landing-primary" style="margin-top: 40px;">
+ <a href="{@docRoot}training/building-wearables.html" class="landing-button landing-primary" style="margin-top: 40px;">
Get Started
</a>
</div>
@@ -62,11 +62,11 @@
</div> <!-- end .wrap -->
<div class="landing-scroll-down-affordance">
<a class="landing-down-arrow" href="#extending-android-to-wearables">
- <img src="/wear/images/carrot.png" alt="Scroll down to read more">
+ <img src="{@docRoot}wear/images/carrot.png" alt="Scroll down to read more">
</a>
</div>
</div> <!-- end .landing-section .landing-hero -->
- </div> <!-- end .landing-hero-container -->
+
<div class="landing-rest-of-page">
<div class="landing-section" id="extending-android-to-wearables">
@@ -85,8 +85,8 @@
<div class="col-3-wide">
<div class="landing-inset-video-container">
- <img class="landing-bezel-only" src="/wear/images/screens/bezel.png" alt="">
- <img class="gif" src="/wear/images/screens/reservation_animated.gif">
+ <img class="landing-bezel-only" src="{@docRoot}wear/images/screens/bezel.png" alt="">
+ <img class="gif" src="{@docRoot}wear/images/screens/reservation_animated.gif">
</div>
<p class="landing-small">
@@ -94,7 +94,7 @@
</p>
</div>
<div class="col-3-wide">
- <img src="/wear/images/screens/circle_message2.png" itemprop="image" alt="">
+ <img src="{@docRoot}wear/images/screens/circle_message2.png" itemprop="image" alt="">
<p class="landing-small">
Get glanceable, actionable information at just the right time with notifications
that are synced from your handheld device.
@@ -102,7 +102,7 @@
</p>
</div>
<div class="col-3-wide">
- <img src="/wear/images/screens/fitness-24.png" alt="">
+ <img src="{@docRoot}wear/images/screens/fitness-24.png" alt="">
<p class="landing-small">
Design apps that can access a wide range of sensors and other hardware
directly on the wearable.
@@ -113,7 +113,7 @@
<p>
Before you start building, check out the
- <a href="/design/devices/wear.html">Android Wear Design Principles</a>
+ <a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a>
to understand how to create great experiences for this exciting, new form factor.</p>
</div>
@@ -133,49 +133,49 @@
<div class="landing-body">
<div class="landing-breakout cols">
<div class="col-4">
- <img src="/wear/images/features/ts2.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts2.png" alt="">
<p>Synced Notifications</p>
<p class="landing-small">
Notifications on handhelds can automatically sync to wearables, so design them
with both devices in mind.
</p>
<p class="landing-small">
- <a href="/training/wearables/notifications/index.html">Build notifications</a>
+ <a href="{@docRoot}training/wearables/notifications/index.html">Build notifications</a>
</p>
</div>
<div class="col-4">
- <img src="/wear/images/features/ts1.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts1.png" alt="">
<p>Wearable Apps</p>
<p class="landing-small">
Create custom experiences with activities, services, sensors, and much
more with the Android SDK.
</p>
<p class="landing-small">
- <a href="/training/wearables/apps/index.html/">Create wearable apps</a>
+ <a href="{@docRoot}training/wearables/apps/index.html">Create wearable apps</a>
</p>
</div>
<div class="col-4">
- <img src="/wear/images/features/ts2.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts2.png" alt="">
<p>Send Data</p>
<p class="landing-small">
Send data and actions between handhelds and wearables with
data replication APIs and RPCs.
</p>
<p class="landing-small">
- <a href="/training/wearables/apps/index.html/">Work with the Data Layer</a>
+ <a href="{@docRoot}training/wearables/data-layer/index.html">Work with the Data Layer</a>
</p>
</div>
<div class="col-4">
- <img src="/wear/images/features/ts4.png" alt="">
+ <img src="{@docRoot}wear/images/features/ts4.png" alt="">
<p>Voice Actions</p>
<p class="landing-small">
Register your app to handle voice actions, like "Ok Google, take a note,"
for a hands-free experience.
</p>
<p class="landing-small">
- <a href="/training/wearables/apps/index.html/">Integrate voice actions</a>
+ <a href="{@docRoot}training/wearables/apps/voice-actions.html">Integrate voice actions</a>
</p>
</div>
</div>
@@ -183,58 +183,6 @@
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
-<!--
-
- <div class="landing-section landing-white-background">
- <div class="wrap">
- <div class="landing-section-header">
- <div class="landing-h2">Building an Ecosystem</div>
- <div class="landing-body landing-align-center">
- <p class="landing-small">
- We’re working with several partners to bring you watches powered by Android Wear later this year!
- </p>
- </div>
- </div>
-
- <div class="landing-partners cols">
- <div class="col-4">
- <img src="/wear/images/partners/asus.png" alt="Asus">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/broadcom.png" alt="Broadcom">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/fossil.png" alt="Fossil">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/htc.png" alt="HTC">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/intel.png" alt="Intel">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/lg.png" alt="LG">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/mediatek.png" alt="Mediatek">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/mips.png" alt="MIPS">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/motorola.png" alt="Motorola">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/qualcomm.png" alt="Qualcomm">
- </div>
- <div class="col-4">
- <img src="/wear/images/partners/samsung.png" alt="Samsung">
- </div>
- </div>
- </div> <!-- end .wrap
-
- </div> <!-- end .landing-section -->
-
<div class="landing-section landing-red-background">
<div class="wrap">
<div class="landing-section-header">
@@ -248,7 +196,7 @@
</div>
</div>
<div class="landing-body">
- <a href="/training/wearables/index.html" class="landing-button landing-secondary" style="margin-top: 20px;">
+ <a href="{@docRoot}training/building-wearables.html" class="landing-button landing-secondary" style="margin-top: 20px;">
Get Started
</a>
</div>
@@ -277,7 +225,7 @@
</div>
<div class="col-3-wide">
<a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-landing-developer-preview.html">
- <img class="landing-social-image" src="/wear/images/blogger.png" alt="">
+ <img class="landing-social-image" src="{@docRoot}wear/images/blogger.png" alt="">
</a>
<div class="landing-social-copy">
<p>Blog Post</p>
@@ -313,12 +261,10 @@
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
</div> <!-- end .landing-rest-of-page -->
-
-
<div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
<div class="layout-content-col col-16" style="padding-top:4px">
<style>#___plusone_0 {float:right !important;}</style>
- <div id="___plusone_0" style="text-indent: 0px; margin: 0px; padding: 0px; border-style: none; float: none; line-height: normal; font-size: 1px; vertical-align: baseline; display: inline-block; width: 90px; height: 20px; background: transparent;"><iframe frameborder="0" hspace="0" marginheight="0" marginwidth="0" scrolling="no" style="position: static; top: 0px; width: 90px; margin: 0px; border-style: none; left: 0px; visibility: visible; height: 20px;" tabindex="0" vspace="0" width="100%" id="I0_1402525433965" name="I0_1402525433965" src="https://apis.google.com/u/0/_/+1/fastbutton?usegapi=1&size=medium&origin=http%3A%2F%2Frobertly.mtv%3A8080&url=http%3A%2F%2Frobertly.mtv%3A8080%2Fwear%2Findex.html&gsrc=3p&jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.en.QxHQHBkhz7M.O%2Fm%3D__features__%2Fam%3DUQ%2Frt%3Dj%2Fd%3D1%2Fz%3Dzcms%2Frs%3DAItRSTMLrMyRVKsu2FQoRingre3w1MT49A#_methods=onPlusOne%2C_ready%2C_close%2C_open%2C_resizeMe%2C_renderstart%2Concircled%2Cdrefresh%2Cerefresh%2Conload&id=I0_1402525433965&parent=http%3A%2F%2Frobertly.mtv%3A8080&pfname=&rpctoken=32453860" data-gapiattached="true" title="+1"></iframe></div>
+ <div class="g-plusone" data-size="medium"></div>
</div>
</div>
<div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
@@ -326,9 +272,20 @@
Except as noted, this content is
licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
Creative Commons Attribution 2.5</a>. For details and
- restrictions, see the <a href="/license.html">Content
+ restrictions, see the <a href="{@docRoot}license.html">Content
License</a>.
</div>
</div>
- </div> <!-- end landing-body-content -->
+ </div> <!-- end .landing-hero-container -->
+
+ <script>
+ $("a.landing-down-arrow").on("click", function(e) {
+ $("body").animate({
+ scrollTop: $(".wear-hero").height() + 120
+ }, 1000, "easeOutQuint");
+ e.preventDefault();
+ });
+ </script>
+
+</div> <!-- end landing-body-content -->
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 2b36016..158801c 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1091,7 +1091,15 @@
if (oval == null) {
throw new NullPointerException();
}
- native_drawOval(mNativeCanvasWrapper, oval, paint.mNativePaint);
+ drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
+ }
+
+ /**
+ * Draw the specified oval using the specified paint. The oval will be
+ * filled or framed based on the Style in the paint.
+ */
+ public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
+ native_drawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.mNativePaint);
}
/**
@@ -1133,10 +1141,34 @@
*/
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
@NonNull Paint paint) {
- if (oval == null) {
- throw new NullPointerException();
- }
- native_drawArc(mNativeCanvasWrapper, oval, startAngle, sweepAngle,
+ drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
+ paint);
+ }
+
+ /**
+ * <p>Draw the specified arc, which will be scaled to fit inside the
+ * specified oval.</p>
+ *
+ * <p>If the start angle is negative or >= 360, the start angle is treated
+ * as start angle modulo 360.</p>
+ *
+ * <p>If the sweep angle is >= 360, then the oval is drawn
+ * completely. Note that this differs slightly from SkPath::arcTo, which
+ * treats the sweep angle modulo 360. If the sweep angle is negative,
+ * the sweep angle is treated as sweep angle modulo 360</p>
+ *
+ * <p>The arc is drawn clockwise. An angle of 0 degrees correspond to the
+ * geometric angle of 0 degrees (3 o'clock on a watch.)</p>
+ *
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise
+ * @param useCenter If true, include the center of the oval in the arc, and
+ close it if it is being stroked. This will draw a wedge
+ * @param paint The paint used to draw the arc
+ */
+ public void drawArc(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle, boolean useCenter, @NonNull Paint paint) {
+ native_drawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
useCenter, paint.mNativePaint);
}
@@ -1908,14 +1940,14 @@
float top, float right,
float bottom,
long nativePaint);
- private static native void native_drawOval(long nativeCanvas, RectF oval,
- long nativePaint);
+ private static native void native_drawOval(long nativeCanvas, float left, float top,
+ float right, float bottom, long nativePaint);
private static native void native_drawCircle(long nativeCanvas, float cx,
float cy, float radius,
long nativePaint);
- private static native void native_drawArc(long nativeCanvas, RectF oval,
- float startAngle, float sweep,
- boolean useCenter,
+ private static native void native_drawArc(long nativeCanvas, float left, float top,
+ float right, float bottom,
+ float startAngle, float sweep, boolean useCenter,
long nativePaint);
private static native void native_drawRoundRect(long nativeCanvas,
float left, float top, float right, float bottom,
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 8837955..17ce026 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1532,20 +1532,22 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_breakText(text, index, count, maxWidth, mBidiFlags, measuredWidth);
+ return native_breakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
+ mBidiFlags, measuredWidth);
}
final float oldSize = getTextSize();
- setTextSize(oldSize*mCompatScaling);
- int res = native_breakText(text, index, count, maxWidth*mCompatScaling, mBidiFlags,
- measuredWidth);
+ setTextSize(oldSize * mCompatScaling);
+ int res = native_breakText(mNativePaint, mNativeTypeface, text, index, count,
+ maxWidth * mCompatScaling, mBidiFlags, measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
return res;
}
- private native int native_breakText(char[] text, int index, int count,
- float maxWidth, int bidiFlags, float[] measuredWidth);
+ private static native int native_breakText(long native_object, long native_typeface,
+ char[] text, int index, int count,
+ float maxWidth, int bidiFlags, float[] measuredWidth);
/**
* Measure the text, stopping early if the measured width exceeds maxWidth.
@@ -1622,19 +1624,21 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_breakText(text, measureForwards, maxWidth, mBidiFlags, measuredWidth);
+ return native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+ maxWidth, mBidiFlags, measuredWidth);
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- int res = native_breakText(text, measureForwards, maxWidth*mCompatScaling, mBidiFlags,
- measuredWidth);
+ int res = native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+ maxWidth*mCompatScaling, mBidiFlags, measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
return res;
}
- private native int native_breakText(String text, boolean measureForwards,
+ private static native int native_breakText(long native_object, long native_typeface,
+ String text, boolean measureForwards,
float maxWidth, int bidiFlags, float[] measuredWidth);
/**
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index c600f47..c40a66d 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -62,7 +62,7 @@
}
mNativePath = init2(valNative);
}
-
+
/**
* Clear any lines and curves from the path, making it empty.
* This does NOT change the fill-type setting.
@@ -205,7 +205,7 @@
* Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside.
*/
INVERSE_EVEN_ODD(3);
-
+
FillType(int ni) {
nativeInt = ni;
}
@@ -425,7 +425,7 @@
* the path is different from the path's current last point, then an
* automatic lineTo() is added to connect the current contour to the
* start of the arc. However, if the path is empty, then we call moveTo()
- * with the first point of the arc. The sweep angle is tread mod 360.
+ * with the first point of the arc.
*
* @param oval The bounds of oval defining shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
@@ -435,10 +435,9 @@
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle,
boolean forceMoveTo) {
- isSimplePath = false;
- native_arcTo(mNativePath, oval, startAngle, sweepAngle, forceMoveTo);
+ arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, forceMoveTo);
}
-
+
/**
* Append the specified arc to the path as a new contour. If the start of
* the path is different from the path's current last point, then an
@@ -451,10 +450,27 @@
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle) {
- isSimplePath = false;
- native_arcTo(mNativePath, oval, startAngle, sweepAngle, false);
+ arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, false);
}
-
+
+ /**
+ * Append the specified arc to the path as a new contour. If the start of
+ * the path is different from the path's current last point, then an
+ * automatic lineTo() is added to connect the current contour to the
+ * start of the arc. However, if the path is empty, then we call moveTo()
+ * with the first point of the arc.
+ *
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated
+ * mod 360.
+ * @param forceMoveTo If true, always begin a new contour with the arc
+ */
+ public void arcTo(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle, boolean forceMoveTo) {
+ isSimplePath = false;
+ native_arcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
+ }
+
/**
* Close the current contour. If the current point is not equal to the
* first point of the contour, a line segment is automatically added.
@@ -473,13 +489,13 @@
CW (1), // must match enum in SkPath.h
/** counter-clockwise */
CCW (2); // must match enum in SkPath.h
-
+
Direction(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
-
+
private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) {
if (mLastDirection == null) {
mLastDirection = dir;
@@ -557,11 +573,19 @@
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
public void addArc(RectF oval, float startAngle, float sweepAngle) {
- if (oval == null) {
- throw new NullPointerException("need oval parameter");
- }
+ addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle);
+ }
+
+ /**
+ * Add the specified arc to the path as a new contour.
+ *
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise
+ */
+ public void addArc(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle) {
isSimplePath = false;
- native_addArc(mNativePath, oval, startAngle, sweepAngle);
+ native_addArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
}
/**
@@ -573,13 +597,22 @@
* @param dir The direction to wind the round-rectangle's contour
*/
public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
- if (rect == null) {
- throw new NullPointerException("need rect parameter");
- }
- isSimplePath = false;
- native_addRoundRect(mNativePath, rect, rx, ry, dir.nativeInt);
+ addRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, dir);
}
-
+
+ /**
+ * Add a closed round-rectangle contour to the path
+ *
+ * @param rx The x-radius of the rounded corners on the round-rectangle
+ * @param ry The y-radius of the rounded corners on the round-rectangle
+ * @param dir The direction to wind the round-rectangle's contour
+ */
+ public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
+ Direction dir) {
+ isSimplePath = false;
+ native_addRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
+ }
+
/**
* Add a closed round-rectangle contour to the path. Each corner receives
* two radius values [X, Y]. The corners are ordered top-left, top-right,
@@ -593,13 +626,26 @@
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
+ addRoundRect(rect.left, rect.top, rect.right, rect.bottom, radii, dir);
+ }
+
+ /**
+ * Add a closed round-rectangle contour to the path. Each corner receives
+ * two radius values [X, Y]. The corners are ordered top-left, top-right,
+ * bottom-right, bottom-left
+ *
+ * @param radii Array of 8 values, 4 pairs of [X,Y] radii
+ * @param dir The direction to wind the round-rectangle's contour
+ */
+ public void addRoundRect(float left, float top, float right, float bottom, float[] radii,
+ Direction dir) {
if (radii.length < 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
isSimplePath = false;
- native_addRoundRect(mNativePath, rect, radii, dir.nativeInt);
+ native_addRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
}
-
+
/**
* Add a copy of src to the path, offset by (dx,dy)
*
@@ -755,19 +801,24 @@
float x2, float y2, float x3, float y3);
private static native void native_rCubicTo(long nPath, float x1, float y1,
float x2, float y2, float x3, float y3);
- private static native void native_arcTo(long nPath, RectF oval,
- float startAngle, float sweepAngle, boolean forceMoveTo);
+ private static native void native_arcTo(long nPath, float left, float top,
+ float right, float bottom, float startAngle,
+ float sweepAngle, boolean forceMoveTo);
private static native void native_close(long nPath);
private static native void native_addRect(long nPath, float left, float top,
float right, float bottom, int dir);
private static native void native_addOval(long nPath, float left, float top,
float right, float bottom, int dir);
private static native void native_addCircle(long nPath, float x, float y, float radius, int dir);
- private static native void native_addArc(long nPath, RectF oval,
- float startAngle, float sweepAngle);
- private static native void native_addRoundRect(long nPath, RectF rect,
+ private static native void native_addArc(long nPath, float left, float top,
+ float right, float bottom,
+ float startAngle, float sweepAngle);
+ private static native void native_addRoundRect(long nPath, float left, float top,
+ float right, float bottom,
float rx, float ry, int dir);
- private static native void native_addRoundRect(long nPath, RectF r, float[] radii, int dir);
+ private static native void native_addRoundRect(long nPath, float left, float top,
+ float right, float bottom,
+ float[] radii, int dir);
private static native void native_addPath(long nPath, long src, float dx, float dy);
private static native void native_addPath(long nPath, long src);
private static native void native_addPath(long nPath, long src, long matrix);
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index be940df..0a394d5 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -928,7 +928,7 @@
mTargetDensity = state.mTargetDensity;
}
- updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
computeBitmapSize();
}
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index cb88e3d..40b55a7 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1260,8 +1260,10 @@
/**
* Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
* attribute's enum value.
+ *
+ * @hide
*/
- static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
+ public static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
switch (value) {
case 3: return Mode.SRC_OVER;
case 5: return Mode.SRC_IN;
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 36ffddd..28335ea 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -671,7 +671,7 @@
mPadding = new Rect(state.mPadding);
}
- updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
setNinePatch(state.mNinePatch);
}
}
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 981efb8..beb300d 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -24,6 +24,7 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
@@ -284,31 +285,13 @@
}
@Override
- public void setTint(ColorStateList tint, Mode tintMode) {
- if (mShapeState.mTint != tint || mShapeState.mTintMode != tintMode) {
- mShapeState.mTint = tint;
- mShapeState.mTintMode = tintMode;
- updateTintFilter();
- invalidateSelf();
- }
- }
+ public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {
+ final ShapeState state = mShapeState;
+ state.mTint = tint;
+ state.mTintMode = tintMode;
- /**
- * Ensures the tint filter is consistent with the current tint color and
- * mode.
- */
- private void updateTintFilter() {
- final ColorStateList tint = mShapeState.mTint;
- final Mode tintMode = mShapeState.mTintMode;
- if (tint != null && tintMode != null) {
- if (mTintFilter == null) {
- mTintFilter = new PorterDuffColorFilter(0, tintMode);
- } else {
- mTintFilter.setMode(tintMode);
- }
- } else {
- mTintFilter = null;
- }
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ invalidateSelf();
}
@Override
@@ -349,17 +332,11 @@
@Override
protected boolean onStateChange(int[] stateSet) {
- final ColorStateList tint = mShapeState.mTint;
- if (tint != null) {
- final int newColor = tint.getColorForState(stateSet, 0);
- final int oldColor = mTintFilter.getColor();
- if (oldColor != newColor) {
- mTintFilter.setColor(newColor);
- invalidateSelf();
- return true;
- }
+ final ShapeState state = mShapeState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ return true;
}
-
return false;
}
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 91dda75..1b3f1fd 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -1768,6 +1768,7 @@
}
mergeInfoLocked(pMergedInfo, pContents);
+ delete pContents;
return true;
}
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 6fd9999..402f28b 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -565,11 +565,8 @@
// call, any texture operation will be performed on the default
// texture (name=0)
- for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
- if (mBoundTextures[i] == texture) {
- mBoundTextures[i] = 0;
- }
- }
+ unbindTexture(texture);
+
glDeleteTextures(1, &texture);
}
@@ -577,6 +574,14 @@
memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
}
+void Caches::unbindTexture(GLuint texture) {
+ for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
+ if (mBoundTextures[i] == texture) {
+ mBoundTextures[i] = 0;
+ }
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Scissor
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index b4b5927..83a5d9a 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -261,6 +261,11 @@
void resetBoundTextures();
/**
+ * Clear the cache of bound textures.
+ */
+ void unbindTexture(GLuint texture);
+
+ /**
* Sets the scissor for the current surface.
*/
bool setScissor(GLint x, GLint y, GLint width, GLint height);
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 1cb87f2..8b32c40 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -33,6 +33,7 @@
}
enum TransformType {
+ TransformInvalid = 0,
TransformRenderNode,
TransformMatrix4,
TransformNone,
@@ -56,6 +57,7 @@
memset(mHead, 0, sizeof(DirtyStack));
// Create a root that we will not pop off
mHead->prev = mHead;
+ mHead->type = TransformNone;
}
void DamageAccumulator::pushCommon() {
@@ -100,6 +102,8 @@
case TransformNone:
mHead->pendingDirty.join(dirtyFrame->pendingDirty);
break;
+ default:
+ LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
}
}
@@ -166,9 +170,12 @@
}
const RenderProperties& props = frame->renderNode->properties();
+ if (props.getAlpha() <= 0) {
+ return;
+ }
// Perform clipping
- if (props.getClipToBounds() && !frame->pendingDirty.isEmpty()) {
+ if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
frame->pendingDirty.setEmpty();
}
@@ -186,8 +193,6 @@
if (projectionReceiver) {
applyTransforms(frame, projectionReceiver);
projectionReceiver->pendingDirty.join(frame->pendingDirty);
- } else {
- ALOGW("Failed to find projection receiver? Dropping on the floor...");
}
frame->pendingDirty.setEmpty();
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 6a2ef2a..1002e13 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -185,6 +185,7 @@
}
void Layer::clearTexture() {
+ caches.unbindTexture(texture.id);
texture.id = 0;
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index df9aee5..a5fd375 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -334,8 +334,10 @@
void LayerRenderer::flushLayer(Layer* layer) {
#ifdef GL_EXT_discard_framebuffer
+ if (!layer) return;
+
GLuint fbo = layer->getFbo();
- if (layer && fbo) {
+ if (fbo) {
// If possible, discard any enqueud operations on deferred
// rendering architectures
if (Extensions::getInstance().hasDiscardFramebuffer()) {
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 59e15e1..310b107 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -57,17 +57,26 @@
#define ROUND_CAP_THRESH 0.25f
#define PI 3.1415926535897932f
+// temporary error thresholds
+#define ERROR_DEPTH 20
+#define ERROR_SCALE 1e10
+#define ERROR_SQR_INV_THRESH 1e-20
+
void PathTessellator::extractTessellationScales(const Matrix4& transform,
float* scaleX, float* scaleY) {
- *scaleX = 1.0f;
- *scaleY = 1.0f;
- if (CC_UNLIKELY(!transform.isPureTranslate())) {
+ if (CC_LIKELY(transform.isPureTranslate())) {
+ *scaleX = 1.0f;
+ *scaleY = 1.0f;
+ } else {
float m00 = transform.data[Matrix4::kScaleX];
float m01 = transform.data[Matrix4::kSkewY];
float m10 = transform.data[Matrix4::kSkewX];
float m11 = transform.data[Matrix4::kScaleY];
*scaleX = sqrt(m00 * m00 + m01 * m01);
*scaleY = sqrt(m10 * m10 + m11 * m11);
+
+ LOG_ALWAYS_FATAL_IF(*scaleX > ERROR_SCALE || *scaleY > ERROR_SCALE,
+ "scales %e x %e too large for tessellation", *scaleX, *scaleY);
}
}
@@ -92,10 +101,12 @@
public:
PaintInfo(const SkPaint* paint, const mat4& transform) :
style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
- inverseScaleX(1.0f), inverseScaleY(1.0f),
halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
// compute inverse scales
- if (CC_UNLIKELY(!transform.isPureTranslate())) {
+ if (CC_LIKELY(transform.isPureTranslate())) {
+ inverseScaleX = 1.0f;
+ inverseScaleY = 1.0f;
+ } else {
float scaleX, scaleY;
PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
@@ -922,6 +933,9 @@
Vector<Vertex>& outputVertices) {
ATRACE_CALL();
+ LOG_ALWAYS_FATAL_IF(sqrInvScaleX < ERROR_SQR_INV_THRESH || sqrInvScaleY < ERROR_SQR_INV_THRESH,
+ "Invalid scale factors used for approx %e, %e", sqrInvScaleX, sqrInvScaleY);
+
// TODO: to support joins other than sharp miter, join vertices should be labelled in the
// perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
SkPath::Iter iter(path, forceClose);
@@ -975,14 +989,14 @@
// Bezier approximation
///////////////////////////////////////////////////////////////////////////////
-// Depth at which recursion is aborted
-#define ABORT_DEPTH 20
-
void PathTessellator::recursiveCubicBezierVertices(
float p1x, float p1y, float c1x, float c1y,
float p2x, float p2y, float c2x, float c2y,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Vector<Vertex>& outputVertices, int depth) {
+ LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR DEPTH exceeded: cubic approx, invscale %e x %e, vertcount %d",
+ sqrInvScaleX, sqrInvScaleY, outputVertices.size());
+
float dx = p2x - p1x;
float dy = p2y - p1y;
float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
@@ -990,8 +1004,7 @@
float d = d1 + d2;
// multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
-
- if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, p2x, p2y);
} else {
@@ -1029,11 +1042,14 @@
float cx, float cy,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Vector<Vertex>& outputVertices, int depth) {
+ LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR_DEPTH exceeded: quadratic approx, invscale %e x %e, vertcount %d",
+ sqrInvScaleX, sqrInvScaleY, outputVertices.size());
+
float dx = bx - ax;
float dy = by - ay;
float d = (cx - bx) * dy - (cy - by) * dx;
- if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, bx, by);
} else {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 378183a..05f4edf 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -117,8 +117,8 @@
}
void RenderNode::damageSelf(TreeInfo& info) {
- if (isRenderable() && properties().getAlpha() > 0) {
- if (properties().getClipToBounds()) {
+ if (isRenderable()) {
+ if (properties().getClipDamageToBounds()) {
info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
} else {
// Hope this is big enough?
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 8c6cc9e..227d56e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -21,6 +21,7 @@
#include <vector>
#include <cutils/compiler.h>
#include <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
#include <SkCamera.h>
#include <SkMatrix.h>
@@ -30,6 +31,7 @@
#include "Rect.h"
#include "RevealClip.h"
#include "Outline.h"
+#include "utils/MathUtils.h"
class SkBitmap;
class SkColorFilter;
@@ -281,6 +283,7 @@
}
bool setScaleX(float scaleX) {
+ LOG_ALWAYS_FATAL_IF(scaleX > 1000000, "invalid scaleX %e", scaleX);
return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX);
}
@@ -289,6 +292,7 @@
}
bool setScaleY(float scaleY) {
+ LOG_ALWAYS_FATAL_IF(scaleY > 1000000, "invalid scaleY %e", scaleY);
return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY);
}
@@ -526,6 +530,15 @@
return mLayerProperties;
}
+ // Returns true if damage calculations should be clipped to bounds
+ // TODO: Figure out something better for getZ(), as children should still be
+ // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX
+ // for this RP's getZ() anyway, this can be optimized when we have a
+ // Z damage estimate instead of INT_MAX
+ bool getClipDamageToBounds() const {
+ return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty());
+ }
+
private:
// Rendering properties
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 440f965..dc1951b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -537,9 +537,12 @@
requireGlContext();
mode = DrawGlInfo::kModeProcess;
}
- (*functor)(mode, NULL);
if (mCanvas) {
+ mCanvas->interrupt();
+ }
+ (*functor)(mode, NULL);
+ if (mCanvas) {
mCanvas->resume();
}
}
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index f70110c..bdd1195 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -583,7 +583,8 @@
}
/**
- * Get the altitude if available, in meters above sea level.
+ * Get the altitude if available, in meters above the WGS 84 reference
+ * ellipsoid.
*
* <p>If this location does not have an altitude then 0.0 is returned.
*/
@@ -592,7 +593,7 @@
}
/**
- * Set the altitude, in meters above sea level.
+ * Set the altitude, in meters above the WGS 84 reference ellipsoid.
*
* <p>Following this call {@link #hasAltitude} will return true.
*/
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 4b4be1b..e05aef0 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -152,6 +152,8 @@
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/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f258063..22db344 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -782,7 +782,7 @@
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 e25714a..66175d0 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1826,11 +1826,7 @@
}
SubtitleTrack track = mInbandSubtitleTracks[index];
if (track != null) {
- long runID = data.getStartTimeUs() + 1;
- track.onData(data.getData(), true /* eos */, runID);
- track.setRunDiscardTimeMs(
- runID,
- (data.getStartTimeUs() + data.getDurationUs()) / 1000);
+ track.onData(data);
}
}
};
diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java
index b0e182d..9fedf63 100644
--- a/media/java/android/media/SubtitleTrack.java
+++ b/media/java/android/media/SubtitleTrack.java
@@ -75,6 +75,14 @@
private long mNextScheduledTimeMs = -1;
+ protected void onData(SubtitleData data) {
+ long runID = data.getStartTimeUs() + 1;
+ onData(data.getData(), true /* eos */, runID);
+ setRunDiscardTimeMs(
+ runID,
+ (data.getStartTimeUs() + data.getDurationUs()) / 1000);
+ }
+
/**
* Called when there is input data for the subtitle track. The
* complete subtitle for a track can include multiple whole units
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8eceee8..2e6b86e 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -133,7 +133,8 @@
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 7ff2c2e..bbe650d 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -144,15 +144,41 @@
/**
* Builds a URI that points to all programs on a given channel.
*
+ * @param channelId The ID of the channel to return programs for.
+ */
+ public static final Uri buildProgramsUriForChannel(long channelId) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_CHANNEL).appendPath(String.valueOf(channelId))
+ .appendPath(PATH_PROGRAM).build();
+ }
+
+ /**
+ * Builds a URI that points to all programs on a given channel.
+ *
* @param channelUri The URI of the channel to return programs for.
*/
public static final Uri buildProgramsUriForChannel(Uri channelUri) {
if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
throw new IllegalArgumentException("Not a channel: " + channelUri);
}
- String channelId = String.valueOf(ContentUris.parseId(channelUri));
- return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
- .appendPath(PATH_CHANNEL).appendPath(channelId).appendPath(PATH_PROGRAM).build();
+ return buildProgramsUriForChannel(ContentUris.parseId(channelUri));
+ }
+
+ /**
+ * Builds a URI that points to programs on a specific channel whose schedules overlap with the
+ * given time frame.
+ *
+ * @param channelId The ID of the channel to return programs for.
+ * @param startTime The start time used to filter programs. The returned programs should have
+ * {@link Programs#COLUMN_END_TIME_UTC_MILLIS} that is greater than this time.
+ * @param endTime The end time used to filter programs. The returned programs should have
+ * {@link Programs#COLUMN_START_TIME_UTC_MILLIS} that is less than this time.
+ */
+ public static final Uri buildProgramsUriForChannel(long channelId, long startTime,
+ long endTime) {
+ Uri uri = buildProgramsUriForChannel(channelId);
+ return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
+ .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
}
/**
@@ -167,9 +193,10 @@
*/
public static final Uri buildProgramsUriForChannel(Uri channelUri, long startTime,
long endTime) {
- Uri uri = buildProgramsUriForChannel(channelUri);
- return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
- .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
+ if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
+ throw new IllegalArgumentException("Not a channel: " + channelUri);
+ }
+ return buildProgramsUriForChannel(ContentUris.parseId(channelUri), startTime, endTime);
}
/**
@@ -821,7 +848,7 @@
*
* @hide
*/
- public static final class WatchedPrograms implements BaseColumns {
+ public static final class WatchedPrograms implements BaseTvColumns {
/** The content:// style URI for this table. */
public static final Uri CONTENT_URI =
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index edfdd60..daa7009 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -533,12 +533,11 @@
/**
* Releases this session.
- *
- * @throws IllegalStateException if the session has been already released.
*/
public void release() {
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.releaseSession(mToken, mUserId);
@@ -553,12 +552,12 @@
* Sets the {@link android.view.Surface} for this session.
*
* @param surface A {@link android.view.Surface} used to render video.
- * @throws IllegalStateException if the session has been already released.
* @hide
*/
public void setSurface(Surface surface) {
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
// surface can be null.
try {
@@ -573,11 +572,11 @@
*
* @param volume A volume value between 0.0f to 1.0f.
* @throws IllegalArgumentException if the volume value is out of range.
- * @throws IllegalStateException if the session has been already released.
*/
public void setStreamVolume(float volume) {
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
if (volume < 0.0f || volume > 1.0f) {
@@ -594,14 +593,14 @@
*
* @param channelUri The URI of a channel.
* @throws IllegalArgumentException if the argument is {@code null}.
- * @throws IllegalStateException if the session has been already released.
*/
public void tune(Uri channelUri) {
if (channelUri == null) {
throw new IllegalArgumentException("channelUri cannot be null");
}
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.tune(mToken, channelUri, mUserId);
@@ -620,8 +619,7 @@
* @param view A view playing TV.
* @param frame A position of the overlay view.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
- * @throws IllegalStateException if {@code view} is not attached to a window or
- * if the session has been already released.
+ * @throws IllegalStateException if {@code view} is not attached to a window.
*/
void createOverlayView(View view, Rect frame) {
if (view == null) {
@@ -634,7 +632,8 @@
throw new IllegalStateException("view must be attached to a window");
}
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
@@ -648,14 +647,14 @@
*
* @param frame A new position of the overlay view.
* @throws IllegalArgumentException if the arguments is {@code null}.
- * @throws IllegalStateException if the session has been already released.
*/
void relayoutOverlayView(Rect frame) {
if (frame == null) {
throw new IllegalArgumentException("frame cannot be null");
}
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.relayoutOverlayView(mToken, frame, mUserId);
@@ -666,12 +665,11 @@
/**
* Removes the current overlay view.
- *
- * @throws IllegalStateException if the session has been already released.
*/
void removeOverlayView() {
if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
+ Log.w(TAG, "The session has been already released");
+ return;
}
try {
mService.removeOverlayView(mToken, mUserId);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 8a7e642..7dba21d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -22,7 +22,6 @@
import android.hardware.ICameraServiceListener;
import android.hardware.IProCameraCallbacks;
import android.hardware.IProCameraUser;
-import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.impl.CameraMetadataNative;
@@ -51,8 +50,14 @@
* </pre>
*/
public class CameraBinderTest extends AndroidTestCase {
+ private static final int MAX_PARAMETERS_LENGTH = 100;
+
static String TAG = "CameraBinderTest";
+ // From ICameraService.h
+ private static final int API_VERSION_1 = 1;
+ private static final int API_VERSION_2 = 2;
+
protected CameraBinderTestUtils mUtils;
public CameraBinderTest() {
@@ -95,6 +100,56 @@
}
}
+ @SmallTest
+ public void testGetLegacyParameters() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ String[] parameters = new String[1];
+ assertEquals("Camera service returned parameters for camera " + cameraId,
+ CameraBinderTestUtils.NO_ERROR,
+ mUtils.getCameraService().getLegacyParameters(cameraId, /*out*/parameters));
+ assertNotNull(parameters[0]);
+ assertTrue("Parameters should have at least one character in it",
+ parameters[0].length() > 0);
+
+ int end = parameters[0].length();
+ if (end > MAX_PARAMETERS_LENGTH) {
+ end = MAX_PARAMETERS_LENGTH;
+ }
+
+ Log.v(TAG, "Camera " + cameraId + " parameters: " + parameters[0].substring(0, end));
+ }
+ }
+
+ /** The camera2 api is only supported on HAL3.2+ devices */
+ @SmallTest
+ public void testSupportsCamera2Api() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_2);
+
+ if (res != CameraBinderTestUtils.NO_ERROR && res != CameraBinderTestUtils.EOPNOTSUPP) {
+ fail("Camera service returned bad value when queried if it supports camera2 api: "
+ + res + " for camera ID " + cameraId);
+ }
+
+ boolean supports = res == CameraBinderTestUtils.NO_ERROR;
+ Log.v(TAG, "Camera " + cameraId + " supports api2: " + supports);
+ }
+ }
+
+ /** The camera1 api is supported on *all* devices regardless of HAL version */
+ @SmallTest
+ public void testSupportsCamera1Api() throws Exception {
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+ int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_1);
+ assertEquals(
+ "Camera service returned bad value when queried if it supports camera1 api: "
+ + res + " for camera ID " + cameraId, CameraBinderTestUtils.NO_ERROR, res);
+ }
+ }
+
static abstract class DummyBase extends Binder implements android.os.IInterface {
@Override
public IBinder asBinder() {
@@ -158,6 +213,7 @@
* android.hardware.camera2.ICameraDeviceCallbacks#onCameraError(int,
* android.hardware.camera2.CaptureResultExtras)
*/
+ @Override
public void onCameraError(int errorCode, CaptureResultExtras resultExtras)
throws RemoteException {
// TODO Auto-generated method stub
@@ -170,6 +226,7 @@
* android.hardware.camera2.ICameraDeviceCallbacks#onCaptureStarted(
* android.hardware.camera2.CaptureResultExtras, long)
*/
+ @Override
public void onCaptureStarted(CaptureResultExtras resultExtras, long timestamp)
throws RemoteException {
// TODO Auto-generated method stub
@@ -183,6 +240,7 @@
* android.hardware.camera2.impl.CameraMetadataNative,
* android.hardware.camera2.CaptureResultExtras)
*/
+ @Override
public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras)
throws RemoteException {
// TODO Auto-generated method stub
@@ -193,6 +251,7 @@
* (non-Javadoc)
* @see android.hardware.camera2.ICameraDeviceCallbacks#onCameraIdle()
*/
+ @Override
public void onCameraIdle() throws RemoteException {
// TODO Auto-generated method stub
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
index 1be2a62..6be538a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
@@ -22,6 +22,7 @@
protected static final int INVALID_OPERATION = -38;
protected static final int ALREADY_EXISTS = -17;
public static final int NO_ERROR = 0;
+ public static final int EOPNOTSUPP = -95;
private final Context mContext;
public CameraBinderTestUtils(Context context) {
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
index bac8cb6..305a82f 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
@@ -19,10 +19,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:zAdjustment="top">
+ android:zAdjustment="normal">
<alpha android:fromAlpha="1.0" android:toAlpha="1.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100"/>
+ android:interpolator="@android:interpolator/linear"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
index b0f8807..863591f 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
@@ -19,10 +19,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:zAdjustment="normal">
+ android:zAdjustment="top">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100"/>
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
index 2857c04..adcefe0 100644
--- a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
@@ -23,6 +23,6 @@
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="150"/>
+ android:interpolator="@android:interpolator/fast_out_linear_in"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
index 1139e72..863591f 100644
--- a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
@@ -23,6 +23,6 @@
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="150"/>
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="200"/>
</set>
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
index d4fdbf3..17100f7 100644
--- a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..e969d4c2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
index 9fc1a3b..b53bd8f 100644
--- a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..657f710
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
index f38de93..09606f6 100644
--- a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..a444c55
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
index 8194605..427cad9 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..29cf44b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/recents_button_bg.xml b/packages/SystemUI/res/drawable/recents_button_bg.xml
new file mode 100644
index 0000000..a4cb088
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_button_bg.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_status_bar_scrim.xml b/packages/SystemUI/res/layout/recents_status_bar_scrim.xml
new file mode 100644
index 0000000..24928d0
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_status_bar_scrim.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
+ android:scaleType="fitXY"
+ android:src="@drawable/recents_status_gradient" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 23f2796..1bab67a 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -32,8 +32,10 @@
android:id="@+id/application_icon"
android:layout_width="@dimen/recents_task_view_application_icon_size"
android:layout_height="@dimen/recents_task_view_application_icon_size"
- android:layout_marginStart="16dp"
- android:layout_gravity="center_vertical|start" />
+ android:layout_marginStart="8dp"
+ android:layout_gravity="center_vertical|start"
+ android:padding="8dp"
+ android:background="@drawable/recents_button_bg" />
<TextView
android:id="@+id/activity_description"
android:layout_width="match_parent"
@@ -56,6 +58,7 @@
android:layout_marginEnd="4dp"
android:layout_gravity="center_vertical|end"
android:padding="18dp"
+ android:background="@drawable/recents_button_bg"
android:visibility="invisible"
android:src="@drawable/recents_dismiss_light" />
</com.android.systemui.recents.views.TaskBarView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index d24d21e..353368b 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 4021f69..71fb8b8 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Прекарайте пръст наляво, за да включите камерата"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Докато не изключите това"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарежда се (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до пълно зареждане)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Гост"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ гост"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"За една минута"</item>
<item quantity="other" msgid="6924190729213550991">"За %d минути"</item>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index b3b4c9b..b1dc302 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -42,7 +42,7 @@
<string name="battery_saver_confirmation_title" msgid="5987726159603849352">"¿Iniciar ahorro de batería?"</string>
<string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Iniciar"</string>
<string name="battery_saver_start_action" msgid="7245333922937402896">"Iniciar ahorro de batería"</string>
- <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Para ayudar a mejorar la duración de la batería, la función de ahorro de energía reducirá el rendimiento del dispositivo.\n\nEsta función estará inhabilitada cuando el dispositivo esté conectado."</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Para ayudar a mejorar la duración de la batería, la función de ahorro de energía reducirá el rendimiento del dispositivo.\n\nEsta función estará inhabilitada cuando el dispositivo esté enchufado."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Ajustes"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Modo avión"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 571e9ba..03ac011 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -245,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Balayez l\'écran vers la gauche pour accéder à l\'appareil photo"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours... (chargée à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Invité"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"Ajouter un invité"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Pendant une minute"</item>
<item quantity="other" msgid="6924190729213550991">"Pendant %d minutes"</item>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index bcb28e9..fcad161 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 bd8b829..024be82 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Prijeđite prstom ulijevo za fotoaparat"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dok ne isključite"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Gost"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ gost"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Jednu minutu"</item>
<item quantity="other" msgid="6924190729213550991">"%d min"</item>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index ee8fa8a..4424338 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -32,26 +32,17 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ծանուցումներ չկան"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ընթացիկ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ծանուցումներ"</string>
- <!-- no translation found for battery_low_title (6456385927409742437) -->
- <skip />
+ <string name="battery_low_title" msgid="6456385927409742437">"Մարտկոցը լիցքաթափվում է"</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"մնում է <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (6534746636002666456) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"Մնաց <xliff:g id="NUMBER">%d%%</xliff:g>: Մարտկոցի տնտեսումը միացված է:"</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB լիցքավորումը չի աջակցվում:\nՕգտվեք միայն գործող լիցքավորիչից:"</string>
- <!-- no translation found for invalid_charger_title (3515740382572798460) -->
- <skip />
- <!-- no translation found for invalid_charger_text (5474997287953892710) -->
- <skip />
- <!-- no translation found for battery_low_why (4553600287639198111) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_title (5987726159603849352) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_ok (7283108887345688413) -->
- <skip />
- <!-- no translation found for battery_saver_start_action (7245333922937402896) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_text (8417584516834617662) -->
- <skip />
+ <string name="invalid_charger_title" msgid="3515740382572798460">"USB լիցքավորումը չի աջակցվում:"</string>
+ <string name="invalid_charger_text" msgid="5474997287953892710">"Օգտագործեք միայն մատակարարի տրամադրած լիցքավորիչը:"</string>
+ <string name="battery_low_why" msgid="4553600287639198111">"Կարգավորումներ"</string>
+ <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Գործարկե՞լ մարտկոցի տնտեսումը:"</string>
+ <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Մեկնարկել"</string>
+ <string name="battery_saver_start_action" msgid="7245333922937402896">"Գործարկել մարտկոցի տնտեսումը"</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Մարտկոցի տնտեսումը կնվազեցնի ձեր սարքի կատարողականը՝ մարտկոցն ավելի երկար օգտագործելու համար:\n\nՄարտկոցի տնտեսումը կանջատվի, հենց սարքը միացնեք հոսանքի աղբյուրին:"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Կարգավորումներ"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Ինքնաթիռային ռեժիմ"</string>
@@ -252,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Խցիկի համար սահեցրեք ձախ"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Քանի դեռ չեք անջատել"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> մինչև լրիվ լիցքավորումը)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Հյուր"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Հյուր"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Մեկ րոպե"</item>
<item quantity="other" msgid="6924190729213550991">"%d րոպե"</item>
@@ -264,10 +253,7 @@
<item quantity="one" msgid="3480040795582254384">"Մեկ ժամ"</item>
<item quantity="other" msgid="5408537517529822157">"%d ժամ"</item>
</plurals>
- <!-- no translation found for battery_saver_notification_title (237918726750955859) -->
- <skip />
- <!-- no translation found for battery_saver_notification_text (7796554871101546872) -->
- <skip />
- <!-- no translation found for battery_saver_notification_action_text (7546297220816993504) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"Մարտկոցի տնտեսումը միացված է"</string>
+ <string name="battery_saver_notification_text" msgid="7796554871101546872">"Սարքի կատարողականը նվազեցված է:"</string>
+ <string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Բացել մարտկոցի տնտեսման կարգավորումները"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 3a8aab8..5dbd490 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Gesek ke kiri untuk kamera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hingga Anda menonaktifkan ini"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengisi daya (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga penuh)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Tamu"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Tamu"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Selama satu menit"</item>
<item quantity="other" msgid="6924190729213550991">"Selama %d menit"</item>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index d595089..6ebd5c7 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -245,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"左にスワイプしてカメラを表示"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"ユーザーがOFFにするまで"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中(フルになるまで<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"ゲスト"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ ゲスト"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1分"</item>
<item quantity="other" msgid="6924190729213550991">"%d分"</item>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 9f7e9bf..8f1f30a 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 aa4f27d..0ffb4b8 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -245,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"카메라를 사용하려면 왼쪽으로 스와이프하세요."</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"이 기능을 사용 중지할 때까지"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"충전 중(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> 후 충전 완료)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"손님"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"새 손님 추가"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1분 동안"</item>
<item quantity="other" msgid="6924190729213550991">"%d분 동안"</item>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 5114935..7d8a1ad 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 be9af97..cecbe7a 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Sveip mot venstre for å åpne kameraet"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Inntil du slår av funksjonen"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Lader (fulladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Gjest"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Gjest"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"I ett minutt"</item>
<item quantity="other" msgid="6924190729213550991">"I %d minutter"</item>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 37aae0a..657e03f 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -194,8 +194,8 @@
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brilho"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotação automática"</string>
<string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotação bloqueada"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Vertical"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Horizontal"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Retrato"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Paisagem"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de Introdução"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Localização"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localização Desativada"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a540efbb..bcf7431 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -32,26 +32,17 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
- <!-- no translation found for battery_low_title (6456385927409742437) -->
- <skip />
+ <string name="battery_low_title" msgid="6456385927409742437">"Bateria fraca"</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> restante"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (6534746636002666456) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"<xliff:g id="NUMBER">%d%%</xliff:g> restante(s). A Economia de bateria está ativada."</string>
<string name="invalid_charger" msgid="4549105996740522523">"O carregamento via USB não é suportado.\nUse apenas o carregador fornecido."</string>
- <!-- no translation found for invalid_charger_title (3515740382572798460) -->
- <skip />
- <!-- no translation found for invalid_charger_text (5474997287953892710) -->
- <skip />
- <!-- no translation found for battery_low_why (4553600287639198111) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_title (5987726159603849352) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_ok (7283108887345688413) -->
- <skip />
- <!-- no translation found for battery_saver_start_action (7245333922937402896) -->
- <skip />
- <!-- no translation found for battery_saver_confirmation_text (8417584516834617662) -->
- <skip />
+ <string name="invalid_charger_title" msgid="3515740382572798460">"O carregamento via USB não é suportado."</string>
+ <string name="invalid_charger_text" msgid="5474997287953892710">"Use apenas o carregador fornecido."</string>
+ <string name="battery_low_why" msgid="4553600287639198111">"Configurações"</string>
+ <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Iniciar a economia de bateria?"</string>
+ <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Iniciar"</string>
+ <string name="battery_saver_start_action" msgid="7245333922937402896">"Iniciar economia de bateria"</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Para ajudar a melhorar a vida útil da bateria, a Economia de bateria reduzirá o desempenho do dispositivo.\n\nA Economia de bateria será desativada quando o dispositivo estiver carregando."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Configurações"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Modo avião"</string>
@@ -254,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Deslize para a esquerda para usar a câmera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Até você desativar"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até concluir)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Convidado"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ convidado"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Por 1 minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Por %d minutos"</item>
@@ -266,10 +255,7 @@
<item quantity="one" msgid="3480040795582254384">"Por 1 hora"</item>
<item quantity="other" msgid="5408537517529822157">"Por %d horas"</item>
</plurals>
- <!-- no translation found for battery_saver_notification_title (237918726750955859) -->
- <skip />
- <!-- no translation found for battery_saver_notification_text (7796554871101546872) -->
- <skip />
- <!-- no translation found for battery_saver_notification_action_text (7546297220816993504) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"A Economia de bateria está ativada"</string>
+ <string name="battery_saver_notification_text" msgid="7796554871101546872">"O desempenho do dispositivo foi reduzido."</string>
+ <string name="battery_saver_notification_action_text" msgid="7546297220816993504">"Abrir configurações de economia de bateria"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index e057b76..c099dd9 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 3f95a0c..8c59e05 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -245,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Чтобы включить камеру, пролистните влево"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Пока я не отключу"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарядка батареи (осталось <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Гость"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"Добавить гостя"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 мин."</item>
<item quantity="other" msgid="6924190729213550991">"%d мин."</item>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index ad12b18..10ba09f 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Povlecite v levo za fotoaparat"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dokler tega ne izklopite"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Gost"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"Dodajanje gosta"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Za eno minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Za %d min"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 22a89a9..3bee434 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-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index b49e1a5..42f262b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Mag-swipe pakaliwa para sa camera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hanggang sa i-off mo ito"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nagtsa-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang mapuno)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Bisita"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Bisita"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Sa loob ng isang minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Sa loob ng %d (na) minuto"</item>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 7bbf9ee..7635c01 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 45677f2..e15d3e5 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 0d27e29..cfd4fe9 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 c5805a1..4b5cf08 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -42,7 +42,7 @@
<string name="battery_saver_confirmation_title" msgid="5987726159603849352">"啟動節約電池用量模式?"</string>
<string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"開始"</string>
<string name="battery_saver_start_action" msgid="7245333922937402896">"啟動節約電池用量模式"</string>
- <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"節約電池用量模式有助於延長電池壽命,但會讓裝置的效能降低。\n\n裝置接上電源時,節約電池用量模式會自動停用。"</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"節約電池用量模式有助於延長電池壽命,但會降低裝置的效能。\n\n裝置接上電源時,節約電池用量模式會自動停用。"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"設定"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"飛行模式"</string>
@@ -245,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"向左快速滑動即可使用相機功能"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"直至您關閉這項設定"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後完成充電)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"訪客"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"新增訪客"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 分鐘"</item>
<item quantity="other" msgid="6924190729213550991">"%d 分鐘"</item>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 8f2d247..a780ad1 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -245,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"向左滑動可使用相機功能"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"手動關閉這項設定前一律啟用"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後充飽)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"訪客"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"新增訪客"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 分鐘"</item>
<item quantity="other" msgid="6924190729213550991">"%d 分鐘"</item>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c8851dc..c64a182 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 7ed3e29..bbcc9c1 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>
@@ -330,4 +330,6 @@
phone hints. -->
<dimen name="edge_tap_area_width">48dp</dimen>
+ <!-- end margin for multi user switch in expanded quick settings -->
+ <dimen name="multi_user_switch_expanded_margin">8dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ffd76a7..b9e2e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -239,6 +239,12 @@
private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
/**
+ * Whether a hide is pending an we are just waiting for #startKeyguardExitAnimation to be
+ * called.
+ * */
+ private boolean mHiding;
+
+ /**
* we send this intent when the keyguard is dismissed.
*/
private static final Intent USER_PRESENT_INTENT = new Intent(Intent.ACTION_USER_PRESENT)
@@ -1169,6 +1175,7 @@
}
mStatusBarKeyguardViewManager.show(options);
+ mHiding = false;
mShowing = true;
mKeyguardDonePending = false;
updateActivityLockScreenState();
@@ -1191,7 +1198,7 @@
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleHide");
try {
-
+ mHiding = true;
if (mShowing) {
// Don't actually hide the Keyguard at the moment, wait for window manager until
@@ -1212,6 +1219,11 @@
private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
synchronized (KeyguardViewMediator.this) {
+ if (!mHiding) {
+ return;
+ }
+ mHiding = false;
+
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index c3ba349..41b1f75 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -152,6 +152,7 @@
.setContentText(mContext.getString(R.string.invalid_charger_text))
.setPriority(Notification.PRIORITY_MAX)
.setCategory(Notification.CATEGORY_SYSTEM)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
.setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_CHARGER), true);
final Notification n = nb.build();
if (n.headsUpContentView != null) {
@@ -171,6 +172,7 @@
.setOngoing(true)
.setPriority(Notification.PRIORITY_MAX)
.setCategory(Notification.CATEGORY_SYSTEM)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
.setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_WARNING), true);
if (hasBatterySettings()) {
nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
@@ -197,7 +199,8 @@
.setContentText(mContext.getString(R.string.battery_saver_notification_text))
.setOngoing(true)
.setShowWhen(false)
- .setCategory(Notification.CATEGORY_SYSTEM);
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setVisibility(Notification.VISIBILITY_PUBLIC);
if (hasSaverSettings()) {
nb.addAction(0,
mContext.getString(R.string.battery_saver_notification_action_text),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
index b981ed6..c4bdb19 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
@@ -69,6 +69,7 @@
@Override
public void onViewAttachedToWindow(View v) {
+ vp.updateStates();
volumeComponent.setVolumePanel(vp);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 1d355cd..ddea0bf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -30,7 +30,7 @@
// Enables the screenshot app->Recents transition
public static final boolean EnableScreenshotAppTransition = false;
// Enables the filtering of tasks according to their grouping
- public static final boolean EnableTaskFiltering = false;
+ public static final boolean EnableTaskFiltering = true;
// Enables clipping of tasks against each other
public static final boolean EnableTaskStackClipping = true;
// Enables the use of theme colors as the task bar background
@@ -48,7 +48,7 @@
// For debugging, this defines the number of mock recents packages to create
public static final int SystemServicesProxyMockPackageCount = 3;
// For debugging, this defines the number of mock recents tasks to create
- public static final int SystemServicesProxyMockTaskCount = 75;
+ public static final int SystemServicesProxyMockTaskCount = 100;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d66968b..88ff726 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -72,6 +72,7 @@
FrameLayout mContainerView;
RecentsView mRecentsView;
View mEmptyView;
+ View mStatusBarScrimView;
View mNavBarScrimView;
FullScreenTransitionView mFullScreenshotView;
@@ -117,7 +118,7 @@
// Otherwise, just finish the activity without launching any other activities
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
}
}
@@ -129,7 +130,7 @@
}
} else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
// Call our callback
onEnterAnimationTriggered();
}
@@ -162,6 +163,13 @@
/** Updates the set of recent tasks */
void updateRecentsTasks(Intent launchIntent) {
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
+ ArrayList<TaskStack> stacks = root.getStacks();
+ if (!stacks.isEmpty()) {
+ mRecentsView.setBSP(root);
+ }
+
// Update the configuration based on the launch intent
mConfig.launchedFromHome = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_FROM_HOME, false);
@@ -171,31 +179,21 @@
AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false);
mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
+ mConfig.launchedWithNoRecentTasks = !root.hasTasks();
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
- ArrayList<TaskStack> stacks = root.getStacks();
- if (!stacks.isEmpty()) {
- mRecentsView.setBSP(root);
- }
-
- if (mConfig.shouldAnimateNavBarScrim()) {
- // Hide the scrim if we animate into Recents with window transitions
- mNavBarScrimView.setVisibility(View.INVISIBLE);
- } else {
- // Show the scrim if we animate into Recents without window transitions
- mNavBarScrimView.setVisibility(View.VISIBLE);
- }
+ // Show the scrim if we animate into Recents without window transitions
+ mNavBarScrimView.setVisibility(mConfig.hasNavBarScrim() &&
+ !mConfig.shouldAnimateNavBarScrim() ? View.VISIBLE : View.INVISIBLE);
+ mStatusBarScrimView.setVisibility(mConfig.hasStatusBarScrim() &&
+ !mConfig.shouldAnimateStatusBarScrim() ? View.VISIBLE : View.INVISIBLE);
// Add the default no-recents layout
- if (stacks.size() == 1 && stacks.get(0).getTaskCount() == 0) {
+ if (mConfig.launchedWithNoRecentTasks) {
mEmptyView.setVisibility(View.VISIBLE);
+ mEmptyView.setBackgroundColor(0x80000000);
} else {
mEmptyView.setVisibility(View.GONE);
}
-
- // Dim the background
- mRecentsView.setBackgroundColor(0x80000000);
}
/** Attempts to allocate and bind the search bar app widget */
@@ -284,7 +282,7 @@
// 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 @@
// 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 @@
}
mContainerView = new FrameLayout(this);
+ mContainerView.addView(mStatusBarScrimView);
mContainerView.addView(mRecentsView);
mContainerView.addView(mEmptyView);
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
@@ -376,7 +379,7 @@
void onConfigurationChange() {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
// Call our callback
onEnterAnimationTriggered();
}
@@ -547,7 +550,7 @@
// Just start the animation out of recents
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
} else {
// Otherwise, try and launch the first task
@@ -555,7 +558,7 @@
// If there are no tasks, then just finish recents
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
null, mFinishRunnable, null);
- mRecentsView.startOnExitAnimation(
+ mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
}
}
@@ -564,15 +567,38 @@
}
public void onEnterAnimationTriggered() {
- // Fade in the scrim
- if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) {
- mNavBarScrimView.setVisibility(View.VISIBLE);
- mNavBarScrimView.setAlpha(0f);
- mNavBarScrimView.animate().alpha(1f)
+ // Fade in the scrims
+ if (mConfig.hasStatusBarScrim() && mConfig.shouldAnimateStatusBarScrim()) {
+ mStatusBarScrimView.setVisibility(View.VISIBLE);
+ mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
+ mStatusBarScrimView.animate()
+ .translationY(0)
.setStartDelay(mConfig.taskBarEnterAnimDelay)
.setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .start();
+ }
+ if (mConfig.hasNavBarScrim() && mConfig.shouldAnimateNavBarScrim()) {
+ mNavBarScrimView.setVisibility(View.VISIBLE);
+ mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
+ mNavBarScrimView.animate()
+ .translationY(0)
+ .setStartDelay(mConfig.taskBarEnterAnimDelay)
+ .setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .start();
+ }
+ }
+
+ @Override
+ public void onExitAnimationTriggered() {
+ // Fade out the scrim
+ if (mConfig.hasNavBarScrim() && mConfig.shouldAnimateNavBarScrim()) {
+ mNavBarScrimView.animate()
+ .translationY(mNavBarScrimView.getMeasuredHeight())
+ .setStartDelay(0)
+ .setDuration(mConfig.taskBarExitAnimDuration)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
- .withLayer()
.start();
}
}
@@ -593,19 +619,9 @@
}
@Override
- public void onTaskLaunching(boolean isTaskInStackBounds) {
+ public void onTaskLaunching() {
mTaskLaunched = true;
- // Fade out the scrim
- if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) {
- mNavBarScrimView.animate().alpha(0f)
- .setStartDelay(0)
- .setDuration(mConfig.taskBarExitAnimDuration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .withLayer()
- .start();
- }
-
// Mark recents as no longer visible
AlternateRecentsComponent.notifyVisibilityChanged(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 3b8c6c9..63ef773 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -47,8 +47,8 @@
public Interpolator quintOutInterpolator;
/** Filtering */
- public int filteringCurrentViewsMinAnimDuration;
- public int filteringNewViewsMinAnimDuration;
+ public int filteringCurrentViewsAnimDuration;
+ public int filteringNewViewsAnimDuration;
/** Insets */
public Rect systemInsets = new Rect();
@@ -97,6 +97,7 @@
/** Launch states */
public boolean launchedWithAltTab;
+ public boolean launchedWithNoRecentTasks;
public boolean launchedFromAppWithThumbnail;
public boolean launchedFromAppWithScreenshot;
public boolean launchedFromHome;
@@ -105,12 +106,30 @@
public boolean developerOptionsEnabled;
/** Private constructor */
- private RecentsConfiguration() {}
+ private RecentsConfiguration(Context context) {
+ // Properties that don't have to be reloaded with each configuration change can be loaded
+ // here.
+
+ // Interpolators
+ fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
+ linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
+ quintOutInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.decelerate_quint);
+
+ // Check if the developer options are enabled
+ ContentResolver cr = context.getContentResolver();
+ developerOptionsEnabled = Settings.Global.getInt(cr,
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
+ }
/** Updates the configuration to the current context */
public static RecentsConfiguration reinitialize(Context context) {
if (sInstance == null) {
- sInstance = new RecentsConfiguration();
+ sInstance = new RecentsConfiguration(context);
}
sInstance.update(context);
return sInstance;
@@ -131,21 +150,11 @@
animationPxMovementPerSecond =
res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
- // Interpolators
- fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
- fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_linear_in);
- linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.linear_out_slow_in);
- quintOutInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.decelerate_quint);
-
// Filtering
- filteringCurrentViewsMinAnimDuration =
- res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
- filteringNewViewsMinAnimDuration =
- res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
+ filteringCurrentViewsAnimDuration =
+ res.getInteger(R.integer.recents_filter_animate_current_views_duration);
+ filteringNewViewsAnimDuration =
+ res.getInteger(R.integer.recents_filter_animate_new_views_duration);
// Insets
displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
@@ -216,11 +225,6 @@
navBarScrimEnterDuration =
res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
- // Check if the developer options are enabled
- ContentResolver cr = context.getContentResolver();
- developerOptionsEnabled = Settings.Global.getInt(cr,
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
-
if (Console.Enabled) {
Console.log(Constants.Log.UI.MeasureAndLayout,
"[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
@@ -245,6 +249,7 @@
* members. */
public void updateOnConfigurationChange() {
launchedWithAltTab = false;
+ launchedWithNoRecentTasks = false;
launchedFromAppWithThumbnail = false;
launchedFromAppWithScreenshot = false;
launchedFromHome = false;
@@ -255,14 +260,26 @@
return searchBarAppWidgetId >= 0;
}
+ /** Returns whether the status bar scrim should be animated when shown for the first time. */
+ public boolean shouldAnimateStatusBarScrim() {
+ return launchedFromHome;
+ }
+
+ /** Returns whether the status bar scrim should be visible. */
+ public boolean hasStatusBarScrim() {
+ return !launchedWithNoRecentTasks;
+ }
+
/** Returns whether the nav bar scrim should be animated when shown for the first time. */
public boolean shouldAnimateNavBarScrim() {
- return !launchedFromHome && !launchedFromAppWithScreenshot;
+ return true;
}
/** Returns whether the nav bar scrim should be visible. */
public boolean hasNavBarScrim() {
- return !transposeRecentsLayoutWithOrientation || !isLandscape;
+ // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
+ return !launchedWithNoRecentTasks &&
+ (!transposeRecentsLayoutWithOrientation || !isLandscape);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index dbcdb94..7762111 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -118,6 +118,7 @@
TaskResourceLoadQueue mLoadQueue;
DrawableLruCache mApplicationIconCache;
BitmapLruCache mThumbnailCache;
+ Bitmap mDefaultThumbnail;
boolean mCancelled;
boolean mWaitingOnLoadQueue;
@@ -125,10 +126,12 @@
/** Constructor, creates a new loading thread that loads task resources in the background */
public TaskResourceLoader(TaskResourceLoadQueue loadQueue,
DrawableLruCache applicationIconCache,
- BitmapLruCache thumbnailCache) {
+ BitmapLruCache thumbnailCache,
+ Bitmap defaultThumbnail) {
mLoadQueue = loadQueue;
mApplicationIconCache = applicationIconCache;
mThumbnailCache = thumbnailCache;
+ mDefaultThumbnail = defaultThumbnail;
mMainThreadHandler = new Handler();
mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
@@ -238,6 +241,7 @@
loadThumbnail = thumbnail;
mThumbnailCache.put(t.key, thumbnail);
} else {
+ loadThumbnail = mDefaultThumbnail;
Console.logError(mContext,
"Failed to load task top thumbnail for: " +
t.key.baseIntent.getComponent().getPackageName());
@@ -330,6 +334,7 @@
BitmapDrawable mDefaultApplicationIcon;
Bitmap mDefaultThumbnail;
+ Bitmap mLoadingThumbnail;
/** Private Constructor */
private RecentsTaskLoader(Context context) {
@@ -356,18 +361,22 @@
mLoadQueue = new TaskResourceLoadQueue();
mApplicationIconCache = new DrawableLruCache(iconCacheSize);
mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
- mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache);
+ mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
+ mDefaultThumbnail);
// Create the default assets
Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
icon.eraseColor(0x00000000);
mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
- mDefaultThumbnail.eraseColor(0x00000000);
+ mDefaultThumbnail.eraseColor(0xFFffffff);
+ mLoadingThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ mLoadingThumbnail.eraseColor(0x00000000);
mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
if (Console.Enabled) {
Console.log(Constants.Log.App.TaskDataLoader,
"[RecentsTaskLoader|defaultBitmaps]",
- "icon: " + mDefaultApplicationIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
+ "icon: " + mDefaultApplicationIcon +
+ " default thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
}
}
@@ -394,7 +403,7 @@
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> tasks =
- ssp.getRecentTasks(25, UserHandle.CURRENT.getIdentifier());
+ ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
Collections.reverse(tasks);
if (Console.Enabled) {
Console.log(Constants.Log.App.TimeSystemCalls,
@@ -544,7 +553,7 @@
requiresLoad = true;
}
if (thumbnail == null) {
- thumbnail = mDefaultThumbnail;
+ thumbnail = mLoadingThumbnail;
requiresLoad = true;
}
if (requiresLoad) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
index 1dd1be6..20be415 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
@@ -56,6 +56,13 @@
return mStack;
}
+ /** Returns whether there are any tasks in any stacks below this node. */
+ public boolean hasTasks() {
+ return (mStack.getTaskCount() > 0) ||
+ (mStartNode != null && mStartNode.hasTasks()) ||
+ (mEndNode != null && mEndNode.hasTasks());
+ }
+
/** Returns whether this is a leaf node */
boolean isLeafNode() {
return (mStartNode == null) && (mEndNode == null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
index ad2fa8d..cadfc56 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
@@ -49,8 +50,8 @@
FullScreenTransitionViewCallbacks mCb;
ImageView mScreenshotView;
-
Rect mClipRect = new Rect();
+ Paint mLayerPaint = new Paint();
boolean mIsAnimating;
AnimatorSet mEnterAnimation;
@@ -99,12 +100,17 @@
@Override
public void draw(Canvas canvas) {
- int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.clipRect(mClipRect);
super.draw(canvas);
canvas.restoreToCount(restoreCount);
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
/** Prepares the screenshot view for the transition into Recents */
public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
if (!mConfig.launchedFromAppWithScreenshot) return;
@@ -154,7 +160,7 @@
int clipBottom = mConfig.systemInsets.top + (int) (ctx.taskRect.height() / scale);
// Enable the HW Layers on the screenshot view
- mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
// Compose the animation
mEnterAnimation = new AnimatorSet();
@@ -168,7 +174,7 @@
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on this view
- setLayerType(View.LAYER_TYPE_NONE, null);
+ setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
if (Console.Enabled) {
Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
@@ -212,7 +218,7 @@
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on the screenshot view
- mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
}
});
mEnterAnimation.setDuration(475);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 6f79683..7248758 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -54,7 +54,8 @@
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
- public void onTaskLaunching(boolean isTaskInStackBounds);
+ public void onTaskLaunching();
+ public void onExitAnimationTriggered();
}
RecentsConfiguration mConfig;
@@ -160,19 +161,19 @@
}
/** Requests all task stacks to start their enter-recents animation */
- public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child instanceof TaskStackView) {
TaskStackView stackView = (TaskStackView) child;
- stackView.startOnEnterAnimation(ctx);
+ stackView.startEnterRecentsAnimation(ctx);
}
}
}
/** Requests all task stacks to start their exit-recents animation */
- public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
// Handle the case when there are no views by incrementing and decrementing after all
// animations are started.
ctx.postAnimationTrigger.increment();
@@ -183,7 +184,7 @@
View child = getChildAt(i);
if (child instanceof TaskStackView) {
TaskStackView stackView = (TaskStackView) child;
- stackView.startOnExitAnimation(ctx);
+ stackView.startExitToHomeAnimation(ctx);
}
}
}
@@ -191,6 +192,9 @@
// Handle the case when there are no views by incrementing and decrementing after all
// animations are started.
ctx.postAnimationTrigger.decrement();
+
+ // Notify of the exit animation
+ mCb.onExitAnimationTriggered();
}
/** Adds the search bar */
@@ -385,13 +389,46 @@
final TaskStack stack, final Task task) {
// Notify any callbacks of the launching of a new task
if (mCb != null) {
- boolean isTaskInStackBounds = false;
- if (stackView != null && tv != null) {
- isTaskInStackBounds = stackView.isTaskInStackBounds(tv);
- }
- mCb.onTaskLaunching(isTaskInStackBounds);
+ mCb.onTaskLaunching();
}
+ // Upfront the processing of the thumbnail
+ TaskViewTransform transform;
+ View sourceView = tv;
+ int offsetX = 0;
+ int offsetY = 0;
+ int stackScroll = stackView.getStackScroll();
+ if (tv == null) {
+ // If there is no actual task view, then use the stack view as the source view
+ // and then offset to the expected transform rect, but bound this to just
+ // outside the display rect (to ensure we don't animate from too far away)
+ sourceView = stackView;
+ transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
+ offsetX = transform.rect.left;
+ offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
+ } else {
+ transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
+ }
+
+ // Compute the thumbnail to scale up from
+ ActivityOptions opts = null;
+ int thumbnailWidth = transform.rect.width();
+ int thumbnailHeight = transform.rect.height();
+ if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
+ task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
+ // Resize the thumbnail to the size of the view that we are animating from
+ Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
+ Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ c.drawBitmap(task.thumbnail,
+ new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
+ new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
+ c.setBitmap(null);
+ opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
+ b, offsetX, offsetY);
+ }
+
+ final ActivityOptions launchOpts = opts;
final Runnable launchRunnable = new Runnable() {
@Override
public void run() {
@@ -400,45 +437,10 @@
Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
}
- TaskViewTransform transform;
- View sourceView = tv;
- int offsetX = 0;
- int offsetY = 0;
- int stackScroll = stackView.getStackScroll();
- if (tv == null) {
- // If there is no actual task view, then use the stack view as the source view
- // and then offset to the expected transform rect, but bound this to just
- // outside the display rect (to ensure we don't animate from too far away)
- sourceView = stackView;
- transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
- offsetX = transform.rect.left;
- offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
- } else {
- transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
- }
-
- // Compute the thumbnail to scale up from
- ActivityOptions opts = null;
- int thumbnailWidth = transform.rect.width();
- int thumbnailHeight = transform.rect.height();
- if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
- task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
- // Resize the thumbnail to the size of the view that we are animating from
- Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
- Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(b);
- c.drawBitmap(task.thumbnail,
- new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
- new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
- c.setBitmap(null);
- opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
- b, offsetX, offsetY);
- }
-
if (task.isActive) {
// Bring an active task to the foreground
RecentsTaskLoader.getInstance().getSystemServicesProxy()
- .moveTaskToFront(task.key.id, opts);
+ .moveTaskToFront(task.key.id, launchOpts);
} else {
// Launch the activity anew with the desired animation
Intent i = new Intent(task.key.baseIntent);
@@ -447,8 +449,8 @@
| Intent.FLAG_ACTIVITY_NEW_TASK);
try {
UserHandle taskUser = new UserHandle(task.userId);
- if (opts != null) {
- getContext().startActivityAsUser(i, opts.toBundle(), taskUser);
+ if (launchOpts != null) {
+ getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
} else {
getContext().startActivityAsUser(i, taskUser);
}
@@ -476,7 +478,7 @@
if (tv == null) {
post(launchRunnable);
} else {
- tv.animateOnLaunchingTask(launchRunnable);
+ stackView.animateOnLaunchingTask(tv, launchRunnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index d4f381b..bf25760 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -23,6 +23,7 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewPropertyAnimator;
@@ -50,6 +51,7 @@
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
+ Paint mLayerPaint = new Paint();
static Paint sHighlightPaint;
public TaskBarView(Context context) {
@@ -91,6 +93,13 @@
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+
+ // Hide the backgrounds if they are ripple drawables
+ if (!Constants.DebugFlags.App.EnableTaskFiltering) {
+ if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
+ mApplicationIcon.setBackground(null);
+ }
+ }
}
@Override
@@ -124,6 +133,11 @@
}
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
/** Binds the bar view to the task */
void rebindToTask(Task t, boolean animate) {
mTask = t;
@@ -158,12 +172,12 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateEnterRecents() {
+ public void prepareEnterRecentsAnimation() {
setVisibility(View.INVISIBLE);
}
/** Animates this task bar as it enters recents */
- public void animateOnEnterRecents(int delay, Runnable postAnimRunnable) {
+ public void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
// Animate the task bar of the first task view
setVisibility(View.VISIBLE);
setTranslationY(-getMeasuredHeight());
@@ -177,7 +191,7 @@
}
/** Animates this task bar as it exits recents */
- public void animateOnLaunchingTask(Runnable preAnimRunnable, final Runnable postAnimRunnable) {
+ public void startLaunchTaskAnimation(Runnable preAnimRunnable, final Runnable postAnimRunnable) {
// Animate the task bar out of the first task view
animate()
.translationY(-getMeasuredHeight())
@@ -194,8 +208,22 @@
.start();
}
+ /** Animates this task bar dismiss button when launching a task. */
+ public void startLaunchTaskDismissAnimation() {
+ if (mDismissButton.getVisibility() == View.VISIBLE) {
+ mDismissButton.animate().cancel();
+ mDismissButton.animate()
+ .alpha(0f)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .withLayer()
+ .start();
+ }
+ }
+
/** Animates this task bar if the user does not interact with the stack after a certain time. */
- public void animateOnNoUserInteraction() {
+ public void startNoUserInteractionAnimation() {
mDismissButton.setVisibility(View.VISIBLE);
mDismissButton.setAlpha(0f);
mDismissButton.animate()
@@ -208,7 +236,7 @@
}
/** Mark this task view that the user does has not interacted with the stack after a certain time. */
- public void setOnNoUserInteraction() {
+ public void setNoUserInteractionState() {
if (mDismissButton.getVisibility() != View.VISIBLE) {
mDismissButton.animate().cancel();
mDismissButton.setVisibility(View.VISIBLE);
@@ -218,11 +246,11 @@
/** Enable the hw layers on this task view */
void enableHwLayers() {
- mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
}
/** Disable the hw layers on this task view */
void disableHwLayers() {
- mDismissButton.setLayerType(View.LAYER_TYPE_NONE, null);
+ mDismissButton.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 79bfa5e..6c12218 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -116,7 +116,7 @@
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
- tv.animateOnNoUserInteraction();
+ tv.startNoUserInteractionAnimation();
}
}
});
@@ -187,12 +187,20 @@
return null;
}
- /** Update/get the transform */
+ /** Update/get the transform (creates a new TaskViewTransform) */
public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) {
TaskViewTransform transform = new TaskViewTransform();
+ return getStackTransform(indexInStack, stackScroll, transform);
+ }
+ /** Update/get the transform */
+ public TaskViewTransform getStackTransform(int indexInStack, int stackScroll,
+ TaskViewTransform transformOut) {
// Return early if we have an invalid index
- if (indexInStack < 0) return transform;
+ if (indexInStack < 0) {
+ transformOut.reset();
+ return transformOut;
+ }
// Map the items to an continuous position relative to the specified scroll
int numPeekCards = Constants.Values.TaskStackView.StackPeekNumCards;
@@ -209,35 +217,35 @@
float scale = Math.max(minScale, Math.min(1f, minScale +
((boundedT + (numPeekCards + 1)) * scaleInc)));
float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
- transform.scale = scale;
+ transformOut.scale = scale;
// Set the y translation
if (boundedT < 0f) {
- transform.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
+ transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
numPeekCards) * peekHeight - scaleYOffset);
} else {
- transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
+ transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
}
// Set the z translation
int minZ = mConfig.taskViewTranslationZMinPx;
int incZ = mConfig.taskViewTranslationZIncrementPx;
- transform.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
+ transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
// Set the alphas
- transform.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
+ transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
// Update the rect and visibility
- transform.rect.set(mTaskRect);
+ transformOut.rect.set(mTaskRect);
if (t < -(numPeekCards + 1)) {
- transform.visible = false;
+ transformOut.visible = false;
} else {
- transform.rect.offset(0, transform.translationY);
- Utilities.scaleRectAboutCenter(transform.rect, transform.scale);
- transform.visible = Rect.intersects(mRect, transform.rect);
+ transformOut.rect.offset(0, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+ transformOut.visible = Rect.intersects(mRect, transformOut.rect);
}
- transform.t = t;
- return transform;
+ transformOut.t = t;
+ return transformOut;
}
/**
@@ -250,14 +258,25 @@
boolean boundTranslationsToRect) {
// XXX: Optimization: Use binary search to find the visible range
+ int taskTransformCount = taskTransforms.size();
int taskCount = tasks.size();
int firstVisibleIndex = -1;
int lastVisibleIndex = -1;
- taskTransforms.clear();
- taskTransforms.ensureCapacity(taskCount);
+
+ // We can reuse the task transforms where possible to reduce object allocation
+ if (taskTransformCount < taskCount) {
+ // If there are less transforms than tasks, then add as many transforms as necessary
+ for (int i = taskTransformCount; i < taskCount; i++) {
+ taskTransforms.add(new TaskViewTransform());
+ }
+ } else if (taskTransformCount > taskCount) {
+ // If there are more transforms than tasks, then just subset the transform list
+ taskTransforms.subList(0, taskCount);
+ }
+
+ // Update the stack transforms
for (int i = 0; i < taskCount; i++) {
- TaskViewTransform transform = getStackTransform(i, stackScroll);
- taskTransforms.add(transform);
+ TaskViewTransform transform = getStackTransform(i, stackScroll, taskTransforms.get(i));
if (transform.visible) {
if (firstVisibleIndex < 0) {
firstVisibleIndex = i;
@@ -532,6 +551,20 @@
}
}
+ /** Animates a task view in this stack as it launches. */
+ public void animateOnLaunchingTask(TaskView tv, final Runnable r) {
+ // Hide each of the task bar dismiss buttons
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView t = (TaskView) getChildAt(i);
+ if (t == tv) {
+ t.startLaunchTaskAnimation(r, true);
+ } else {
+ t.startLaunchTaskAnimation(null, false);
+ }
+ }
+ }
+
/** Focuses the task at the specified index in the stack */
void focusTask(int taskIndex, boolean scrollToNewPosition) {
if (Console.Enabled) {
@@ -818,14 +851,14 @@
int offscreenY = mRect.bottom - (mTaskRect.top - mRect.top);
for (int i = childCount - 1; i >= 0; i--) {
TaskView tv = (TaskView) getChildAt(i);
- tv.prepareAnimateEnterRecents((i == (getChildCount() - 1)), offsetTopAlign,
+ tv.prepareEnterRecentsAnimation((i == (getChildCount() - 1)), offsetTopAlign,
offscreenY, mTaskRect);
}
// If the enter animation started already and we haven't completed a layout yet, do the
// enter animation now
if (mStartEnterAnimationRequestedAfterLayout) {
- startOnEnterAnimation(mStartEnterAnimationContext);
+ startEnterRecentsAnimation(mStartEnterAnimationContext);
mStartEnterAnimationRequestedAfterLayout = false;
mStartEnterAnimationContext = null;
}
@@ -838,7 +871,7 @@
}
/** Requests this task stacks to start it's enter-recents animation */
- public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
// If we are still waiting to layout, then just defer until then
if (mAwaitingFirstLayout) {
mStartEnterAnimationRequestedAfterLayout = true;
@@ -858,18 +891,18 @@
ctx.stackViewCount = childCount;
ctx.isFrontMost = (i == (getChildCount() - 1));
ctx.transform = transform;
- tv.animateOnEnterRecents(ctx);
+ tv.startEnterRecentsAnimation(ctx);
}
}
/** Requests this task stacks to start it's exit-recents animation. */
- public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
// Animate all the task views into view
ctx.offscreenTranslationY = mRect.bottom - (mTaskRect.top - mRect.top);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
- tv.animateOnExitRecents(ctx);
+ tv.startExitToHomeAnimation(ctx);
}
}
@@ -944,6 +977,7 @@
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 @@
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 @@
}
}
}
- 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 @@
// 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 @@
TaskView tv = getChildViewForTask(t);
if (tv != null) {
// For visible children, defer removing the task until after the animation
- tv.animateRemoval(new Runnable() {
+ tv.startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
mStack.removeTask(t);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
index dc8a420..c2b2094 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
@@ -54,10 +54,13 @@
@Override
public void draw(Canvas canvas) {
if (mClipTaskBar && (mClipRect != null)) {
- // Apply the clip rect
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.clipRect(mClipRect);
+ super.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ } else {
+ super.draw(canvas);
}
- super.draw(canvas);
}
/** Updates the clip rect based on the given task bar. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index b7e834b..0b19162 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Outline;
+import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
@@ -63,6 +64,7 @@
Point mLastTouchDown = new Point();
Path mRoundedRectClipPath = new Path();
Rect mTmpRect = new Rect();
+ Paint mLayerPaint = new Paint();
TaskThumbnailView mThumbnailView;
TaskBarView mBarView;
@@ -259,15 +261,15 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateEnterRecents(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
- Rect taskRect) {
+ public void prepareEnterRecentsAnimation(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
+ Rect taskRect) {
if (mConfig.launchedFromAppWithScreenshot) {
if (isTaskViewFrontMost) {
// Hide the task view as we are going to animate the full screenshot into view
// and then replace it with this view once we are done
setVisibility(View.INVISIBLE);
// Also hide the front most task bar view so we can animate it in
- mBarView.prepareAnimateEnterRecents();
+ mBarView.prepareEnterRecentsAnimation();
} else {
// Top align the task views
setTranslationY(offsetY);
@@ -278,7 +280,7 @@
} else if (mConfig.launchedFromAppWithThumbnail) {
if (isTaskViewFrontMost) {
// Hide the front most task bar view so we can animate it in
- mBarView.prepareAnimateEnterRecents();
+ mBarView.prepareEnterRecentsAnimation();
// Set the dim to 0 so we can animate it in
setDim(0);
}
@@ -286,13 +288,14 @@
} else if (mConfig.launchedFromHome) {
// Move the task view off screen (below) so we can animate it in
setTranslationY(offscreenY);
+ setTranslationZ(0);
setScaleX(1f);
setScaleY(1f);
}
}
/** Animates this task view as it enters recents */
- public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
TaskViewTransform transform = ctx.transform;
if (mConfig.launchedFromAppWithScreenshot) {
@@ -302,7 +305,7 @@
@Override
public void run() {
// Animate the task bar of the first task view
- mBarView.animateOnEnterRecents(0, mEnableThumbnailClip);
+ mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
setVisibility(View.VISIBLE);
}
});
@@ -324,7 +327,7 @@
} else if (mConfig.launchedFromAppWithThumbnail) {
if (ctx.isFrontMost) {
// Animate the task bar of the first task view
- mBarView.animateOnEnterRecents(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
+ mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
// Animate the dim into view as well
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
@@ -345,6 +348,7 @@
.scaleX(transform.scale)
.scaleY(transform.scale)
.translationY(transform.translationY)
+ .translationZ(transform.translationZ)
.setStartDelay(delay)
.setUpdateListener(null)
.setInterpolator(mConfig.quintOutInterpolator)
@@ -355,8 +359,8 @@
}
}
- /** Animates this task view as it leaves recents */
- public void animateOnExitRecents(ViewAnimation.TaskViewExitContext ctx) {
+ /** Animates this task view as it leaves recents by pressing home. */
+ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
animate()
.translationY(ctx.offscreenTranslationY)
.setStartDelay(0)
@@ -369,32 +373,27 @@
ctx.postAnimationTrigger.increment();
}
- /** Animates this task view if the user does not interact with the stack after a certain time. */
- public void animateOnNoUserInteraction() {
- mBarView.animateOnNoUserInteraction();
- }
-
- /** Mark this task view that the user does has not interacted with the stack after a certain time. */
- public void setOnNoUserInteraction() {
- mBarView.setOnNoUserInteraction();
- }
-
/** Animates this task view as it exits recents */
- public void animateOnLaunchingTask(final Runnable r) {
- // Disable the thumbnail clip and animate the bar out
- mBarView.animateOnLaunchingTask(mDisableThumbnailClip, r);
+ public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
+ if (isLaunchingTask) {
+ // Disable the thumbnail clip and animate the bar out
+ mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
- // Animate the dim
- if (mDim > 0) {
- ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
- anim.setDuration(mConfig.taskBarExitAnimDuration);
- anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
- anim.start();
+ // Animate the dim
+ if (mDim > 0) {
+ ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
+ anim.setDuration(mConfig.taskBarExitAnimDuration);
+ anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+ anim.start();
+ }
+ } else {
+ // Hide the dismiss button
+ mBarView.startLaunchTaskDismissAnimation();
}
}
/** Animates the deletion of this task view */
- public void animateRemoval(final Runnable r) {
+ public void startDeleteTaskAnimation(final Runnable r) {
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
@@ -422,6 +421,16 @@
.start();
}
+ /** Animates this task view if the user does not interact with the stack after a certain time. */
+ public void startNoUserInteractionAnimation() {
+ mBarView.startNoUserInteractionAnimation();
+ }
+
+ /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+ public void setNoUserInteractionState() {
+ mBarView.setNoUserInteractionState();
+ }
+
/** Returns the rect we want to clip (it may not be the full rect) */
Rect getClippingRect(Rect outRect) {
getHitRect(outRect);
@@ -433,13 +442,13 @@
/** Enable the hw layers on this task view */
void enableHwLayers() {
- mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
mBarView.enableHwLayers();
}
/** Disable the hw layers on this task view */
void disableHwLayers() {
- mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
mBarView.disableHwLayers();
}
@@ -489,10 +498,11 @@
@Override
public void draw(Canvas canvas) {
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
// Apply the rounded rect clip path on the whole view
canvas.clipPath(mRoundedRectClipPath);
-
super.draw(canvas);
+ canvas.restoreToCount(restoreCount);
// Apply the dim if necessary
if (mDim > 0) {
@@ -581,7 +591,7 @@
} 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 6c420e1..1947e30 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 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 @@
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 20684a1..5bc23b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -311,8 +311,27 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- if (mNotificationData.findByKey(sbn.getKey()) != null ||
- isHeadsUp(sbn.getKey())) {
+ Notification n = sbn.getNotification();
+ boolean isUpdate = mNotificationData.findByKey(sbn.getKey()) != null
+ || isHeadsUp(sbn.getKey());
+ boolean isGroupedChild = n.getGroup() != null
+ && (n.flags & Notification.FLAG_GROUP_SUMMARY) == 0;
+ if (isGroupedChild) {
+ if (DEBUG) {
+ Log.d(TAG, "Ignoring group child: " + sbn);
+ }
+ // Don't show grouped notifications. If this is an
+ // update, i.e. the notification existed before but
+ // wasn't a group child, remove the old instance.
+ // Otherwise just update the ranking.
+ if (isUpdate) {
+ removeNotificationInternal(sbn.getKey(), rankingMap);
+ } else {
+ updateRankingInternal(rankingMap);
+ }
+ return;
+ }
+ if (isUpdate) {
updateNotificationInternal(sbn, rankingMap);
} else {
addNotificationInternal(sbn, rankingMap);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 34179cb..dde95bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -688,6 +688,9 @@
@Override
protected boolean isScrolledToBottom() {
+ if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+ return true;
+ }
if (!isInSettings()) {
return mNotificationStackScroller.isScrolledToBottom();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index c8ab027..1712124 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 @@
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 @@
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 @@
updateZTranslation();
updateClickTargets();
updateWidth();
+ updatePadding();
+ updateMultiUserSwitch();
if (mQSPanel != null) {
mQSPanel.setExpanded(expanded && !overscrolled);
}
@@ -256,6 +264,21 @@
}
}
+ private void updatePadding() {
+ boolean padded = !mKeyguardShowing || mExpanded;
+ int padding = padded ? mPadding : 0;
+ setPaddingRelative(padding, 0, padding, 0);
+ }
+
+ private void updateMultiUserSwitch() {
+ int marginEnd = !mKeyguardShowing || mExpanded ? mMultiUserExpandedMargin : 0;
+ MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
+ if (marginEnd != lp.getMarginEnd()) {
+ lp.setMarginEnd(marginEnd);
+ mMultiUserSwitch.setLayoutParams(lp);
+ }
+ }
+
public void setExpansion(float height) {
height = (height - mCollapsedHeight) * EXPANSION_RUBBERBAND_FACTOR + mCollapsedHeight;
if (height < mCollapsedHeight) {
@@ -301,6 +324,8 @@
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 4e1b686..ccbaed3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -762,11 +762,12 @@
}
}
}
-
- mActivePointerId = INVALID_POINTER;
- endDrag();
}
+
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
}
+
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
@@ -968,7 +969,7 @@
* @param animate Should an animation be performed.
*/
public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
- setOverScrollAmount(numPixels * getRubberBandFactor(), onTop, animate, true);
+ setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
}
/**
@@ -1004,7 +1005,7 @@
if (animate) {
mStateAnimator.animateOverScrollToAmount(amount, onTop);
} else {
- setOverScrolledPixels(amount / getRubberBandFactor(), onTop);
+ setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
mAmbientState.setOverScrollAmount(amount, onTop);
if (onTop) {
notifyOverscrollTopListener(amount);
@@ -1226,13 +1227,14 @@
mOwnScrollY -= (int) topAmount;
mDontReportNextOverScroll = true;
setOverScrollAmount(0, true, false);
- mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
* mOverflingDistance + topAmount;
} else if (velocityY > 0 && bottomAmount > 0) {
mOwnScrollY += bottomAmount;
setOverScrollAmount(0, false, false);
- mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
- * mOverflingDistance + bottomAmount;
+ mMaxOverScroll = Math.abs(velocityY) / 1000f
+ * getRubberBandFactor(false /* onTop */) * mOverflingDistance
+ + bottomAmount;
} else {
// it will be set once we reach the boundary
mMaxOverScroll = 0.0f;
@@ -1274,7 +1276,10 @@
return Math.max(desiredPadding, mIntrinsicPadding);
}
- private float getRubberBandFactor() {
+ private float getRubberBandFactor(boolean onTop) {
+ if (!onTop) {
+ return RUBBER_BAND_FACTOR_NORMAL;
+ }
if (mExpandedInThisMotion) {
return RUBBER_BAND_FACTOR_AFTER_EXPAND;
} else if (mIsExpansionChanging) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index cbad9dc..602c22b 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 @@
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 @@
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/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java
index b22aeb7..4318b9d 100644
--- a/rs/java/android/renderscript/Font.java
+++ b/rs/java/android/renderscript/Font.java
@@ -111,10 +111,10 @@
FontFamily serifFamily = new FontFamily();
serifFamily.mNames = sSerifNames;
- serifFamily.mNormalFileName = "DroidSerif-Regular.ttf";
- serifFamily.mBoldFileName = "DroidSerif-Bold.ttf";
- serifFamily.mItalicFileName = "DroidSerif-Italic.ttf";
- serifFamily.mBoldItalicFileName = "DroidSerif-BoldItalic.ttf";
+ serifFamily.mNormalFileName = "NotoSerif-Regular.ttf";
+ serifFamily.mBoldFileName = "NotoSerif-Bold.ttf";
+ serifFamily.mItalicFileName = "NotoSerif-Italic.ttf";
+ serifFamily.mBoldItalicFileName = "NotoSerif-BoldItalic.ttf";
addFamilyToMap(serifFamily);
FontFamily monoFamily = new FontFamily();
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index cef6830..3b1e88a 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -28,6 +28,7 @@
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
import android.app.backup.FullBackup;
+import android.app.backup.RestoreDescription;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
import android.app.backup.IFullBackupRestoreObserver;
@@ -5177,7 +5178,9 @@
void restorePmMetadata() {
try {
- String packageName = mTransport.nextRestorePackage();
+ RestoreDescription desc = mTransport.nextRestorePackage();
+ // TODO: handle full-data stream restore payloads
+ String packageName = desc.getPackageName();
if (packageName == null) {
Slog.e(TAG, "Error getting first restore package");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
@@ -5245,7 +5248,9 @@
void restoreNextAgent() {
try {
- String packageName = mTransport.nextRestorePackage();
+ final RestoreDescription desc = mTransport.nextRestorePackage();
+ // TODO: handle full-data stream restore payloads
+ String packageName = desc.getPackageName();
if (packageName == null) {
Slog.e(TAG, "Error getting next restore package");
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index aeb195f..912a181 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -18,6 +18,7 @@
import android.database.ContentObserver;
import android.os.BatteryStats;
+
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
@@ -29,6 +30,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.BatteryProperties;
import android.os.Binder;
import android.os.FileUtils;
@@ -83,7 +85,7 @@
* service asynchronously itself.
* </p>
*/
-public final class BatteryService extends Binder {
+public final class BatteryService extends SystemService {
private static final String TAG = BatteryService.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -140,10 +142,12 @@
private boolean mSentLowBatteryBroadcast = false;
- public BatteryService(Context context, LightsManager lightsManager) {
+ public BatteryService(Context context) {
+ super(context);
+
mContext = context;
mHandler = new Handler(true /*async*/);
- mLed = new Led(context, lightsManager);
+ mLed = new Led(context, getLocalService(LightsManager.class));
mBatteryStats = BatteryStatsService.getService();
mCriticalBatteryLevel = mContext.getResources().getInteger(
@@ -160,7 +164,10 @@
mInvalidChargerObserver.startObserving(
"DEVPATH=/devices/virtual/switch/invalid_charger");
}
+ }
+ @Override
+ public void onStart() {
IBinder b = ServiceManager.getService("batteryproperties");
final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
IBatteryPropertiesRegistrar.Stub.asInterface(b);
@@ -169,28 +176,34 @@
} catch (RemoteException e) {
// Should never happen.
}
+
+ publishBinderService("battery", new BinderService());
+ publishLocalService(BatteryManagerInternal.class, new LocalService());
}
- void systemReady() {
- // check our power situation now that it is safe to display the shutdown dialog.
- synchronized (mLock) {
- ContentObserver obs = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- synchronized (mLock) {
- updateBatteryWarningLevelLocked();
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+ // check our power situation now that it is safe to display the shutdown dialog.
+ synchronized (mLock) {
+ ContentObserver obs = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateBatteryWarningLevelLocked();
+ }
}
- }
- };
- final ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
- false, obs, UserHandle.USER_ALL);
- updateBatteryWarningLevelLocked();
+ };
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+ false, obs, UserHandle.USER_ALL);
+ updateBatteryWarningLevelLocked();
+ }
}
}
- void updateBatteryWarningLevelLocked() {
+ private void updateBatteryWarningLevelLocked() {
final ContentResolver resolver = mContext.getContentResolver();
int defWarnLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
@@ -207,15 +220,6 @@
processValuesLocked(true);
}
- /**
- * Returns true if the device is plugged into any of the specified plug types.
- */
- public boolean isPowered(int plugTypeSet) {
- synchronized (mLock) {
- return isPoweredLocked(plugTypeSet);
- }
- }
-
private boolean isPoweredLocked(int plugTypeSet) {
// assume we are powered if battery state is unknown so
// the "stay on while plugged in" option will work.
@@ -234,34 +238,7 @@
return false;
}
- /**
- * Returns the current plug type.
- */
- public int getPlugType() {
- synchronized (mLock) {
- return mPlugType;
- }
- }
-
- /**
- * Returns battery level as a percentage.
- */
- public int getBatteryLevel() {
- synchronized (mLock) {
- return mBatteryProps.batteryLevel;
- }
- }
-
- /**
- * Returns whether we currently consider the battery level to be low.
- */
- public boolean getBatteryLevelLow() {
- synchronized (mLock) {
- return mBatteryLevelLow;
- }
- }
-
- public boolean shouldSendBatteryLowLocked() {
+ private boolean shouldSendBatteryLowLocked() {
final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
@@ -277,15 +254,6 @@
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
}
- /**
- * Returns a non-zero value if an unsupported charger is attached.
- */
- public int getInvalidCharger() {
- synchronized (mLock) {
- return mInvalidCharger;
- }
- }
-
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
@@ -640,17 +608,7 @@
}
}
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
-
- pw.println("Permission Denial: can't dump Battery service from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
+ private void dumpInternal(PrintWriter pw, String[] args) {
synchronized (mLock) {
if (args == null || args.length == 0 || "-a".equals(args[0])) {
pw.println("Current Battery Service state:");
@@ -801,4 +759,57 @@
}
}
}
+
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ pw.println("Permission Denial: can't dump Battery service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ dumpInternal(pw, args);
+ }
+ }
+
+ private final class LocalService extends BatteryManagerInternal {
+ @Override
+ public boolean isPowered(int plugTypeSet) {
+ synchronized (mLock) {
+ return isPoweredLocked(plugTypeSet);
+ }
+ }
+
+ @Override
+ public int getPlugType() {
+ synchronized (mLock) {
+ return mPlugType;
+ }
+ }
+
+ @Override
+ public int getBatteryLevel() {
+ synchronized (mLock) {
+ return mBatteryProps.batteryLevel;
+ }
+ }
+
+ @Override
+ public boolean getBatteryLevelLow() {
+ synchronized (mLock) {
+ return mBatteryLevelLow;
+ }
+ }
+
+ @Override
+ public int getInvalidCharger() {
+ synchronized (mLock) {
+ return mInvalidCharger;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/IdleMaintenanceService.java b/services/core/java/com/android/server/IdleMaintenanceService.java
deleted file mode 100644
index acc6abe..0000000
--- a/services/core/java/com/android/server/IdleMaintenanceService.java
+++ /dev/null
@@ -1,818 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.maintenance.IIdleCallback;
-import android.app.maintenance.IIdleService;
-import android.app.maintenance.IdleService;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * This service observes the device state and when applicable sends
- * broadcasts at the beginning and at the end of a period during which
- * observers can perform idle maintenance tasks. Typical use of the
- * idle maintenance is to perform somehow expensive tasks that can be
- * postponed to a moment when they will not degrade user experience.
- *
- * The current implementation is very simple. The start of a maintenance
- * window is announced if: the screen is off or showing a dream AND the
- * battery level is more than twenty percent AND at least one hour passed
- * activity).
- *
- * The end of a maintenance window is announced only if: a start was
- * announced AND the screen turned on or a dream was stopped.
- *
- * Method naming note:
- * Methods whose name ends with "Tm" must only be called from the main thread.
- */
-public class IdleMaintenanceService extends BroadcastReceiver {
-
- private static final boolean DEBUG = false;
-
- private static final String TAG = IdleMaintenanceService.class.getSimpleName();
-
- private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
-
- private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
-
- private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
-
- private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
-
- private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
-
- private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
-
- private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
-
- private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
- "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
-
- private static final String ACTION_FORCE_IDLE_MAINTENANCE =
- "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";
-
- static final int MSG_OP_COMPLETE = 1;
- static final int MSG_IDLE_FINISHED = 2;
- static final int MSG_TIMEOUT = 3;
-
- // when a timeout happened, what were we expecting?
- static final int VERB_BINDING = 1;
- static final int VERB_IDLING = 2;
- static final int VERB_ENDING = 3;
-
- // What are our relevant timeouts / allocated slices?
- static final long OP_TIMEOUT = 8 * 1000; // 8 seconds to bind or ack the start
- static final long IDLE_TIMESLICE = 10 * 60 * 1000; // ten minutes for each idler
-
- private final AlarmManager mAlarmService;
- private final BatteryService mBatteryService;
- private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
- private final Context mContext;
- private final WakeLock mWakeLock;
- private final WorkSource mSystemWorkSource = new WorkSource(Process.myUid());
-
- private long mLastIdleMaintenanceStartTimeMillis;
- private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
- private boolean mIdleMaintenanceStarted;
-
- final IdleCallback mCallback;
- final Handler mHandler;
-
- final Random mTokenGenerator = new Random();
-
- int makeToken() {
- int token;
- do {
- token = mTokenGenerator.nextInt(Integer.MAX_VALUE);
- } while (token == 0);
- return token;
- }
-
- class ActiveTask {
- public IdleServiceInfo who;
- public int verb;
- public int token;
-
- ActiveTask(IdleServiceInfo target, int action) {
- who = target;
- verb = action;
- token = makeToken();
- }
-
- @Override
- public String toString() {
- return "ActiveTask{" + Integer.toHexString(this.hashCode())
- + " : verb=" + verb
- + " : token=" + token
- + " : "+ who + "}";
- }
- }
-
- // What operations are in flight?
- final SparseArray<ActiveTask> mPendingOperations = new SparseArray<ActiveTask>();
-
- // Idle service queue management
- class IdleServiceInfo {
- public final ComponentName componentName;
- public final int uid;
- public IIdleService service;
-
- IdleServiceInfo(ResolveInfo info, ComponentName cname) {
- componentName = cname; // derived from 'info' but this avoids an extra object
- uid = info.serviceInfo.applicationInfo.uid;
- service = null;
- }
-
- @Override
- public int hashCode() {
- return componentName.hashCode();
- }
-
- @Override
- public String toString() {
- return "IdleServiceInfo{" + componentName
- + " / " + (service == null ? "null" : service.asBinder()) + "}";
- }
- }
-
- final ArrayMap<ComponentName, IdleServiceInfo> mIdleServices =
- new ArrayMap<ComponentName, IdleServiceInfo>();
- final LinkedList<IdleServiceInfo> mIdleServiceQueue = new LinkedList<IdleServiceInfo>();
- IdleServiceInfo mCurrentIdler; // set when we've committed to launching an idler
- IdleServiceInfo mLastIdler; // end of queue when idling begins
-
- void reportNoTimeout(int token, boolean result) {
- final Message msg = mHandler.obtainMessage(MSG_OP_COMPLETE, result ? 1 : 0, token);
- mHandler.sendMessage(msg);
- }
-
- // Binder acknowledgment trampoline
- class IdleCallback extends IIdleCallback.Stub {
- @Override
- public void acknowledgeStart(int token, boolean result) throws RemoteException {
- reportNoTimeout(token, result);
- }
-
- @Override
- public void acknowledgeStop(int token) throws RemoteException {
- reportNoTimeout(token, false);
- }
-
- @Override
- public void idleFinished(int token) throws RemoteException {
- if (DEBUG) {
- Slog.v(TAG, "idleFinished: " + token);
- }
- final Message msg = mHandler.obtainMessage(MSG_IDLE_FINISHED, 0, token);
- mHandler.sendMessage(msg);
- }
- }
-
- // Stuff that we run on a Handler
- class IdleHandler extends Handler {
- public IdleHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- final int token = msg.arg2;
-
- switch (msg.what) {
- case MSG_OP_COMPLETE: {
- if (DEBUG) {
- Slog.i(TAG, "MSG_OP_COMPLETE of " + token);
- }
- ActiveTask task = mPendingOperations.get(token);
- if (task != null) {
- mPendingOperations.remove(token);
- removeMessages(MSG_TIMEOUT);
-
- handleOpCompleteTm(task, msg.arg1);
- } else {
- // Can happen in a race between timeout and actual
- // (belated) completion of a "begin idling" or similar
- // operation. In that state we've already processed the
- // timeout, so we intentionally no-op here.
- if (DEBUG) {
- Slog.w(TAG, "Belated op-complete of " + token);
- }
- }
- break;
- }
-
- case MSG_IDLE_FINISHED: {
- if (DEBUG) {
- Slog.i(TAG, "MSG_IDLE_FINISHED of " + token);
- }
- ActiveTask task = mPendingOperations.get(token);
- if (task != null) {
- if (DEBUG) {
- Slog.i(TAG, "... removing task " + token);
- }
- mPendingOperations.remove(token);
- removeMessages(MSG_TIMEOUT);
-
- handleIdleFinishedTm(task);
- } else {
- // Can happen "legitimately" from an app explicitly calling
- // idleFinished() after already having been told that its slice
- // has ended.
- if (DEBUG) {
- Slog.w(TAG, "Belated idle-finished of " + token);
- }
- }
- break;
- }
-
- case MSG_TIMEOUT: {
- if (DEBUG) {
- Slog.i(TAG, "MSG_TIMEOUT of " + token);
- }
- ActiveTask task = mPendingOperations.get(token);
- if (task != null) {
- mPendingOperations.remove(token);
- removeMessages(MSG_OP_COMPLETE);
-
- handleTimeoutTm(task);
- } else {
- // This one should not happen; we flushed timeout messages
- // whenever we entered a state after which we have established
- // that they are not appropriate.
- Slog.w(TAG, "Unexpected timeout of " + token);
- }
- break;
- }
-
- default:
- Slog.w(TAG, "Unknown message: " + msg.what);
- }
- }
- }
-
- void handleTimeoutTm(ActiveTask task) {
- switch (task.verb) {
- case VERB_BINDING: {
- // We were trying to bind to this service, but it wedged or otherwise
- // failed to respond in time. Let it stay in the queue for the next
- // time around, but just give up on it for now and go on to the next.
- startNextIdleServiceTm();
- break;
- }
- case VERB_IDLING: {
- // The service has reached the end of its designated idle timeslice.
- // This is not considered an error.
- if (DEBUG) {
- Slog.i(TAG, "Idler reached end of timeslice: " + task.who);
- }
- sendEndIdleTm(task.who);
- break;
- }
- case VERB_ENDING: {
- if (mCurrentIdler == task.who) {
- if (DEBUG) {
- Slog.i(TAG, "Task timed out when ending; unbind needed");
- }
- handleIdleFinishedTm(task);
- } else {
- if (DEBUG) {
- Slog.w(TAG, "Ending timeout for non-current idle service!");
- }
- }
- break;
- }
- default: {
- Slog.w(TAG, "Unknown timeout state " + task.verb);
- break;
- }
- }
- }
-
- void handleOpCompleteTm(ActiveTask task, int result) {
- if (DEBUG) {
- Slog.i(TAG, "handleOpComplete : task=" + task + " result=" + result);
- }
- if (task.verb == VERB_IDLING) {
- // If the service was told to begin idling and responded positively, then
- // it has begun idling and will eventually either explicitly finish, or
- // reach the end of its allotted timeslice. It's running free now, so we
- // just schedule the idle-expiration timeout under the token it's already been
- // given and let it keep going.
- if (result != 0) {
- scheduleOpTimeoutTm(task);
- } else {
- // The idle service has indicated that it does not, in fact,
- // need to run at present, so we immediately indicate that it's
- // to finish idling, and go on to the next idler.
- if (DEBUG) {
- Slog.i(TAG, "Idler declined idling; moving along");
- }
- sendEndIdleTm(task.who);
- }
- } else {
- // In the idling case, the task will be cleared either as the result of a timeout
- // or of an explicit idleFinished(). For all other operations (binding, ending) we
- // are done with the task as such, so we remove it from our bookkeeping.
- if (DEBUG) {
- Slog.i(TAG, "Clearing task " + task);
- }
- mPendingOperations.remove(task.token);
- if (task.verb == VERB_ENDING) {
- // The last bit of handshaking around idle cessation for this target
- handleIdleFinishedTm(task);
- }
- }
- }
-
- void handleIdleFinishedTm(ActiveTask task) {
- final IdleServiceInfo who = task.who;
- if (who == mCurrentIdler) {
- if (DEBUG) {
- Slog.i(TAG, "Current idler has finished: " + who);
- Slog.i(TAG, "Attributing wakelock to system work source");
- }
- mContext.unbindService(mConnection);
- startNextIdleServiceTm();
- } else {
- Slog.w(TAG, "finish from non-current idle service? " + who);
- }
- }
-
- void updateIdleServiceQueueTm() {
- if (DEBUG) {
- Slog.i(TAG, "Updating idle service queue");
- }
- PackageManager pm = mContext.getPackageManager();
- Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
- List<ResolveInfo> services = pm.queryIntentServices(idleIntent, 0);
- for (ResolveInfo info : services) {
- if (info.serviceInfo != null) {
- if (IdleService.PERMISSION_BIND.equals(info.serviceInfo.permission)) {
- final ComponentName componentName = new ComponentName(
- info.serviceInfo.packageName,
- info.serviceInfo.name);
- if (DEBUG) {
- Slog.i(TAG, " - " + componentName);
- }
- if (!mIdleServices.containsKey(componentName)) {
- if (DEBUG) {
- Slog.i(TAG, " + not known; adding");
- }
- IdleServiceInfo serviceInfo = new IdleServiceInfo(info, componentName);
- mIdleServices.put(componentName, serviceInfo);
- mIdleServiceQueue.add(serviceInfo);
- }
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Idle service " + info.serviceInfo
- + " does not have required permission; ignoring");
- }
- }
- }
- }
- }
-
- void startNextIdleServiceTm() {
- mWakeLock.setWorkSource(mSystemWorkSource);
-
- if (mLastIdler == null) {
- // we've run the queue; nothing more to do until the next idle interval.
- if (DEBUG) {
- Slog.i(TAG, "Queue already drained; nothing more to do");
- }
- return;
- }
-
- if (DEBUG) {
- Slog.i(TAG, "startNextIdleService : last=" + mLastIdler + " cur=" + mCurrentIdler);
- if (mIdleServiceQueue.size() > 0) {
- int i = 0;
- Slog.i(TAG, "Queue (" + mIdleServiceQueue.size() + "):");
- for (IdleServiceInfo info : mIdleServiceQueue) {
- Slog.i(TAG, " " + i + " : " + info);
- i++;
- }
- }
- }
- if (mCurrentIdler != mLastIdler) {
- if (mIdleServiceQueue.size() > 0) {
- IdleServiceInfo target = mIdleServiceQueue.pop();
- if (DEBUG) {
- Slog.i(TAG, "starting next idle service " + target);
- }
- Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
- idleIntent.setComponent(target.componentName);
- mCurrentIdler = target;
- ActiveTask task = new ActiveTask(target, VERB_BINDING);
- scheduleOpTimeoutTm(task);
- boolean bindOk = mContext.bindServiceAsUser(idleIntent, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, UserHandle.OWNER);
- if (!bindOk) {
- if (DEBUG) {
- Slog.w(TAG, "bindService() to " + target.componentName
- + " failed");
- }
- } else {
- mIdleServiceQueue.add(target); // at the end for next time
- if (DEBUG) { Slog.i(TAG, "Attributing wakelock to target uid " + target.uid); }
- mWakeLock.setWorkSource(new WorkSource(target.uid));
- }
- } else {
- // Queue is empty but mLastIdler is non-null -- eeep. Clear *everything*
- // and wind up until the next time around.
- Slog.e(TAG, "Queue unexpectedly empty; resetting. last="
- + mLastIdler + " cur=" + mCurrentIdler);
- mHandler.removeMessages(MSG_TIMEOUT);
- mPendingOperations.clear();
- stopIdleMaintenanceTm();
- }
- } else {
- // we've reached the place we started, so mark the queue as drained
- if (DEBUG) {
- Slog.i(TAG, "Reached end of queue.");
- }
- stopIdleMaintenanceTm();
- }
- }
-
- void sendStartIdleTm(IdleServiceInfo who) {
- ActiveTask task = new ActiveTask(who, VERB_IDLING);
- scheduleOpTimeoutTm(task);
- try {
- who.service.startIdleMaintenance(mCallback, task.token);
- } catch (RemoteException e) {
- // We bound to it, but now we can't reach it. Bail and go on to the
- // next service.
- mContext.unbindService(mConnection);
- if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
- mHandler.removeMessages(MSG_TIMEOUT);
- startNextIdleServiceTm();
- }
- }
-
- void sendEndIdleTm(IdleServiceInfo who) {
- ActiveTask task = new ActiveTask(who, VERB_ENDING);
- scheduleOpTimeoutTm(task);
- if (DEBUG) {
- Slog.i(TAG, "Sending end-idle to " + who);
- }
- try {
- who.service.stopIdleMaintenance(mCallback, task.token);
- } catch (RemoteException e) {
- // We bound to it, but now we can't reach it. Bail and go on to the
- // next service.
- mContext.unbindService(mConnection);
- if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
- mHandler.removeMessages(MSG_TIMEOUT);
- startNextIdleServiceTm();
- }
- }
-
- ServiceConnection mConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) {
- Slog.i(TAG, "onServiceConnected(" + name + ")");
- }
- IdleServiceInfo info = mIdleServices.get(name);
- if (info != null) {
- // Bound! Cancel the bind timeout
- mHandler.removeMessages(MSG_TIMEOUT);
- // Now tell it to start its idle work
- info.service = IIdleService.Stub.asInterface(service);
- sendStartIdleTm(info);
- } else {
- // We bound to a service we don't know about. That's ungood.
- Slog.e(TAG, "Connected to unexpected component " + name);
- mContext.unbindService(this);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) {
- Slog.i(TAG, "onServiceDisconnected(" + name + ")");
- }
- IdleServiceInfo who = mIdleServices.get(name);
- if (who == mCurrentIdler) {
- // Hm, okay; they didn't tell us they were finished but they
- // went away. Crashed, probably. Oh well. They're gone, so
- // we can't finish them cleanly; just force things along.
- Slog.w(TAG, "Idler unexpectedly vanished: " + mCurrentIdler);
- mContext.unbindService(this);
- mHandler.removeMessages(MSG_TIMEOUT);
- startNextIdleServiceTm();
- } else {
- // Not the current idler, so we don't interrupt our process...
- if (DEBUG) {
- Slog.w(TAG, "Disconnect of abandoned or unexpected service " + name);
- }
- }
- }
- };
-
- // Schedules a timeout / end-of-work based on the task verb
- void scheduleOpTimeoutTm(ActiveTask task) {
- final long timeoutMillis = (task.verb == VERB_IDLING) ? IDLE_TIMESLICE : OP_TIMEOUT;
- if (DEBUG) {
- Slog.i(TAG, "Scheduling timeout (token " + task.token
- + " : verb " + task.verb + ") for " + task + " in " + timeoutMillis);
- }
- mPendingOperations.put(task.token, task);
- mHandler.removeMessages(MSG_TIMEOUT);
- final Message msg = mHandler.obtainMessage(MSG_TIMEOUT, 0, task.token);
- mHandler.sendMessageDelayed(msg, timeoutMillis);
- }
-
- // -------------------------------------------------------------------------------
- public IdleMaintenanceService(Context context, BatteryService batteryService) {
- mContext = context;
- mBatteryService = batteryService;
-
- mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-
- PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
- mHandler = new IdleHandler(mContext.getMainLooper());
- mCallback = new IdleCallback();
-
- Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
- intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
- intent, PendingIntent.FLAG_UPDATE_CURRENT);
-
- register(mHandler);
- }
-
- public void register(Handler handler) {
- IntentFilter intentFilter = new IntentFilter();
-
- // Alarm actions.
- intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
-
- // Battery actions.
- intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
-
- // Screen actions.
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-
- // Dream actions.
- intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
- intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
-
- mContext.registerReceiverAsUser(this, UserHandle.ALL,
- intentFilter, null, mHandler);
-
- intentFilter = new IntentFilter();
- intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE);
- mContext.registerReceiverAsUser(this, UserHandle.ALL,
- intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler);
- }
-
- private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
- final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
- mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
- mUpdateIdleMaintenanceStatePendingIntent);
- }
-
- private void unscheduleUpdateIdleMaintenanceState() {
- mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
- }
-
- private void updateIdleMaintenanceStateTm(boolean noisy) {
- if (mIdleMaintenanceStarted) {
- // Idle maintenance can be interrupted by user activity, or duration
- // time out, or low battery.
- final boolean batteryOk
- = batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning();
- if (!lastUserActivityPermitsIdleMaintenanceRunning() || !batteryOk) {
- unscheduleUpdateIdleMaintenanceState();
- mIdleMaintenanceStarted = false;
- // We stopped since we don't have enough battery or timed out but the
- // user is not using the device, so we should be able to run maintenance
- // in the next maintenance window since the battery may be charged
- // without interaction and the min interval between maintenances passed.
- if (!batteryOk) {
- scheduleUpdateIdleMaintenanceState(
- getNextIdleMaintenanceIntervalStartFromNow());
- }
-
- EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
- mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
- isBatteryCharging() ? 1 : 0);
- scheduleIdleFinishTm();
- }
- } else if (deviceStatePermitsIdleMaintenanceStart(noisy)
- && lastUserActivityPermitsIdleMaintenanceStart(noisy)
- && lastRunPermitsIdleMaintenanceStart(noisy)) {
- // Now that we started idle maintenance, we should schedule another
- // update for the moment when the idle maintenance times out.
- scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
- mIdleMaintenanceStarted = true;
- EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
- mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
- isBatteryCharging() ? 1 : 0);
- mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
- startIdleMaintenanceTm();
- } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
- if (lastRunPermitsIdleMaintenanceStart(noisy)) {
- // The user does not use the device and we did not run maintenance in more
- // than the min interval between runs, so schedule an update - maybe the
- // battery will be charged latter.
- scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
- } else {
- // The user does not use the device but we have run maintenance in the min
- // interval between runs, so schedule an update after the min interval ends.
- scheduleUpdateIdleMaintenanceState(
- getNextIdleMaintenanceIntervalStartFromNow());
- }
- }
- }
-
- void startIdleMaintenanceTm() {
- if (DEBUG) {
- Slog.i(TAG, "*** Starting idle maintenance ***");
- }
- if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
- mWakeLock.setWorkSource(mSystemWorkSource);
- mWakeLock.acquire();
- updateIdleServiceQueueTm();
- mCurrentIdler = null;
- mLastIdler = (mIdleServiceQueue.size() > 0) ? mIdleServiceQueue.peekLast() : null;
- startNextIdleServiceTm();
- }
-
- // Start a graceful wind-down of the idle maintenance state: end the current idler
- // and pretend that we've finished running the queue. If there's no current idler,
- // this is a no-op.
- void scheduleIdleFinishTm() {
- if (mCurrentIdler != null) {
- if (DEBUG) {
- Slog.i(TAG, "*** Finishing idle maintenance ***");
- }
- mLastIdler = mCurrentIdler;
- sendEndIdleTm(mCurrentIdler);
- } else {
- if (DEBUG) {
- Slog.w(TAG, "Asked to finish idle maintenance but we're done already");
- }
- }
- }
-
- // Actual finalization of the idle maintenance sequence
- void stopIdleMaintenanceTm() {
- if (mLastIdler != null) {
- if (DEBUG) {
- Slog.i(TAG, "*** Idle maintenance shutdown ***");
- }
- mWakeLock.setWorkSource(mSystemWorkSource);
- mLastIdler = mCurrentIdler = null;
- updateIdleMaintenanceStateTm(false); // resets 'started' and schedules next window
- mWakeLock.release();
- } else {
- Slog.e(TAG, "ERROR: idle shutdown but invariants not held. last=" + mLastIdler
- + " cur=" + mCurrentIdler + " size=" + mIdleServiceQueue.size());
- }
- }
-
- private long getNextIdleMaintenanceIntervalStartFromNow() {
- return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
- - SystemClock.elapsedRealtime();
- }
-
- private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
- final int minBatteryLevel = isBatteryCharging()
- ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
- : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
- boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
- && mBatteryService.getBatteryLevel() > minBatteryLevel);
- if (!allowed && noisy) {
- Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power");
- }
- return allowed;
- }
-
- private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) {
- // The last time the user poked the device is above the threshold.
- boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
- && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
- > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
- if (!allowed && noisy) {
- Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity");
- }
- return allowed;
- }
-
- private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) {
- // Enough time passed since the last maintenance run.
- boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
- > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
- if (!allowed && noisy) {
- Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last");
- }
- return allowed;
- }
-
- private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
- // The user is not using the device.
- return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
- }
-
- private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
- // Battery not too low and the maintenance duration did not timeout.
- return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
- && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
- > SystemClock.elapsedRealtime());
- }
-
- private boolean isBatteryCharging() {
- return mBatteryService.getPlugType() > 0
- && mBatteryService.getInvalidCharger() == 0;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) {
- Log.i(TAG, intent.getAction());
- }
- String action = intent.getAction();
- if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
- // We care about battery only if maintenance is in progress so we can
- // stop it if battery is too low. Note that here we assume that the
- // maintenance clients are properly holding a wake lock. We will
- // refactor the maintenance to use services instead of intents for the
- // next release. The only client for this for now is internal an holds
- // a wake lock correctly.
- if (mIdleMaintenanceStarted) {
- updateIdleMaintenanceStateTm(false);
- }
- } else if (Intent.ACTION_SCREEN_ON.equals(action)
- || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
- mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
- // Unschedule any future updates since we already know that maintenance
- // cannot be performed since the user is back.
- unscheduleUpdateIdleMaintenanceState();
- // If the screen went on/stopped dreaming, we know the user is using the
- // device which means that idle maintenance should be stopped if running.
- updateIdleMaintenanceStateTm(false);
- } else if (Intent.ACTION_SCREEN_OFF.equals(action)
- || Intent.ACTION_DREAMING_STARTED.equals(action)) {
- mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
- // If screen went off/started dreaming, we may be able to start idle maintenance
- // after the minimal user inactivity elapses. We schedule an alarm for when
- // this timeout elapses since the device may go to sleep by then.
- scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
- } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
- updateIdleMaintenanceStateTm(false);
- } else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) {
- long now = SystemClock.elapsedRealtime() - 1;
- mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START;
- mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
- updateIdleMaintenanceStateTm(true);
- }
- }
-}
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index bcb6e9e..215d92d 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -30,8 +30,7 @@
private static final String TAG = "MountServiceIdler";
private static ComponentName sIdleService =
- new ComponentName(MountServiceIdler.class.getPackage().getName(),
- MountServiceIdler.class.getName());
+ new ComponentName("android", MountServiceIdler.class.getName());
private static int MOUNT_JOB_ID = 808;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index d26f3fc..7022294 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -41,6 +41,7 @@
import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
+import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.NetworkStats;
import android.net.NetworkUtils;
@@ -145,6 +146,7 @@
public static final int InterfaceClassActivity = 613;
public static final int InterfaceAddressChange = 614;
public static final int InterfaceDnsServerInfo = 615;
+ public static final int RouteChange = 616;
}
static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
@@ -580,6 +582,28 @@
}
}
+ /**
+ * Notify our observers of a route change.
+ */
+ private void notifyRouteChange(String action, RouteInfo route) {
+ final int length = mObservers.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ if (action.equals("updated")) {
+ mObservers.getBroadcastItem(i).routeUpdated(route);
+ } else {
+ mObservers.getBroadcastItem(i).routeRemoved(route);
+ }
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
+ }
+ } finally {
+ mObservers.finishBroadcast();
+ }
+ }
+
//
// Netd Callback handling
//
@@ -722,6 +746,47 @@
}
return true;
// break;
+ case NetdResponseCode.RouteChange:
+ /*
+ * A route has been updated or removed.
+ * Format: "NNN Route <updated|removed> <dst> [via <gateway] [dev <iface>]"
+ */
+ if (!cooked[1].equals("Route") || cooked.length < 6) {
+ throw new IllegalStateException(errorMessage);
+ }
+
+ String via = null;
+ String dev = null;
+ boolean valid = true;
+ for (int i = 4; (i + 1) < cooked.length && valid; i += 2) {
+ if (cooked[i].equals("dev")) {
+ if (dev == null) {
+ dev = cooked[i+1];
+ } else {
+ valid = false; // Duplicate interface.
+ }
+ } else if (cooked[i].equals("via")) {
+ if (via == null) {
+ via = cooked[i+1];
+ } else {
+ valid = false; // Duplicate gateway.
+ }
+ } else {
+ valid = false; // Unknown syntax.
+ }
+ }
+ if (valid) {
+ try {
+ // InetAddress.parseNumericAddress(null) inexplicably returns ::1.
+ InetAddress gateway = null;
+ if (via != null) gateway = InetAddress.parseNumericAddress(via);
+ RouteInfo route = new RouteInfo(new IpPrefix(cooked[3]), gateway, dev);
+ notifyRouteChange(cooked[2], route);
+ return true;
+ } catch (IllegalArgumentException e) {}
+ }
+ throw new IllegalStateException(errorMessage);
+ // break;
default: break;
}
return false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 697e1f2..34c1ecd 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 @@
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 @@
}
}
- 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 @@
// 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 @@
@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 @@
}
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 11f855e..dd9cae9 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager.TaskDescription;
import android.os.PersistableBundle;
import android.os.Trace;
+
import com.android.internal.app.ResolverActivity;
import com.android.internal.util.XmlUtils;
import com.android.server.AttributeCache;
@@ -49,6 +50,7 @@
import android.util.TimeUtils;
import android.view.IApplicationToken;
import android.view.WindowManager;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -58,6 +60,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Objects;
/**
* An entry in the history stack, representing an activity.
@@ -83,6 +86,7 @@
final ActivityManagerService service; // owner
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
+ final ApplicationInfo appInfo; // information about activity's app
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
final int userId; // Which user is this running for?
@@ -103,9 +107,6 @@
static final int RECENTS_ACTIVITY_TYPE = 2;
int mActivityType;
- final String baseDir; // where activity source (resources etc) located
- final String resDir; // where public activity source (public resources etc) located
- final String dataDir; // where activity data should go
CharSequence nonLocalizedLabel; // the label information from the package mgr.
int labelRes; // the label information from the package mgr.
int icon; // resource identifier of activity's icon.
@@ -184,11 +185,13 @@
pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
pw.print(prefix); pw.print("realActivity=");
pw.println(realActivity.flattenToShortString());
- pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
- if (!resDir.equals(baseDir)) {
- pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ if (appInfo != null) {
+ pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
+ if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+ pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
+ }
+ pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
}
- pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
pw.print(" componentSpecified="); pw.print(componentSpecified);
pw.print(" mActivityType="); pw.println(mActivityType);
@@ -418,9 +421,7 @@
taskAffinity = aInfo.taskAffinity;
stateNotNeeded = (aInfo.flags&
ActivityInfo.FLAG_STATE_NOT_NEEDED) != 0;
- baseDir = aInfo.applicationInfo.sourceDir;
- resDir = aInfo.applicationInfo.publicSourceDir;
- dataDir = aInfo.applicationInfo.dataDir;
+ appInfo = aInfo.applicationInfo;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes;
if (nonLocalizedLabel == null && labelRes == 0) {
@@ -488,9 +489,7 @@
realActivity = null;
taskAffinity = null;
stateNotNeeded = false;
- baseDir = null;
- resDir = null;
- dataDir = null;
+ appInfo = null;
processName = null;
packageName = null;
fullscreen = true;
@@ -568,7 +567,10 @@
}
boolean isPersistable() {
- return (info.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+ return (info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY ||
+ info.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS) &&
+ (intent == null ||
+ (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0);
}
void makeFinishing() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 66e9eb3e..9b1d0e3 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -227,14 +227,14 @@
* receivers to launch an activity and get that to run before the device
* goes back to sleep.
*/
- final PowerManager.WakeLock mLaunchingActivity;
+ PowerManager.WakeLock mLaunchingActivity;
/**
* Set when the system is going to sleep, until we have
* successfully paused the current activity and released our wake lock.
* At that point the system is allowed to actually sleep.
*/
- final PowerManager.WakeLock mGoingToSleep;
+ PowerManager.WakeLock mGoingToSleep;
/** Stack id of the front stack when user switched, indexed by userId. */
SparseIntArray mUserStackInFront = new SparseIntArray(2);
@@ -251,16 +251,20 @@
/** 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 eb253eb..f403d08 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -74,12 +74,19 @@
mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout)
* 1000L);
+ }
+
+ /**
+ * At the time when the constructor runs, the power manager has not yet been
+ * initialized. So we initialize the low power observer later.
+ */
+ public void initPowerManagement() {
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mPowerManagerInternal.registerLowPowerModeObserver(this);
mStats.noteLowPowerMode(mPowerManagerInternal.getLowPowerModeEnabled());
(new WakeupReasonThread()).start();
- }
-
+ }
+
public void shutdown() {
Slog.w("BatteryStats", "Writing battery stats before shutdown...");
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index e54c95e..f79c026 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -45,6 +45,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* A running application service.
@@ -70,9 +71,6 @@
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String permission;// permission needed to access service
- final String baseDir; // where activity source (resources etc) located
- final String resDir; // where public activity source (public resources etc) located
- final String dataDir; // where activity data should go
final boolean exported; // from ServiceInfo.exported
final Runnable restarter; // used to schedule retries of starting the service
final long createTime; // when this service was created
@@ -209,11 +207,13 @@
}
long now = SystemClock.uptimeMillis();
long nowReal = SystemClock.elapsedRealtime();
- pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
- if (!resDir.equals(baseDir)) {
- pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ if (appInfo != null) {
+ pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
+ if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+ pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
+ }
+ pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
}
- pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
if (isolatedProc != null) {
pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
@@ -305,9 +305,6 @@
packageName = sInfo.applicationInfo.packageName;
processName = sInfo.processName;
permission = sInfo.permission;
- baseDir = sInfo.applicationInfo.sourceDir;
- resDir = sInfo.applicationInfo.publicSourceDir;
- dataDir = sInfo.applicationInfo.dataDir;
exported = sInfo.exported;
this.restarter = restarter;
createTime = SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index bb289fa..c79b33d 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -45,7 +45,7 @@
static final boolean DEBUG = false;
/** When in slow mode don't write tasks out faster than this */
- private static final long INTER_TASK_DELAY_MS = 60000;
+ private static final long INTER_TASK_DELAY_MS = 10000;
private static final long DEBUG_INTER_TASK_DELAY_MS = 5000;
private static final String RECENTS_FILENAME = "_task";
@@ -69,6 +69,7 @@
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) {
sTasksDir = new File(systemDir, TASKS_DIRNAME);
if (!sTasksDir.exists()) {
+ if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
if (!sTasksDir.mkdir()) {
Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
}
@@ -76,6 +77,7 @@
sImagesDir = new File(systemDir, IMAGES_DIRNAME);
if (!sImagesDir.exists()) {
+ if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
if (!sImagesDir.mkdir()) {
Slog.e(TAG, "Failure creating images directory " + sImagesDir);
}
@@ -172,14 +174,15 @@
TaskRecord.restoreFromXml(in, mStackSupervisor);
if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + task);
if (task != null) {
+ task.isPersistable = true;
tasks.add(task);
final int taskId = task.taskId;
recoveredTaskIds.add(taskId);
mStackSupervisor.setNextTaskId(taskId);
}
} else {
- Slog.e(TAG, "restoreTasksLocked Unknown xml event=" + event + " name="
- + name);
+ Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event +
+ " name=" + name);
}
}
XmlUtils.skipCurrentTag(in);
@@ -195,6 +198,7 @@
}
}
if (!DEBUG && deleteFile) {
+ if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
taskFile.delete();
}
}
@@ -209,7 +213,7 @@
Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
@Override
public int compare(TaskRecord lhs, TaskRecord rhs) {
- final long diff = lhs.mLastTimeMoved - rhs.mLastTimeMoved;
+ final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved;
if (diff < 0) {
return -1;
} else if (diff > 0) {
@@ -233,8 +237,7 @@
try {
taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
} catch (Exception e) {
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Can't parse file=" +
- file.getName());
+ Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
file.delete();
continue;
}
@@ -288,15 +291,18 @@
synchronized(mService) {
final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
persistentTaskIds.clear();
+ if (DEBUG) Slog.d(TAG, "mRecents=" + tasks);
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
task = tasks.get(taskNdx);
if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" +
task.isPersistable + " needsPersisting=" + task.needsPersisting);
- if (task.isPersistable) {
+ if (task.isPersistable && !task.stack.isHomeStack()) {
+ if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
persistentTaskIds.add(task.taskId);
if (task.needsPersisting) {
try {
+ if (DEBUG) Slog.d(TAG, "Saving task=" + task);
stringWriter = saveToXml(task);
break;
} catch (IOException e) {
@@ -305,6 +311,8 @@
task.needsPersisting = false;
}
}
+ } else {
+ if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task);
}
}
}
@@ -330,6 +338,8 @@
// Made it through the entire list and didn't find anything new that needed
// persisting.
if (!DEBUG) {
+ if (DEBUG) Slog.d(TAG, "Calling removeObsoleteFiles persistentTaskIds=" +
+ persistentTaskIds);
removeObsoleteFiles(persistentTaskIds);
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 81a0b36..1cde41f 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 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 a77443d..c7f4f6a 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -119,7 +119,7 @@
public int height;
/**
- * The refresh rate of the display.
+ * The refresh rate of the display, in frames per second.
*/
public float refreshRate;
@@ -144,6 +144,20 @@
public float yDpi;
/**
+ * This is a positive value indicating the phase offset of the VSYNC events provided by
+ * Choreographer relative to the display refresh. For example, if Choreographer reports
+ * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos).
+ */
+ public long appVsyncOffsetNanos;
+
+ /**
+ * This is how far in advance a buffer must be queued for presentation at
+ * a given time. If you want a buffer to appear on the screen at
+ * time N, you must submit the buffer before (N - bufferDeadlineNanos).
+ */
+ public long presentationDeadlineNanos;
+
+ /**
* Display flags.
*/
public int flags;
@@ -219,6 +233,8 @@
&& densityDpi == other.densityDpi
&& xDpi == other.xDpi
&& yDpi == other.yDpi
+ && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+ && presentationDeadlineNanos == other.presentationDeadlineNanos
&& flags == other.flags
&& touch == other.touch
&& rotation == other.rotation
@@ -242,6 +258,8 @@
densityDpi = other.densityDpi;
xDpi = other.xDpi;
yDpi = other.yDpi;
+ appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+ presentationDeadlineNanos = other.presentationDeadlineNanos;
flags = other.flags;
touch = other.touch;
rotation = other.rotation;
@@ -261,6 +279,8 @@
sb.append(", ").append(refreshRate).append(" fps, ");
sb.append("density ").append(densityDpi);
sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
+ sb.append(", appVsyncOff ").append(appVsyncOffsetNanos);
+ sb.append(", presDeadline ").append(presentationDeadlineNanos);
sb.append(", touch ").append(touchToString(touch));
sb.append(", rotation ").append(rotation);
sb.append(", type ").append(Display.typeToString(type));
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index e80aecd..098537c 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -161,6 +161,8 @@
mInfo.width = mPhys.width;
mInfo.height = mPhys.height;
mInfo.refreshRate = mPhys.refreshRate;
+ mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos;
+ mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos;
mInfo.state = mState;
// Assume that all built-in displays that have secure output (eg. HDCP) also
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index d61a35b..ed619d9 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -213,6 +213,8 @@
mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
+ mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos;
+ mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos;
mBaseDisplayInfo.state = deviceInfo.state;
mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index bfd8372c..3b23b6a 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -191,6 +191,7 @@
private final int mWidth;
private final int mHeight;
private final float mRefreshRate;
+ private final long mDisplayPresentationDeadlineNanos;
private final int mDensityDpi;
private final boolean mSecure;
@@ -200,7 +201,7 @@
private DisplayDeviceInfo mInfo;
public OverlayDisplayDevice(IBinder displayToken, String name,
- int width, int height, float refreshRate,
+ int width, int height, float refreshRate, long presentationDeadlineNanos,
int densityDpi, boolean secure, int state,
SurfaceTexture surfaceTexture) {
super(OverlayDisplayAdapter.this, displayToken);
@@ -208,6 +209,7 @@
mWidth = width;
mHeight = height;
mRefreshRate = refreshRate;
+ mDisplayPresentationDeadlineNanos = presentationDeadlineNanos;
mDensityDpi = densityDpi;
mSecure = secure;
mState = state;
@@ -249,6 +251,8 @@
mInfo.densityDpi = mDensityDpi;
mInfo.xDpi = mDensityDpi;
mInfo.yDpi = mDensityDpi;
+ mInfo.presentationDeadlineNanos = mDisplayPresentationDeadlineNanos +
+ 1000000000L / (int) mRefreshRate; // display's deadline + 1 frame
mInfo.flags = DisplayDeviceInfo.FLAG_PRESENTATION;
if (mSecure) {
mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
@@ -297,12 +301,13 @@
// Called on the UI thread.
@Override
- public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate, int state) {
+ public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate,
+ long presentationDeadlineNanos, int state) {
synchronized (getSyncRoot()) {
IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure);
mDevice = new OverlayDisplayDevice(displayToken, mName,
- mWidth, mHeight, refreshRate, mDensityDpi, mSecure,
- state, surfaceTexture);
+ mWidth, mHeight, refreshRate, presentationDeadlineNanos,
+ mDensityDpi, mSecure, state, surfaceTexture);
sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
index 06891f3..9ca5fda 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
@@ -303,7 +303,7 @@
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
int width, int height) {
mListener.onWindowCreated(surfaceTexture, mDefaultDisplayInfo.refreshRate,
- mDefaultDisplayInfo.state);
+ mDefaultDisplayInfo.presentationDeadlineNanos, mDefaultDisplayInfo.state);
}
@Override
@@ -373,7 +373,7 @@
*/
public interface Listener {
public void onWindowCreated(SurfaceTexture surfaceTexture,
- float refreshRate, int state);
+ float refreshRate, long presentationDeadlineNanos, int state);
public void onWindowDestroyed();
public void onStateChanged(int state);
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 14ef5a9..ec14cf1 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -171,6 +171,7 @@
mInfo.densityDpi = mDensityDpi;
mInfo.xDpi = mDensityDpi;
mInfo.yDpi = mDensityDpi;
+ mInfo.presentationDeadlineNanos = 1000000000L / (int) mInfo.refreshRate; // 1 frame
mInfo.flags = 0;
if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index cd57941..a05bf2c 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -127,7 +127,7 @@
pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
-
+
// Try to dump the controller state.
if (mDisplayController == null) {
pw.println("mDisplayController=null");
@@ -729,6 +729,7 @@
mInfo.width = mWidth;
mInfo.height = mHeight;
mInfo.refreshRate = mRefreshRate;
+ mInfo.presentationDeadlineNanos = 1000000000L / (int) mRefreshRate; // 1 frame
mInfo.flags = mFlags;
mInfo.type = Display.TYPE_WIFI;
mInfo.address = mAddress;
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
new file mode 100644
index 0000000..2941574
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -0,0 +1,328 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.fingerprint;
+
+import android.app.Service;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.service.fingerprint.FingerprintManager;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+import android.service.fingerprint.FingerprintUtils;
+import android.service.fingerprint.IFingerprintService;
+import android.service.fingerprint.IFingerprintServiceReceiver;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * A service to manage multiple clients that want to access the fingerprint HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * fingerprint -related events.
+ *
+ * @hide
+ */
+public class FingerprintService extends SystemService {
+ private final String TAG = "FingerprintService";
+ private static final boolean DEBUG = true;
+ private ArrayMap<IBinder, ClientData> mClients = new ArrayMap<IBinder, ClientData>();
+
+ private static final int MSG_NOTIFY = 10;
+
+ Handler mHandler = new Handler() {
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_NOTIFY:
+ handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj);
+ break;
+
+ default:
+ Slog.w(TAG, "Unknown message:" + msg.what);
+ }
+ }
+ };
+ private Context mContext;
+
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_LISTENING = 1;
+ private static final int STATE_ENROLLING = 2;
+ private static final int STATE_REMOVING = 3;
+ private static final long MS_PER_SEC = 1000;
+ public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
+ public static final String ENROLL_FINGERPRINT = "android.permission.ENROLL_FINGERPRINT";
+
+ private static final class ClientData {
+ public IFingerprintServiceReceiver receiver;
+ int state;
+ int userId;
+ public TokenWatcher tokenWatcher;
+ IBinder getToken() { return tokenWatcher.getToken(); }
+ }
+
+ private class TokenWatcher implements IBinder.DeathRecipient {
+ WeakReference<IBinder> token;
+
+ TokenWatcher(IBinder token) {
+ this.token = new WeakReference<IBinder>(token);
+ }
+
+ IBinder getToken() { return token.get(); }
+ public void binderDied() {
+ mClients.remove(token);
+ this.token = null;
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ if (token != null) {
+ if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
+ mClients.remove(token);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+
+ public FingerprintService(Context context) {
+ super(context);
+ mContext = context;
+ nativeInit(this);
+ }
+
+ // TODO: Move these into separate process
+ // JNI methods to communicate from FingerprintManagerService to HAL
+ native int nativeEnroll(int timeout);
+ native int nativeEnrollCancel();
+ native int nativeRemove(int fingerprintId);
+ native int nativeOpenHal();
+ native int nativeCloseHal();
+ native void nativeInit(FingerprintService service);
+
+ // JNI methods for communicating from HAL to clients
+ void notify(int msg, int arg1, int arg2) {
+ mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget();
+ }
+
+ void handleNotify(int msg, int arg1, int arg2) {
+ Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")");
+ for (int i = 0; i < mClients.size(); i++) {
+ ClientData clientData = mClients.valueAt(i);
+ if (clientData == null || clientData.receiver == null) {
+ if (DEBUG) Slog.v(TAG, "clientData at " + i + " is invalid!!");
+ continue;
+ }
+ switch (msg) {
+ case FingerprintManager.FINGERPRINT_ERROR: {
+ final int error = arg1;
+ try {
+ clientData.receiver.onError(error);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ }
+ break;
+ case FingerprintManager.FINGERPRINT_ACQUIRED: {
+ final int acquireInfo = arg1;
+ try {
+ clientData.receiver.onAcquired(acquireInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ break;
+ }
+ case FingerprintManager.FINGERPRINT_PROCESSED: {
+ final int fingerId = arg1;
+ try {
+ clientData.receiver.onProcessed(fingerId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ break;
+ }
+ case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
+ final int fingerId = arg1;
+ final int remaining = arg2;
+ if (clientData.state == STATE_ENROLLING) {
+ // Only send enroll updates to clients that are actually enrolling
+ try {
+ clientData.receiver.onEnrollResult(fingerId, remaining);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ // Update the database with new finger id.
+ // TODO: move to client code (Settings)
+ if (remaining == 0) {
+ FingerprintUtils.addFingerprintIdForUser(fingerId,
+ mContext.getContentResolver(), clientData.userId);
+ clientData.state = STATE_IDLE; // Nothing left to do
+ }
+ } else {
+ if (DEBUG) Slog.w(TAG, "Client not enrolling");
+ break;
+ }
+ break;
+ }
+ case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
+ int fingerId = arg1;
+ if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
+ FingerprintUtils.removeFingerprintIdForUser(fingerId,
+ mContext.getContentResolver(), clientData.userId);
+ if (clientData.receiver != null) {
+ try {
+ clientData.receiver.onRemoved(fingerId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ mClients.remove(mClients.keyAt(i));
+ }
+ }
+ clientData.state = STATE_LISTENING;
+ }
+ break;
+ }
+ }
+ }
+
+ void startEnroll(IBinder token, long timeout, int userId) {
+ ClientData clientData = mClients.get(token);
+ if (clientData != null) {
+ if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+ clientData.state = STATE_ENROLLING;
+ nativeEnroll((int) (timeout / MS_PER_SEC));
+ } else {
+ Slog.w(TAG, "enroll(): No listener registered");
+ }
+ }
+
+ void startEnrollCancel(IBinder token, int userId) {
+ ClientData clientData = mClients.get(token);
+ if (clientData != null) {
+ if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+ clientData.state = STATE_LISTENING;
+ nativeEnrollCancel();
+ } else {
+ Slog.w(TAG, "enrollCancel(): No listener registered");
+ }
+ }
+
+ // Remove all fingerprints for the given user.
+ void startRemove(IBinder token, int fingerId, int userId) {
+ ClientData clientData = mClients.get(token);
+ if (clientData != null) {
+ if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+ clientData.state = STATE_REMOVING;
+ // The fingerprint id will be removed when we get confirmation from the HAL
+ int result = nativeRemove(fingerId);
+ if (result != 0) {
+ Slog.w(TAG, "Error removing fingerprint with id = " + fingerId);
+ }
+ } else {
+ Slog.w(TAG, "remove(" + token + "): No listener registered");
+ }
+ }
+
+ void addListener(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
+ if (DEBUG) Slog.v(TAG, "startListening(" + receiver + ")");
+ if (mClients.get(token) == null) {
+ ClientData clientData = new ClientData();
+ clientData.state = STATE_LISTENING;
+ clientData.receiver = receiver;
+ clientData.userId = userId;
+ clientData.tokenWatcher = new TokenWatcher(token);
+ try {
+ token.linkToDeath(clientData.tokenWatcher, 0);
+ mClients.put(token, clientData);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+ }
+ } else {
+ if (DEBUG) Slog.v(TAG, "listener already registered for " + token);
+ }
+ }
+
+ void removeListener(IBinder token, int userId) {
+ if (DEBUG) Slog.v(TAG, "stopListening(" + token + ")");
+ ClientData clientData = mClients.get(token);
+ if (clientData != null) {
+ token.unlinkToDeath(clientData.tokenWatcher, 0);
+ mClients.remove(token);
+ } else {
+ if (DEBUG) Slog.v(TAG, "listener not registered: " + token);
+ }
+ mClients.remove(token);
+ }
+
+ void checkPermission(String permisison) {
+ // TODO
+ }
+
+ private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+ @Override // Binder call
+ public void enroll(IBinder token, long timeout, int userId) {
+ checkPermission(ENROLL_FINGERPRINT);
+ startEnroll(token, timeout, userId);
+ }
+
+ @Override // Binder call
+ public void enrollCancel(IBinder token,int userId) {
+ checkPermission(ENROLL_FINGERPRINT);
+ startEnrollCancel(token, userId);
+ }
+
+ @Override // Binder call
+ public void remove(IBinder token, int fingerprintId, int userId) {
+ checkPermission(ENROLL_FINGERPRINT); // TODO: Maybe have another permission
+ startRemove(token, fingerprintId, userId);
+ }
+
+ @Override // Binder call
+ public void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId)
+ {
+ checkPermission(USE_FINGERPRINT);
+ addListener(token, receiver, userId);
+ }
+
+ @Override // Binder call
+ public void stopListening(IBinder token, int userId) {
+ checkPermission(USE_FINGERPRINT);
+ removeListener(token, userId);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
+ nativeOpenHal();
+ }
+
+}
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index bcd08ebf..74eaf2a 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -26,31 +26,29 @@
/**
* Handles CEC command <Active Source>.
- *
- * <p>Used by feature actions that need to handle the command in their flow.
+ * <p>
+ * Used by feature actions that need to handle the command in their flow.
*/
final class ActiveSourceHandler {
private static final String TAG = "ActiveSourceHandler";
+ private final HdmiCecLocalDevice mSource;
private final HdmiControlService mService;
- private final int mSourceAddress;
- private final int mSourcePath;
- @Nullable private final IHdmiControlCallback mCallback;
+ @Nullable
+ private final IHdmiControlCallback mCallback;
- static ActiveSourceHandler create(HdmiControlService service, int sourceAddress,
- int sourcePath, IHdmiControlCallback callback) {
- if (service == null) {
+ static ActiveSourceHandler create(HdmiCecLocalDevice source,
+ IHdmiControlCallback callback) {
+ if (source == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback);
+ return new ActiveSourceHandler(source, callback);
}
- private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath,
- IHdmiControlCallback callback) {
- mService = service;
- mSourceAddress = sourceAddress;
- mSourcePath = sourcePath;
+ private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
+ mSource = source;
+ mService = mSource.getService();
mCallback = callback;
}
@@ -61,7 +59,7 @@
* @param routingPath routing path of the device to be the active source
*/
void process(int deviceLogicalAddress, int routingPath) {
- if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) {
+ if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) {
invokeCallback(HdmiCec.RESULT_SUCCESS);
return;
}
@@ -69,14 +67,14 @@
if (device == null) {
// "New device action" initiated by <Active Source> does not require
// "Routing change action".
- mService.addAndStartAction(new NewDeviceAction(mService, mSourceAddress,
- deviceLogicalAddress, routingPath, false));
+ mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress,
+ routingPath, false));
}
- if (!mService.isInPresetInstallationMode()) {
- int prevActiveInput = mService.getActiveInput();
- mService.updateActiveDevice(deviceLogicalAddress, routingPath);
- if (prevActiveInput != mService.getActiveInput()) {
+ if (!mSource.isInPresetInstallationMode()) {
+ int prevActiveInput = mSource.getActivePortId();
+ mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
+ if (prevActiveInput != mSource.getActivePortId()) {
// TODO: change port input here.
}
invokeCallback(HdmiCec.RESULT_SUCCESS);
@@ -84,24 +82,33 @@
// TV is in a mode that should keep its current source/input from
// being changed for its operation. Reclaim the active source
// or switch the port back to the one used for the current mode.
- if (mService.getActiveSource() == mSourceAddress) {
+ if (mSource.getActiveSource() == getSourceAddress()) {
HdmiCecMessage activeSource =
- HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath);
+ HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(),
+ getSourcePath());
mService.sendCecCommand(activeSource);
- mService.updateActiveDevice(deviceLogicalAddress, routingPath);
+ mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
invokeCallback(HdmiCec.RESULT_SUCCESS);
} else {
- int activePath = mService.getActivePath();
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
+ int activePath = mSource.getActivePath();
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
routingPath, activePath));
// TODO: Start port select action here
- // PortSelectAction action = new PortSelectAction(mService, mSourceAddress,
- // activePath, mCallback);
+ // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(),
+ // activePath, mCallback);
// mService.addActionAndStart(action);
}
}
}
+ private final int getSourceAddress() {
+ return mSource.getDeviceInfo().getLogicalAddress();
+ }
+
+ private final int getSourcePath() {
+ return mSource.getDeviceInfo().getPhysicalAddress();
+ }
+
private void invokeCallback(int result) {
if (mCallback == null) {
return;
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index f7392e9..02f088f 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -94,12 +94,10 @@
/**
* Constructor.
*
- * @param service an instance of {@link HdmiControlService}.
- * @param sourceAddress a logical address which initiates this action
+ * @param source an instance of {@link HdmiCecLocalDevice}.
*/
- DeviceDiscoveryAction(HdmiControlService service, int sourceAddress,
- DeviceDiscoveryCallback callback) {
- super(service, sourceAddress);
+ DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
+ super(source);
mCallback = Preconditions.checkNotNull(callback);
}
@@ -108,7 +106,7 @@
mDevices.clear();
mState = STATE_WAITING_FOR_DEVICE_POLLING;
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
if (ackedAddress.isEmpty()) {
@@ -121,7 +119,8 @@
allocateDevices(ackedAddress);
startPhysicalAddressStage();
}
- }, HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES, DEVICE_POLLING_RETRY);
+ }, HdmiConstants.POLL_ITERATION_REVERSE_ORDER
+ | HdmiConstants.POLL_STRATEGY_REMOTES_DEVICES, DEVICE_POLLING_RETRY);
return true;
}
@@ -156,7 +155,7 @@
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, address));
+ sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
@@ -179,7 +178,7 @@
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_SET_OSD_NAME)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, address));
+ sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
@@ -203,12 +202,13 @@
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, address));
+ sendCommand(
+ HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
private boolean mayProcessMessageIfCached(int address, int opcode) {
- HdmiCecMessage message = mService.getCecMessageCache().getMessage(address, opcode);
+ HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
if (message != null) {
processCommand(message);
return true;
diff --git a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
index 63c2182..51df473 100644
--- a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
@@ -16,9 +16,10 @@
* limitations under the License.
*/
-import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -40,18 +41,18 @@
private final int mTargetAddress;
private final IHdmiControlCallback mCallback;
- static DevicePowerStatusAction create(HdmiControlService service, int sourceAddress,
+ static DevicePowerStatusAction create(HdmiCecLocalDevice source,
int targetAddress, IHdmiControlCallback callback) {
- if (service == null || callback == null) {
+ if (source == null || callback == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new DevicePowerStatusAction(service, sourceAddress, targetAddress, callback);
+ return new DevicePowerStatusAction(source, targetAddress, callback);
}
- private DevicePowerStatusAction(HdmiControlService service, int sourceAddress,
+ private DevicePowerStatusAction(HdmiCecLocalDevice localDevice,
int targetAddress, IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ super(localDevice);
mTargetAddress = targetAddress;
mCallback = callback;
}
@@ -65,8 +66,8 @@
}
private void queryDevicePowerStatus() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ mTargetAddress));
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index a8696a1..dbe3b80 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -16,10 +16,11 @@
package com.android.server.hdmi;
-import android.hardware.hdmi.IHdmiControlCallback;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiTvClient;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -66,24 +67,20 @@
private final HdmiCecDeviceInfo mTarget;
private final IHdmiControlCallback mCallback;
- private final int mSourcePath;
private int mPowerStatusCounter = 0;
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address of TV initiating this action
- * @param sourcePath physical address of TV
+ * @param source {@link HdmiCecLocalDevice} instance
* @param target target logical device that will be a new active source
* @param callback callback object
*/
- public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath,
+ public DeviceSelectAction(HdmiCecLocalDevice source,
HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ super(source);
mCallback = callback;
- mSourcePath = sourcePath;
mTarget = target;
}
@@ -96,7 +93,7 @@
private void queryDevicePowerStatus() {
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mSourceAddress, mTarget.getLogicalAddress()));
+ getSourceAddress(), mTarget.getLogicalAddress()));
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, TIMEOUT_MS);
}
@@ -118,7 +115,8 @@
case STATE_WAIT_FOR_ACTIVE_SOURCE:
if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
int activePath = HdmiUtils.twoBytesToInt(params);
- ActiveSourceHandler.create(mService, mSourceAddress, mSourcePath, mCallback)
+ ActiveSourceHandler
+ .create(localDevice(), mCallback)
.process(cmd.getSource(), activePath);
finish();
return true;
@@ -174,15 +172,15 @@
private void sendSetStreamPath() {
sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(
- mSourceAddress, mTarget.getPhysicalAddress()));
+ getSourceAddress(), mTarget.getPhysicalAddress()));
mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS);
}
private void sendRemoteKeyCommand(int keyCode) {
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
mTarget.getLogicalAddress(), keyCode));
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
mTarget.getLogicalAddress()));
}
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
index ae272b4..0ec17f6 100644
--- a/services/core/java/com/android/server/hdmi/FeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -22,6 +22,9 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import java.util.List;
/**
* Encapsulates a sequence of CEC/MHL command exchange for a certain feature.
@@ -33,14 +36,13 @@
* of the object. All the actual action classes inherit FeatureAction.
*
* <p>More than one FeatureAction objects can be up and running simultaneously,
- * maintained by {@link HdmiControlService}. Each action is passed a new command
+ * maintained by {@link HdmiCecLocalDevice}. Each action is passed a new command
* arriving from the bus, and either consumes it if the command is what the action expects,
* or yields it to other action.
*
* Declared as package private, accessed by {@link HdmiControlService} only.
*/
abstract class FeatureAction {
-
private static final String TAG = "FeatureAction";
// Timer handler message used for timeout event
@@ -56,19 +58,16 @@
// Internal state indicating the progress of action.
protected int mState = STATE_NONE;
- protected final HdmiControlService mService;
-
- // Logical address of the device for which the feature action is taken. The commands
- // generated in an action all use this field as source address.
- protected final int mSourceAddress;
+ private final HdmiControlService mService;
+ private final HdmiCecLocalDevice mSource;
// Timer that manages timeout events.
protected ActionTimer mActionTimer;
- FeatureAction(HdmiControlService service, int sourceAddress) {
- mService = service;
- mSourceAddress = sourceAddress;
- mActionTimer = createActionTimer(service.getServiceLooper());
+ FeatureAction(HdmiCecLocalDevice source) {
+ mSource = source;
+ mService = mSource.getService();
+ mActionTimer = createActionTimer(mService.getServiceLooper());
}
@VisibleForTesting
@@ -175,6 +174,42 @@
mService.sendCecCommand(cmd, callback);
}
+ protected final void addAndStartAction(FeatureAction action) {
+ mSource.addAndStartAction(action);
+ }
+
+ protected final <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+ return mSource.getActions(clazz);
+ }
+
+ protected final HdmiCecMessageCache getCecMessageCache() {
+ return mSource.getCecMessageCache();
+ }
+
+ /**
+ * Remove the action from the action queue. This is called after the action finishes
+ * its role.
+ *
+ * @param action
+ */
+ protected final void removeAction(FeatureAction action) {
+ mSource.removeAction(action);
+ }
+
+ protected final <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ mSource.removeActionExcept(clazz, null);
+ }
+
+ protected final <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ mSource.removeActionExcept(clazz, exception);
+ }
+
+ protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
+ int retryCount) {
+ mService.pollDevices(callback, pickStrategy, retryCount);
+ }
+
/**
* Clean up action's state.
*
@@ -194,13 +229,23 @@
removeAction(this);
}
- /**
- * Remove the action from the action queue. This is called after the action finishes
- * its role.
- *
- * @param action
- */
- private void removeAction(FeatureAction action) {
- mService.removeAction(action);
+ protected final HdmiCecLocalDevice localDevice() {
+ return mSource;
+ }
+
+ protected final HdmiCecLocalDevicePlayback playback() {
+ return (HdmiCecLocalDevicePlayback) mSource;
+ }
+
+ protected final HdmiCecLocalDeviceTv tv() {
+ return (HdmiCecLocalDeviceTv) mSource;
+ }
+
+ protected final int getSourceAddress() {
+ return mSource.getDeviceInfo().getLogicalAddress();
+ }
+
+ protected final int getSourcePath() {
+ return mSource.getDeviceInfo().getPhysicalAddress();
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index a0c635d..6d05e82 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -17,7 +17,6 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Handler;
@@ -109,10 +108,6 @@
private HdmiControlService mService;
- // Map-like container of all cec devices including local ones.
- // A logical address of device is used as key of container.
- private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
-
// Stores the local CEC devices in the system. Device type is used for key.
private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
@@ -224,90 +219,6 @@
return body;
}
- /**
- * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
- * logical address as new device info's.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
- * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
- * that has the same logical address as new one has.
- */
- HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
- if (oldDeviceInfo != null) {
- removeDeviceInfo(deviceInfo.getLogicalAddress());
- }
- mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
- return oldDeviceInfo;
- }
-
- /**
- * Remove a device info corresponding to the given {@code logicalAddress}.
- * It returns removed {@link HdmiCecDeviceInfo} if exists.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param logicalAddress logical address of device to be removed
- * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
- */
- HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
- if (deviceInfo != null) {
- mDeviceInfos.remove(logicalAddress);
- }
- return deviceInfo;
- }
-
- /**
- * Clear all device info.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- */
- void clearDeviceInfoList() {
- assertRunOnServiceThread();
- mDeviceInfos.clear();
- }
-
- /**
- * Return a list of all {@link HdmiCecDeviceInfo}.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param includeLocalDevice whether to add local device or not
- */
- List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
- assertRunOnServiceThread();
- if (includeLocalDevice) {
- return sparseArrayToList(mDeviceInfos);
- } else {
- ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
- for (int i = 0; i < mDeviceInfos.size(); ++i) {
- HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
- if (mRemoteDeviceAddressPredicate.apply(info.getLogicalAddress())) {
- infoList.add(info);
- }
- }
- return infoList;
- }
- }
-
- /**
- * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param logicalAddress logical address to be retrieved
- * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
- * Returns null if no logical address matched
- */
- HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- return mDeviceInfos.get(logicalAddress);
- }
HdmiPortInfo[] getPortInfos() {
return nativeGetPortInfos(mNativePtr);
@@ -451,33 +362,33 @@
*/
List<HdmiCecLocalDevice> getLocalDeviceList() {
assertRunOnServiceThread();
- return sparseArrayToList(mLocalDevices);
+ return HdmiUtils.sparseArrayToList(mLocalDevices);
}
private List<Integer> pickPollCandidates(int pickStrategy) {
- int strategy = pickStrategy & HdmiControlService.POLL_STRATEGY_MASK;
+ int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK;
Predicate<Integer> pickPredicate = null;
switch (strategy) {
- case HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO:
+ case HdmiConstants.POLL_STRATEGY_SYSTEM_AUDIO:
pickPredicate = mSystemAudioAddressPredicate;
break;
- case HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES:
+ case HdmiConstants.POLL_STRATEGY_REMOTES_DEVICES:
default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
pickPredicate = mRemoteDeviceAddressPredicate;
break;
}
- int iterationStrategy = pickStrategy & HdmiControlService.POLL_ITERATION_STRATEGY_MASK;
+ int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK;
ArrayList<Integer> pollingCandidates = new ArrayList<>();
switch (iterationStrategy) {
- case HdmiControlService.POLL_ITERATION_IN_ORDER:
+ case HdmiConstants.POLL_ITERATION_IN_ORDER:
for (int i = HdmiCec.ADDR_TV; i <= HdmiCec.ADDR_SPECIFIC_USE; ++i) {
if (pickPredicate.apply(i)) {
pollingCandidates.add(i);
}
}
break;
- case HdmiControlService.POLL_ITERATION_REVERSE_ORDER:
+ case HdmiConstants.POLL_ITERATION_REVERSE_ORDER:
default: // The default is reverse order.
for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
if (pickPredicate.apply(i)) {
@@ -489,14 +400,6 @@
return pollingCandidates;
}
- private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
- ArrayList<T> list = new ArrayList<>();
- for (int i = 0; i < array.size(); ++i) {
- list.add(array.valueAt(i));
- }
- return list;
- }
-
private boolean isAllocatedLocalDeviceAddress(int address) {
for (int i = 0; i < mLocalDevices.size(); ++i) {
if (mLocalDevices.valueAt(i).isAddressOf(address)) {
@@ -539,7 +442,7 @@
// new logical address for the device because no device uses
// it as logical address of the device.
if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY)
- == HdmiControlService.SEND_RESULT_SUCCESS) {
+ == HdmiConstants.SEND_RESULT_SUCCESS) {
return true;
}
}
@@ -606,7 +509,7 @@
byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
cecMessage.getDestination(), body);
- if (error != HdmiControlService.SEND_RESULT_SUCCESS) {
+ if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
Slog.w(TAG, "Failed to send " + cecMessage);
}
if (callback != null) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
similarity index 99%
rename from services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java
rename to services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index ebb6f50..7080a56 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -21,7 +21,7 @@
/**
* Helper class to translate android keycode to hdmi cec keycode and vice versa.
*/
-public class HdmiCecKeycodeTranslator {
+public class HdmiCecKeycode {
public static final int UNSUPPORTED_KEYCODE = -1;
public static final int NO_PARAM = -1;
@@ -151,7 +151,7 @@
public static final int UI_SOUND_PRESENTATION_TREBLE_NEUTRAL = 0xC2;
public static final int UI_SOUND_PRESENTATION_TREBLE_STEP_MINUS = 0xC3;
- private HdmiCecKeycodeTranslator() {
+ private HdmiCecKeycode() {
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 7a2a6cc..f86d655 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -19,8 +19,16 @@
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.os.Looper;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
/**
* Class that models a logical CEC device hosted in this system. Handles initialization,
* CEC commands that call for actions customized per device type.
@@ -34,10 +42,37 @@
protected int mPreferredAddress;
protected HdmiCecDeviceInfo mDeviceInfo;
+ // Logical address of the active source.
+ @GuardedBy("mLock")
+ private int mActiveSource;
+
+ // Active routing path. Physical address of the active source but not all the time, such as
+ // when the new active source does not claim itself to be one. Note that we don't keep
+ // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
+ @GuardedBy("mLock")
+ private int mActiveRoutingPath;
+
+ // Set to true while the service is in normal mode. While set to false, no input change is
+ // allowed. Used for situations where input change can confuse users such as channel auto-scan,
+ // system upgrade, etc., a.k.a. "prohibit mode".
+ @GuardedBy("mLock")
+ private boolean mInputChangeEnabled;
+
+ protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
+ protected final Object mLock;
+
+ // A collection of FeatureAction.
+ // Note that access to this collection should happen in service thread.
+ private final LinkedList<FeatureAction> mActions = new LinkedList<>();
+
protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
mService = service;
mDeviceType = deviceType;
mAddress = HdmiCec.ADDR_UNREGISTERED;
+ mLock = service.getServiceLock();
+
+ // TODO: Get control flag from persistent storage
+ mInputChangeEnabled = true;
}
// Factory method that returns HdmiCecLocalDevice of corresponding type.
@@ -69,14 +104,23 @@
* @return true if consumed a message; otherwise, return false.
*/
final boolean dispatchMessage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
int dest = message.getDestination();
if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) {
return false;
}
+ // Cache incoming message. Note that it caches only white-listed one.
+ mCecMessageCache.cacheMessage(message);
return onMessage(message);
}
protected final boolean onMessage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
+ if (dispatchMessageToAction(message)) {
+ return true;
+ }
switch (message.getOpcode()) {
case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
return handleGetMenuLanguage(message);
@@ -90,12 +134,31 @@
return handleGetCecVersion(message);
case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
return handleReportPhysicalAddress(message);
+ case HdmiCec.MESSAGE_INITIATE_ARC:
+ return handleInitiateArc(message);
+ case HdmiCec.MESSAGE_TERMINATE_ARC:
+ return handleTerminateArc(message);
+ case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ return handleSetSystemAudioMode(message);
+ case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+ return handleSystemAudioModeStatus(message);
default:
return false;
}
}
+ private boolean dispatchMessageToAction(HdmiCecMessage message) {
+ for (FeatureAction action : mActions) {
+ if (action.processCommand(message)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
protected boolean handleGivePhysicalAddress() {
+ assertRunOnServiceThread();
+
int physicalAddress = mService.getPhysicalAddress();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, physicalAddress, mDeviceType);
@@ -104,6 +167,8 @@
}
protected boolean handleGiveDeviceVendorId() {
+ assertRunOnServiceThread();
+
int vendorId = mService.getVendorId();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, vendorId);
@@ -112,6 +177,8 @@
}
protected boolean handleGetCecVersion(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
int version = mService.getCecVersion();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
message.getSource(), version);
@@ -120,6 +187,8 @@
}
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
mService.sendCecCommand(
HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
@@ -129,6 +198,8 @@
}
protected boolean handleGiveOsdName(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
// Note that since this method is called after logical address allocation is done,
// mDeviceInfo should not be null.
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
@@ -149,34 +220,235 @@
return false;
}
+ protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleTerminateArc(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleInitiateArc(HdmiCecMessage message) {
+ return false;
+ }
+
final void handleAddressAllocated(int logicalAddress) {
+ assertRunOnServiceThread();
+
mAddress = mPreferredAddress = logicalAddress;
onAddressAllocated(logicalAddress);
}
HdmiCecDeviceInfo getDeviceInfo() {
+ assertRunOnServiceThread();
return mDeviceInfo;
}
void setDeviceInfo(HdmiCecDeviceInfo info) {
+ assertRunOnServiceThread();
mDeviceInfo = info;
}
// Returns true if the logical address is same as the argument.
boolean isAddressOf(int addr) {
+ assertRunOnServiceThread();
return addr == mAddress;
}
// Resets the logical address to unregistered(15), meaning the logical device is invalid.
void clearAddress() {
+ assertRunOnServiceThread();
mAddress = HdmiCec.ADDR_UNREGISTERED;
}
void setPreferredAddress(int addr) {
+ assertRunOnServiceThread();
mPreferredAddress = addr;
}
int getPreferredAddress() {
+ assertRunOnServiceThread();
return mPreferredAddress;
}
+
+ void addAndStartAction(final FeatureAction action) {
+ assertRunOnServiceThread();
+ mActions.add(action);
+ action.start();
+ }
+
+ // See if we have an action of a given type in progress.
+ <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
+ assertRunOnServiceThread();
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Returns all actions matched with given class type.
+ <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+ assertRunOnServiceThread();
+ ArrayList<T> actions = new ArrayList<>();
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ actions.add((T) action);
+ }
+ }
+ return actions;
+ }
+
+ /**
+ * Remove the given {@link FeatureAction} object from the action queue.
+ *
+ * @param action {@link FeatureAction} to remove
+ */
+ void removeAction(final FeatureAction action) {
+ assertRunOnServiceThread();
+ mActions.remove(action);
+ }
+
+ // Remove all actions matched with the given Class type.
+ <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ removeActionExcept(clazz, null);
+ }
+
+ // Remove all actions matched with the given Class type besides |exception|.
+ <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ assertRunOnServiceThread();
+ Iterator<FeatureAction> iter = mActions.iterator();
+ while (iter.hasNext()) {
+ FeatureAction action = iter.next();
+ if (action != exception && action.getClass().equals(clazz)) {
+ action.clear();
+ mActions.remove(action);
+ }
+ }
+ }
+
+ protected void assertRunOnServiceThread() {
+ if (Looper.myLooper() != mService.getServiceLooper()) {
+ throw new IllegalStateException("Should run on service thread.");
+ }
+ }
+
+ /**
+ * Called when a hot-plug event issued.
+ *
+ * @param portId id of port where a hot-plug event happened
+ * @param connected whether to connected or not on the event
+ */
+ void onHotplug(int portId, boolean connected) {
+ }
+
+ final HdmiControlService getService() {
+ return mService;
+ }
+
+ final boolean isConnectedToArcPort(int path) {
+ return mService.isConnectedToArcPort(path);
+ }
+
+ int getActiveSource() {
+ synchronized (mLock) {
+ return mActiveSource;
+ }
+ }
+
+ /**
+ * Returns the active routing path.
+ */
+ int getActivePath() {
+ synchronized (mLock) {
+ return mActiveRoutingPath;
+ }
+ }
+
+ /**
+ * Returns the ID of the active HDMI port. The active port is the one that has the active
+ * routing path connected to it directly or indirectly under the device hierarchy.
+ */
+ int getActivePortId() {
+ synchronized (mLock) {
+ return mService.pathToPortId(mActiveRoutingPath);
+ }
+ }
+
+ /**
+ * Update the active port.
+ *
+ * @param portId the new active port id
+ */
+ void setActivePortId(int portId) {
+ synchronized (mLock) {
+ // We update active routing path instead, since we get the active port id from
+ // the active routing path.
+ mActiveRoutingPath = mService.portIdToPath(portId);
+ }
+ }
+
+ void updateActiveDevice(int logicalAddress, int physicalAddress) {
+ synchronized (mLock) {
+ mActiveSource = logicalAddress;
+ mActiveRoutingPath = physicalAddress;
+ }
+ }
+
+ void setInputChangeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mInputChangeEnabled = enabled;
+ }
+ }
+
+ boolean isInPresetInstallationMode() {
+ synchronized (mLock) {
+ return !mInputChangeEnabled;
+ }
+ }
+
+ /**
+ * Whether the given path is located in the tail of current active path.
+ *
+ * @param path to be tested
+ * @return true if the given path is located in the tail of current active path; otherwise,
+ * false
+ */
+ // TODO: move this to local device tv.
+ boolean isTailOfActivePath(int path) {
+ synchronized (mLock) {
+ // If active routing path is internal source, return false.
+ if (mActiveRoutingPath == 0) {
+ return false;
+ }
+ for (int i = 12; i >= 0; i -= 4) {
+ int curActivePath = (mActiveRoutingPath >> i) & 0xF;
+ if (curActivePath == 0) {
+ return true;
+ } else {
+ int curPath = (path >> i) & 0xF;
+ if (curPath != curActivePath) {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ HdmiCecMessageCache getCecMessageCache() {
+ assertRunOnServiceThread();
+ return mCecMessageCache;
+ }
+
+ int pathToPortId(int newPath) {
+ assertRunOnServiceThread();
+ return mService.pathToPortId(newPath);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d79e283..01345ef 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -17,11 +17,15 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.RemoteException;
+import android.util.Slog;
/**
* Represent a logical device of type Playback residing in Android system.
*/
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
+ private static final String TAG = "HdmiCecLocalDevicePlayback";
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiCec.DEVICE_PLAYBACK);
@@ -32,4 +36,54 @@
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, mService.getPhysicalAddress(), mDeviceType));
}
+
+ void oneTouchPlay(IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (hasAction(OneTouchPlayAction.class)) {
+ Slog.w(TAG, "oneTouchPlay already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+
+ // TODO: Consider the case of multiple TV sets. For now we always direct the command
+ // to the primary one.
+ OneTouchPlayAction action = OneTouchPlayAction.create(this, HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate oneTouchPlay");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
+ }
+
+ void queryDisplayStatus(IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (hasAction(DevicePowerStatusAction.class)) {
+ Slog.w(TAG, "queryDisplayStatus already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+ DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
+ HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate queryDisplayStatus");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
+ }
+
+ private void invokeCallback(IHdmiControlCallback callback, int result) {
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
+
+ @Override
+ void onHotplug(int portId, boolean connected) {
+ // TODO: clear devices connected to the given port id.
+ mCecMessageCache.flushAll();
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 625b256..353f603 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -22,9 +22,12 @@
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -35,12 +38,27 @@
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDeviceTv";
+ // Whether ARC is "enabled" or not.
+ @GuardedBy("mLock")
+ private boolean mArcStatusEnabled = false;
+
+ @GuardedBy("mLock")
+ // Whether SystemAudioMode is "On" or not.
+ private boolean mSystemAudioMode;
+
+ // Map-like container of all cec devices including local ones.
+ // A logical address of device is used as key of container.
+ private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
+
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiCec.DEVICE_TV);
+
+ // TODO: load system audio mode and set it to mSystemAudioMode.
}
@Override
protected void onAddressAllocated(int logicalAddress) {
+ assertRunOnServiceThread();
// TODO: vendor-specific initialization here.
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
@@ -59,14 +77,68 @@
* @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 @@
@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 @@
@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 @@
// 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 @@
}
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 8dbfd85..361a063 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -377,6 +377,17 @@
return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED);
}
+ /**
+ * Build <Give System Audio Mode Status> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildGiveSystemAudioModeStatus(int src, int dest) {
+ return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
+ }
+
/***** Please ADD new buildXXX() methods above. ******/
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
index b558f2b..5294506 100644
--- a/services/core/java/com/android/server/hdmi/HdmiConstants.java
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java
@@ -80,5 +80,22 @@
static final int INVALID_PORT_ID = -1;
static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+ // Send result codes.
+ static final int SEND_RESULT_SUCCESS = 0;
+ static final int SEND_RESULT_NAK = -1;
+ static final int SEND_RESULT_FAILURE = -2;
+
+ // Strategy for device polling.
+ // Should use "OR(|) operation of POLL_STRATEGY_XXX and POLL_ITERATION_XXX.
+ static final int POLL_STRATEGY_MASK = 0x3; // first and second bit.
+ static final int POLL_STRATEGY_REMOTES_DEVICES = 0x1;
+ static final int POLL_STRATEGY_SYSTEM_AUDIO = 0x2;
+
+ static final int POLL_ITERATION_STRATEGY_MASK = 0x30000; // first and second bit.
+ static final int POLL_ITERATION_IN_ORDER = 0x10000;
+ static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
+
+ static final int UNKNOWN_VOLUME = -1;
+
private HdmiConstants() { /* cannot be instantiated */ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index eb356e9..fddb833 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.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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
}
/**
- * 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 @@
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 @@
}
/**
- * 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 @@
}
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 @@
*/
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 @@
}
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 @@
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 @@
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 @@
}
@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 @@
}
@Override
- public void portSelect(int portId, IHdmiControlCallback callback) {
- // TODO: Implement this
- }
-
- @Override
- public void sendKeyEvent(int keyCode, boolean isPressed) {
- // TODO: Implement this
- }
-
- @Override
public List<HdmiPortInfo> getPortInfo() {
enforceAccessPermission();
return mPortInfo;
}
}
- private void oneTouchPlay(IHdmiControlCallback callback) {
- if (hasAction(OneTouchPlayAction.class)) {
- Slog.w(TAG, "oneTouchPlay already in progress");
- invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
- return;
- }
- HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ private void oneTouchPlay(final IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ HdmiCecLocalDevicePlayback source = playback();
if (source == null) {
Slog.w(TAG, "Local playback device not available");
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
- // TODO: Consider the case of multiple TV sets. For now we always direct the command
- // to the primary one.
- OneTouchPlayAction action = OneTouchPlayAction.create(this,
- source.getDeviceInfo().getLogicalAddress(),
- source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback);
- if (action == null) {
- Slog.w(TAG, "Cannot initiate oneTouchPlay");
- invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
- return;
- }
- addAndStartAction(action);
+ source.oneTouchPlay(callback);
}
- private void queryDisplayStatus(IHdmiControlCallback callback) {
- if (hasAction(DevicePowerStatusAction.class)) {
- Slog.w(TAG, "queryDisplayStatus already in progress");
- invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
- return;
- }
- HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ private void queryDisplayStatus(final IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ HdmiCecLocalDevicePlayback source = playback();
if (source == null) {
Slog.w(TAG, "Local playback device not available");
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
- DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
- source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback);
- if (action == null) {
- Slog.w(TAG, "Cannot initiate queryDisplayStatus");
- invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
- return;
- }
- addAndStartAction(action);
+ source.queryDisplayStatus(callback);
}
private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
@@ -1003,43 +683,6 @@
}
}
- 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 @@
}
}
- HdmiCecMessageCache getCecMessageCache() {
- return mCecMessageCache;
- }
-
private static boolean hasSameTopPort(int path1, int path2) {
return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
== (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
}
- /**
- * Whether the given path is located in the tail of current active path.
- *
- * @param path to be tested
- * @return true if the given path is located in the tail of current active path; otherwise,
- * false
- */
- // TODO: move this to local device tv.
- boolean isTailOfActivePath(int path) {
- // If active routing path is internal source, return false.
- if (mActiveRoutingPath == 0) {
- return false;
- }
- for (int i = 12; i >= 0; i -= 4) {
- int curActivePath = (mActiveRoutingPath >> i) & 0xF;
- if (curActivePath == 0) {
- return true;
- } else {
- int curPath = (path >> i) & 0xF;
- if (curPath != curActivePath) {
- return false;
- }
- }
- }
- return false;
+ private HdmiCecLocalDeviceTv tv() {
+ return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+ }
+
+ private HdmiCecLocalDevicePlayback playback() {
+ return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index b534377..9b7cc8d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -19,6 +19,7 @@
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.util.Slog;
+import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Collections;
@@ -111,4 +112,13 @@
static int threeBytesToInt(byte[] data) {
return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
}
+
+ static <T> List<T> sparseArrayToList(SparseArray<T> array) {
+ ArrayList<T> list = new ArrayList<>();
+ for (int i = 0; i < array.size(); ++i) {
+ list.add(array.valueAt(i));
+ }
+ return list;
+ }
+
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index ae20eda..647cc88 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -52,11 +52,10 @@
/**
* Constructor
*
- * @param service instance of {@link HdmiControlService}
- * @param sourceAddress logical address of a device that initiate this action
+ * @param source {@link HdmiCecLocalDevice} instance
*/
- HotplugDetectionAction(HdmiControlService service, int sourceAddress) {
- super(service, sourceAddress);
+ HotplugDetectionAction(HdmiCecLocalDevice source) {
+ super(source);
}
@Override
@@ -110,7 +109,7 @@
if (mTimeoutCount == 0) {
pollAllDevices();
} else {
- if (mService.getSystemAudioMode()) {
+ if (tv().getSystemAudioMode()) {
pollAudioSystem();
}
}
@@ -121,29 +120,29 @@
private void pollAllDevices() {
Slog.v(TAG, "Poll all devices.");
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
checkHotplug(ackedAddress, false);
}
- }, HdmiControlService.POLL_ITERATION_IN_ORDER
- | HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES, POLL_RETRY_COUNT);
+ }, HdmiConstants.POLL_ITERATION_IN_ORDER
+ | HdmiConstants.POLL_STRATEGY_REMOTES_DEVICES, POLL_RETRY_COUNT);
}
private void pollAudioSystem() {
Slog.v(TAG, "Poll audio system.");
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
checkHotplug(ackedAddress, true);
}
- }, HdmiControlService.POLL_ITERATION_IN_ORDER
- | HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO, POLL_RETRY_COUNT);
+ }, HdmiConstants.POLL_ITERATION_IN_ORDER
+ | HdmiConstants.POLL_STRATEGY_SYSTEM_AUDIO, POLL_RETRY_COUNT);
}
private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
- BitSet currentInfos = infoListToBitSet(mService.getDeviceInfoList(false), audioOnly);
+ BitSet currentInfos = infoListToBitSet(tv().getDeviceInfoList(false), audioOnly);
BitSet polledResult = addressListToBitSet(ackedAddress);
// At first, check removed devices.
@@ -195,7 +194,8 @@
private void addDevice(int addedAddress) {
// Send <Give Physical Address>.
- sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, addedAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(),
+ addedAddress));
}
private void removeDevice(int removedAddress) {
@@ -206,7 +206,7 @@
mayCancelOneTouchRecord(removedAddress);
mayDisableSystemAudioAndARC(removedAddress);
- mService.removeCecDevice(removedAddress);
+ tv().removeCecDevice(removedAddress);
}
private void mayChangeRoutingPath(int address) {
@@ -217,7 +217,7 @@
}
private void mayCancelDeviceSelect(int address) {
- List<DeviceSelectAction> actions = mService.getActions(DeviceSelectAction.class);
+ List<DeviceSelectAction> actions = getActions(DeviceSelectAction.class);
if (actions.isEmpty()) {
return;
}
@@ -225,7 +225,7 @@
// Should ave only one Device Select Action
DeviceSelectAction action = actions.get(0);
if (action.getTargetAddress() == address) {
- mService.removeAction(DeviceSelectAction.class);
+ removeAction(DeviceSelectAction.class);
}
}
@@ -239,11 +239,9 @@
}
// Turn off system audio mode.
- mService.setSystemAudioMode(false);
- if (mService.getArcStatus()) {
- mService.addAndStartAction(
- new RequestArcTerminationAction(mService, mSourceAddress, address));
+ tv().setSystemAudioMode(false);
+ if (tv().getArcStatus()) {
+ addAndStartAction(new RequestArcTerminationAction(localDevice(), address));
}
-
}
}
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 2cae507..4a49f09 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -56,15 +56,14 @@
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param deviceLogicalAddress logical address of the device in interest
* @param devicePhysicalAddress physical address of the device in interest
* @param requireRoutingChange whether to initiate routing change or not
*/
- NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress,
+ NewDeviceAction(HdmiCecLocalDevice source, int deviceLogicalAddress,
int devicePhysicalAddress, boolean requireRoutingChange) {
- super(service, sourceAddress);
+ super(source);
mDeviceLogicalAddress = deviceLogicalAddress;
mDevicePhysicalAddress = devicePhysicalAddress;
mVendorId = HdmiCec.UNKNOWN_VENDOR_ID;
@@ -73,16 +72,16 @@
@Override
public boolean start() {
- if (HdmiCec.getTypeFromAddress(mSourceAddress) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
- if (mService.getAvrDeviceInfo() == null) {
+ if (HdmiCec.getTypeFromAddress(getSourceAddress()) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
+ if (tv().getAvrDeviceInfo() == null) {
// TODO: Start system audio initiation action
}
// If new device is connected through ARC enabled port,
// initiates ARC channel establishment.
- if (mService.isConnectedToArcPort(mDevicePhysicalAddress)) {
- mService.addAndStartAction(new RequestArcInitiationAction(mService, mSourceAddress,
- mDeviceLogicalAddress));
+ if (tv().isConnectedToArcPort(mDevicePhysicalAddress)) {
+ addAndStartAction(new RequestArcInitiationAction(localDevice(),
+ mDeviceLogicalAddress));
}
}
@@ -95,7 +94,7 @@
return true;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(),
mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
return true;
@@ -155,14 +154,14 @@
private void startRoutingChange() {
// Stop existing routing control.
- mService.removeAction(RoutingControlAction.class);
+ removeAction(RoutingControlAction.class);
// Send routing change. The the address is a path of the active port.
int newPath = toTopMostPortPath(mDevicePhysicalAddress);
- sendCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
- mService.getActivePath(), newPath));
- mService.addAndStartAction(new RoutingControlAction(mService, mSourceAddress,
- mService.pathToPortId(newPath), null));
+ sendCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
+ localDevice().getActivePath(), newPath));
+ addAndStartAction(new RoutingControlAction(localDevice(),
+ localDevice().pathToPortId(newPath), null));
}
private static int toTopMostPortPath(int physicalAddress) {
@@ -170,7 +169,7 @@
}
private boolean mayProcessCommandIfCached(int destAddress, int opcode) {
- HdmiCecMessage message = mService.getCecMessageCache().getMessage(destAddress, opcode);
+ HdmiCecMessage message = getCecMessageCache().getMessage(destAddress, opcode);
if (message != null) {
return processCommand(message);
}
@@ -184,7 +183,7 @@
if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(),
mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
}
@@ -193,7 +192,7 @@
if (mDisplayName == null) {
mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress);
}
- mService.addCecDevice(new HdmiCecDeviceInfo(
+ tv().addCecDevice(new HdmiCecDeviceInfo(
mDeviceLogicalAddress, mDevicePhysicalAddress,
HdmiCec.getTypeFromAddress(mDeviceLogicalAddress),
mVendorId, mDisplayName));
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 69fad13..e0a3a8b 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -30,7 +30,7 @@
* <p>Package-private, accessed by {@link HdmiControlService} only.
*/
-public final class OneTouchPlayAction extends FeatureAction {
+final class OneTouchPlayAction extends FeatureAction {
private static final String TAG = "OneTouchPlayAction";
// State in which the action is waiting for <Report Power Status>. In normal situation
@@ -48,34 +48,32 @@
// We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds.
private static final int LOOP_COUNTER_MAX = 10;
- private final int mSourcePath;
private final int mTargetAddress;
private final IHdmiControlCallback mCallback;
private int mPowerStatusCounter = 0;
// Factory method. Ensures arguments are valid.
- static OneTouchPlayAction create(HdmiControlService service, int sourceAddress,
- int sourcePath, int targetAddress, IHdmiControlCallback callback) {
- if (service == null || callback == null) {
+ static OneTouchPlayAction create(HdmiCecLocalDevice source,
+ int targetAddress, IHdmiControlCallback callback) {
+ if (source == null || callback == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new OneTouchPlayAction(service, sourceAddress, sourcePath, targetAddress, callback);
+ return new OneTouchPlayAction(source, targetAddress,
+ callback);
}
- private OneTouchPlayAction(HdmiControlService service, int sourceAddress, int sourcePath,
- int targetAddress, IHdmiControlCallback callback) {
- super(service, sourceAddress);
- mSourcePath = sourcePath;
+ private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
+ IHdmiControlCallback callback) {
+ super(localDevice);
mTargetAddress = targetAddress;
mCallback = callback;
}
@Override
boolean start() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildTextViewOn(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
broadcastActiveSource();
queryDevicePowerStatus();
mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
@@ -84,13 +82,12 @@
}
private void broadcastActiveSource() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath));
+ sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()));
}
private void queryDevicePowerStatus() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ mTargetAddress));
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 08ca306..a2e08f1 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -35,16 +35,14 @@
/**
* @Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address. It should
- * TV type
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type
* @throw IllegalArugmentException if device type of sourceAddress and avrAddress
* is invalid
*/
- RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress);
- HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ RequestArcAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
}
@@ -72,9 +70,9 @@
protected final void disableArcTransmission() {
// Start Set ARC Transmission State action.
- SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(mService,
- mSourceAddress, mAvrAddress, false);
- mService.addAndStartAction(action);
+ SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(localDevice(),
+ mAvrAddress, false);
+ addAndStartAction(action);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index 343aff7..55fb65a 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -31,18 +31,18 @@
*
* For more details look at {@link RequestArcAction#RequestArcAction}.
*/
- RequestArcInitiationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress, avrAddress);
+ RequestArcInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source, avrAddress);
}
@Override
boolean start() {
- HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress,
- mAvrAddress);
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ if (error == HdmiConstants.SEND_RESULT_SUCCESS) {
mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
addTimer(mState, TIMEOUT_MS);
} else {
diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
index d4a35f8..62ca8f6 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
@@ -31,18 +31,18 @@
*
* @see RequestArcAction#RequestArcAction
*/
- RequestArcTerminationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress, avrAddress);
+ RequestArcTerminationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source, avrAddress);
}
@Override
boolean start() {
HdmiCecMessage command =
- HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress);
+ HdmiCecMessageBuilder.buildRequestArcTermination(getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ if (error == HdmiConstants.SEND_RESULT_SUCCESS) {
mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
addTimer(mState, TIMEOUT_MS);
} else {
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 19974ea..0d657b2 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -39,7 +39,7 @@
* <li> Routing at CEC enable time
* </ul>
*/
-public class RoutingControlAction extends FeatureAction {
+final class RoutingControlAction extends FeatureAction {
private static final String TAG = "RoutingControlAction";
// State in which we wait for <Routing Information> to arrive. If timed out, we use the
@@ -63,9 +63,8 @@
// The latest routing path. Updated by each <Routing Information> from CEC switches.
private int mCurrentRoutingPath;
- RoutingControlAction(HdmiControlService service, int sourceAddress, int path,
- IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ RoutingControlAction(HdmiCecLocalDevice localDevice, int path, IHdmiControlCallback callback) {
+ super(localDevice);
mCallback = callback;
mCurrentRoutingPath = path;
}
@@ -92,7 +91,7 @@
}
mCurrentRoutingPath = routingPath;
// Stop possible previous routing change sequence if in progress.
- mService.removeAction(RoutingControlAction.class);
+ removeAction(RoutingControlAction.class);
addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
return true;
} else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
@@ -130,7 +129,8 @@
}
private void sendSetStreamPath() {
- sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(mSourceAddress, mCurrentRoutingPath));
+ sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
+ mCurrentRoutingPath));
}
private static boolean isInActiveRoutingPath(int activePath, int newPath) {
@@ -164,9 +164,9 @@
}
switch (timeoutState) {
case STATE_WAIT_FOR_ROUTING_INFORMATION:
- HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath);
+ HdmiCecDeviceInfo device = tv().getDeviceInfoByPath(mCurrentRoutingPath);
if (device == null) {
- maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+ maybeChangeActiveInput(tv().pathToPortId(mCurrentRoutingPath));
} else {
// TODO: Also check followings and then proceed:
// if routing change was neither triggered by TV at CEC enable time, nor
@@ -184,7 +184,7 @@
case STATE_WAIT_FOR_REPORT_POWER_STATUS:
int tvPowerStatus = getTvPowerStatus();
if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) {
- if (!maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath))) {
+ if (!maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath))) {
sendSetStreamPath();
}
}
@@ -196,7 +196,7 @@
// Called whenever an HDMI input of the TV shall become the active input.
private boolean maybeChangeActiveInput(int path) {
- if (mService.getActiveInput() == mService.pathToPortId(path)) {
+ if (localDevice().getActivePortId() == localDevice().pathToPortId(path)) {
return false;
}
// TODO: Remember the currently active input
@@ -207,7 +207,7 @@
}
private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
- sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, address),
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address),
callback);
}
@@ -216,7 +216,7 @@
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
} else {
- maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+ maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath));
}
}
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index 8e6998f..c3078a2 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -16,10 +16,8 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCecMessage;
-import android.view.KeyEvent;
import android.util.Slog;
-
-import libcore.util.EmptyArray;
+import android.view.KeyEvent;
/**
* Feature action that transmits remote control key command (User Control Press/
@@ -56,13 +54,12 @@
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param targetAddress logical address of the device to send the keys to
* @param keyCode remote control key code as defined in {@link KeyEvent}
*/
- SendKeyAction(HdmiControlService service, int sourceAddress, int targetAddress, int keyCode) {
- super(service, sourceAddress);
+ SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keyCode) {
+ super(source);
mTargetAddress = targetAddress;
mLastKeyCode = keyCode;
}
@@ -112,12 +109,13 @@
if (keyCodeAndParam == null) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress, mTargetAddress,
- keyCodeAndParam));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
+ mTargetAddress, keyCodeAndParam));
}
private void sendKeyUp() {
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
+ mTargetAddress));
}
@Override
@@ -146,6 +144,6 @@
// KeyEvent.KEYCODE_TV_BROADCAST_CABLE.
// The return byte array contains both UI command (keycode) and optional parameter.
private byte[] getCecKeyCodeAndParam(int keyCode) {
- return HdmiCecKeycodeTranslator.androidKeyToCecKey(keyCode);
+ return HdmiCecKeycode.androidKeyToCecKey(keyCode);
}
}
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index d53d88d..cdedd6b 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -39,14 +39,13 @@
/**
* @Constructor
*
- * @param service an instance of {@link HdmiControlService}
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param enabled whether to enable ARC Transmission channel
*/
- SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress,
+ SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress,
boolean enabled) {
- super(service, sourceAddress);
- HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ super(source);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
mEnabled = enabled;
@@ -65,11 +64,11 @@
private void sendReportArcInitiated() {
HdmiCecMessage command =
- HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress);
+ HdmiCecMessageBuilder.buildReportArcInitiated(getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ if (error == HdmiConstants.SEND_RESULT_SUCCESS) {
// Enable ARC status immediately after sending <Report Arc Initiated>.
// If AVR responds with <Feature Abort>, disable ARC status again.
// This is different from spec that says that turns ARC status to
@@ -93,14 +92,14 @@
}
private void setArcStatus(boolean enabled) {
- boolean wasEnabled = mService.setArcStatus(enabled);
+ boolean wasEnabled = tv().setArcStatus(enabled);
Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
// If enabled before and set to "disabled" and send <Report Arc Terminated> to
// av reciever.
if (!enabled && wasEnabled) {
- sendCommand(
- HdmiCecMessageBuilder.buildReportArcTerminated(mSourceAddress, mAvrAddress));
+ sendCommand(HdmiCecMessageBuilder.buildReportArcTerminated(getSourceAddress(),
+ mAvrAddress));
}
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index dde3342..e48b0dc 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 @@
/**
* 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 @@
}
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 @@
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 @@
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 @@
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 @@
}
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 c5eb44b..3907b71 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -25,16 +25,15 @@
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param tvAddress logical address of TV device
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
*/
- SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress,
+ SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
boolean targetStatus) {
- super(service, tvAddress, avrAddress, targetStatus);
- HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ super(source, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
}
@Override
@@ -45,13 +44,13 @@
}
private void handleSystemAudioActionFromAvr() {
- if (mTargetAudioStatus == mService.getSystemAudioMode()) {
+ if (mTargetAudioStatus == tv().getSystemAudioMode()) {
finish();
return;
}
- if (mService.isInPresetInstallationMode()) {
+ if (tv().isInPresetInstallationMode()) {
sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
- mSourceAddress, mAvrLogicalAddress,
+ getSourceAddress(), mAvrLogicalAddress,
HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED));
mTargetAudioStatus = false;
sendSystemAudioModeRequest();
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
index 9994de6..e0c4ff4 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -26,16 +26,15 @@
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param tvAddress logical address of TV device
+ * @param sourceAddress {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of tvAddress is invalid
*/
- SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress,
+ SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
boolean targetStatus) {
- super(service, tvAddress, avrAddress, targetStatus);
- HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ super(sourceAddress, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
new file mode 100644
index 0000000..e4d82ef
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+/**
+ * Action to initiate system audio once AVR is detected on Device discovery action.
+ */
+final class SystemAudioAutoInitiationAction extends FeatureAction {
+ private final int mAvrAddress;
+
+ // State that waits for <System Audio Mode Status> once send
+ // <Give System Audio Mode Status> to AV Receiver.
+ private static final int STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS = 1;
+
+ SystemAudioAutoInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source);
+ mAvrAddress = avrAddress;
+ }
+
+ @Override
+ boolean start() {
+ mState = STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS;
+
+ addTimer(mState, TIMEOUT_MS);
+ sendGiveSystemAudioModeStatus();
+ return true;
+ }
+
+ private void sendGiveSystemAudioModeStatus() {
+ sendCommand(HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(getSourceAddress(),
+ mAvrAddress), new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
+ tv().setSystemAudioMode(false);
+ finish();
+ }
+ }
+ });
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS) {
+ return false;
+ }
+
+ switch (cmd.getOpcode()) {
+ case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+ handleSystemAudioModeStatusMessage();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void handleSystemAudioModeStatusMessage() {
+ // If the last setting is system audio, turn on system audio whatever AVR status is.
+ if (tv().getSystemAudioMode()) {
+ if (canChangeSystemAudio()) {
+ addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true));
+ }
+ } else {
+ // If the last setting is non-system audio, turn off system audio mode
+ // and update system audio status (volume or mute).
+ tv().setSystemAudioMode(false);
+ if (canChangeSystemAudio()) {
+ addAndStartAction(new SystemAudioStatusAction(tv(), mAvrAddress));
+ }
+ }
+ finish();
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
+ switch (mState) {
+ case STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS:
+ handleSystemAudioModeStatusTimeout();
+ break;
+ }
+ }
+
+ private void handleSystemAudioModeStatusTimeout() {
+ if (tv().getSystemAudioMode()) {
+ if (canChangeSystemAudio()) {
+ addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true));
+ }
+ } else {
+ tv().setSystemAudioMode(false);
+ }
+ finish();
+ }
+
+ private boolean canChangeSystemAudio() {
+ return tv().canChangeSystemAudio();
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
new file mode 100644
index 0000000..75e4fef
--- /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 9d45702..c954b50 100644
--- a/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
+++ b/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
@@ -12,8 +12,8 @@
private static final int STATE_1 = 1;
private static final int STATE_2 = 2;
- VendorSpecificAction(HdmiControlService service, int sourceAddress) {
- super(service, sourceAddress);
+ VendorSpecificAction(HdmiCecLocalDevice source) {
+ super(source);
// Modify the constructor if additional arguments are necessary.
}
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 4aef2d31..538a252 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -19,19 +19,16 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
-import android.os.BatteryProperty;
-import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.os.BatteryManagerInternal;
import android.os.SystemClock;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.BatteryService;
+import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
@@ -158,14 +155,10 @@
mContext.registerReceiver(this, filter);
// Initialise tracker state.
- BatteryService batteryService = (BatteryService) ServiceManager.getService("battery");
- if (batteryService != null) {
- mBatteryHealthy = !batteryService.getBatteryLevelLow();
- mCharging = batteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
- } else {
- // Unavailable for some reason, we default to false and let ACTION_BATTERY_[OK,LOW]
- // sort it out.
- }
+ BatteryManagerInternal batteryManagerInternal =
+ LocalServices.getService(BatteryManagerInternal.class);
+ mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow();
+ mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
}
boolean isOnStablePower() {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index f2db791..2a7b4f6 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,12 +16,13 @@
package com.android.server.pm;
-import android.content.BroadcastReceiver;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.util.Log;
import java.util.HashSet;
@@ -30,62 +31,63 @@
/**
* {@hide}
*/
-public class BackgroundDexOptService {
-
+public class BackgroundDexOptService extends JobService {
static final String TAG = "BackgroundDexOptService";
- private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) {
- onIdleStart();
- } else if (Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
- onIdleStop();
- }
- }
- };
-
- final PackageManagerService mPackageManager;
+ static final int BACKGROUND_DEXOPT_JOB = 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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1209386..dc76455 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -42,6 +42,7 @@
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.NativeLibraryHelper.ApkHandle;
import com.android.internal.content.PackageHelper;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -1247,7 +1248,7 @@
}
}
- public static final IPackageManager main(Context context, Installer installer,
+ public static final PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
@@ -2242,11 +2243,12 @@
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
return null;
}
+ // App code is gone, so we aren't worried about split paths
pkg = new PackageParser.Package(packageName);
pkg.applicationInfo.packageName = packageName;
pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
- pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
pkg.applicationInfo.sourceDir = ps.codePathString;
+ pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
pkg.applicationInfo.dataDir =
getDataPathForPackage(packageName, 0).getPath();
pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
@@ -4081,6 +4083,7 @@
return false;
}
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ // TODO: generate idmap for split APKs
if (mInstaller.idmap(pkg.codePath, opkg.codePath, sharedGid) != 0) {
Slog.e(TAG, "Failed to generate idmap for " + pkg.codePath + " and " + opkg.codePath);
return false;
@@ -4362,23 +4365,30 @@
}
}
- String codePath = null;
+ final String codePath = pkg.codePath;
+ final String[] splitCodePaths = pkg.splitCodePaths;
+
String resPath = null;
+ String[] splitResPaths = null;
if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
if (ps != null && ps.resourcePathString != null) {
resPath = ps.resourcePathString;
+ splitResPaths = deriveSplitResPaths(pkg.splitCodePaths);
} else {
// Should not happen at all. Just log an error.
Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
}
} else {
resPath = pkg.codePath;
+ splitResPaths = pkg.splitCodePaths;
}
- codePath = pkg.codePath;
// Set application objects path explicitly.
pkg.applicationInfo.sourceDir = codePath;
pkg.applicationInfo.publicSourceDir = resPath;
+ pkg.applicationInfo.splitSourceDirs = splitCodePaths;
+ pkg.applicationInfo.splitPublicSourceDirs = splitResPaths;
+
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
| SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride);
@@ -4626,52 +4636,56 @@
}
}
- boolean performed = false;
- if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
- String path = pkg.codePath;
- try {
- boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
- pkg.packageName,
- instructionSet,
- defer);
- // There are three basic cases here:
- // 1.) we need to dexopt, either because we are forced or it is needed
- // 2.) we are defering a needed dexopt
- // 3.) we are skipping an unneeded dexopt
- if (forceDex || (!defer && isDexOptNeededInternal)) {
- Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
- pkg.packageName, instructionSet);
- // Note that we ran dexopt, since rerunning will
- // probably just result in an error again.
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ final 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;
+ }
+ if (defer && isDexOptNeededInternal) {
+ if (mDeferredDexOpt == null) {
+ mDeferredDexOpt = new HashSet<PackageParser.Package>();
+ }
+ mDeferredDexOpt.add(pkg);
+ return DEX_OPT_DEFERRED;
+ }
pkg.mDexOptNeeded = false;
- if (ret < 0) {
- return DEX_OPT_FAILED;
- }
- return DEX_OPT_PERFORMED;
+ return DEX_OPT_SKIPPED;
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Apk not found for dexopt: " + path);
+ return DEX_OPT_FAILED;
+ } catch (IOException e) {
+ Slog.w(TAG, "IOException reading apk: " + path, e);
+ return DEX_OPT_FAILED;
+ } catch (StaleDexCacheError e) {
+ Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e);
+ return DEX_OPT_FAILED;
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception when doing dexopt : ", e);
+ return DEX_OPT_FAILED;
}
- if (defer && isDexOptNeededInternal) {
- if (mDeferredDexOpt == null) {
- mDeferredDexOpt = new HashSet<PackageParser.Package>();
- }
- mDeferredDexOpt.add(pkg);
- return DEX_OPT_DEFERRED;
- }
- pkg.mDexOptNeeded = false;
- return DEX_OPT_SKIPPED;
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Apk not found for dexopt: " + path);
- return DEX_OPT_FAILED;
- } catch (IOException e) {
- Slog.w(TAG, "IOException reading apk: " + path, e);
- return DEX_OPT_FAILED;
- } catch (StaleDexCacheError e) {
- Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e);
- return DEX_OPT_FAILED;
- } catch (Exception e) {
- Slog.w(TAG, "Exception when doing dexopt : ", e);
- return DEX_OPT_FAILED;
}
}
return DEX_OPT_SKIPPED;
@@ -4819,6 +4833,9 @@
}
if (p != null) {
usesLibraryFiles.add(p.codePath);
+ if (!ArrayUtils.isEmpty(p.splitCodePaths)) {
+ Collections.addAll(usesLibraryFiles, p.splitCodePaths);
+ }
}
}
@@ -4906,7 +4923,7 @@
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
final File scanFile = new File(pkg.codePath);
- if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
+ if (pkg.applicationInfo.sourceDir == null ||
pkg.applicationInfo.publicSourceDir == null) {
// Bail out. The resource and code paths haven't been set.
Slog.w(TAG, " Code and resource paths haven't been set correctly");
@@ -5355,6 +5372,7 @@
* only for non-system apps and system app upgrades.
*/
if (pkg.applicationInfo.nativeLibraryDir != null) {
+ // TODO: extend to extract native code from split APKs
final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
try {
// Enable gross and lame hacks for apps that are built with old
@@ -5908,6 +5926,8 @@
a.info.packageName = pkg.applicationInfo.packageName;
a.info.sourceDir = pkg.applicationInfo.sourceDir;
a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
+ a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;
+ a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;
a.info.dataDir = pkg.applicationInfo.dataDir;
a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
mInstrumentation.put(a.getComponentName(), a);
@@ -9021,6 +9041,10 @@
abstract boolean doPostDeleteLI(boolean delete);
abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException;
+ String[] getSplitCodePaths() {
+ return null;
+ }
+
/**
* Called before the source arguments are copied. This is used mostly
* for MoveParams when it needs to read the source file to put it in the
@@ -9109,10 +9133,6 @@
}
}
- String getCodePath() {
- return codeFileName;
- }
-
void createCopyFile() {
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
codeFileName = createTempPackageFile(installDir).getPath();
@@ -9268,10 +9288,6 @@
return status;
}
- String getResourcePath() {
- return resourceFileName;
- }
-
private String getResourcePathFromCodePath() {
final String codePath = getCodePath();
if (isFwdLocked()) {
@@ -9302,6 +9318,16 @@
}
@Override
+ String getCodePath() {
+ return codeFileName;
+ }
+
+ @Override
+ String getResourcePath() {
+ return resourceFileName;
+ }
+
+ @Override
String getNativeLibraryPath() {
if (libraryPath == null) {
libraryPath = getLibraryPathFromCodePath();
@@ -9768,6 +9794,20 @@
return codePath.substring(sidx+1, eidx);
}
+ private static String[] deriveSplitResPaths(String[] splitCodePaths) {
+ String[] splitResPaths = null;
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ splitResPaths = new String[splitCodePaths.length];
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ final String splitCodePath = splitCodePaths[i];
+ final String resName = getApkName(splitCodePath) + ".zip";
+ splitResPaths[i] = new File(new File(splitCodePath).getParentFile(),
+ resName).getAbsolutePath();
+ }
+ }
+ return splitResPaths;
+ }
+
class PackageInstalledInfo {
String name;
int uid;
@@ -9824,7 +9864,7 @@
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
- updateSettingsLI(null, newPackage, installerPackageName, null, null, res);
+ updateSettingsLI(newPackage, installerPackageName, null, null, res);
// delete the partially installed application. the data directory will have to be
// restored if it was already existing
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -9914,8 +9954,7 @@
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
- updateSettingsLI(deletedPackage, newPackage, installerPackageName, allUsers,
- perUserInstalled, res);
+ updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);
updatedSettings = true;
}
}
@@ -10041,8 +10080,7 @@
}
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSettingsLI(deletedPackage, newPackage, installerPackageName, allUsers,
- perUserInstalled, res);
+ updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);
updatedSettings = true;
}
}
@@ -10068,21 +10106,13 @@
}
// Utility method used to move dex files during install.
- private int moveDexFilesLI(PackageParser.Package oldPackage, PackageParser.Package newPackage) {
- // TODO: extend to handle splits
- if ((newPackage.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ private int moveDexFilesLI(String oldCodePath, PackageParser.Package newPackage) {
+ // TODO: extend to move split APK dex files
+ if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
final String instructionSet = getAppInstructionSet(newPackage.applicationInfo);
-
- boolean moveSuccess = false;
- if (oldPackage != null
- && (oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
- if (mInstaller.movedex(oldPackage.codePath, newPackage.codePath, instructionSet)
- == 0) {
- moveSuccess = true;
- }
- }
-
- if (!moveSuccess) {
+ int retCode = mInstaller.movedex(oldCodePath, newPackage.codePath,
+ instructionSet);
+ if (retCode != 0) {
/*
* Programs may be lazily run through dexopt, so the
* source may not exist. However, something seems to
@@ -10091,19 +10121,17 @@
* remove the target to make sure there isn't a stale
* file from a previous version of the package.
*/
- if (oldPackage != null) {
- mInstaller.rmdex(oldPackage.codePath, instructionSet);
- }
- mInstaller.rmdex(newPackage.codePath, instructionSet);
newPackage.mDexOptNeeded = true;
+ mInstaller.rmdex(oldCodePath, instructionSet);
+ mInstaller.rmdex(newPackage.codePath, instructionSet);
}
}
return PackageManager.INSTALL_SUCCEEDED;
}
- private void updateSettingsLI(PackageParser.Package oldPackage,
- PackageParser.Package newPackage, String installerPackageName, int[] allUsers,
- boolean[] perUserInstalled, PackageInstalledInfo res) {
+ private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
+ int[] allUsers, boolean[] perUserInstalled,
+ PackageInstalledInfo res) {
String pkgName = newPackage.packageName;
synchronized (mPackages) {
//write settings. the installStatus will be incomplete at this stage.
@@ -10113,12 +10141,6 @@
mSettings.writeLPr();
}
- if ((res.returnCode = moveDexFilesLI(oldPackage, newPackage))
- != PackageManager.INSTALL_SUCCEEDED) {
- // Discontinue if moving dex files failed.
- return;
- }
-
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
synchronized (mPackages) {
@@ -10315,6 +10337,9 @@
pkg.codePath = args.getCodePath();
pkg.applicationInfo.sourceDir = args.getCodePath();
pkg.applicationInfo.publicSourceDir = args.getResourcePath();
+ pkg.applicationInfo.splitSourceDirs = args.getSplitCodePaths();
+ pkg.applicationInfo.splitPublicSourceDirs = deriveSplitResPaths(
+ pkg.applicationInfo.splitSourceDirs);
pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode, args.user,
@@ -12857,15 +12882,12 @@
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- PackageParser.Package oldPackage = new PackageParser.Package(
- pkg.packageName);
- oldPackage.codePath = pkg.codePath;
pkg.codePath = newCodePath;
// Move dex files around
- if (moveDexFilesLI(oldPackage, pkg) != PackageManager.INSTALL_SUCCEEDED) {
+ if (moveDexFilesLI(oldCodePath, pkg) != PackageManager.INSTALL_SUCCEEDED) {
// Moving of dex files failed. Set
// error code and abort move.
- pkg.codePath = oldPackage.codePath;
+ pkg.codePath = oldCodePath;
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index fb4b8f0..bd80b54 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -19,10 +19,10 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
-import com.android.server.BatteryService;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
+import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.Watchdog;
@@ -42,6 +42,7 @@
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.net.Uri;
import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -163,13 +164,14 @@
private static final int POWER_HINT_LOW_POWER_MODE = 5;
private final Context mContext;
+ private final ServiceThread mHandlerThread;
+ private final PowerManagerHandler mHandler;
+
private LightsManager mLightsManager;
- private BatteryService mBatteryService;
+ private BatteryManagerInternal mBatteryManagerInternal;
private DisplayManagerInternal mDisplayManagerInternal;
private IBatteryStats mBatteryStats;
private IAppOpsService mAppOps;
- private ServiceThread mHandlerThread;
- private PowerManagerHandler mHandler;
private WindowManagerPolicy mPolicy;
private Notifier mNotifier;
private WirelessChargerDetector mWirelessChargerDetector;
@@ -429,6 +431,11 @@
public PowerManagerService(Context context) {
super(context);
mContext = context;
+ mHandlerThread = new ServiceThread(TAG,
+ Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
+ mHandlerThread.start();
+ mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
@@ -451,39 +458,19 @@
public void onStart() {
publishBinderService(Context.POWER_SERVICE, new BinderService());
publishLocalService(PowerManagerInternal.class, new LocalService());
- }
-
- /**
- * Initialize the power manager.
- * Must be called before any other functions within the power manager are called.
- */
- public void init(LightsManager ls,
- BatteryService bs, IBatteryStats bss,
- IAppOpsService appOps) {
- mLightsManager = ls;
- mBatteryService = bs;
- mBatteryStats = bss;
- mAppOps = appOps;
- mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
- mHandlerThread = new ServiceThread(TAG,
- Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
- mHandlerThread.start();
- mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
}
- void setPolicy(WindowManagerPolicy policy) {
- synchronized (mLock) {
- mPolicy = policy;
- }
- }
-
- public void systemReady() {
+ public void systemReady(IAppOpsService appOps) {
synchronized (mLock) {
mSystemReady = true;
- mDreamManager = LocalServices.getService(DreamManagerInternal.class);
+ mAppOps = appOps;
+ mDreamManager = getLocalService(DreamManagerInternal.class);
+ mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
+ mPolicy = getLocalService(WindowManagerPolicy.class);
+ mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
@@ -494,6 +481,7 @@
// The notifier runs on the system server's main looper so as not to interfere
// with the animations and other critical functions of the power manager.
+ mBatteryStats = BatteryStatsService.getService();
mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
mScreenOnBlocker, mPolicy);
@@ -502,6 +490,8 @@
createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
+
+ mLightsManager = getLocalService(LightsManager.class);
mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
// Initialize display power management.
@@ -1168,10 +1158,10 @@
final boolean wasPowered = mIsPowered;
final int oldPlugType = mPlugType;
final boolean oldLevelLow = mBatteryLevelLow;
- mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
- mPlugType = mBatteryService.getPlugType();
- mBatteryLevel = mBatteryService.getBatteryLevel();
- mBatteryLevelLow = mBatteryService.getBatteryLevelLow();
+ mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+ mPlugType = mBatteryManagerInternal.getPlugType();
+ mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
+ mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
if (DEBUG_SPEW) {
Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
@@ -1254,7 +1244,7 @@
final boolean wasStayOn = mStayOn;
if (mStayOnWhilePluggedInSetting != 0
&& !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
- mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting);
+ mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
} else {
mStayOn = false;
}
@@ -3076,10 +3066,5 @@
mLowPowerModeListeners.add(listener);
}
}
-
- @Override
- public void setPolicy(WindowManagerPolicy policy) {
- PowerManagerService.this.setPolicy(policy);
- }
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index e34f42b..d72ed9e 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 @@
}
}
+ 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 @@
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 @@
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 @@
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 10a67c4..cbbcc58 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -19,12 +19,15 @@
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.OperationApplicationException;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -66,11 +69,12 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/** This class provides a system service that manages television inputs. */
public final class TvInputManagerService extends SystemService {
@@ -123,6 +127,44 @@
buildTvInputListLocked(mCurrentUserId);
}
}
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ synchronized (mLock) {
+ UserState userState = getUserStateLocked(mCurrentUserId);
+ if (!userState.packageList.contains(packageName)) {
+ // Not a TV input package.
+ return;
+ }
+ }
+
+ ArrayList<ContentProviderOperation> operations =
+ new ArrayList<ContentProviderOperation>();
+
+ String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?";
+ String[] selectionArgs = { packageName };
+
+ operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI)
+ .withSelection(selection, selectionArgs).build());
+ operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI)
+ .withSelection(selection, selectionArgs).build());
+ operations.add(ContentProviderOperation
+ .newDelete(TvContract.WatchedPrograms.CONTENT_URI)
+ .withSelection(selection, selectionArgs).build());
+
+ ContentProviderResult[] results = null;
+ try {
+ results = mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+ } catch (RemoteException | OperationApplicationException e) {
+ Slog.e(TAG, "error in applyBatch" + e);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid
+ + ")");
+ Slog.d(TAG, "results=" + results);
+ }
+ }
};
monitor.register(mContext, null, UserHandle.ALL, true);
@@ -145,6 +187,7 @@
private void buildTvInputListLocked(int userId) {
UserState userState = getUserStateLocked(userId);
userState.inputMap.clear();
+ userState.packageList.clear();
if (DEBUG) Slog.d(TAG, "buildTvInputList");
PackageManager pm = mContext.getPackageManager();
@@ -162,6 +205,7 @@
TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
if (DEBUG) Slog.d(TAG, "add " + info.getId());
userState.inputMap.put(info.getId(), info);
+ userState.packageList.add(si.packageName);
} catch (IOException | XmlPullParserException e) {
Slog.e(TAG, "Can't load TV input " + si.name, e);
}
@@ -348,7 +392,7 @@
if (session == null) {
removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
- null, null, sessionState.mSeq, userId);
+ null, null, sessionState.mSeq);
} else {
try {
session.asBinder().linkToDeath(sessionState, 0);
@@ -364,7 +408,7 @@
clientState.mSessionTokens.add(sessionState.mSessionToken);
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
- sessionToken, channels[0], sessionState.mSeq, userId);
+ sessionToken, channels[0], sessionState.mSeq);
}
channels[0].dispose();
}
@@ -449,13 +493,13 @@
Slog.e(TAG, "error in createSession", e);
removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
- sessionState.mSeq, userId);
+ sessionState.mSeq);
}
channels[1].dispose();
}
private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
- IBinder sessionToken, InputChannel channel, int seq, int userId) {
+ IBinder sessionToken, InputChannel channel, int seq) {
try {
client.onSessionCreated(inputId, sessionToken, channel, seq);
} catch (RemoteException exception) {
@@ -672,7 +716,7 @@
}
// Send a null token immediately while reconnecting.
if (serviceState.mReconnecting == true) {
- sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
+ sendSessionTokenToClientLocked(client, inputId, null, null, seq);
return;
}
@@ -784,7 +828,10 @@
}
// Create a log entry and fill it later.
+ String packageName = userState.inputMap.get(sessionState.mInputId)
+ .getServiceInfo().packageName;
ContentValues values = new ContentValues();
+ values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
currentTime);
values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0);
@@ -931,6 +978,9 @@
// A mapping from the TV input id to its TvInputInfo.
private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
+ // A list of all TV input packages.
+ private final Set<String> packageList = new HashSet<String>();
+
// A mapping from the token of a client to its state.
private final Map<IBinder, ClientState> clientStateMap =
new HashMap<IBinder, ClientState>();
@@ -1095,8 +1145,7 @@
if (sessionState.mSession == null) {
removeSessionStateLocked(sessionToken, sessionState.mUserId);
sendSessionTokenToClientLocked(sessionState.mClient,
- sessionState.mInputId, null, null, sessionState.mSeq,
- sessionState.mUserId);
+ sessionState.mInputId, null, null, sessionState.mSeq);
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 6cb6b76..d05d0c7 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -961,8 +961,6 @@
// If the window is not touchable, do not report it but take into account
// the space it takes since the content behind it cannot be touched.
if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- unaccountedSpace.op(boundsInScreen, unaccountedSpace,
- Region.Op.DIFFERENCE);
continue;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 45326f7..f1d0585d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -391,7 +391,7 @@
final WindowStateAnimator winAnimator = unForceHiding.get(i);
winAnimator.setAnimation(a);
winAnimator.mAnimationIsEntrance = true;
- if (startKeyguardExit) {
+ if (startKeyguardExit && mKeyguardGoingAway) {
// Do one time only.
mPolicy.startKeyguardExitAnimation(mCurrentTime + a.getStartOffset(),
a.getDuration());
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2d8a34b..d04d668 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -766,6 +766,8 @@
mDisplaySettings = new DisplaySettings(context);
mDisplaySettings.readSettingsLocked();
+ LocalServices.addService(WindowManagerPolicy.class, mPolicy);
+
mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG));
mFxSession = new SurfaceSession();
@@ -779,7 +781,6 @@
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
- mPowerManagerInternal.setPolicy(mPolicy); // TODO: register as local service instead
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4574caf..765a33d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -195,7 +195,7 @@
= new ArrayList<ActiveAdmin>();
// This is the list of component allowed to start lock task mode.
- final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>();
+ final List<String> mLockTaskPackages = new ArrayList<String>();
ComponentName mRestrictionsProvider;
@@ -1014,10 +1014,10 @@
out.endTag(null, "active-password");
}
- for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
- ComponentName component = policy.mLockTaskComponents.get(i);
+ for (int i=0; i<policy.mLockTaskPackages.size(); i++) {
+ String component = policy.mLockTaskPackages.get(i);
out.startTag(null, LOCK_TASK_COMPONENTS_XML);
- out.attribute(null, "name", component.flattenToString());
+ out.attribute(null, "name", component);
out.endTag(null, LOCK_TASK_COMPONENTS_XML);
}
@@ -1077,7 +1077,7 @@
type = parser.next();
int outerDepth = parser.getDepth();
- policy.mLockTaskComponents.clear();
+ policy.mLockTaskPackages.clear();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1131,9 +1131,7 @@
parser.getAttributeValue(null, "nonletter"));
XmlUtils.skipCurrentTag(parser);
} else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) {
- policy.mLockTaskComponents.add
- (ComponentName.unflattenFromString
- (parser.getAttributeValue(null, "name")));
+ policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
XmlUtils.skipCurrentTag(parser);
} else {
Slog.w(LOG_TAG, "Unknown tag: " + tag);
@@ -3723,38 +3721,40 @@
}
/**
- * Sets which componets may enter lock task mode.
+ * Sets which packages may enter lock task mode.
*
* This function can only be called by the device owner or the profile owner.
* @param components The list of components allowed to enter lock task mode.
*/
- public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+ public void setLockTaskPackages(String[] packages) throws SecurityException {
// Get the package names of the caller.
int uid = Binder.getCallingUid();
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
- // Check whether any of the package name is the device owner or the profile owner.
- for (int i=0; i<packageNames.length; i++) {
- String packageName = packageNames[i];
- int userHandle = UserHandle.getUserId(uid);
- String profileOwnerPackage = getProfileOwner(userHandle);
- if (isDeviceOwner(packageName) ||
- (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) {
+ synchronized (this) {
+ // Check whether any of the package name is the device owner or the profile owner.
+ for (int i=0; i<packageNames.length; i++) {
+ String packageName = packageNames[i];
+ int userHandle = UserHandle.getUserId(uid);
+ String profileOwnerPackage = getProfileOwner(userHandle);
+ if (isDeviceOwner(packageName) ||
+ (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) {
- // If a package name is the device owner or the profile owner,
- // we update the component list.
- DevicePolicyData policy = getUserData(userHandle);
- policy.mLockTaskComponents.clear();
- if (components != null) {
- for (int j=0; j<components.length; j++) {
- ComponentName component = components[j];
- policy.mLockTaskComponents.add(component);
+ // If a package name is the device owner or the profile owner,
+ // we update the component list.
+ DevicePolicyData policy = getUserData(userHandle);
+ policy.mLockTaskPackages.clear();
+ if (packages != null) {
+ for (int j = 0; j < packages.length; j++) {
+ String pkg = packages[j];
+ policy.mLockTaskPackages.add(pkg);
+ }
}
- }
- // Store the settings persistently.
- saveSettingsLocked(userHandle);
- return;
+ // Store the settings persistently.
+ saveSettingsLocked(userHandle);
+ return;
+ }
}
}
throw new SecurityException();
@@ -3763,30 +3763,33 @@
/**
* 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 3102cce..0d05c5f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -63,6 +63,7 @@
import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
+import com.android.server.fingerprint.FingerprintService;
import com.android.server.hdmi.HdmiControlService;
import com.android.server.input.InputManagerService;
import com.android.server.job.JobSchedulerService;
@@ -146,8 +147,13 @@
private PowerManagerService mPowerManagerService;
private ActivityManagerService mActivityManagerService;
private DisplayManagerService mDisplayManagerService;
+ private PackageManagerService mPackageManagerService;
+ private PackageManager mPackageManager;
private ContentResolver mContentResolver;
+ private boolean mOnlyCore;
+ private boolean mFirstBoot;
+
/**
* Called to initialize native system services.
*/
@@ -161,6 +167,7 @@
}
public SystemServer() {
+ // Check for factory test mode.
mFactoryTestMode = FactoryTest.getMode();
}
@@ -243,7 +250,7 @@
startBootstrapServices();
startCoreServices();
startOtherServices();
- } catch (RuntimeException ex) {
+ } catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
@@ -287,36 +294,84 @@
mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}
+ /**
+ * Starts the small tangle of critical services that are needed to get
+ * the system off the ground. These services have complex mutual dependencies
+ * which is why we initialize them all in one place here. Unless your service
+ * is also entwined in these dependencies, it should be initialized in one of
+ * the other functions.
+ */
private void startBootstrapServices() {
// Wait for installd to finish starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
// permissions. We need this to complete before we initialize other services.
mInstaller = mSystemServiceManager.startService(Installer.class);
- // Power manager needs to be started early because other services need it.
- // TODO: The conversion to the new pattern is incomplete. We need to switch
- // the power manager's dependencies over then we can use boot phases to arrange
- // initialization order and remove the mPowerManagerService field.
- mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
-
// Activity manager runs the show.
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
- }
- private void startCoreServices() {
+ // Power manager needs to be started early because other services need it.
+ // Native daemons may be watching for it to be registered so it must be ready
+ // to handle incoming binder calls immediately (including being able to verify
+ // the permissions for those calls).
+ mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
+
+ // Now that the power manager has been started, let the activity manager
+ // initialize power management features.
+ mActivityManagerService.initPowerManagement();
+
// Display manager is needed to provide display metrics before package manager
// starts up.
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
+
+ // We need the default display before we can initialize the package manager.
+ mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ // Only run "core" apps if we're encrypting the device.
+ String cryptState = SystemProperties.get("vold.decrypt");
+ if (ENCRYPTING_STATE.equals(cryptState)) {
+ Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
+ mOnlyCore = true;
+ } else if (ENCRYPTED_STATE.equals(cryptState)) {
+ Slog.w(TAG, "Device encrypted - only parsing core apps");
+ mOnlyCore = true;
+ }
+
+ // Start the package manager.
+ Slog.i(TAG, "Package Manager");
+ mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
+ mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
+ mFirstBoot = mPackageManagerService.isFirstBoot();
+ mPackageManager = mSystemContext.getPackageManager();
+
+ // Initialize attribute cache used to cache resources from packages.
+ AttributeCache.init(mSystemContext);
+
+ // Set up the Application instance for the system process and get started.
+ mActivityManagerService.setSystemProcess();
}
+ /**
+ * Starts some essential services that are not tangled up in the bootstrap process.
+ */
+ private void startCoreServices() {
+ // Manages LEDs and display backlight.
+ mSystemServiceManager.startService(LightsService.class);
+
+ // Tracks the battery level. Requires LightService.
+ mSystemServiceManager.startService(BatteryService.class);
+ }
+
+ /**
+ * Starts a miscellaneous grab bag of stuff that has yet to be refactored
+ * and organized.
+ */
private void startOtherServices() {
final Context context = mSystemContext;
AccountManagerService accountManager = null;
ContentService contentService = null;
- LightsManager lights = null;
- BatteryService battery = null;
VibratorService vibrator = null;
IAlarmManager alarm = null;
MountService mountService = null;
@@ -326,7 +381,6 @@
ConnectivityService connectivity = null;
NetworkScoreService networkScore = null;
NsdService serviceDiscovery= null;
- IPackageManager pm = null;
WindowManagerService wm = null;
BluetoothManagerService bluetooth = null;
UsbService usb = null;
@@ -339,8 +393,6 @@
ConsumerIrService consumerIr = null;
AudioService audioService = null;
- boolean onlyCore = false;
- boolean firstBoot = false;
boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
@@ -352,38 +404,12 @@
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
try {
- Slog.i(TAG, "Telephony Registry");
- telephonyRegistry = new TelephonyRegistry(context);
- ServiceManager.addService("telephony.registry", telephonyRegistry);
-
Slog.i(TAG, "Scheduling Policy");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
- AttributeCache.init(context);
-
- // We need the default display before we can initialize the package manager.
- mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
-
- Slog.i(TAG, "Package Manager");
- // Only run "core" apps if we're encrypting the device.
- String cryptState = SystemProperties.get("vold.decrypt");
- if (ENCRYPTING_STATE.equals(cryptState)) {
- Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
- onlyCore = true;
- } else if (ENCRYPTED_STATE.equals(cryptState)) {
- Slog.w(TAG, "Device encrypted - only parsing core apps");
- onlyCore = true;
- }
-
- pm = PackageManagerService.main(context, mInstaller,
- mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
- onlyCore);
- try {
- firstBoot = pm.isFirstBoot();
- } catch (RemoteException e) {
- }
-
- mActivityManagerService.setSystemProcess();
+ Slog.i(TAG, "Telephony Registry");
+ telephonyRegistry = new TelephonyRegistry(context);
+ ServiceManager.addService("telephony.registry", telephonyRegistry);
Slog.i(TAG, "Entropy Mixer");
ServiceManager.addService("entropy", new EntropyMixer(context));
@@ -411,24 +437,10 @@
Slog.i(TAG, "System Content Providers");
mActivityManagerService.installSystemProviders();
- mSystemServiceManager.startService(LightsService.class);
- lights = LocalServices.getService(LightsManager.class);
-
- Slog.i(TAG, "Battery Service");
- battery = new BatteryService(context, lights);
- ServiceManager.addService("battery", battery);
-
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
- // TODO: use boot phase
- // only initialize the power service after we have started the
- // lights service, content providers and the battery service.
- mPowerManagerService.init(lights, battery,
- BatteryStatsService.getService(),
- mActivityManagerService.getAppOpsService());
-
Slog.i(TAG, "Consumer IR Service");
consumerIr = new ConsumerIrService(context);
ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
@@ -447,7 +459,7 @@
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
- !firstBoot, onlyCore);
+ !mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
@@ -521,7 +533,7 @@
}
try {
- pm.performBootDexOpt();
+ mPackageManagerService.performBootDexOpt();
} catch (Throwable e) {
reportWtf("performing boot dexopt", e);
}
@@ -559,13 +571,9 @@
reportWtf("starting LockSettingsService service", e);
}
- try {
- // Always start the Device Policy Manager, so that the API is compatible with
- // API8.
- mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
- } catch (Throwable e) {
- reportWtf("starting DevicePolicyService", e);
- }
+ // Always start the Device Policy Manager, so that the API is compatible with
+ // API8.
+ mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
}
if (!disableSystemUI) {
@@ -636,39 +644,17 @@
reportWtf("starting NetworkPolicy Service", e);
}
- try {
- mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi P2pService", e);
- }
+ mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
- try {
- mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi PasspointService", e);
- }
+ mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
- try {
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi Service", e);
- }
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- try {
- Slog.i(TAG, "Wi-Fi Scanning Service");
- mSystemServiceManager.startService(
+ mSystemServiceManager.startService(
"com.android.server.wifi.WifiScanningService");
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi Scanning Service", e);
- }
-
if (!isEmulator) {
- try {
- mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting Ethernet Service", e);
- }
+ mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
} else {
// Don't start the Ethernet service on the emulator because
// it interferes with qemu's SLIRP emulation, which uses
@@ -712,7 +698,7 @@
* AppWidget Provider. Make sure MountService is completely started
* first before continuing.
*/
- if (mountService != null && !onlyCore) {
+ if (mountService != null && !mOnlyCore) {
mountService.waitForAsecScan();
}
@@ -810,14 +796,11 @@
}
if (!disableNonCoreServices) {
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST) ||
- pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY)) {
- // Manage USB host and device support
- mSystemServiceManager.startService(USB_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- reportWtf("starting UsbService", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
+ || mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_USB_ACCESSORY)) {
+ // Manage USB host and device support
+ mSystemServiceManager.startService(USB_SERVICE_CLASS);
}
try {
@@ -837,20 +820,12 @@
mSystemServiceManager.startService(JobSchedulerService.class);
if (!disableNonCoreServices) {
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
- mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting Backup Service", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
+ mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
}
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
- mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- reportWtf("starting AppWidget Service", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+ mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
}
try {
@@ -860,13 +835,8 @@
reportWtf("starting Recognition Service", e);
}
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
- Slog.i(TAG, "Voice Recognition Service");
- mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- reportWtf("starting Voice Recognition Service", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
+ mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
}
}
@@ -932,45 +902,17 @@
}
}
- try {
- Slog.i(TAG, "IdleMaintenanceService");
- new IdleMaintenanceService(context, battery);
- } catch (Throwable e) {
- reportWtf("starting IdleMaintenanceService", e);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
+ mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
}
- try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
- mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
- }
- } catch (Throwable e) {
- reportWtf("starting Print Service", e);
- }
+ mSystemServiceManager.startService(RestrictionsManagerService.class);
- try {
- mSystemServiceManager.startService(RestrictionsManagerService.class);
- } catch (Throwable e) {
- reportWtf("starting RestrictionsManagerService", e);
- }
+ mSystemServiceManager.startService(MediaSessionService.class);
- try {
- mSystemServiceManager.startService(MediaSessionService.class);
- } catch (Throwable e) {
- reportWtf("starting MediaSessionService", e);
- }
+ mSystemServiceManager.startService(HdmiControlService.class);
- try {
- mSystemServiceManager.startService(HdmiControlService.class);
- } catch (Throwable e) {
- reportWtf("starting HdmiControlService", e);
- }
-
- try {
- Slog.i(TAG, "TvInputManagerService");
- mSystemServiceManager.startService(TvInputManagerService.class);
- } catch (Throwable e) {
- reportWtf("starting TvInputManagerService", e);
- }
+ mSystemServiceManager.startService(TvInputManagerService.class);
if (!disableNonCoreServices) {
try {
@@ -981,27 +923,20 @@
reportWtf("starting MediaRouterService", e);
}
- try {
- Slog.i(TAG, "Trust Manager");
- mSystemServiceManager.startService(TrustManagerService.class);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting TrustManagerService", e);
- }
+ mSystemServiceManager.startService(TrustManagerService.class);
+
+ mSystemServiceManager.startService(FingerprintService.class);
try {
Slog.i(TAG, "BackgroundDexOptService");
- new BackgroundDexOptService(context);
+ BackgroundDexOptService.schedule(context);
} catch (Throwable e) {
reportWtf("starting BackgroundDexOptService", e);
}
+
}
- try {
- Slog.i(TAG, "LauncherAppsService");
- mSystemServiceManager.startService(LauncherAppsService.class);
- } catch (Throwable t) {
- reportWtf("starting LauncherAppsService", t);
- }
+ mSystemServiceManager.startService(LauncherAppsService.class);
}
// Before things start rolling, be sure we have decided whether
@@ -1058,27 +993,26 @@
try {
// TODO: use boot phase
- mPowerManagerService.systemReady();
+ mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
} catch (Throwable e) {
reportWtf("making Power Manager Service ready", e);
}
try {
- pm.systemReady();
+ mPackageManagerService.systemReady();
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
try {
// TODO: use boot phase and communicate these flags some other way
- mDisplayManagerService.systemReady(safeMode, onlyCore);
+ mDisplayManagerService.systemReady(safeMode, mOnlyCore);
} catch (Throwable e) {
reportWtf("making Display Manager Service ready", e);
}
// These are needed to propagate to the runnable below.
final MountService mountServiceF = mountService;
- final BatteryService batteryF = battery;
final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
@@ -1127,11 +1061,6 @@
reportWtf("making Mount Service ready", e);
}
try {
- if (batteryF != null) batteryF.systemReady();
- } catch (Throwable e) {
- reportWtf("making Battery Service ready", e);
- }
- try {
if (networkScoreF != null) networkScoreF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Score Service ready", e);
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index d974509..8a4e123 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -20,15 +20,9 @@
import android.os.Bundle;
import android.telephony.DisconnectCause;
-import android.os.SystemClock;
-
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* A {@link android.app.Service} that provides telephone connections to
diff --git a/telecomm/java/android/telecomm/Subscription.aidl b/telecomm/java/android/telecomm/Subscription.aidl
new file mode 100644
index 0000000..6327fcc
--- /dev/null
+++ b/telecomm/java/android/telecomm/Subscription.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecomm;
+
+/**
+ * {@hide}
+ */
+parcelable Subscription;
diff --git a/telecomm/java/android/telecomm/Subscription.java b/telecomm/java/android/telecomm/Subscription.java
index f187f4d..964db4a 100644
--- a/telecomm/java/android/telecomm/Subscription.java
+++ b/telecomm/java/android/telecomm/Subscription.java
@@ -16,25 +16,169 @@
package android.telecomm;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Rlog;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import java.util.MissingResourceException;
/**
* Represents a distinct subscription, line of service or call placement method that
- * a {@link ConnectionService} can use to place phone calls.
+ * the system can use to place phone calls.
*/
public class Subscription implements Parcelable {
- public Subscription() {}
+ private static final int NO_DENSITY = -1;
+
+ private static final String LOG_TAG = "Subscription";
+
+ private final ComponentName mComponentName;
+ private final String mId;
+ private final Uri mHandle;
+ private final int mLabelResId;
+ private final int mShortDescriptionResId;
+ private final int mIconResId;
+ private final boolean mIsEnabled;
+ private final boolean mIsSystemDefault;
+
+ public Subscription(
+ ComponentName componentName,
+ String id,
+ Uri handle,
+ int labelResId,
+ int shortDescriptionResId,
+ int iconResId,
+ boolean isEnabled,
+ boolean isSystemDefault) {
+ mComponentName = componentName;
+ mId = id;
+ mHandle = handle;
+ mLabelResId = labelResId;
+ mShortDescriptionResId = shortDescriptionResId;
+ mIconResId = iconResId;
+ mIsSystemDefault = isSystemDefault;
+ mIsEnabled = isEnabled;
+ }
+
+ /**
+ * The {@code ComponentName} of the {@link android.telecomm.ConnectionService} which is
+ * responsible for making phone calls using this {@code Subscription}.
+ *
+ * @return A suitable {@code ComponentName}.
+ */
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
+ * A unique identifier for this {@code Subscription}, generated by and meaningful to the
+ * {@link android.telecomm.ConnectionService} that created it.
+ *
+ * @return A unique identifier for this {@code Subscription}.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * The handle (e.g., a phone number) associated with this {@code Subscription}. This represents
+ * the destination from which outgoing calls using this {@code Subscription} will appear to come
+ * from, if applicable, and the destination to which incoming calls using this
+ * {@code Subscription} may be addressed.
+ *
+ * @return A handle expressed as a {@code Uri}, for example, a phone number.
+ */
+ public Uri getHandle() {
+ return mHandle;
+ }
+
+ /**
+ * A short string label describing this {@code Subscription}.
+ *
+ * @param context The invoking {@code Context}, used for retrieving resources.
+ *
+ * @return A label for this {@code Subscription}.
+ */
+ public String getLabel(Context context) {
+ return getString(context, mLabelResId);
+ }
+
+ /**
+ * A short paragraph describing this {@code Subscription}.
+ *
+ * @param context The invoking {@code Context}, used for retrieving resources.
+ *
+ * @return A description for this {@code Subscription}.
+ */
+ public String getShortDescription(Context context) {
+ return getString(context, mShortDescriptionResId);
+ }
+
+ /**
+ * An icon to represent this {@code Subscription} in a user interface.
+ *
+ * @param context The invoking {@code Context}, used for retrieving resources.
+ *
+ * @return An icon for this {@code Subscription}.
+ */
+ public Drawable getIcon(Context context) {
+ return getIcon(context, mIconResId, NO_DENSITY);
+ }
+
+ /**
+ * An icon to represent this {@code Subscription} in a user interface.
+ *
+ * @param context The invoking {@code Context}, used for retrieving resources.
+ * @param density A display density from {@link DisplayMetrics}.
+ *
+ * @return An icon for this {@code Subscription}.
+ */
+ public Drawable getIcon(Context context, int density) {
+ return getIcon(context, mIconResId, density);
+ }
+
+ /**
+ * Whether this {@code Subscription} is enabled for use.
+ *
+ * @return {@code true} if this {@code Subscription} is enabled.
+ */
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /**
+ * Whether this {@code Subscription} is the system default.
+ *
+ * @return {@code true} if this {@code Subscription} is the system default.
+ */
+ public boolean isSystemDefault() {
+ return mIsSystemDefault;
+ }
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel out, int flags) {}
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mComponentName, flags);
+ out.writeString(mId);
+ out.writeString(mHandle != null ? mHandle.toString() : "");
+ out.writeInt(mLabelResId);
+ out.writeInt(mShortDescriptionResId);
+ out.writeInt(mIconResId);
+ out.writeInt(mIsEnabled ? 1 : 0);
+ out.writeInt(mIsSystemDefault ? 1 : 0);
+ }
- public static final Parcelable.Creator<Subscription> CREATOR
- = new Parcelable.Creator<Subscription>() {
+ public static final Creator<Subscription> CREATOR
+ = new Creator<Subscription>() {
public Subscription createFromParcel(Parcel in) {
return new Subscription(in);
}
@@ -44,5 +188,54 @@
}
};
- private Subscription(Parcel in) {}
+ private Subscription(Parcel in) {
+ mComponentName = in.readParcelable(getClass().getClassLoader());
+ mId = in.readString();
+ String uriString = in.readString();
+ mHandle = uriString.length() > 0 ? Uri.parse(uriString) : null;
+ mLabelResId = in.readInt();
+ mShortDescriptionResId = in.readInt();
+ mIconResId = in.readInt();
+ mIsEnabled = in.readInt() == 1;
+ mIsSystemDefault = in.readInt() == 1;
+ }
+
+ private String getString(Context context, int resId) {
+ Context packageContext;
+ try {
+ packageContext = context.createPackageContext(mComponentName.getPackageName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ if (Rlog.isLoggable(LOG_TAG, Log.WARN)) {
+ Rlog.w(LOG_TAG, "Cannot find package " + mComponentName.getPackageName());
+ }
+ return null;
+ }
+ String result = packageContext.getString(resId);
+ if (result == null && Rlog.isLoggable(LOG_TAG, Log.WARN)) {
+ Rlog.w(LOG_TAG, "Cannot find string " + resId + " in package " +
+ mComponentName.getPackageName());
+ }
+ return result;
+ }
+
+ private Drawable getIcon(Context context, int resId, int density) {
+ Context packageContext;
+ try {
+ packageContext = context.createPackageContext(mComponentName.getPackageName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ if (Rlog.isLoggable(LOG_TAG, Log.WARN)) {
+ Rlog.w(LOG_TAG, "Cannot find package " + mComponentName.getPackageName());
+ }
+ return null;
+ }
+ try {
+ return density == NO_DENSITY ?
+ packageContext.getResources().getDrawable(resId) :
+ packageContext.getResources().getDrawableForDensity(resId, density);
+ } catch (MissingResourceException e) {
+ Rlog.e(LOG_TAG, "Cannot find icon " + resId + " in package " +
+ mComponentName.getPackageName() + ": " + e.toString());
+ return null;
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 4ca878e..0952097 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;
@@ -70,6 +71,13 @@
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/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 638b86a..dc2b869 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecomm;
+import android.telecomm.Subscription;
import android.content.ComponentName;
/**
@@ -45,4 +46,19 @@
* 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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 50bbb1e..3fde36e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -19,15 +19,12 @@
import android.annotation.SystemApi;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.telephony.Rlog;
+import android.telecomm.Subscription;
import android.util.Log;
import com.android.internal.telecomm.ITelecommService;
@@ -40,7 +37,6 @@
import java.io.FileInputStream;
import java.io.IOException;
-import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -302,6 +298,17 @@
public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
/**
+ * The lookup key used with an {@link android.content.Intent#ACTION_CALL} or
+ * {@link android.content.Intent#ACTION_DIAL} {@code Intent} for a {@link Subscription}
+ * object indicating a preference when making a phone connection.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_SUBSCRIPTION = "subscription";
+
+ /**
* Broadcast intent action indicating that a precise call state
* (cellular) on the device has changed.
*
@@ -3112,4 +3119,40 @@
}
return false;
}
+
+ /**
+ * Return a list of Subscriptions that can be used to indicate a preference when making
+ * a phone call.
+ *
+ * @see #EXTRA_SUBSCRIPTION
+ * @return A list of {@code Subscription} objects.
+ */
+ public List<Subscription> getSubscriptions() {
+ try {
+ return getTelecommService().getSubscriptions();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getSubscriptions", e);
+ }
+ return null;
+ }
+
+ /** @hide */
+ @SystemApi
+ public void setEnabled(Subscription subscription, boolean enabled) {
+ try {
+ getTelecommService().setEnabled(subscription, enabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setEnabled", e);
+ }
+ }
+
+ /** @hide */
+ @SystemApi
+ public void setSystemDefault(Subscription subscription) {
+ try {
+ getTelecommService().setSystemDefault(subscription);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setSystemDefault", e);
+ }
+ }
}
diff --git a/tests/IdleServiceTest/Android.mk b/tests/IdleServiceTest/Android.mk
deleted file mode 100644
index a7879c5..0000000
--- a/tests/IdleServiceTest/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := IdleServiceTest
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/tests/IdleServiceTest/AndroidManifest.xml b/tests/IdleServiceTest/AndroidManifest.xml
deleted file mode 100644
index 16d2324..0000000
--- a/tests/IdleServiceTest/AndroidManifest.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.idleservicetest">
-
- <application>
- <service android:name="TestService"
- android:exported="true"
- android:enabled="true"
- android:permission="android.permission.BIND_IDLE_SERVICE" >
- <intent-filter>
- <action android:name="android.service.idle.IdleService" />
- </intent-filter>
- </service>
-
- <service android:name="CrashingTestService"
- android:exported="true"
- android:enabled="true"
- android:permission="android.permission.BIND_IDLE_SERVICE" >
- <intent-filter>
- <action android:name="android.service.idle.IdleService" />
- </intent-filter>
- </service>
-
- <service android:name="TimeoutTestService"
- android:exported="true"
- android:enabled="true"
- android:permission="android.permission.BIND_IDLE_SERVICE" >
- <intent-filter>
- <action android:name="android.service.idle.IdleService" />
- </intent-filter>
- </service>
-
- <!-- UnpermissionedTestService should never run because it does
- not require the necessary permission in its <service> block -->
- <service android:name="UnpermissionedTestService"
- android:exported="true"
- android:enabled="true" >
- <intent-filter>
- <action android:name="android.service.idle.IdleService" />
- </intent-filter>
- </service>
-
- </application>
-</manifest>
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java
deleted file mode 100644
index 022ebcf..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.os.Handler;
-import android.util.Log;
-
-public class CrashingTestService extends IdleService {
- static final String TAG = "CrashingTestService";
-
- String mNull = null;
-
- @Override
- public boolean onIdleStart() {
- Log.i(TAG, "Idle maintenance: onIdleStart()");
-
- Handler h = new Handler();
- Runnable r = new Runnable() {
- @Override
- public void run() {
- Log.i(TAG, "Explicitly crashing");
- if (mNull.equals("")) {
- Log.i(TAG, "won't happen");
- }
- }
- };
- Log.i(TAG, "Posting explicit crash in 15 seconds");
- h.postDelayed(r, 15 * 1000);
- return true;
- }
-
- @Override
- public void onIdleStop() {
- Log.i(TAG, "Idle maintenance: onIdleStop()");
- }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java
deleted file mode 100644
index 7e9805f..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.os.Handler;
-import android.util.Log;
-
-public class TestService extends IdleService {
- static final String TAG = "TestService";
-
- @Override
- public boolean onIdleStart() {
- Log.i(TAG, "Idle maintenance: onIdleStart()");
-
- Handler h = new Handler();
- Runnable r = new Runnable() {
- @Override
- public void run() {
- Log.i(TAG, "Explicitly finishing idle");
- finishIdle();
- }
- };
- Log.i(TAG, "Posting explicit finish in 15 seconds");
- h.postDelayed(r, 15 * 1000);
- return true;
- }
-
- @Override
- public void onIdleStop() {
- Log.i(TAG, "Idle maintenance: onIdleStop()");
- }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java
deleted file mode 100644
index b2ba21b..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.util.Log;
-
-public class TimeoutTestService extends IdleService {
- private static final String TAG = "TimeoutTestService";
-
- @Override
- public boolean onIdleStart() {
- Log.i(TAG, "onIdleStart() but anticipating time-slice timeout");
- return true;
- }
-
- @Override
- public void onIdleStop() {
- Log.i(TAG, "onIdleStop() so we're done");
- }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java
deleted file mode 100644
index b9fe32b..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.util.Log;
-
-// Should never be invoked because its manifest declaration does not
-// require the necessary permission.
-public class UnpermissionedTestService extends IdleService {
- private static final String TAG = "UnpermissionedTestService";
-
- @Override
- public boolean onIdleStart() {
- Log.e(TAG, "onIdleStart() for this service should never be called!");
- return false;
- }
-
- @Override
- public void onIdleStop() {
- Log.e(TAG, "onIdleStop() for this service should never be called!");
- }
-
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
index aec8797..33db3f5 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
@@ -213,22 +213,22 @@
public String venueName;
/** list of network authentication types */
- public List<NetworkAuthType> networkAuthType;
+ public List<NetworkAuthType> networkAuthTypeList;
/** list of roaming consortium OIs */
- public List<String> roamingConsortium;
+ public List<String> roamingConsortiumList;
/** IP address availability */
public IpAddressType ipAddrTypeAvailability;
- /** NAI realm */
- public List<NaiRealm> naiRealm;
+ /** list of NAI realm */
+ public List<NaiRealm> naiRealmList;
- /** 3GPP cellular network */
- public List<CellularNetwork> cellularNetwork;
+ /** list of 3GPP cellular network */
+ public List<CellularNetwork> cellularNetworkList;
- /** fully qualified domain name (FQDN) */
- public List<String> domainName;
+ /** list of fully qualified domain name (FQDN) */
+ public List<String> domainNameList;
/** HS 2.0 operator friendly name */
public String operatorFriendlyName;
@@ -236,10 +236,10 @@
/** HS 2.0 wan metrics */
public WanMetrics wanMetrics;
- /** HS 2.0 list of IP proto port */
- public List<IpProtoPort> connectionCapability;
+ /** list of HS 2.0 IP proto port */
+ public List<IpProtoPort> connectionCapabilityList;
- /** HS 2.0 list of OSU providers */
+ /** list of HS 2.0 OSU providers */
public List<WifiPasspointOsuProvider> osuProviderList;
/**
@@ -292,15 +292,15 @@
sb.append(" venueName: ").append("(")
.append(venueName.replace("\n", "\\n")).append(")");
- if (networkAuthType != null) {
+ if (networkAuthTypeList != null) {
sb.append(" networkAuthType: ");
- for (NetworkAuthType auth : networkAuthType)
+ for (NetworkAuthType auth : networkAuthTypeList)
sb.append("(").append(auth.toString()).append(")");
}
- if (roamingConsortium != null) {
+ if (roamingConsortiumList != null) {
sb.append(" roamingConsortium: ");
- for (String oi : roamingConsortium)
+ for (String oi : roamingConsortiumList)
sb.append("(").append(oi).append(")");
}
@@ -309,21 +309,21 @@
.append(ipAddrTypeAvailability.toString()).append(")");
}
- if (naiRealm != null) {
+ if (naiRealmList != null) {
sb.append(" naiRealm: ");
- for (NaiRealm realm : naiRealm)
+ for (NaiRealm realm : naiRealmList)
sb.append("(").append(realm.toString()).append(")");
}
- if (cellularNetwork != null) {
+ if (cellularNetworkList != null) {
sb.append(" cellularNetwork: ");
- for (CellularNetwork plmn : cellularNetwork)
+ for (CellularNetwork plmn : cellularNetworkList)
sb.append("(").append(plmn.toString()).append(")");
}
- if (domainName != null) {
+ if (domainNameList != null) {
sb.append(" domainName: ");
- for (String fqdn : domainName)
+ for (String fqdn : domainNameList)
sb.append("(").append(fqdn).append(")");
}
@@ -335,9 +335,9 @@
sb.append(" wanMetrics: ").append("(")
.append(wanMetrics.toString()).append(")");
- if (connectionCapability != null) {
+ if (connectionCapabilityList != null) {
sb.append(" connectionCapability: ");
- for (IpProtoPort ip : connectionCapability)
+ for (IpProtoPort ip : connectionCapabilityList)
sb.append("(").append(ip.toString()).append(")");
}
@@ -356,21 +356,21 @@
out.writeString(bssid);
out.writeString(venueName);
- if (networkAuthType == null) {
+ if (networkAuthTypeList == null) {
out.writeInt(0);
} else {
- out.writeInt(networkAuthType.size());
- for (NetworkAuthType auth : networkAuthType) {
+ out.writeInt(networkAuthTypeList.size());
+ for (NetworkAuthType auth : networkAuthTypeList) {
out.writeInt(auth.type);
out.writeString(auth.redirectUrl);
}
}
- if (roamingConsortium == null) {
+ if (roamingConsortiumList == null) {
out.writeInt(0);
} else {
- out.writeInt(roamingConsortium.size());
- for (String oi : roamingConsortium)
+ out.writeInt(roamingConsortiumList.size());
+ for (String oi : roamingConsortiumList)
out.writeString(oi);
}
@@ -380,32 +380,32 @@
out.writeInt(ipAddrTypeAvailability.availability);
}
- if (naiRealm == null) {
+ if (naiRealmList == null) {
out.writeInt(0);
} else {
- out.writeInt(naiRealm.size());
- for (NaiRealm realm : naiRealm) {
+ out.writeInt(naiRealmList.size());
+ for (NaiRealm realm : naiRealmList) {
out.writeInt(realm.encoding);
out.writeString(realm.realm);
}
}
- if (cellularNetwork == null) {
+ if (cellularNetworkList == null) {
out.writeInt(0);
} else {
- out.writeInt(cellularNetwork.size());
- for (CellularNetwork plmn : cellularNetwork) {
+ out.writeInt(cellularNetworkList.size());
+ for (CellularNetwork plmn : cellularNetworkList) {
out.writeString(plmn.mcc);
out.writeString(plmn.mnc);
}
}
- if (domainName == null) {
+ if (domainNameList == null) {
out.writeInt(0);
} else {
- out.writeInt(domainName.size());
- for (String fqdn : domainName)
+ out.writeInt(domainNameList.size());
+ for (String fqdn : domainNameList)
out.writeString(fqdn);
}
@@ -423,11 +423,11 @@
out.writeInt(wanMetrics.lmd);
}
- if (connectionCapability == null) {
+ if (connectionCapabilityList == null) {
out.writeInt(0);
} else {
- out.writeInt(connectionCapability.size());
- for (IpProtoPort ip : connectionCapability) {
+ out.writeInt(connectionCapabilityList.size());
+ for (IpProtoPort ip : connectionCapabilityList) {
out.writeInt(ip.proto);
out.writeInt(ip.port);
out.writeInt(ip.status);
@@ -462,20 +462,20 @@
n = in.readInt();
if (n > 0) {
- p.networkAuthType = new ArrayList<NetworkAuthType>();
+ p.networkAuthTypeList = new ArrayList<NetworkAuthType>();
for (int i = 0; i < n; i++) {
NetworkAuthType auth = new NetworkAuthType();
auth.type = in.readInt();
auth.redirectUrl = in.readString();
- p.networkAuthType.add(auth);
+ p.networkAuthTypeList.add(auth);
}
}
n = in.readInt();
if (n > 0) {
- p.roamingConsortium = new ArrayList<String>();
+ p.roamingConsortiumList = new ArrayList<String>();
for (int i = 0; i < n; i++)
- p.roamingConsortium.add(in.readString());
+ p.roamingConsortiumList.add(in.readString());
}
n = in.readInt();
@@ -486,31 +486,31 @@
n = in.readInt();
if (n > 0) {
- p.naiRealm = new ArrayList<NaiRealm>();
+ p.naiRealmList = new ArrayList<NaiRealm>();
for (int i = 0; i < n; i++) {
NaiRealm realm = new NaiRealm();
realm.encoding = in.readInt();
realm.realm = in.readString();
- p.naiRealm.add(realm);
+ p.naiRealmList.add(realm);
}
}
n = in.readInt();
if (n > 0) {
- p.cellularNetwork = new ArrayList<CellularNetwork>();
+ p.cellularNetworkList = new ArrayList<CellularNetwork>();
for (int i = 0; i < n; i++) {
CellularNetwork plmn = new CellularNetwork();
plmn.mcc = in.readString();
plmn.mnc = in.readString();
- p.cellularNetwork.add(plmn);
+ p.cellularNetworkList.add(plmn);
}
}
n = in.readInt();
if (n > 0) {
- p.domainName = new ArrayList<String>();
+ p.domainNameList = new ArrayList<String>();
for (int i = 0; i < n; i++)
- p.domainName.add(in.readString());
+ p.domainNameList.add(in.readString());
}
p.operatorFriendlyName = in.readString();
@@ -528,13 +528,13 @@
n = in.readInt();
if (n > 0) {
- p.connectionCapability = new ArrayList<IpProtoPort>();
+ p.connectionCapabilityList = new ArrayList<IpProtoPort>();
for (int i = 0; i < n; i++) {
IpProtoPort ip = new IpProtoPort();
ip.proto = in.readInt();
ip.port = in.readInt();
ip.status = in.readInt();
- p.connectionCapability.add(ip);
+ p.connectionCapabilityList.add(ip);
}
}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index 2f158c2..e7e6767 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -325,7 +325,7 @@
listener = getListener(message.arg2, true);
if (listener != null) {
ParcelableString str = (ParcelableString) message.obj;
- if (str.string == null)
+ if (str == null || str.string == null)
((OsuRemListener) listener).onBrowserDismiss();
else
((OsuRemListener) listener).onBrowserLaunch(str.string);
@@ -485,7 +485,7 @@
*
* @return The list of credentials
*/
- public List<WifiPasspointCredential> getSavedCredentials() {
+ public List<WifiPasspointCredential> getCredentials() {
return null;
}
@@ -529,7 +529,7 @@
Log.d(TAG, "startOsu end");
}
- public void startUserRemediation(Channel c, OsuRemListener listener) {
+ public void startRemediation(Channel c, OsuRemListener listener) {
}
public void connect(WifiPasspointPolicy policy) {