diff options
105 files changed, 1962 insertions, 565 deletions
@@ -152,6 +152,8 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/display/IDisplayManager.aidl \ core/java/android/hardware/display/IDisplayManagerCallback.aidl \ core/java/android/hardware/display/IVirtualDisplayCallback.aidl \ + core/java/android/hardware/fingerprint/IFingerprintService.aidl \ + core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl \ core/java/android/hardware/hdmi/IHdmiControlCallback.aidl \ core/java/android/hardware/hdmi/IHdmiControlService.aidl \ core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl \ @@ -227,8 +229,6 @@ LOCAL_SRC_FILES += \ core/java/android/service/dreams/IDreamManager.aidl \ core/java/android/service/dreams/IDreamService.aidl \ core/java/android/service/persistentdata/IPersistentDataBlockService.aidl \ - core/java/android/service/fingerprint/IFingerprintService.aidl \ - core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl \ core/java/android/service/trust/ITrustAgentService.aidl \ core/java/android/service/trust/ITrustAgentServiceCallback.aidl \ core/java/android/service/voice/IVoiceInteractionService.aidl \ diff --git a/api/current.txt b/api/current.txt index b3d5fd0..f1bf3a5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -354,6 +354,7 @@ package android { field public static final int bottomRightRadius = 16843180; // 0x10101ac field public static final int breadCrumbShortTitle = 16843524; // 0x1010304 field public static final int breadCrumbTitle = 16843523; // 0x1010303 + field public static final int breakStrategy = 16844011; // 0x10104eb field public static final int bufferType = 16843086; // 0x101014e field public static final int button = 16843015; // 0x1010107 field public static final int buttonBarButtonStyle = 16843567; // 0x101032f @@ -9447,11 +9448,10 @@ package android.content.res { public class ColorStateList implements android.os.Parcelable { ctor public ColorStateList(int[][], int[]); - method public void applyTheme(android.content.res.Resources.Theme); - method public boolean canApplyTheme(); method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public int describeContents(); + method public int getChangingConfigurations(); method public int getColorForState(int[], int); method public int getDefaultColor(); method public boolean isOpaque(); @@ -18801,6 +18801,7 @@ package android.net.wifi { public static final class WifiEnterpriseConfig.Eap { field public static final int AKA = 5; // 0x5 + field public static final int AKA_PRIME = 6; // 0x6 field public static final int NONE = -1; // 0xffffffff field public static final int PEAP = 0; // 0x0 field public static final int PWD = 3; // 0x3 @@ -30920,6 +30921,9 @@ package android.text { method public final void increaseWidthTo(int); method public boolean isRtlCharAt(int); method protected final boolean isSpanned(); + field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2 + field public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; // 0x1 + field public static final int BREAK_STRATEGY_SIMPLE = 0; // 0x0 field public static final int DIR_LEFT_TO_RIGHT = 1; // 0x1 field public static final int DIR_RIGHT_TO_LEFT = -1; // 0xffffffff } @@ -40082,6 +40086,7 @@ package android.widget { method public void endBatchEdit(); method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText); method public final int getAutoLinkMask(); + method public int getBreakStrategy(); method public int getCompoundDrawablePadding(); method public android.content.res.ColorStateList getCompoundDrawableTintList(); method public android.graphics.PorterDuff.Mode getCompoundDrawableTintMode(); @@ -40183,6 +40188,7 @@ package android.widget { method public void removeTextChangedListener(android.text.TextWatcher); method public void setAllCaps(boolean); method public final void setAutoLinkMask(int); + method public void setBreakStrategy(int); method public void setCompoundDrawablePadding(int); method public void setCompoundDrawableTintList(android.content.res.ColorStateList); method public void setCompoundDrawableTintMode(android.graphics.PorterDuff.Mode); diff --git a/api/system-current.txt b/api/system-current.txt index 9462054..5fc3013 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -135,6 +135,7 @@ package android { field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS"; field public static final java.lang.String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT"; field public static final java.lang.String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING"; + field public static final java.lang.String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION"; 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 PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT"; @@ -426,6 +427,7 @@ package android { field public static final int bottomRightRadius = 16843180; // 0x10101ac field public static final int breadCrumbShortTitle = 16843524; // 0x1010304 field public static final int breadCrumbTitle = 16843523; // 0x1010303 + field public static final int breakStrategy = 16844011; // 0x10104eb field public static final int bufferType = 16843086; // 0x101014e field public static final int button = 16843015; // 0x1010107 field public static final int buttonBarButtonStyle = 16843567; // 0x101032f @@ -6331,6 +6333,7 @@ package android.bluetooth { method public android.bluetooth.BluetoothDevice getRemoteDevice(byte[]); method public int getScanMode(); method public int getState(); + method public boolean isBleScanAlwaysAvailable(); method public boolean isDiscovering(); method public boolean isEnabled(); method public boolean isMultipleAdvertisementSupported(); @@ -6347,6 +6350,7 @@ package android.bluetooth { field public static final java.lang.String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; field public static final java.lang.String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED"; field public static final java.lang.String ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED"; + field public static final java.lang.String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE"; field public static final java.lang.String ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"; field public static final java.lang.String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; field public static final java.lang.String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; @@ -9733,11 +9737,10 @@ package android.content.res { public class ColorStateList implements android.os.Parcelable { ctor public ColorStateList(int[][], int[]); - method public void applyTheme(android.content.res.Resources.Theme); - method public boolean canApplyTheme(); method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public int describeContents(); + method public int getChangingConfigurations(); method public int getColorForState(int[], int); method public int getDefaultColor(); method public boolean isOpaque(); @@ -20497,6 +20500,7 @@ package android.net.wifi { public static final class WifiEnterpriseConfig.Eap { field public static final int AKA = 5; // 0x5 + field public static final int AKA_PRIME = 6; // 0x6 field public static final int NONE = -1; // 0xffffffff field public static final int PEAP = 0; // 0x0 field public static final int PWD = 3; // 0x3 @@ -33462,6 +33466,9 @@ package android.text { method public final void increaseWidthTo(int); method public boolean isRtlCharAt(int); method protected final boolean isSpanned(); + field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2 + field public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; // 0x1 + field public static final int BREAK_STRATEGY_SIMPLE = 0; // 0x0 field public static final int DIR_LEFT_TO_RIGHT = 1; // 0x1 field public static final int DIR_RIGHT_TO_LEFT = -1; // 0xffffffff } @@ -42925,6 +42932,7 @@ package android.widget { method public void endBatchEdit(); method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText); method public final int getAutoLinkMask(); + method public int getBreakStrategy(); method public int getCompoundDrawablePadding(); method public android.content.res.ColorStateList getCompoundDrawableTintList(); method public android.graphics.PorterDuff.Mode getCompoundDrawableTintMode(); @@ -43026,6 +43034,7 @@ package android.widget { method public void removeTextChangedListener(android.text.TextWatcher); method public void setAllCaps(boolean); method public final void setAutoLinkMask(int); + method public void setBreakStrategy(int); method public void setCompoundDrawablePadding(int); method public void setCompoundDrawableTintList(android.content.res.ColorStateList); method public void setCompoundDrawableTintMode(android.graphics.PorterDuff.Mode); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index a0f40f6..b3aa6be 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -99,8 +99,8 @@ import android.os.Vibrator; import android.os.storage.StorageManager; import android.print.IPrintManager; import android.print.PrintManager; -import android.service.fingerprint.FingerprintManager; -import android.service.fingerprint.IFingerprintService; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.IFingerprintService; import android.service.persistentdata.IPersistentDataBlockService; import android.service.persistentdata.PersistentDataBlockManager; import android.telecom.TelecomManager; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index be26eac..edb768d 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -206,6 +207,23 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.action.REQUEST_ENABLE"; /** + * Activity Action: Show a system activity that allows user to enable BLE scans even when + * Bluetooth is turned off.<p> + * + * Notification of result of this activity is posted using + * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be + * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or + * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an + * error occurred. + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = + "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE"; + + /** * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter * has changed. * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link @@ -916,6 +934,22 @@ public final class BluetoothAdapter { } /** + * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p> + * + * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and + * fetch scan results even when Bluetooth is turned off.<p> + * + * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}. + * + * @hide + */ + @SystemApi + public boolean isBleScanAlwaysAvailable() { + // TODO: implement after Settings UI change. + return false; + } + + /** * Returns whether peripheral mode is supported. * * @hide diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7a99a79..514802e9 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1041,6 +1041,18 @@ public class Intent implements Parcelable, Cloneable { */ public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED"; /** + * Activity action: Activate the current SIM card. If SIM cards do not require activation, + * sending this intent is a no-op. + * <p>Input: No data should be specified. get*Extra may have an optional + * {@link #EXTRA_SIM_ACTIVATION_RESPONSE} field containing a PendingIntent through which to + * send the activation result. + * <p>Output: nothing. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SIM_ACTIVATION_REQUEST = + "android.intent.action.SIM_ACTIVATION_REQUEST"; + /** * Activity Action: Send a message to someone specified by the data. * <p>Input: {@link #getData} is URI describing the target. * <p>Output: nothing. @@ -3620,6 +3632,15 @@ public class Intent implements Parcelable, Cloneable { /** {@hide} */ public static final String EXTRA_REASON = "android.intent.extra.REASON"; + /** + * Optional {@link android.app.PendingIntent} extra used to deliver the result of the SIM + * activation request. + * TODO: Add information about the structure and response data used with the pending intent. + * @hide + */ + public static final String EXTRA_SIM_ACTIVATION_RESPONSE = + "android.intent.extra.SIM_ACTIVATION_RESPONSE"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 7d8dff3..fdafb04 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -71,10 +71,15 @@ import java.util.Arrays; */ public class ColorStateList implements Parcelable { private static final String TAG = "ColorStateList"; + private static final int DEFAULT_COLOR = Color.RED; private static final int[][] EMPTY = new int[][] { new int[0] }; - private static final SparseArray<WeakReference<ColorStateList>> sCache = - new SparseArray<WeakReference<ColorStateList>>(); + + /** Thread-safe cache of single-color ColorStateLists. */ + private static final SparseArray<WeakReference<ColorStateList>> sCache = new SparseArray<>(); + + /** Lazily-created factory for this color state list. */ + private ColorStateListFactory mFactory; private int[][] mThemeAttrs; private int mChangingConfigurations; @@ -125,7 +130,7 @@ public class ColorStateList implements Parcelable { } final ColorStateList csl = new ColorStateList(EMPTY, new int[] { color }); - sCache.put(color, new WeakReference<ColorStateList>(csl)); + sCache.put(color, new WeakReference<>(csl)); return csl; } } @@ -141,11 +146,13 @@ public class ColorStateList implements Parcelable { */ private ColorStateList(ColorStateList orig) { if (orig != null) { + mChangingConfigurations = orig.mChangingConfigurations; mStateSpecs = orig.mStateSpecs; mDefaultColor = orig.mDefaultColor; mIsOpaque = orig.mIsOpaque; - // Deep copy, this may change due to theming. + // Deep copy, these may change due to applyTheme(). + mThemeAttrs = orig.mThemeAttrs.clone(); mColors = orig.mColors.clone(); } } @@ -329,6 +336,7 @@ public class ColorStateList implements Parcelable { * attributes. * * @return whether a theme can be applied to this color state list + * @hide only for resource preloading */ public boolean canApplyTheme() { return mThemeAttrs != null; @@ -336,10 +344,15 @@ public class ColorStateList implements Parcelable { /** * Applies a theme to this color state list. + * <p> + * <strong>Note:</strong> Applying a theme may affect the changing + * configuration parameters of this color state list. After calling this + * method, any dependent configurations must be updated by obtaining the + * new configuration mask from {@link #getChangingConfigurations()}. * * @param t the theme to apply */ - public void applyTheme(Theme t) { + private void applyTheme(Theme t) { if (mThemeAttrs == null) { return; } @@ -376,6 +389,38 @@ public class ColorStateList implements Parcelable { onColorsChanged(); } + /** + * Returns an appropriately themed color state list. + * + * @param t the theme to apply + * @return a copy of the color state list with the theme applied, or the + * color state list itself if there were no unresolved theme + * attributes + * @hide only for resource preloading + */ + public ColorStateList obtainForTheme(Theme t) { + if (t == null || !canApplyTheme()) { + return this; + } + + final ColorStateList clone = new ColorStateList(this); + clone.applyTheme(t); + return clone; + } + + /** + * Returns a mask of the configuration parameters for which this color + * state list may change, requiring that it be re-created. + * + * @return a mask of the changing configuration parameters, as defined by + * {@link android.content.pm.ActivityInfo} + * + * @see android.content.pm.ActivityInfo + */ + public int getChangingConfigurations() { + return mChangingConfigurations; + } + private int modulateColorAlpha(int baseColor, float alphaMod) { if (alphaMod == 1.0f) { return baseColor; @@ -383,8 +428,7 @@ public class ColorStateList implements Parcelable { final int baseAlpha = Color.alpha(baseColor); final int alpha = MathUtils.constrain((int) (baseAlpha * alphaMod + 0.5f), 0, 255); - final int color = (baseColor & 0xFFFFFF) | (alpha << 24); - return color; + return (baseColor & 0xFFFFFF) | (alpha << 24); } /** @@ -534,14 +578,18 @@ public class ColorStateList implements Parcelable { } /** - * @return A factory that can create new instances of this ColorStateList. + * @return a factory that can create new instances of this ColorStateList + * @hide only for resource preloading */ - ColorStateListFactory getFactory() { - return new ColorStateListFactory(this); + public ConstantState<ColorStateList> getConstantState() { + if (mFactory != null) { + mFactory = new ColorStateListFactory(this); + } + return mFactory; } - static class ColorStateListFactory extends ConstantState<ColorStateList> { - final ColorStateList mSrc; + private static class ColorStateListFactory extends ConstantState<ColorStateList> { + private final ColorStateList mSrc; public ColorStateListFactory(ColorStateList src) { mSrc = src; @@ -559,13 +607,7 @@ public class ColorStateList implements Parcelable { @Override public ColorStateList newInstance(Resources res, Theme theme) { - if (theme == null || !mSrc.canApplyTheme()) { - return mSrc; - } - - final ColorStateList clone = new ColorStateList(mSrc); - clone.applyTheme(theme); - return clone; + return mSrc.obtainForTheme(theme); } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 44018ff..7ce3721 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -41,7 +41,6 @@ import android.annotation.RawRes; import android.annotation.StringRes; import android.annotation.XmlRes; import android.content.pm.ActivityInfo; -import android.content.res.ColorStateList.ColorStateListFactory; import android.graphics.Movie; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -112,8 +111,8 @@ public class Resources { private static final LongSparseArray<ConstantState>[] sPreloadedDrawables; private static final LongSparseArray<ConstantState> sPreloadedColorDrawables = new LongSparseArray<>(); - private static final LongSparseArray<ColorStateListFactory> sPreloadedColorStateLists - = new LongSparseArray<>(); + private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>> + sPreloadedColorStateLists = new LongSparseArray<>(); // Pool of TypedArrays targeted to this Resources object. final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5); @@ -2667,7 +2666,8 @@ public class Resources { // Handle inline color definitions. if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { - final ColorStateListFactory factory = sPreloadedColorStateLists.get(key); + final android.content.res.ConstantState<ColorStateList> factory = + sPreloadedColorStateLists.get(key); if (factory != null) { return factory.newInstance(); } @@ -2677,7 +2677,7 @@ public class Resources { if (mPreloading) { if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId, "color")) { - sPreloadedColorStateLists.put(key, csl.getFactory()); + sPreloadedColorStateLists.put(key, csl.getConstantState()); } } @@ -2691,7 +2691,8 @@ public class Resources { return csl; } - final ColorStateListFactory factory = sPreloadedColorStateLists.get(key); + final android.content.res.ConstantState<ColorStateList> factory = + sPreloadedColorStateLists.get(key); if (factory != null) { csl = factory.newInstance(this, theme); } @@ -2704,10 +2705,10 @@ public class Resources { if (mPreloading) { if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId, "color")) { - sPreloadedColorStateLists.put(key, csl.getFactory()); + sPreloadedColorStateLists.put(key, csl.getConstantState()); } } else { - cache.put(key, theme, csl.getFactory()); + cache.put(key, theme, csl.getConstantState()); } } diff --git a/core/java/android/service/fingerprint/Fingerprint.aidl b/core/java/android/hardware/fingerprint/Fingerprint.aidl index c9fd989..4743354 100644 --- a/core/java/android/service/fingerprint/Fingerprint.aidl +++ b/core/java/android/hardware/fingerprint/Fingerprint.aidl @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; // @hide parcelable Fingerprint; diff --git a/core/java/android/service/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java index 37552eb..c307634 100644 --- a/core/java/android/service/fingerprint/Fingerprint.java +++ b/core/java/android/hardware/fingerprint/Fingerprint.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index bb90e40..e3572a2 100644 --- a/core/java/android/service/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.app.ActivityManagerNative; import android.content.ContentResolver; @@ -28,7 +28,7 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; -import android.service.fingerprint.FingerprintManager.EnrollmentCallback; +import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; import android.util.Log; import android.util.Slog; diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/hardware/fingerprint/FingerprintUtils.java index 62acbb9..ae3d4a4 100644 --- a/core/java/android/service/fingerprint/FingerprintUtils.java +++ b/core/java/android/hardware/fingerprint/FingerprintUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.content.ContentResolver; import android.provider.Settings; diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index e5d3ad4..c5a45e2 100644 --- a/core/java/android/service/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.os.Bundle; -import android.service.fingerprint.IFingerprintServiceReceiver; -import android.service.fingerprint.Fingerprint; +import android.hardware.fingerprint.IFingerprintServiceReceiver; +import android.hardware.fingerprint.Fingerprint; import java.util.List; /** diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl index f025064..e82395f 100644 --- a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.os.Bundle; import android.os.UserHandle; diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index bf3d9aa..f305b2a 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -366,7 +366,6 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { public String toSafeString() { String scheme = getScheme(); String ssp = getSchemeSpecificPart(); - String authority = null; if (scheme != null) { if (scheme.equalsIgnoreCase("tel") || scheme.equalsIgnoreCase("sip") || scheme.equalsIgnoreCase("sms") || scheme.equalsIgnoreCase("smsto") @@ -385,9 +384,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } } return builder.toString(); - } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) { - ssp = null; - authority = "//" + getAuthority() + "/..."; + } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https") + || scheme.equalsIgnoreCase("ftp")) { + ssp = "//" + ((getHost() != null) ? getHost() : "") + + ((getPort() != -1) ? (":" + getPort()) : "") + + "/..."; } } // Not a sensitive scheme, but let's still be conservative about @@ -401,9 +402,6 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { if (ssp != null) { builder.append(ssp); } - if (authority != null) { - builder.append(authority); - } return builder.toString(); } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 8b3ecae..508fdee 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -4415,7 +4415,7 @@ public abstract class BatteryStats implements Parcelable { if (!checkin) { pw.println(header); } - String[] lineArgs = new String[4]; + String[] lineArgs = new String[5]; for (int i=0; i<count; i++) { long duration = steps.getDurationAt(i); int level = steps.getLevelAt(i); @@ -4430,7 +4430,7 @@ public abstract class BatteryStats implements Parcelable { case Display.STATE_ON: lineArgs[2] = "s+"; break; case Display.STATE_DOZE: lineArgs[2] = "sd"; break; case Display.STATE_DOZE_SUSPEND: lineArgs[2] = "sds"; break; - default: lineArgs[1] = "?"; break; + default: lineArgs[2] = "?"; break; } } else { lineArgs[2] = ""; @@ -4441,9 +4441,9 @@ public abstract class BatteryStats implements Parcelable { lineArgs[3] = ""; } if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) { - lineArgs[3] = (initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0 ? "i+" : "i-"; + lineArgs[4] = (initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0 ? "i+" : "i-"; } else { - lineArgs[3] = ""; + lineArgs[4] = ""; } dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs); } else { @@ -4459,7 +4459,7 @@ public abstract class BatteryStats implements Parcelable { case Display.STATE_ON: pw.print("screen-on"); break; case Display.STATE_DOZE: pw.print("screen-doze"); break; case Display.STATE_DOZE_SUSPEND: pw.print("screen-doze-suspend"); break; - default: lineArgs[1] = "screen-?"; break; + default: pw.print("screen-?"); break; } haveModes = true; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8e5d245..f79ef35 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6375,6 +6375,14 @@ public final class Settings { "wifi_scan_always_enabled"; /** + * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for + * connectivity. + * @hide + */ + public static final String BLE_SCAN_ALWAYS_AVAILABLE = + "ble_scan_always_enabled"; + + /** * Used to save the Wifi_ON state prior to tethering. * This state will be checked to restore Wifi after * the user turns off tethering. diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 7d2e1ef..239b386 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -79,7 +79,8 @@ public class DynamicLayout extends Layout boolean includepad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, - spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth); + spacingmult, spacingadd, includepad, StaticLayout.BREAK_STRATEGY_SIMPLE, + ellipsize, ellipsizedWidth); } /** @@ -95,7 +96,7 @@ public class DynamicLayout extends Layout TextPaint paint, int width, Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, - boolean includepad, + boolean includepad, int breakStrategy, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { super((ellipsize == null) ? display @@ -120,6 +121,7 @@ public class DynamicLayout extends Layout mObjects = new PackedObjectVector<Directions>(1); mIncludePad = includepad; + mBreakStrategy = breakStrategy; /* * This is annoying, but we can't refer to the layout until @@ -279,10 +281,9 @@ public class DynamicLayout extends Layout sBuilder = null; } - // TODO: make sure reflowed is properly initialized if (reflowed == null) { reflowed = new StaticLayout(null); - b = StaticLayout.Builder.obtain(); + b = StaticLayout.Builder.obtain(text, where, where + after, getWidth()); } b.setText(text, where, where + after) @@ -292,7 +293,8 @@ public class DynamicLayout extends Layout .setSpacingMult(getSpacingMultiplier()) .setSpacingAdd(getSpacingAdd()) .setEllipsizedWidth(mEllipsizedWidth) - .setEllipsize(mEllipsizeAt); + .setEllipsize(mEllipsizeAt) + .setBreakStrategy(mBreakStrategy); reflowed.generate(b, false, true); int n = reflowed.getLineCount(); @@ -717,6 +719,7 @@ public class DynamicLayout extends Layout private boolean mEllipsize; private int mEllipsizedWidth; private TextUtils.TruncateAt mEllipsizeAt; + private int mBreakStrategy; private PackedIntVector mInts; private PackedObjectVector<Directions> mObjects; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 22abb18..16ae5e2 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -16,6 +16,7 @@ package android.text; +import android.annotation.IntDef; import android.emoji.EmojiFactory; import android.graphics.Canvas; import android.graphics.Paint; @@ -33,6 +34,8 @@ import android.text.style.TabStopSpan; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** @@ -43,6 +46,31 @@ import java.util.Arrays; * For text that will not change, use a {@link StaticLayout}. */ public abstract class Layout { + /** @hide */ + @IntDef({BREAK_STRATEGY_SIMPLE, BREAK_STRATEGY_HIGH_QUALITY, BREAK_STRATEGY_BALANCED}) + @Retention(RetentionPolicy.SOURCE) + public @interface BreakStrategy {} + + /** + * Value for break strategy indicating simple line breaking. Automatic hyphens are not added + * (though soft hyphens are respected), and modifying text generally doesn't affect the layout + * before it (which yields a more consistent user experience when editing), but layout may not + * be the highest quality. + */ + public static final int BREAK_STRATEGY_SIMPLE = 0; + + /** + * Value for break strategy indicating high quality line breaking, including automatic + * hyphenation and doing whole-paragraph optimization of line breaks. + */ + public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; + + /** + * Value for break strategy indicating balanced line breaking. The breaks are chosen to + * make all lines as close to the same length as possible, including automatic hyphenation. + */ + public static final int BREAK_STRATEGY_BALANCED = 2; + private static final ParagraphStyle[] NO_PARA_SPANS = ArrayUtils.emptyArray(ParagraphStyle.class); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 4174df0..2bcb352 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -23,6 +23,7 @@ import android.text.style.LineHeightSpan; import android.text.style.MetricAffectingSpan; import android.text.style.TabStopSpan; import android.util.Log; +import android.util.Pools.SynchronizedPool; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; @@ -56,28 +57,23 @@ public class StaticLayout extends Layout { mNativePtr = nNewBuilder(); } - static Builder obtain() { - Builder b = null; - synchronized (sLock) { - for (int i = 0; i < sCached.length; i++) { - if (sCached[i] != null) { - b = sCached[i]; - sCached[i] = null; - break; - } - } - } + public static Builder obtain(CharSequence source, int start, int end, int width) { + Builder b = sPool.acquire(); if (b == null) { b = new Builder(); } // set default initial values - b.mWidth = 0; + b.mText = source; + b.mStart = start; + b.mEnd = end; + b.mWidth = width; + b.mAlignment = Alignment.ALIGN_NORMAL; b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; b.mSpacingMult = 1.0f; b.mSpacingAdd = 0.0f; b.mIncludePad = true; - b.mEllipsizedWidth = 0; + b.mEllipsizedWidth = width; b.mEllipsize = null; b.mMaxLines = Integer.MAX_VALUE; @@ -85,18 +81,11 @@ public class StaticLayout extends Layout { return b; } - static void recycle(Builder b) { + private static void recycle(Builder b) { b.mPaint = null; b.mText = null; MeasuredText.recycle(b.mMeasuredText); - synchronized (sLock) { - for (int i = 0; i < sCached.length; i++) { - if (sCached[i] == null) { - sCached[i] = b; - break; - } - } - } + sPool.release(b); } // release any expensive state @@ -129,6 +118,11 @@ public class StaticLayout extends Layout { return this; } + public Builder setAlignment(Alignment alignment) { + mAlignment = alignment; + return this; + } + public Builder setTextDir(TextDirectionHeuristic textDir) { mTextDir = textDir; return this; @@ -166,6 +160,11 @@ public class StaticLayout extends Layout { return this; } + public Builder setBreakStrategy(@BreakStrategy int breakStrategy) { + mBreakStrategy = breakStrategy; + return this; + } + /** * Measurement and break iteration is done in native code. The protocol for using * the native code is as follows. @@ -207,10 +206,8 @@ public class StaticLayout extends Layout { } public StaticLayout build() { - // TODO: can optimize based on whether ellipsis is needed - StaticLayout result = new StaticLayout(mText); - result.generate(this, this.mIncludePad, this.mIncludePad); - recycle(this); + StaticLayout result = new StaticLayout(this); + Builder.recycle(this); return result; } @@ -230,6 +227,7 @@ public class StaticLayout extends Layout { int mEnd; TextPaint mPaint; int mWidth; + Alignment mAlignment; TextDirectionHeuristic mTextDir; float mSpacingMult; float mSpacingAdd; @@ -237,6 +235,7 @@ public class StaticLayout extends Layout { int mEllipsizedWidth; TextUtils.TruncateAt mEllipsize; int mMaxLines; + int mBreakStrategy; Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); @@ -245,8 +244,7 @@ public class StaticLayout extends Layout { Locale mLocale; - private static final Object sLock = new Object(); - private static final Builder[] sCached = new Builder[3]; + private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3); } public StaticLayout(CharSequence source, TextPaint paint, @@ -316,10 +314,9 @@ public class StaticLayout extends Layout { : new Ellipsizer(source), paint, outerwidth, align, textDir, spacingmult, spacingadd); - Builder b = Builder.obtain(); - b.setText(source, bufstart, bufend) + Builder b = Builder.obtain(source, bufstart, bufend, outerwidth) .setPaint(paint) - .setWidth(outerwidth) + .setAlignment(align) .setTextDir(textDir) .setSpacingMult(spacingmult) .setSpacingAdd(spacingadd) @@ -366,6 +363,35 @@ public class StaticLayout extends Layout { mLines = new int[mLineDirections.length]; } + private StaticLayout(Builder b) { + super((b.mEllipsize == null) + ? b.mText + : (b.mText instanceof Spanned) + ? new SpannedEllipsizer(b.mText) + : new Ellipsizer(b.mText), + b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd); + + if (b.mEllipsize != null) { + Ellipsizer e = (Ellipsizer) getText(); + + e.mLayout = this; + e.mWidth = b.mEllipsizedWidth; + e.mMethod = b.mEllipsize; + mEllipsizedWidth = b.mEllipsizedWidth; + + mColumns = COLUMNS_ELLIPSIZE; + } else { + mColumns = COLUMNS_NORMAL; + mEllipsizedWidth = b.mWidth; + } + + mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns); + mLines = new int[mLineDirections.length]; + mMaximumVisibleLineCount = b.mMaxLines; + + generate(b, b.mIncludePad, b.mIncludePad); + } + /* package */ void generate(Builder b, boolean includepad, boolean trackpad) { CharSequence source = b.mText; int bufStart = b.mStart; @@ -477,10 +503,9 @@ public class StaticLayout extends Layout { } } - int breakStrategy = 0; // 0 = kBreakStrategy_Greedy nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, - variableTabStops, TAB_INCREMENT, breakStrategy); + variableTabStops, TAB_INCREMENT, b.mBreakStrategy); // measurement has to be done before performing line breaking // but we don't want to recompute fontmetrics or span ranges the diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 87f3e94..31c3fe8 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -5727,12 +5727,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be - resultSize = 0; + resultSize = size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be - resultSize = 0; + resultSize = size; resultMode = MeasureSpec.UNSPECIFIED; } break; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 77082b0..ec527d5 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -1562,7 +1562,7 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Sets whether this node is visible to the user. + * Gets whether this node is visible to the user. * * @return Whether the node is visible to the user. */ diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index e2f8efc..ec2528f 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -187,7 +187,6 @@ class DayPickerView extends ViewPager { * @param setSelected whether to set the specified day as selected */ private void setDate(long timeInMillis, boolean animate, boolean setSelected) { - // Set the selected day if (setSelected) { mSelectedDay.setTimeInMillis(timeInMillis); } @@ -196,6 +195,9 @@ class DayPickerView extends ViewPager { if (position != getCurrentItem()) { setCurrentItem(position, animate); } + + mTempCalendar.setTimeInMillis(timeInMillis); + mAdapter.setSelectedDay(mTempCalendar); } public long getDate() { diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 349f3f0..a50941b 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -815,12 +815,12 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback mContext = context; mIntent = intent; - mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); - - mLayoutInflater = LayoutInflater.from(context); if (mIntent == null) { throw new IllegalArgumentException("Non-null Intent must be specified."); } + + mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); + mLayoutInflater = LayoutInflater.from(context); mRequestedViews = new RemoteViewsFrameLayoutRefSet(); // Strip the previously injected app widget id from service intent diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index 3fb096c..d9f1f0e 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -80,11 +80,12 @@ class SimpleMonthView extends View { private final SimpleDateFormat mTitleFormatter; private final SimpleDateFormat mDayOfWeekFormatter; - private final int mMonthHeight; - private final int mDayOfWeekHeight; - private final int mDayHeight; - private final int mCellWidth; - private final int mDaySelectorRadius; + // Desired dimensions. + private final int mDesiredMonthHeight; + private final int mDesiredDayOfWeekHeight; + private final int mDesiredDayHeight; + private final int mDesiredCellWidth; + private final int mDesiredDaySelectorRadius; // Next/previous drawables. private final Drawable mPrevDrawable; @@ -99,6 +100,13 @@ class SimpleMonthView extends View { private int mMonth; private int mYear; + // Dimensions as laid out. + private int mMonthHeight; + private int mDayOfWeekHeight; + private int mDayHeight; + private int mCellWidth; + private int mDaySelectorRadius; + private int mPaddedWidth; private int mPaddedHeight; @@ -158,11 +166,11 @@ class SimpleMonthView extends View { super(context, attrs, defStyleAttr, defStyleRes); final Resources res = context.getResources(); - mMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height); - mDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height); - mDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height); - mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width); - mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius); + mDesiredMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height); + mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height); + mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height); + mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width); + mDesiredDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius); mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left); mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right); @@ -400,7 +408,7 @@ class SimpleMonthView extends View { final TextPaint p = mDayOfWeekPaint; final int headerHeight = mMonthHeight; final int rowHeight = mDayOfWeekHeight; - final int colWidth = mPaddedWidth / DAYS_IN_WEEK; + final int colWidth = mCellWidth; // Text is vertically centered within the day of week height. final float halfLineHeight = (p.ascent() + p.descent()) / 2f; @@ -426,7 +434,7 @@ class SimpleMonthView extends View { final TextPaint p = mDayPaint; final int headerHeight = mMonthHeight + mDayOfWeekHeight; final int rowHeight = mDayHeight; - final int colWidth = mPaddedWidth / DAYS_IN_WEEK; + final int colWidth = mCellWidth; // Text is vertically centered within the row height. final float halfLineHeight = (p.ascent() + p.descent()) / 2f; @@ -627,9 +635,9 @@ class SimpleMonthView extends View { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int preferredHeight = mDayHeight * mNumWeeks + mDayOfWeekHeight + mMonthHeight - + getPaddingTop() + getPaddingBottom(); - final int preferredWidth = mCellWidth * DAYS_IN_WEEK + final int preferredHeight = mDesiredDayHeight * mNumWeeks + mDesiredDayOfWeekHeight + + mDesiredMonthHeight + getPaddingTop() + getPaddingBottom(); + final int preferredWidth = mDesiredCellWidth * DAYS_IN_WEEK + getPaddingStart() + getPaddingEnd(); final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec); final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec); @@ -637,16 +645,46 @@ class SimpleMonthView extends View { } @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - final int paddedLeft = getPaddingLeft(); - final int paddedTop = getPaddingTop(); - final int paddedRight = w - getPaddingRight(); - final int paddedBottom = h - getPaddingBottom(); - mPaddedWidth = paddedRight - paddedLeft; - mPaddedHeight = paddedBottom - paddedTop; - - final int monthHeight = mMonthHeight; + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (!changed) { + return; + } + + // Let's initialize a completely reasonable number of variables. + final int w = right - left; + final int h = bottom - top; + final int paddingLeft = getPaddingLeft(); + final int paddingTop = getPaddingTop(); + final int paddingRight = getPaddingRight(); + final int paddingBottom = getPaddingBottom(); + final int paddedRight = w - paddingRight; + final int paddedBottom = h - paddingBottom; + final int paddedWidth = paddedRight - paddingLeft; + final int paddedHeight = paddedBottom - paddingTop; + if (paddedWidth == mPaddedWidth || paddedHeight == mPaddedHeight) { + return; + } + + mPaddedWidth = paddedWidth; + mPaddedHeight = paddedHeight; + + // We may have been laid out smaller than our preferred size. If so, + // scale all dimensions to fit. + final int measuredPaddedHeight = getMeasuredHeight() - paddingTop - paddingBottom; + final float scaleH = paddedHeight / (float) measuredPaddedHeight; + final int monthHeight = (int) (mDesiredMonthHeight * scaleH); final int cellWidth = mPaddedWidth / DAYS_IN_WEEK; + mMonthHeight = monthHeight; + mDayOfWeekHeight = (int) (mDesiredDayOfWeekHeight * scaleH); + mDayHeight = (int) (mDesiredDayHeight * scaleH); + mCellWidth = cellWidth; + + // Compute the largest day selector radius that's still within the clip + // bounds and desired selector radius. + final int maxSelectorWidth = cellWidth / 2 + Math.min(paddingLeft, paddingRight); + final int maxSelectorHeight = mDayHeight / 2 + paddingBottom; + mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius, + Math.min(maxSelectorWidth, maxSelectorHeight)); // Vertically center the previous/next drawables within the month // header, horizontally center within the day cell, then expand the @@ -660,7 +698,7 @@ class SimpleMonthView extends View { // Button bounds don't include padding, but hit area does. prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH); - mPrevHitArea.set(0, 0, paddedLeft + cellWidth, paddedTop + monthHeight); + mPrevHitArea.set(0, 0, paddingLeft + cellWidth, paddingTop + monthHeight); } final Drawable nextDrawable = mNextDrawable; @@ -668,11 +706,11 @@ class SimpleMonthView extends View { final int dW = nextDrawable.getIntrinsicWidth(); final int dH = nextDrawable.getIntrinsicHeight(); final int iconTop = (monthHeight - dH) / 2; - final int iconRight = mPaddedWidth - (cellWidth - dW) / 2; + final int iconRight = paddedWidth - (cellWidth - dW) / 2; // Button bounds don't include padding, but hit area does. nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH); - mNextHitArea.set(paddedRight - cellWidth, 0, w, paddedTop + monthHeight); + mNextHitArea.set(paddedRight - cellWidth, 0, w, paddingTop + monthHeight); } // Invalidate cached accessibility information. @@ -753,7 +791,7 @@ class SimpleMonthView extends View { // Compute left edge. final int col = index % DAYS_IN_WEEK; - final int colWidth = mPaddedWidth / DAYS_IN_WEEK; + final int colWidth = mCellWidth; final int left = getPaddingLeft() + col * colWidth; // Compute top edge. diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 718ef93..2723080 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -543,6 +543,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mSpacingMult = 1.0f; private float mSpacingAdd = 0.0f; + private int mBreakStrategy; + private int mMaximum = Integer.MAX_VALUE; private int mMaxMode = LINES; private int mMinimum = 0; @@ -680,6 +682,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean elegant = false; float letterSpacing = 0; String fontFeatureSettings = null; + mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; final Resources.Theme theme = context.getTheme(); @@ -1133,6 +1136,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_fontFeatureSettings: fontFeatureSettings = a.getString(attr); break; + + case com.android.internal.R.styleable.TextView_breakStrategy: + mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE); } } a.recycle(); @@ -2960,6 +2966,35 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Sets the break strategy for breaking paragraphs into lines. The default value for + * TextView is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}, and the default value for + * EditText is {@link Layout#BREAK_STRATEGY_SIMPLE}, the latter to avoid the + * text "dancing" when being edited. + * + * @attr ref android.R.styleable#TextView_breakStrategy + * @see #getBreakStrategy() + */ + public void setBreakStrategy(@Layout.BreakStrategy int breakStrategy) { + mBreakStrategy = breakStrategy; + if (mLayout != null) { + nullLayouts(); + requestLayout(); + invalidate(); + } + } + + /** + * @return the currently set break strategy. + * + * @attr ref android.R.styleable#TextView_breakStrategy + * @see #setBreakStrategy(int) + */ + @Layout.BreakStrategy + public int getBreakStrategy() { + return mBreakStrategy; + } + + /** * Sets font feature settings. The format is the same as the CSS * font-feature-settings attribute: * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings @@ -6492,27 +6527,25 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hintBoring, mIncludePad, mEllipsize, ellipsisWidth); } - } else if (shouldEllipsize) { - mHintLayout = new StaticLayout(mHint, - 0, mHint.length(), - mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, mEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - mHintLayout = new StaticLayout(mHint, mTextPaint, - hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); } - } else if (shouldEllipsize) { - mHintLayout = new StaticLayout(mHint, - 0, mHint.length(), - mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, mEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - mHintLayout = new StaticLayout(mHint, mTextPaint, - hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); + } + // TODO: code duplication with makeSingleLayout() + if (mHintLayout == null) { + StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0, + mHint.length(), hintWidth) + .setPaint(mTextPaint) + .setAlignment(alignment) + .setTextDir(mTextDir) + .setSpacingMult(mSpacingMult) + .setSpacingAdd(mSpacingAdd) + .setIncludePad(mIncludePad) + .setBreakStrategy(mBreakStrategy); + if (shouldEllipsize) { + builder.setEllipsize(mEllipsize) + .setEllipsizedWidth(ellipsisWidth) + .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); + } + mHintLayout = builder.build(); } } @@ -6544,9 +6577,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Layout result = null; if (mText instanceof Spannable) { result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth, - alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, getKeyListener() == null ? effectiveEllipsize : null, - ellipsisWidth); + alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mBreakStrategy, + getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring); @@ -6583,29 +6615,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boring, mIncludePad, effectiveEllipsize, ellipsisWidth); } - } else if (shouldEllipsize) { - result = new StaticLayout(mTransformed, - 0, mTransformed.length(), - mTextPaint, wantWidth, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, effectiveEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - result = new StaticLayout(mTransformed, mTextPaint, - wantWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); } - } else if (shouldEllipsize) { - result = new StaticLayout(mTransformed, - 0, mTransformed.length(), - mTextPaint, wantWidth, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, effectiveEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - result = new StaticLayout(mTransformed, mTextPaint, - wantWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); } } + if (result == null) { + StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed, + 0, mTransformed.length(), wantWidth) + .setPaint(mTextPaint) + .setAlignment(alignment) + .setTextDir(mTextDir) + .setSpacingMult(mSpacingMult) + .setSpacingAdd(mSpacingAdd) + .setIncludePad(mIncludePad) + .setBreakStrategy(mBreakStrategy); + if (shouldEllipsize) { + builder.setEllipsize(effectiveEllipsize) + .setEllipsizedWidth(ellipsisWidth) + .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); + } + // TODO: explore always setting maxLines + result = builder.build(); + } return result; } diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java index c58d5cb..2365b48 100644 --- a/core/java/android/widget/TimePickerClockDelegate.java +++ b/core/java/android/widget/TimePickerClockDelegate.java @@ -25,8 +25,10 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; +import android.text.SpannableStringBuilder; import android.text.format.DateFormat; import android.text.format.DateUtils; +import android.text.style.TtsSpan; import android.util.AttributeSet; import android.util.Log; import android.util.StateSet; @@ -155,13 +157,16 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl mHourView.setMinWidth(computeStableWidth(mHourView, 24)); mMinuteView.setMinWidth(computeStableWidth(mMinuteView, 60)); + final SpannableStringBuilder amLabel = new SpannableStringBuilder() + .append(amPmStrings[0], new TtsSpan.VerbatimBuilder(amPmStrings[0]).build(), 0); + // Set up AM/PM labels. mAmPmLayout = mainView.findViewById(R.id.ampm_layout); mAmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.am_label); - mAmLabel.setText(amPmStrings[0]); + mAmLabel.setText(obtainVerbatim(amPmStrings[0])); mAmLabel.setOnClickListener(mClickListener); mPmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.pm_label); - mPmLabel.setText(amPmStrings[1]); + mPmLabel.setText(obtainVerbatim(amPmStrings[1])); mPmLabel.setOnClickListener(mClickListener); // For the sake of backwards compatibility, attempt to extract the text @@ -220,6 +225,11 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX); } + private static final CharSequence obtainVerbatim(String text) { + return new SpannableStringBuilder().append(text, + new TtsSpan.VerbatimBuilder(text).build(), 0); + } + /** * The legacy text color might have been poorly defined. Ensures that it * has an appropriate activated state, using the selected state if one diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 93dc995..05ed3ab 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -104,7 +104,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 122 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 123 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 84568e4..cd117eb1 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -151,7 +151,6 @@ LOCAL_SRC_FILES:= \ android_util_FileObserver.cpp \ android/opengl/poly_clip.cpp.arm \ android/opengl/util.cpp \ - android_server_FingerprintManager.cpp \ android_server_NetworkManagementSocketTagger.cpp \ android_server_Watchdog.cpp \ android_ddm_DdmHandleNativeHeap.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index a4c91b3..f1b04a5 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -552,6 +552,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX]; char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX]; char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX]; + char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX]; char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX]; char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX]; char dex2oatXmsFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX]; @@ -829,6 +830,10 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) snprintf(cpuAbiListBuf, sizeof(cpuAbiListBuf), "--cpu-abilist=%s", propBuf); addOption(cpuAbiListBuf); + // Dalvik-cache pruning counter. + parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf, + "-Xzygote-max-boot-retry="); + initArgs.version = JNI_VERSION_1_4; initArgs.options = mOptions.editArray(); initArgs.nOptions = mOptions.size(); diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 4a98f27..fc05a6d 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -142,7 +142,14 @@ static struct { static const char* const kEventHandlerClassPathName = "android/media/AudioPortEventHandler"; -static jmethodID gPostEventFromNative; +static struct { + jfieldID mJniCallback; +} gEventHandlerFields; +static struct { + jmethodID postEventFromNative; +} gAudioPortEventHandlerMethods; + +static Mutex gLock; enum AudioError { kAudioStatusOk = 0, @@ -173,14 +180,14 @@ public: private: void sendEvent(int event); - jclass mClass; // Reference to AudioPortEventHandlerDelegate class - jobject mObject; // Weak ref to AudioPortEventHandlerDelegate Java object to call on + jclass mClass; // Reference to AudioPortEventHandler class + jobject mObject; // Weak ref to AudioPortEventHandler Java object to call on }; JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz) { - // Hold onto the SoundTriggerModule class for use in calling the static method + // Hold onto the AudioPortEventHandler class for use in calling the static method // that posts events to the application thread. jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { @@ -189,7 +196,7 @@ JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject we } mClass = (jclass)env->NewGlobalRef(clazz); - // We use a weak reference so the SoundTriggerModule object can be garbage collected. + // We use a weak reference so the AudioPortEventHandler object can be garbage collected. // The reference is only used as a proxy for callbacks. mObject = env->NewGlobalRef(weak_thiz); } @@ -211,7 +218,7 @@ void JNIAudioPortCallback::sendEvent(int event) if (env == NULL) { return; } - env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, + env->CallStaticVoidMethod(mClass, gAudioPortEventHandlerMethods.postEventFromNative, mObject, event, 0, 0, NULL); if (env->ExceptionCheck()) { ALOGW("An exception occurred while notifying an event."); @@ -234,6 +241,23 @@ void JNIAudioPortCallback::onServiceDied() sendEvent(AUDIOPORT_EVENT_SERVICE_DIED); } +static sp<JNIAudioPortCallback> setJniCallback(JNIEnv* env, + jobject thiz, + const sp<JNIAudioPortCallback>& callback) +{ + Mutex::Autolock l(gLock); + sp<JNIAudioPortCallback> old = + (JNIAudioPortCallback*)env->GetLongField(thiz, gEventHandlerFields.mJniCallback); + if (callback.get()) { + callback->incStrong((void*)setJniCallback); + } + if (old != 0) { + old->decStrong((void*)setJniCallback); + } + env->SetLongField(thiz, gEventHandlerFields.mJniCallback, (jlong)callback.get()); + return old; +} + static int check_AudioSystem_Command(status_t status) { switch (status) { @@ -1355,7 +1379,9 @@ android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject w sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this); - AudioSystem::setAudioPortCallback(callback); + if (AudioSystem::addAudioPortCallback(callback) == NO_ERROR) { + setJniCallback(env, thiz, callback); + } } static void @@ -1363,9 +1389,11 @@ android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz) { ALOGV("eventHandlerFinalize"); - sp<JNIAudioPortCallback> callback; + sp<JNIAudioPortCallback> callback = setJniCallback(env, thiz, 0); - AudioSystem::setAudioPortCallback(callback); + if (callback != 0) { + AudioSystem::removeAudioPortCallback(callback); + } } static jint @@ -1636,9 +1664,11 @@ int register_android_media_AudioSystem(JNIEnv *env) "Landroid/media/AudioHandle;"); jclass eventHandlerClass = FindClassOrDie(env, kEventHandlerClassPathName); - gPostEventFromNative = GetStaticMethodIDOrDie(env, eventHandlerClass, "postEventFromNative", - "(Ljava/lang/Object;IIILjava/lang/Object;)V"); - + gAudioPortEventHandlerMethods.postEventFromNative = GetStaticMethodIDOrDie( + env, eventHandlerClass, "postEventFromNative", + "(Ljava/lang/Object;IIILjava/lang/Object;)V"); + gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env, + eventHandlerClass, "mJniCallback", "J"); jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix"); gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1cb0455..b0771dd 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2601,6 +2601,12 @@ android:description="@string/permdesc_performCdmaProvisioning" android:protectionLevel="signature|system" /> + <!-- @SystemApi Allows an application to perform SIM Activation @hide --> + <permission android:name="android.permission.PERFORM_SIM_ACTIVATION" + android:label="@string/permlab_performSimActivation" + android:description="@string/permdesc_performSimActivation" + android:protectionLevel="signature|system" /> + <!-- @SystemApi Allows enabling/disabling location update notifications from the radio. <p>Not for use by third-party applications. --> diff --git a/core/res/res/values-mcc310-mnc260/strings.xml b/core/res/res/values-mcc310-mnc260/strings.xml index 75b1b53..dc79877 100644 --- a/core/res/res/values-mcc310-mnc260/strings.xml +++ b/core/res/res/values-mcc310-mnc260/strings.xml @@ -25,9 +25,13 @@ <string-array name="wfcOperatorErrorCodes" translatable="false"> <item>REG09</item> </string-array> - <!-- WFC Operator Error Messages --> - <string-array name="wfcOperatorErrorMessages"> - <item>Wi-Fi Calling isn\'t available. Contact your carrier to enable Wi-Fi Calling.</item> + <!-- WFC Operator Error Messages showed as alerts --> + <string-array name="wfcOperatorErrorAlertMessages"> + <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item> + </string-array> + <!-- WFC Operator Error Messages showed as notifications --> + <string-array name="wfcOperatorErrorNotificationMessages"> + <item>Register with your carrier</item> </string-array> <!-- Template for showing cellular network operator name while WFC is active --> <string name="wfcSpnFormat">%s Wi-Fi Calling</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index b6d32b2..3945222 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4283,6 +4283,15 @@ <attr name="letterSpacing" /> <!-- Font feature settings. --> <attr name="fontFeatureSettings" /> + <!-- Break strategy (control over paragraph layout). --> + <attr name="breakStrategy"> + <!-- Line breaking uses simple strategy. --> + <enum name="simple" value="0" /> + <!-- Line breaking uses high-quality strategy, including hyphenation. --> + <enum name="high_quality" value="1" /> + <!-- Line breaking stratgegy balances line lengths. --> + <enum name="balanced" value="2" /> + </attr> </declare-styleable> <declare-styleable name="TextViewAppearance"> <!-- Base text color, typeface, size, and style. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1b2e952..e879244 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -471,6 +471,18 @@ <!-- Wifi driver supports batched scan --> <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool> + <!-- Idle Receive current for wifi radio. 0 by default--> + <integer translatable="false" name="config_wifi_idle_receive_cur_ma">1</integer> + + <!-- Rx current for wifi radio. 0 by default--> + <integer translatable="false" name="config_wifi_active_rx_cur_ma">2</integer> + + <!-- Tx current for wifi radio. 0 by default--> + <integer translatable="false" name="config_wifi_tx_cur_ma">3</integer> + + <!-- Operating volatage for wifi radio. 0 by default--> + <integer translatable="false" name="config_wifi_operating_voltage_mv">4</integer> + <!-- Flag indicating whether the we should enable the automatic brightness in Settings. Software implementation will be used if config_hardware_auto_brightness_available is not set --> <bool name="config_automatic_brightness_available">false</bool> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 5c7daf2..f59a4d8 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2656,5 +2656,6 @@ <!--IntentFilter auto verification --> <public type="attr" name="autoVerify" /> + <public type="attr" name="breakStrategy" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0a77014..6cd3139 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -249,10 +249,20 @@ <string name="wfcRegErrorTitle">Wi-Fi Calling</string> <!-- WFC Operator Error Codes --> <string-array name="wfcOperatorErrorCodes" translatable="false" /> - <!-- WFC Operator Error Messages --> - <string-array name="wfcOperatorErrorMessages" /> + <!-- WFC Operator Error Messages showed as alerts --> + <string-array name="wfcOperatorErrorAlertMessages" /> + <!-- WFC Operator Error Messages showed as notifications --> + <string-array name="wfcOperatorErrorNotificationMessages" /> <!-- Template for showing cellular network operator name while WFC is active --> <string name="wfcSpnFormat">%s</string> + <!-- WFC, summary for Disabled --> + <string name="wifi_calling_off_summary">Off</string> + <!-- WFC, summary for Wi-Fi Preferred --> + <string name="wfc_mode_wifi_preferred_summary">Wi-Fi preferred</string> + <!-- WFC, summary for Cellular Preferred --> + <string name="wfc_mode_cellular_preferred_summary">Cellular preferred</string> + <!-- WFC, summary for Wi-Fi Only --> + <string name="wfc_mode_wifi_only_summary">Wi-Fi only</string> <!-- {0} is one of "bearerServiceCode*" @@ -1889,6 +1899,10 @@ <string name="permdesc_performCdmaProvisioning">Allows the app to start CDMA provisioning. Malicious apps may unnecessarily start CDMA provisioning.</string> + <string name="permlab_performSimActivation">start SIM card setup</string> + <string name="permdesc_performSimActivation">Allows the app to handle SIM activation requests. + The app may directly perform activation or may delegate to another app.</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_locationUpdates">control location update notifications</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index cc64b43..3c3d286 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -497,6 +497,7 @@ please see styles_device_defaults.xml. <item name="textEditSideNoPasteWindowLayout">?attr/textEditSideNoPasteWindowLayout</item> <item name="textEditSuggestionItemLayout">?attr/textEditSuggestionItemLayout</item> <item name="textCursorDrawable">?attr/textCursorDrawable</item> + <item name="breakStrategy">high_quality</item> </style> <style name="Widget.CheckedTextView"> @@ -527,6 +528,7 @@ please see styles_device_defaults.xml. <item name="textAppearance">?attr/textAppearanceMediumInverse</item> <item name="textColor">?attr/editTextColor</item> <item name="gravity">center_vertical</item> + <item name="breakStrategy">simple</item> </style> <style name="Widget.ExpandableListView" parent="Widget.ListView"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 19352c9..220d5e7 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -328,6 +328,10 @@ <java-symbol type="integer" name="config_wifi_framework_current_network_boost" /> <java-symbol type="string" name="config_wifi_random_mac_oui" /> <java-symbol type="integer" name="config_wifi_network_switching_blacklist_time" /> + <java-symbol type="integer" name="config_wifi_idle_receive_cur_ma" /> + <java-symbol type="integer" name="config_wifi_active_rx_cur_ma" /> + <java-symbol type="integer" name="config_wifi_tx_cur_ma" /> + <java-symbol type="integer" name="config_wifi_operating_voltage_mv" /> <java-symbol type="bool" name="editable_voicemailnumber" /> @@ -759,8 +763,13 @@ <java-symbol type="string" name="phoneTypeWorkPager" /> <java-symbol type="string" name="wfcRegErrorTitle" /> <java-symbol type="array" name="wfcOperatorErrorCodes" /> - <java-symbol type="array" name="wfcOperatorErrorMessages" /> + <java-symbol type="array" name="wfcOperatorErrorAlertMessages" /> + <java-symbol type="array" name="wfcOperatorErrorNotificationMessages" /> <java-symbol type="string" name="wfcSpnFormat" /> + <java-symbol type="string" name="wifi_calling_off_summary" /> + <java-symbol type="string" name="wfc_mode_wifi_preferred_summary" /> + <java-symbol type="string" name="wfc_mode_cellular_preferred_summary" /> + <java-symbol type="string" name="wfc_mode_wifi_only_summary" /> <java-symbol type="string" name="policydesc_disableCamera" /> <java-symbol type="string" name="policydesc_encryptedStorage" /> <java-symbol type="string" name="policydesc_expirePassword" /> diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index cd45017..6fa28b1 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -804,4 +804,56 @@ public class UriTest extends TestCase { assertFalse(Uri.parse("content://com.example/path/path").isPathPrefixMatch( Uri.parse("content://com.example/path%2Fpath"))); } + + public void testToSafeString() { + checkToSafeString("tel:xxxxxx", "tel:Google"); + checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890"); + checkToSafeString("tEl:xxx.xxx-xxxx", "tEl:123.456-7890"); + + checkToSafeString("sms:xxxxxx", "sms:123abc"); + checkToSafeString("smS:xxx.xxx-xxxx", "smS:123.456-7890"); + + checkToSafeString("smsto:xxxxxx", "smsto:123abc"); + checkToSafeString("SMSTo:xxx.xxx-xxxx", "SMSTo:123.456-7890"); + + checkToSafeString("mailto:xxxxxxx@xxxxxxx.xxx", "mailto:android@android.com"); + checkToSafeString("Mailto:xxxxxxx@xxxxxxx.xxxxxxxxxx", + "Mailto:android@android.com/secret"); + + checkToSafeString("sip:xxxxxxx@xxxxxxx.xxxxxxxx", "sip:android@android.com:1234"); + checkToSafeString("sIp:xxxxxxx@xxxxxxx.xxx", "sIp:android@android.com"); + + checkToSafeString("http://www.android.com/...", "http://www.android.com"); + checkToSafeString("HTTP://www.android.com/...", "HTTP://www.android.com"); + checkToSafeString("http://www.android.com/...", "http://www.android.com/"); + checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param"); + checkToSafeString("http://www.android.com/...", + "http://user:pwd@www.android.com/secretUrl?param"); + checkToSafeString("http://www.android.com/...", + "http://user@www.android.com/secretUrl?param"); + checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param"); + checkToSafeString("http:///...", "http:///path?param"); + checkToSafeString("http:///...", "http://"); + checkToSafeString("http://:12345/...", "http://:12345/"); + + checkToSafeString("https://www.android.com/...", "https://www.android.com/secretUrl?param"); + checkToSafeString("https://www.android.com:8443/...", + "https://user:pwd@www.android.com:8443/secretUrl?param"); + checkToSafeString("https://www.android.com/...", "https://user:pwd@www.android.com"); + checkToSafeString("Https://www.android.com/...", "Https://user:pwd@www.android.com"); + + checkToSafeString("ftp://ftp.android.com/...", "ftp://ftp.android.com/"); + checkToSafeString("ftP://ftp.android.com/...", "ftP://anonymous@ftp.android.com/"); + checkToSafeString("ftp://ftp.android.com:2121/...", + "ftp://root:love@ftp.android.com:2121/"); + + checkToSafeString("unsupported://ajkakjah/askdha/secret?secret", + "unsupported://ajkakjah/askdha/secret?secret"); + checkToSafeString("unsupported:ajkakjah/askdha/secret?secret", + "unsupported:ajkakjah/askdha/secret?secret"); + } + + private void checkToSafeString(String expectedSafeString, String original) { + assertEquals(expectedSafeString, Uri.parse(original).toSafeString()); + } } diff --git a/docs/html/google/gcm/c2dm.jd b/docs/html/google/gcm/c2dm.jd index 6ae7c1a..d0f8c71 100644 --- a/docs/html/google/gcm/c2dm.jd +++ b/docs/html/google/gcm/c2dm.jd @@ -33,7 +33,10 @@ page.title=Migration </div> </div> -<p>Android Cloud to Device Messaging (C2DM) is deprecated. The C2DM service will continue to be maintained in the short term, but C2DM will accept no new users, and it will grant no new quotas. <strong>C2DM developers are strongly encouraged to move to Google Cloud Messaging (GCM)</strong>. GCM is the next generation of C2DM.</p> +<p>Android Cloud to Device Messaging (C2DM) was officially deprecated on June 26, 2012, and has been + shut down completely as of April 1, 2015. <strong>C2DM developers are strongly encouraged to move + to Google Cloud Messaging (GCM)</strong>. GCM is the next generation of C2DM.</p> + <p>This document is addressed to C2DM developers who are moving to GCM. It describes the differences between GCM and C2DM, and explains how to migrate existing C2DM apps to GCM.</p> diff --git a/docs/html/images/tv/channel-info.png b/docs/html/images/tv/channel-info.png Binary files differnew file mode 100644 index 0000000..5a48078 --- /dev/null +++ b/docs/html/images/tv/channel-info.png diff --git a/docs/html/images/tv/do-not-attempt.png b/docs/html/images/tv/do-not-attempt.png Binary files differnew file mode 100644 index 0000000..18a8775 --- /dev/null +++ b/docs/html/images/tv/do-not-attempt.png diff --git a/docs/html/images/tv/prog-guide.png b/docs/html/images/tv/prog-guide.png Binary files differnew file mode 100644 index 0000000..caa2278 --- /dev/null +++ b/docs/html/images/tv/prog-guide.png diff --git a/docs/html/images/tv/tvinput-life.png b/docs/html/images/tv/tvinput-life.png Binary files differnew file mode 100644 index 0000000..fc53f89 --- /dev/null +++ b/docs/html/images/tv/tvinput-life.png diff --git a/docs/html/training/custom-views/optimizing-view.jd b/docs/html/training/custom-views/optimizing-view.jd index 7f2e762..022618b 100644 --- a/docs/html/training/custom-views/optimizing-view.jd +++ b/docs/html/training/custom-views/optimizing-view.jd @@ -12,33 +12,21 @@ previous.link=making-interactive.html <div id="tb"> <h2>This lesson teaches you to</h2> - <ol> - <li><a href="#less">Do Less, Less Frequently</a></li> - <li><a href="#accelerate">Use Hardware Acceleration</a></li> - </ol> - - <h2>You should also read</h2> <ul> - <li><a href="{@docRoot}guide/topics/graphics/hardware-accel.html"> - Hardware Acceleration - </a> - </li> - </ul> -<h2>Try it out</h2> -<div class="download-box"> -<a href="{@docRoot}shareables/training/CustomView.zip" -class="button">Download the sample</a> -<p class="filename">CustomView.zip</p> -</div> -</div> + <li><a href="#less">Do Less, Less Frequently</a></li> + </ul> + <h2>Try it out</h2> + <div class="download-box"> + <a href="{@docRoot}shareables/training/CustomView.zip" + class="button">Download the sample</a> + <p class="filename">CustomView.zip</p> </div> - + </div> +</div> <p>Now that you have a well-designed view that responds to gestures and transitions between states, -you need to ensure -that the view runs fast. To avoid a UI that feels sluggish or stutters during playback, you must -ensure that your -animations consistently run at 60 frames per second.</p> +ensure that the view runs fast. To avoid a UI that feels sluggish or stutters during playback, +ensure that animations consistently run at 60 frames per second.</p> <h2 id="less">Do Less, Less Frequently</h2> @@ -52,19 +40,13 @@ would cause a stutter. Allocate objects during initialization, or between animat allocation while an animation is running.</p> -<p>In addition to making {@link android.view.View#onDraw onDraw()} leaner, you should also make sure +<p>In addition to making {@link android.view.View#onDraw onDraw()} leaner, also make sure it's called as infrequently as possible. Most calls to {@link android.view.View#onDraw onDraw()} are the result of a call to {@link android.view.View#invalidate() invalidate()}, so eliminate unnecessary calls to {@link android.view.View#invalidate() -invalidate()}. When possible, call the four-parameter variant of {@link -android.view.View#invalidate() invalidate()} -rather than the version that takes no parameters. The no-parameter variant invalidates the entire -view, while the -four-parameter variant invalidates only a specified portion of the view. This approach allows draw calls to -be more efficient and -can eliminate unnecessary invalidation of views that fall outside the invalid rectangle.</p> +invalidate()}.</p> <p>Another very expensive operation is traversing layouts. Any time a view calls {@link android.view.View#requestLayout() @@ -78,7 +60,7 @@ behave properly. These deep view hierarchies cause performance problems. Make yo as shallow as possible.</p> -<p>If you have a complex UI, you should consider writing a custom {@link android.view.ViewGroup +<p>If you have a complex UI, consider writing a custom {@link android.view.ViewGroup ViewGroup} to perform its layout. Unlike the built-in views, your custom view can make application-specific assumptions about the size and @@ -88,89 +70,3 @@ to extend {@link android.view.ViewGroup ViewGroup} as part of a custom view. Pie views, but it never measures them. Instead, it sets their sizes directly according to its own custom layout algorithm.</p> - -<h2 id="accelerate">Use Hardware Acceleration</h2> - -<p>As of Android 3.0, the Android 2D graphics system can be accelerated by the GPU (Graphics -Processing Unit) hardware -found in most newer Android devices. GPU hardware acceleration can result in a tremendous -performance increase for many -applications, but it isn't the right choice for every application. The Android framework -gives you the ability to finely control which parts of your application are or are not -hardware accelerated.</p> - -<p>See <a href="{@docRoot}guide/topics/graphics/hardware-accel.html">Hardware Acceleration</a> - in the Android Developers Guide for directions on how to enable acceleration at the - application, activity, or window level. Notice that in addition to the directions in - the developer guide, you must also set your application's target API to 11 or higher by - specifying {@code <uses-sdk - android:targetSdkVersion="11"/>} in your {@code AndroidManifest.xml} file.</p> - -<p>Once you've enabled hardware acceleration, you may or may not see a performance increase. -Mobile GPUs are very good at certain tasks, such as scaling, rotating, and translating -bitmapped images. They are not particularly good at other tasks, such as drawing lines or curves. To -get the most out of GPU acceleration, you should maximize the number of operations that the GPU is -good at, and minimize the number of operations that the GPU isn't good at.</p> - -<p>In the PieChart example, for instance, drawing the pie is relatively expensive. Redrawing the pie -each time it's -rotated causes the UI to feel sluggish. The solution is to place the pie chart into a child -{@link android.view.View} and set that -{@link android.view.View}'s -<a href="{@docRoot}reference/android/view/View.html#setLayerType(int, android.graphics.Paint)"> - layer type</a> to {@link android.view.View#LAYER_TYPE_HARDWARE}, so that the GPU can cache it as -a static -image. The sample -defines the child view as an inner class of {@code PieChart}, which minimizes the amount of code -changes that are needed -to implement this solution.</p> - -<pre> - private class PieView extends View { - - public PieView(Context context) { - super(context); - if (!isInEditMode()) { - setLayerType(View.LAYER_TYPE_HARDWARE, null); - } - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - for (Item it : mData) { - mPiePaint.setShader(it.mShader); - canvas.drawArc(mBounds, - 360 - it.mEndAngle, - it.mEndAngle - it.mStartAngle, - true, mPiePaint); - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - mBounds = new RectF(0, 0, w, h); - } - - RectF mBounds; - } -</pre> - -<p>After this code change, {@code PieChart.PieView.onDraw()} is called only when the view is first -shown. During the rest -of the application's lifetime, the pie chart is cached as an image, and redrawn at different -rotation angles by the GPU. -GPU hardware is particularly good at this sort of thing, and the performance difference is -immediately noticeable.</p> - -<p>There is a tradeoff, though. Caching images as hardware layers consumes video memory, which is a -limited resource. -For this reason, the final version of {@code PieChart.PieView} only sets its layer type to -{@link android.view.View#LAYER_TYPE_HARDWARE} -while the user is actively scrolling. At all other times, it sets its layer type to -{@link android.view.View#LAYER_TYPE_NONE}, which -allows the GPU to stop caching the image.</p> - -<p>Finally, don't forget to profile your code. Techniques that improve performance on one view -might negatively affect performance on another.</p> diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 11ae1a6..2873b5b 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -998,10 +998,25 @@ include the action bar on devices running Android 2.1 or higher." Building TV Games</a> </li> - <li> - <a href="<?cs var:toroot ?>training/tv/tif/index.html" + <li class="nav-section"> + <div class="nav-section-header"> + <a href="<?cs var:toroot ?>training/tv/tif/index.html" description="How to build Live TV apps."> Building Live TV Apps</a> + </div> + <ul> + <li> + <a href="<?cs var:toroot ?>training/tv/tif/tvinput.html"> + Developing a TV Input Service</a> + <li> + <a href="<?cs var:toroot ?>training/tv/tif/channel.html"> + Working with Channel Data</a> + </li> + <li> + <a href="<?cs var:toroot ?>training/tv/tif/ui.html"> + Managing User Interaction</a> + </li> + </ul> </li> <li> diff --git a/docs/html/training/tv/tif/channel.jd b/docs/html/training/tv/tif/channel.jd new file mode 100644 index 0000000..999f1ca --- /dev/null +++ b/docs/html/training/tv/tif/channel.jd @@ -0,0 +1,239 @@ +page.title=Working with Channel Data +page.tags=tv, tif +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + <h2>This lesson teaches you to</h2> + <ol> + <li><a href="#permission">Get Permission</a></li> + <li><a href="#register">Register Channels in the Database</a></li> + <li><a href="#update">Update Channel Data</a></li> + </ol> + <h2>Try It Out</h2> + <ul> + <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs"> + TV Input Service sample app</a></li> + </ul> +</div> +</div> + +<p>Your TV input must provide Electronic Program Guide (EPG) data for at least one channel in its +setup activity. You should also periodically update that data, with consideration for the size of +the update and the processing thread that handles it. This lesson discusses creating and updating +channel and program data on the system database with these considerations in mind.</p> + +<p> </p> + +<h2 id="permission">Get Permission</h2> + +<p>In order for your TV input to work with EPG data, it must declare the +read and write permissions in its Android manifest file as follows:</p> + +<pre> +<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" /> +<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> +</pre> + +<h2 id="register">Register Channels in the Database</h2> + +<p>The Android TV system database maintains records of channel data for TV inputs. In your setup +activity, for each of your channels, you must map your channel data to the following fields of the +{@link android.media.tv.TvContract.Channels} class:</p> + +<ul> + <li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NAME} - the displayed name of the + channel</li> + <li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER} - the displayed channel + number</li> + <li>{@link android.media.tv.TvContract.Channels#COLUMN_INPUT_ID} - the ID of the TV input service</li> + <li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_TYPE} - the channel's service type</li> + <li>{@link android.media.tv.TvContract.Channels#COLUMN_TYPE} - the channel's broadcast standard + type</li> + <li>{@link android.media.tv.TvContract.Channels#COLUMN_VIDEO_FORMAT} - the default video format + for the channel</li> +</ul> + +<p>Although the TV input framework is generic enough to handle both traditional broadcast and +over-the-top (OTT) content without any distinction, you may want to define the following columns in +addition to those above to better identify traditional broadcast channels:</p> + +<ul> + <li>{@link android.media.tv.TvContract.Channels#COLUMN_ORIGINAL_NETWORK_ID} - the television + network ID</li> + <li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_ID} - the service ID</li> + <li>{@link android.media.tv.TvContract.Channels#COLUMN_TRANSPORT_STREAM_ID} - the transport stream + ID</li> +</ul> + +<p>For internet streaming based TV inputs, assign your own values to the above accordingly so that +each channel can be identified uniquely.</p> + +<p>Pull your channel metadata (in XML, JSON, or whatever) from your backend server, and in your setup +activity map the values to the system database as follows:</p> + +<pre> +ContentValues values = new ContentValues(); + +values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.mNumber); +values.put(Channels.COLUMN_DISPLAY_NAME, channel.mName); +values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.mOriginalNetworkId); +values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.mTransportStreamId); +values.put(Channels.COLUMN_SERVICE_ID, channel.mServiceId); +values.put(Channels.COLUMN_VIDEO_FORMAT, channel.mVideoFormat); + +Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values); +</pre> + +<p>In the example above, <code>channel</code> is an object which holds channel metadata from the +backend server.</p> + +<h3 id="art">Present Channel and Program Information</h2> + +<p>The system TV app presents channel and program information to users as they flip through channels, +as shown in figure 1. To make sure the channel and program information works with the system TV app's +channel and program information presenter, follow the guidelines below.</p> + +<ol> +<li><strong>Channel number</strong> ({@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER}) +<li><strong>Icon</strong> +(<a href="guide/topics/manifest/application-element.html#icon"><code>android:icon</code></a> in the +TV input's manifest)</li> +<li><strong>Program description</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_SHORT_DESCRIPTION}) +<li><strong>Program title</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_TITLE})</li> +<li><strong>Channel logo</strong> ({@link android.media.tv.TvContract.Channels.Logo}) + <ul> + <li>Use the color #EEEEEE to match the surrounding text</li> + <li>Don't include padding + </ul></li> +<li><strong>Poster art</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_POSTER_ART_URI}) + <ul> + <li>Aspect ratio between 16:9 and 4:3</li> + </ul> +</ol> + +<img src="{@docRoot}images/tv/channel-info.png" id="figure1"> +<p class="img-caption"> + <strong>Figure 1.</strong> The system TV app channel and program information presenter. +</p> + +<p>The system TV app provides the same information through the program guide, including poster art, +as shown in figure 2.</p> + +<img src="{@docRoot}images/tv/prog-guide.png" id="figure2"> +<p class="img-caption"> + <strong>Figure 2.</strong> The system TV app program guide. +</p> + +<h2 id="update">Update Channel Data</h2> + +<p>When updating existing channel data, use the +{@link android.content.ContentProvider#update(android.net.Uri, android.content.ContentValues, +java.lang.String, java.lang.String[]) update()} +method instead of deleting and re-adding the data. You can identify the current version of the data +by using {@link android.media.tv.TvContract.Channels#COLUMN_VERSION_NUMBER Channels.COLUMN_VERSION_NUMBER} +and {@link android.media.tv.TvContract.Programs#COLUMN_VERSION_NUMBER Programs.COLUMN_VERSION_NUMBER} +when choosing the records to update.</p> + +<p class="note"><strong>Note:</strong> Adding channel data to the {@link android.content.ContentProvider} +can take time. Only add current programs (those within two hours of the current time) when you update, +and use a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">Sync Adapter</a> to +update the rest of the channel data in the background. See the <a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs/blob/master/app/src/main/java/com/example/android/sampletvinput/syncadapter/SyncAdapter.java"> +Android TV Live TV Sample App</a> for an example.</p> + +<h3 id="batch">Batch Loading Channel Data</h3> + +<p>When updating the system database with a large amount of channel data, use the {@link android.content.ContentResolver} +{@link android.content.ContentResolver#applyBatch applyBatch()} +or +{@link android.content.ContentResolver#bulkInsert(android.net.Uri, android.content.ContentValues[]) bulkInsert()} +method. Here's an example using {@link android.content.ContentResolver#applyBatch applyBatch()}:<p> + +<pre> +ArrayList<ContentProviderOperation> ops = new ArrayList<>(); +int programsCount = mChannelInfo.mPrograms.size(); +for (int j = 0; j < programsCount; ++j) { + ProgramInfo program = mChannelInfo.mPrograms.get(j); + ops.add(ContentProviderOperation.newInsert( + TvContract.Programs.CONTENT_URI) + .withValues(programs.get(j)) + .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, + programStartSec * 1000) + .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, + (programStartSec + program.mDurationSec) * 1000) + .build()); + programStartSec = programStartSec + program.mDurationSec; + if (j % 100 == 99 || j == programsCount - 1) { + try { + <strong>getContentResolver().applyBatch(TvContract.AUTHORITY, ops);</strong> + } catch (RemoteException | OperationApplicationException e) { + Log.e(TAG, "Failed to insert programs.", e); + return; + } + ops.clear(); + } +} +</pre> + +<h3 id="async">Processing Channel Data Asynchronously</h3> + +<p>Data manipulation, such as fetching a stream from the server or accessing the database, should +not block the UI thread. Using an {@link android.os.AsyncTask} is one +way to perform updates asynchronously. For example, when loading channel info from a backend server, +you can use {@link android.os.AsyncTask} as follows:</p> + +<pre> +private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void>> { + + private Context mContext; + + public LoadTvInputTask(Context context) { + mContext = context; + } + + @Override + protected Void doInBackground(Uri... uris) { + try { + fetchUri(uris[0]); + } catch (IOException e) { + Log.d(“LoadTvInputTask”, “fetchUri error”); + } + return null; + } + + private void fetchUri(Uri videoUri) throws IOException { + InputStream inputStream = null; + try { + inputStream = mContext.getContentResolver().openInputStream(videoUri); + XmlPullParser parser = Xml.newPullParser(); + try { + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(inputStream, null); + sTvInput = ChannelXMLParser.parseTvInput(parser); + sSampleChannels = ChannelXMLParser.parseChannelXML(parser); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + } +} +</pre> + +<p>If you need to update EPG data on a regular basis, consider using +a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html"> +Sync Adapter</a> or {@link android.app.job.JobScheduler} to run the update process during idle time, +such as every day at 3:00 a.m. See the <a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs/blob/master/app/src/main/java/com/example/android/sampletvinput/syncadapter/SyncAdapter.java"> +Android TV live TV sample app</a> for an example.</p> + +<p>Other techniques to separate the data update tasks from the UI thread include using the +{@link android.os.HandlerThread} class, or you may implement your own using {@link android.os.Looper} +and {@link android.os.Handler} classes. See <a href="{@docRoot}guide/components/processes-and-threads.html"> +Processes and Threads</a> for more information.</p>
\ No newline at end of file diff --git a/docs/html/training/tv/tif/index.jd b/docs/html/training/tv/tif/index.jd index 9c10850..5739294 100644 --- a/docs/html/training/tv/tif/index.jd +++ b/docs/html/training/tv/tif/index.jd @@ -1,17 +1,26 @@ page.title=Building Live TV Apps page.tags=tv, tif helpoutsWidget=true -page.article=true +startpage=true @jd:body <div id="tb-wrapper"> <div id="tb"> + <h2>Dependencies and Prerequisites</h2> + <ul> + <li>Android 5.0 (API level 21) or higher</li> + </ul> <h2>You should also read</h2> <ul> <li><a href="{@docRoot}reference/android/media/tv/package-summary.html"> android.media.tv</a></li> </ul> + <h2>Try It Out</h2> + <ul> + <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs"> + TV Input Service sample app</a></li> + </ul> </div> </div> @@ -44,6 +53,17 @@ page.article=true Building a TV input service for your content can help make it more accessible on TV devices. </p> -<p>For more information about TV Input Framework, see the -<a href="{@docRoot}reference/android/media/tv/package-summary.html">android.media.tv</a> -reference.</p> +<h2>Topics</h2> + +<dl> + <dt><b><a href="tvinput.html">Developing a TV Input Service</a></b></dt> + <dd>Learn how to develop a TV input service, which works with the system TV app.</dd> + + <dt><b><a href="channel.html">Working with Channel Data</a></b></dt> + <dd>Learn how to describe channel and program data for the system.</dd> + + <dt><b><a href="ui.html">Managing User Interaction</a></b></dt> + <dd>Learn how to present overlays, manage content availability, and handle content selection.</dd> +</dl> + + diff --git a/docs/html/training/tv/tif/tvinput.jd b/docs/html/training/tv/tif/tvinput.jd new file mode 100644 index 0000000..91f8ded --- /dev/null +++ b/docs/html/training/tv/tif/tvinput.jd @@ -0,0 +1,177 @@ +page.title=Developing a TV Input Service +page.tags=tv, tif +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + <h2>This lesson teaches you to</h2> + <ol> + <li><a href="#manifest">Declare Your TV Input Service in the Manifest</a></li> + <li><a href="#tvinput">Define Your TV Input Service</a></li> + <li><a href="#setup">Define Setup and Settings Activities</a></li> + </ol> + <h2>You should also read</h2> + <ul> + <li><a href="{@docRoot}reference/android/media/tv/package-summary.html"> + android.media.tv</a></li> + <li><a class="external-lin" href="http://source.android.com/devices/tv/index.html"> + TV Input Framework</a></li> + </ul> + <h2>Try It Out</h2> + <ul> + <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs"> + TV Input Service sample app</a></li> + </ul> +</div> +</div> + +<p>A TV input service represents a media stream source, and lets you present your media content in a +linear, broadcast TV fashion as channels and programs. With the TV input service, you can provide +parental controls, program guide information, and content ratings. The TV input service works +with the Android system TV app, developed for the device and immutable by third-party apps, which +ultimately controls and presents content on the TV. See +<a class="external-link" href="http://source.android.com/devices/tv/index.html"> +TV Input Framework</a> for more information about the framework architecture and its components.</p> + +<p>To develop a TV input service, you implement the following components:</p> + +<ul> + <li>{@link android.media.tv.TvInputService} provides long-running and background availability for + the TV input</li> + <li>{@link android.media.tv.TvInputService.Session} maintains the TV input state and communicates + with the hosting app</li> + <li>{@link android.media.tv.TvContract} describes the channels and programs available to the TV + input</li> + <li>{@link android.media.tv.TvContract.Channels} represents information about a TV channel</li> + <li>{@link android.media.tv.TvContract.Programs} describes a TV program with data such as program + title and start time</li> + <li>{@link android.media.tv.TvTrackInfo} represents an audio, video, or subtitle track</li> + <li>{@link android.media.tv.TvContentRating} describes a content rating, allows for custom content + rating schemes</li> + <li>{@link android.media.tv.TvInputManager} provides an API to the system TV app and manages + the interaction with TV inputs and apps</li> +</ul> + +<h2 id="manifest">Declare Your TV Input Service in the Manifest</h2> + +<p>Your app manifest must declare your {@link android.media.tv.TvInputService}. Within that +declaration, specify the {@link android.Manifest.permission#BIND_TV_INPUT} permission to allow the +service to connect the TV input to the system. A system service (<code>TvInputManagerService</code>) +performs the binding and has that permission. The system TV app sends requests to TV input services +via the {@link android.media.tv.TvInputManager} interface. The service declaration must also +include an intent filter that specifies the {@link android.media.tv.TvInputService} +as the action to perform with the intent. Also within the service declaration, declare the service +meta data in a separate XML resource. The service declaration, the intent filter and the service +meta data are described in the following example.</p> + +<pre> +<service android:name="com.example.sampletvinput.SampleTvInput" + android:label="@string/sample_tv_input_label" + android:permission="android.permission.BIND_TV_INPUT"> + <intent-filter> + <action android:name="android.media.tv.TvInputService" /> + </intent-filter> + <meta-data android:name="android.media.tv.input" + android:resource="@xml/sample_tv_input" /> +</service> +</pre> + +<p>Define the service meta data in separate XML file, as shown in the following example. The service +meta data must include a setup interface that describes the TV input's initial configuration and +channel scan. Also, the service meta data may (optionally) describe a settings activity for users to +modify the TV input's behavior. The service meta data file is located in the XML resources directory +for your application and must match the name of the resource in the manifest. Using the example +manifest entries above, you would create an XML file in the location +<code>res/xml/sample_tv_input.xml</code>, with the following contents:</p> + +<pre> +<tv-input xmlns:android="http://schemas.android.com/apk/res/android" + <!-- Required: activity for setting up the input --> + android:setupActivity="com.example.sampletvinput.SampleTvInputSetupActivity" + <!-- Optional: activity for controlling the settings --> + android:settingsActivity="com.example.sampletvinput.SampleTvInputSettingsActivity" /> +</pre> + +<h2 id="tvinput">Define Your TV Input Service</h2> + +<div class="figure"> +<img id="tvinputlife" src="{@docRoot}images/tv/tvinput-life.png" alt=""/> +<p class="img-caption"><strong>Figure 1.</strong>TvInputService lifecycle.</p> +</div> + +<p>For your service, you extend the {@link android.media.tv.TvInputService} class. A +{@link android.media.tv.TvInputService} implementation is a +<a href="{@docRoot}guide/components/bound-services.html">bound service</a> where the system service +(<code>TvInputManagerService</code>) is the client that binds to it. The service life cycle methods +you need to implement are illustrated in figure 1.</p> + +<p>The {@link android.app.Service#onCreate()} method initializes and starts the +{@link android.os.HandlerThread} which provides a process thread separate from the UI thread to +handle system-driven actions. In the following example, the {@link android.app.Service#onCreate()} +method initializes the {@link android.view.accessibility.CaptioningManager} and prepares to handle +the {@link android.media.tv.TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} +and {@link android.media.tv.TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} actions. These +actions describe system intents fired when the user changes the parental control settings, and when +there is a change on the list of blocked ratings.</p> + +<pre> +@Override +public void onCreate() { + super.onCreate(); + mHandlerThread = new HandlerThread(getClass() + .getSimpleName()); + mHandlerThread.start(); + mDbHandler = new Handler(mHandlerThread.getLooper()); + mHandler = new Handler(); + mCaptioningManager = (CaptioningManager) + getSystemService(Context.CAPTIONING_SERVICE); + + setTheme(android.R.style.Theme_Holo_Light_NoActionBar); + + mSessions = new ArrayList<BaseTvInputSessionImpl>(); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TvInputManager + .ACTION_BLOCKED_RATINGS_CHANGED); + intentFilter.addAction(TvInputManager + .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED); + registerReceiver(mBroadcastReceiver, intentFilter); +} +</pre> + +<p> See <a href="{@docRoot}training/tv/tif/ui.html#control"> +Control Content</a> for more information about working with blocked content and providing +parental control. See {@link android.media.tv.TvInputManager} for more system-driven actions that +you may want to handle in your TV input service.</p> + +<p>The {@link android.media.tv.TvInputService} creates a +{@link android.media.tv.TvInputService.Session} that implements {@link android.os.Handler.Callback} +to handle player state changes. With {@link android.media.tv.TvInputService.Session#onSetSurface(android.view.Surface) onSetSurface()}, +the {@link android.media.tv.TvInputService.Session} sets the {@link android.view.Surface} with the +video content. See <a href="{@docRoot}training/tv/tif/ui.html#surface">Integrate Player with Surface</a> +for more information about working with {@link android.view.Surface} to render video.</p> + +<p>The {@link android.media.tv.TvInputService.Session} handles the +{@link android.media.tv.TvInputService.Session#onTune(android.net.Uri) onTune()} +event when the user selects a channel, and notifies the system TV app for changes in the content and +content meta data. These <code>notify()</code>code> methods are described in +<a href="{@docRoot}training/tv/tif/ui.html#control"> +Control Content</a> and <a href="training/tv/tif/ui.html#track">Handle Track Selection</a> further +in this training.</p> + +<h2 id="setup">Define Setup and Settings Activities</h2> + +<p>The system TV app works with the setup and settings activities you define for your TV input. The +setup activity is required and must provide at least one channel record for the system database. The +system TV app will invoke the setup activity when it cannot find a channel for the TV input. +<p>The setup activity describes to the system TV app the channels made available through the TV +input, as demonstrated in the next lesson, <a href="{@docRoot}training/tv/tif/channel.html">Creating +and Updating Channel Data</a>.</p> + +<p>The settings activity is optional. You can define a settings activity to turn on parental +controls, enable closed captions, set the display attributes, and so forth.</p> + + diff --git a/docs/html/training/tv/tif/ui.jd b/docs/html/training/tv/tif/ui.jd new file mode 100644 index 0000000..6ead3db --- /dev/null +++ b/docs/html/training/tv/tif/ui.jd @@ -0,0 +1,304 @@ +page.title=Managing User Interaction +page.tags=tv, tif +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + <h2>This lesson teaches you to</h2> + <ol> + <li><a href="#surface">Integrate Player with Surface</a></li> + <li><a href="#overlay">Use an Overlay</a></li> + <li><a href="#control">Control Content</a></li> + <li><a href="#track">Handle Track Selection</a></li> + </ol> + <h2>Try It Out</h2> + <ul> + <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs"> + TV Input Service sample app</a></li> + </ul> +</div> +</div> + +<p>In the live TV experience the user changes channels and is presented with +channel and program information briefly before the information disappears. Other types of information, +such as messages ("DO NOT ATTEMPT AT HOME"), subtitles, or ads may need to persist. As with any TV +app, such information should not interfere with the program content playing on the screen.</p> + +<img src="{@docRoot}images/tv/do-not-attempt.png" id="figure1"> +<p class="img-caption"> + <strong>Figure 1.</strong> An overlay message in a live TV app. +</p> + +<p>Also consider whether certain program content should be presented, given the +content's rating and parental control settings, and how your app behaves and informs the user when +content is blocked or unavailable. This lesson describes how to develop your TV input's user +experience for these considerations.</p> + +<h2 id="surface">Integrate Player with Surface</h2> + +<p>Your TV input must render video onto a {@link android.view.Surface} object, which is passed by +the {@link android.media.tv.TvInputService.Session#onSetSurface(android.view.Surface) TvInputService.Session.onSetSurface()} +method. Here's an example of how to use a {@link android.media.MediaPlayer} instance for playing +content in the {@link android.view.Surface} object:</p> + +<pre> +@Override +public boolean onSetSurface(Surface surface) { + if (mPlayer != null) { + mPlayer.setSurface(surface); + } + mSurface = surface; + return true; +} + +@Override +public void onSetStreamVolume(float volume) { + if (mPlayer != null) { + mPlayer.setVolume(volume, volume); + } + mVolume = volume; +} +</pre> + +<p>Similarly, here's how to do it using <a href="{@docRoot}guide/topics/media/exoplayer.html"> +ExoPlayer</a>:</p> + +<pre> +@Override +public boolean onSetSurface(Surface surface) { + if (mPlayer != null) { + mPlayer.sendMessage(mVideoRenderer, + MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, + surface); + } + mSurface = surface; + return true; +} + +@Override +public void onSetStreamVolume(float volume) { + if (mPlayer != null) { + mPlayer.sendMessage(mAudioRenderer, + MediaCodecAudioTrackRenderer.MSG_SET_VOLUME, + volume); + } + mVolume = volume; +} +</pre> + +<h2 id="overlay">Use an Overlay</h2> + +<p>Use an overlay to display subtitles, messages, ads or MHEG-5 data broadcasts. By default, the +overlay is disabled. You can enable it when you create the session by calling +{@link android.media.tv.TvInputService.Session#setOverlayViewEnabled(boolean) TvInputService.Session.setOverlayViewEnabled(true)}, +as in the following example:</p> + +<pre> +@Override +public final Session onCreateSession(String inputId) { + BaseTvInputSessionImpl session = onCreateSessionInternal(inputId); + session.setOverlayViewEnabled(true); + mSessions.add(session); + return session; +} +</pre> + +<p>Use a {@link android.view.View} object for the overlay, returned from {@link android.media.tv.TvInputService.Session#onCreateOverlayView() TvInputService.Session.onCreateOverlayView()}, as shown here:</p> + +<pre> +@Override +public View onCreateOverlayView() { + LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.overlayview, null); + mSubtitleView = (SubtitleView) view.findViewById(R.id.subtitles); + + // Configure the subtitle view. + CaptionStyleCompat captionStyle; + float captionTextSize = getCaptionFontSize(); + captionStyle = CaptionStyleCompat.createFromCaptionStyle( + mCaptioningManager.getUserStyle()); + captionTextSize *= mCaptioningManager.getFontScale(); + mSubtitleView.setStyle(captionStyle); + mSubtitleView.setTextSize(captionTextSize); + return view; +} +</pre> + +<p>The layout definition for the overlay might look something like this:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.google.android.exoplayer.text.SubtitleView + android:id="@+id/subtitles" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|center_horizontal" + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" + android:layout_marginBottom="32dp" + android:visibility="invisible"/> +</FrameLayout> +</pre> + +<h2 id="control">Control Content</h2> + +<p>When the user selects a channel, your TV input handles the {@link android.media.tv.TvInputService.Session#onTune(android.net.Uri) +onTune()} callback in the {@link android.media.tv.TvInputService.Session} object. The system TV +app's parental controls determine what content displays, given the content rating. +The following sections describe how to manage channel and program selection using the +{@link android.media.tv.TvInputService.Session} <code>notify</code> methods that +communicate with the system TV app.</p> + +<h3 id="unavailable">Make Video Unavailable</h3> + +<p>When the user changes the channel, you want to make sure the screen doesn't display any stray +video artifacts before your TV input renders the content. When you call {@link android.media.tv.TvInputService.Session#onTune(android.net.Uri) TvInputService.Session.onTune()}, +you can prevent the video from being presented by calling {@link android.media.tv.TvInputService.Session#notifyVideoUnavailable(int) TvInputService.Session.notifyVideoUnavailable()} +and passing the {@link android.media.tv.TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING} constant, as +shown in the following example.</p> + +<pre> +@Override +public boolean onTune(Uri channelUri) { + if (mSubtitleView != null) { + mSubtitleView.setVisibility(View.INVISIBLE); + } + notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING); + mUnblockedRatingSet.clear(); + + mDbHandler.removeCallbacks(mPlayCurrentProgramRunnable); + mPlayCurrentProgramRunnable = new PlayCurrentProgramRunnable(channelUri); + mDbHandler.post(mPlayCurrentProgramRunnable); + return true; +} +</pre> + +<p>Then, when the content is rendered to the {@link android.view.Surface}, you call +{@link android.media.tv.TvInputService.Session#notifyVideoAvailable() TvInputService.Session.notifyVideoAvailable()} +to allow the video to display, like so:</p> + +<pre> +@Override +public void onDrawnToSurface(Surface surface) { + mFirstFrameDrawn = true; + notifyVideoAvailable(); +} +</pre> + +<p>This transition lasts only for fractions of a second, but presenting a blank screen is +visually better than allowing the picture to flash odd blips and jitters.</p> + +<p>See also, <a href="#surface">Integrate Player with Surface</a> for more information about working +with {@link android.view.Surface} to render video.</p> + +<h3 id="parental">Provide Parental Control</h3> + +<p>To determine if a given content is blocked by parental controls and content rating, you check the +{@link android.media.tv.TvInputManager} class methods, {@link android.media.tv.TvInputManager#isParentalControlsEnabled()} +and {@link android.media.tv.TvInputManager#isRatingBlocked(android.media.tv.TvContentRating)}. You +might also want to make sure the content's {@link android.media.tv.TvContentRating} is included in a +set of currently allowed content ratings. These considerations are shown in the following sample.</p> + +<pre> +private void checkContentBlockNeeded() { + if (mCurrentContentRating == null || !mTvInputManager.isParentalControlsEnabled() + || !mTvInputManager.isRatingBlocked(mCurrentContentRating) + || mUnblockedRatingSet.contains(mCurrentContentRating)) { + // Content rating is changed so we don't need to block anymore. + // Unblock content here explicitly to resume playback. + unblockContent(null); + return; + } + + mLastBlockedRating = mCurrentContentRating; + if (mPlayer != null) { + // Children restricted content might be blocked by TV app as well, + // but TIF should do its best not to show any single frame of blocked content. + releasePlayer(); + } + + notifyContentBlocked(mCurrentContentRating); +} +</pre> + +<p>Once you have determined if the content should or should not be blocked, notify the system TV +app by calling the +{@link android.media.tv.TvInputService.Session} method {@link android.media.tv.TvInputService.Session#notifyContentAllowed() notifyContentAllowed()} +or +{@link android.media.tv.TvInputService.Session#notifyContentBlocked(android.media.tv.TvContentRating) notifyContentBlocked()} +, as shown in the previous example.</p> + +<p>Use the {@link android.media.tv.TvContentRating} class to generate the system-defined string for +the {@link android.media.tv.TvContract.Programs#COLUMN_CONTENT_RATING} with the +<code><a href="{@docRoot}reference/android/media/tv/TvContentRating.html#createRating(java.lang.String, java.lang.String, java.lang.String, java.lang.String...)">TvContentRating.createRating()</a></code> +method, as shown here:</p> + +<pre> +TvContentRating rating = TvContentRating.createRating( + "com.android.tv", + "US_TV", + "US_TV_PG", + "US_TV_D", "US_TV_L"); +</pre> + +<h2 id="track">Handle Track Selection</h2> + +<p>The {@link android.media.tv.TvTrackInfo} class holds information about media tracks such +as the track type (video, audio, or subtitle) and so forth. </p> + +<p>The first time your TV input session is able to get track information, it should call +<code><a href="{@docRoot}reference/android/media/tv/TvInputService.Session.html#notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>)">TvInputService.Session.notifyTracksChanged()</a></code> with a list of all tracks to update the system TV app. When there +is a change in track information, call +<code><a href="{@docRoot}reference/android/media/tv/TvInputService.Session.html#notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>)">notifyTracksChanged()</a></code> +again to update the system. + +</p> + +<p>The system TV app provides an interface for the user to select a specific track if more than one +track is available for a given track type; for example, subtitles in different languages. Your TV +input responds to the +{@link android.media.tv.TvInputService.Session#onSelectTrack(int, java.lang.String) onSelectTrack()} +call from the system TV app by calling +{@link android.media.tv.TvInputService.Session#notifyTrackSelected(int, java.lang.String) notifyTrackSelected()} +, as shown in the following example. Note that when <code>null</code> +is passed as the track ID, this <em>deselects</em> the track.</p> + +<pre> +@Override +public boolean onSelectTrack(int type, String trackId) { + if (mPlayer != null) { + if (type == TvTrackInfo.TYPE_SUBTITLE) { + if (!mCaptionEnabled && trackId != null) { + return false; + } + mSelectedSubtitleTrackId = trackId; + if (trackId == null) { + mSubtitleView.setVisibility(View.INVISIBLE); + } + } + if (mPlayer.selectTrack(type, trackId)) { + notifyTrackSelected(type, trackId); + return true; + } + } + return false; +} +</pre> + + + + + + + diff --git a/docs/html/training/wearables/apps/voice.jd b/docs/html/training/wearables/apps/voice.jd index 6d49319..3aef3c4 100644 --- a/docs/html/training/wearables/apps/voice.jd +++ b/docs/html/training/wearables/apps/voice.jd @@ -131,6 +131,17 @@ named <code>MyNoteActivity</code>: </tr> <tr> + <td>Start stopwatch</td> + <td>"Ok Google, start stopwatch"</td> + <td> + <dl> + <dt>Action</dt> + <dd><code>com.google.android.wearable.action.STOPWATCH</code></dd> + </dl> + </td> + </tr> + + <tr> <td>Start/Stop a bike ride</td> <td>"OK Google, start cycling"<br/><br/>"OK Google, start my bike ride"<br/><br/>"OK Google, stop cycling"</td> <td> diff --git a/docs/html/training/wearables/watch-faces/drawing.jd b/docs/html/training/wearables/watch-faces/drawing.jd index 3c5da34..60da5d5 100644 --- a/docs/html/training/wearables/watch-faces/drawing.jd +++ b/docs/html/training/wearables/watch-faces/drawing.jd @@ -377,7 +377,8 @@ public void onPropertiesChanged(Bundle properties) { <ul> <li>For devices that use low-bit ambient mode, the screen supports fewer bits for each color -in ambient mode, so you should disable anti-aliasing.</li> +in ambient mode, so you should disable anti-aliasing and bitmap filtering when the device switches +to ambient mode.</li> <li>For devices that require burn-in protection, avoid using large blocks of white pixels in ambient mode and do not place content within 10 pixels of the edge of the screen, since the system shifts the content periodically to avoid pixel burn-in.</li> @@ -385,7 +386,9 @@ system shifts the content periodically to avoid pixel burn-in.</li> <p>For more information about low-bit ambient mode and burn-in protection, see <a href="{@docRoot}design/wear/watchfaces.html#SpecialScreens">Optimize for Special -Screens</a>.</p> +Screens</a>. For more information on how to disable bitmap filtering, see +<a href="{@docRoot}training/wearables/watch-faces/performance.html#BitmapFiltering">Bitmap +Filtering</a>.</p> <h2 id="Modes">Respond to Changes Between Modes</h2> diff --git a/docs/html/training/wearables/watch-faces/performance.jd b/docs/html/training/wearables/watch-faces/performance.jd index 68438fe..118bc6a 100644 --- a/docs/html/training/wearables/watch-faces/performance.jd +++ b/docs/html/training/wearables/watch-faces/performance.jd @@ -99,7 +99,9 @@ enabled (right).</p> setFilterBitmap()} method. <a href="#fig2">Figure 2</a> shows a magnified view of a clock hand with and without bitmap filtering.</p> - +<p class="note"><strong>Note:</strong> In low-bit ambient mode, the system does not reliably +render the colors in the image for bitmap filtering to process successfully. When ambient mode is +active, disable bitmap filtering.</p> <h2 id="OutDrawing">Move Expensive Operations Outside the Drawing Method</h2> diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 1da198c..d3cb9b1 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -2120,9 +2120,9 @@ public class Paint { int contextLen = contextEnd - contextStart; char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, contextStart, contextEnd, buf, 0); - int result = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt); + int relPos = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt); TemporaryBuffer.recycle(buf); - return result; + return (relPos == -1) ? -1 : relPos + contextStart; } /** diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index a56e87e..28c26ff 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -173,7 +173,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { @Override public int getChangingConfigurations() { - return super.getChangingConfigurations() | mAnimatedVectorState.mChangingConfigurations; + return super.getChangingConfigurations() | mAnimatedVectorState.getChangingConfigurations(); } @Override diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index dc10a81..6fe6b56 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -454,7 +454,7 @@ public class BitmapDrawable extends Drawable { @Override public int getChangingConfigurations() { - return super.getChangingConfigurations() | mBitmapState.mChangingConfigurations; + return super.getChangingConfigurations() | mBitmapState.getChangingConfigurations(); } private boolean needMirroring() { @@ -834,7 +834,7 @@ public class BitmapDrawable extends Drawable { // Apply theme to contained color state list. if (state.mTint != null && state.mTint.canApplyTheme()) { - state.mTint.applyTheme(t); + state.mTint = state.mTint.obtainForTheme(t); } // Update local properties. @@ -882,7 +882,7 @@ public class BitmapDrawable extends Drawable { @Override public final ConstantState getConstantState() { - mBitmapState.mChangingConfigurations = getChangingConfigurations(); + mBitmapState.mChangingConfigurations |= getChangingConfigurations(); return mBitmapState; } @@ -950,7 +950,8 @@ public class BitmapDrawable extends Drawable { @Override public int getChangingConfigurations() { - return mChangingConfigurations; + return mChangingConfigurations + | (mTint != null ? mTint.getChangingConfigurations() : 0); } } diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java index f75ab36..8e91621 100644 --- a/graphics/java/android/graphics/drawable/ColorDrawable.java +++ b/graphics/java/android/graphics/drawable/ColorDrawable.java @@ -276,7 +276,7 @@ public class ColorDrawable extends Drawable { } if (state.mTint != null && state.mTint.canApplyTheme()) { - state.mTint.applyTheme(t); + state.mTint = state.mTint.obtainForTheme(t); } updateLocalState(t.getResources()); @@ -327,7 +327,8 @@ public class ColorDrawable extends Drawable { @Override public int getChangingConfigurations() { - return mChangingConfigurations; + return mChangingConfigurations + | (mTint != null ? mTint.getChangingConfigurations() : 0); } } diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 56876e9..e8b8c77 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -816,9 +816,12 @@ public abstract class Drawable { * returned. You can use the method {@link #resolveOpacity} to perform a * standard reduction of two opacities to the appropriate single output. * - * <p>Note that the returned value does <em>not</em> take into account a + * <p>Note that the returned value does not necessarily take into account a * custom alpha or color filter that has been applied by the client through - * the {@link #setAlpha} or {@link #setColorFilter} methods. + * the {@link #setAlpha} or {@link #setColorFilter} methods. Some subclasses, + * such as {@link BitmapDrawable}, {@link ColorDrawable}, and {@link GradientDrawable}, + * do account for the value of {@link #setAlpha}, but the general behavior is dependent + * upon the implementation of the subclass. * * @return int The opacity class of the Drawable. * diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index ddcb48b..b03fe3a 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -87,8 +87,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @Override public int getChangingConfigurations() { return super.getChangingConfigurations() - | mDrawableContainerState.mChangingConfigurations - | mDrawableContainerState.mChildrenChangingConfigurations; + | mDrawableContainerState.getChangingConfigurations(); } private boolean needsMirroring() { @@ -865,6 +864,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { for (int i = 0; i < N; i++) { if (drawables[i] != null && drawables[i].canApplyTheme()) { drawables[i].applyTheme(theme); + + // Update cached mask of child changing configurations. + mChildrenChangingConfigurations |= drawables[i].getChangingConfigurations(); } } } diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java index 1d6c60f..0da4275 100644 --- a/graphics/java/android/graphics/drawable/DrawableWrapper.java +++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java @@ -180,8 +180,7 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb @Override public int getChangingConfigurations() { return super.getChangingConfigurations() - | (mState != null ? mState.mChangingConfigurations : 0) - | mDrawable.getChangingConfigurations(); + | (mState != null ? mState.getChangingConfigurations() : 0); } @Override @@ -433,7 +432,7 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb @Override public int getChangingConfigurations() { - return mChangingConfigurations; + return mChangingConfigurations | mDrawableState.getChangingConfigurations(); } public boolean canConstantState() { diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index eff152c..4c2817c 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -491,19 +491,21 @@ public class GradientDrawable extends Drawable { } /** - * <p>Sets the colors used to draw the gradient. Each color is specified as an - * ARGB integer and the array must contain at least 2 colors.</p> - * <p><strong>Note</strong>: changing colors will affect all instances - * of a drawable loaded from a resource. It is recommended to invoke - * {@link #mutate()} before changing the colors.</p> - * - * @param colors 2 or more ARGB colors + * Sets the colors used to draw the gradient. + * <p> + * Each color is specified as an ARGB integer and the array must contain at + * least 2 colors. + * <p> + * <strong>Note</strong>: changing colors will affect all instances of a + * drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing the colors. * + * @param colors an array containing 2 or more ARGB colors * @see #mutate() * @see #setColor(int) */ public void setColors(@ColorInt int[] colors) { - mGradientState.setColors(colors); + mGradientState.setGradientColors(colors); mGradientIsDirty = true; invalidateSelf(); } @@ -568,7 +570,7 @@ public class GradientDrawable extends Drawable { mFillPaint.setAlpha(currFillAlpha); mFillPaint.setDither(st.mDither); mFillPaint.setColorFilter(colorFilter); - if (colorFilter != null && st.mColorStateList == null) { + if (colorFilter != null && st.mSolidColors == null) { mFillPaint.setColor(mAlpha << 24); } if (haveStroke) { @@ -715,7 +717,7 @@ public class GradientDrawable extends Drawable { * @see #setColors(int[]) */ public void setColor(@ColorInt int argb) { - mGradientState.setColorStateList(ColorStateList.valueOf(argb)); + mGradientState.setSolidColors(ColorStateList.valueOf(argb)); mFillPaint.setColor(argb); invalidateSelf(); } @@ -734,7 +736,7 @@ public class GradientDrawable extends Drawable { * @see #mutate() */ public void setColor(ColorStateList colorStateList) { - mGradientState.setColorStateList(colorStateList); + mGradientState.setSolidColors(colorStateList); final int color; if (colorStateList == null) { color = Color.TRANSPARENT; @@ -751,9 +753,9 @@ public class GradientDrawable extends Drawable { boolean invalidateSelf = false; final GradientState s = mGradientState; - final ColorStateList stateList = s.mColorStateList; - if (stateList != null) { - final int newColor = stateList.getColorForState(stateSet, 0); + final ColorStateList solidColors = s.mSolidColors; + if (solidColors != null) { + final int newColor = solidColors.getColorForState(stateSet, 0); final int oldColor = mFillPaint.getColor(); if (oldColor != newColor) { mFillPaint.setColor(newColor); @@ -763,12 +765,12 @@ public class GradientDrawable extends Drawable { final Paint strokePaint = mStrokePaint; if (strokePaint != null) { - final ColorStateList strokeStateList = s.mStrokeColorStateList; - if (strokeStateList != null) { - final int newStrokeColor = strokeStateList.getColorForState(stateSet, 0); - final int oldStrokeColor = strokePaint.getColor(); - if (oldStrokeColor != newStrokeColor) { - strokePaint.setColor(newStrokeColor); + final ColorStateList strokeColors = s.mStrokeColors; + if (strokeColors != null) { + final int newColor = strokeColors.getColorForState(stateSet, 0); + final int oldColor = strokePaint.getColor(); + if (oldColor != newColor) { + strokePaint.setColor(newColor); invalidateSelf = true; } } @@ -791,14 +793,14 @@ public class GradientDrawable extends Drawable { public boolean isStateful() { final GradientState s = mGradientState; return super.isStateful() - || (s.mColorStateList != null && s.mColorStateList.isStateful()) - || (s.mStrokeColorStateList != null && s.mStrokeColorStateList.isStateful()) + || (s.mSolidColors != null && s.mSolidColors.isStateful()) + || (s.mStrokeColors != null && s.mStrokeColors.isStateful()) || (s.mTint != null && s.mTint.isStateful()); } @Override public int getChangingConfigurations() { - return super.getChangingConfigurations() | mGradientState.mChangingConfigurations; + return super.getChangingConfigurations() | mGradientState.getChangingConfigurations(); } @Override @@ -899,10 +901,10 @@ public class GradientDrawable extends Drawable { mRect.set(bounds.left + inset, bounds.top + inset, bounds.right - inset, bounds.bottom - inset); - final int[] colors = st.mColors; - if (colors != null) { - RectF r = mRect; - float x0, x1, y0, y1; + final int[] gradientColors = st.mGradientColors; + if (gradientColors != null) { + final RectF r = mRect; + final float x0, x1, y0, y1; if (st.mGradient == LINEAR_GRADIENT) { final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f; @@ -942,7 +944,7 @@ public class GradientDrawable extends Drawable { } mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1, - colors, st.mPositions, Shader.TileMode.CLAMP)); + gradientColors, st.mPositions, Shader.TileMode.CLAMP)); } else if (st.mGradient == RADIAL_GRADIENT) { x0 = r.left + (r.right - r.left) * st.mCenterX; y0 = r.top + (r.bottom - r.top) * st.mCenterY; @@ -971,22 +973,22 @@ public class GradientDrawable extends Drawable { } mFillPaint.setShader(new RadialGradient( - x0, y0, radius, colors, null, Shader.TileMode.CLAMP)); + x0, y0, radius, gradientColors, null, Shader.TileMode.CLAMP)); } else if (st.mGradient == SWEEP_GRADIENT) { x0 = r.left + (r.right - r.left) * st.mCenterX; y0 = r.top + (r.bottom - r.top) * st.mCenterY; - int[] tempColors = colors; + int[] tempColors = gradientColors; float[] tempPositions = null; if (st.mUseLevel) { tempColors = st.mTempColors; - final int length = colors.length; + final int length = gradientColors.length; if (tempColors == null || tempColors.length != length + 1) { tempColors = st.mTempColors = new int[length + 1]; } - System.arraycopy(colors, 0, tempColors, 0, length); - tempColors[length] = colors[length - 1]; + System.arraycopy(gradientColors, 0, tempColors, 0, length); + tempColors[length] = gradientColors[length - 1]; tempPositions = st.mTempPositions; final float fraction = 1.0f / (length - 1); @@ -1006,7 +1008,7 @@ public class GradientDrawable extends Drawable { // If we don't have a solid color, the alpha channel must be // maxed out so that alpha modulation works correctly. - if (st.mColorStateList == null) { + if (st.mSolidColors == null) { mFillPaint.setColor(Color.BLACK); } } @@ -1044,15 +1046,15 @@ public class GradientDrawable extends Drawable { } if (state.mTint != null && state.mTint.canApplyTheme()) { - state.mTint.applyTheme(t); + state.mTint = state.mTint.obtainForTheme(t); } - if (state.mColorStateList != null && state.mColorStateList.canApplyTheme()) { - state.mColorStateList.applyTheme(t); + if (state.mSolidColors != null && state.mSolidColors.canApplyTheme()) { + state.mSolidColors = state.mSolidColors.obtainForTheme(t); } - if (state.mStrokeColorStateList != null && state.mStrokeColorStateList.canApplyTheme()) { - state.mStrokeColorStateList.applyTheme(t); + if (state.mStrokeColors != null && state.mStrokeColors.canApplyTheme()) { + state.mStrokeColors = state.mStrokeColors.obtainForTheme(t); } applyThemeChildElements(t); @@ -1288,7 +1290,7 @@ public class GradientDrawable extends Drawable { ColorStateList colorStateList = a.getColorStateList( R.styleable.GradientDrawableStroke_color); if (colorStateList == null) { - colorStateList = st.mStrokeColorStateList; + colorStateList = st.mStrokeColors; } if (dashWidth != 0.0f) { @@ -1346,10 +1348,10 @@ public class GradientDrawable extends Drawable { R.styleable.GradientDrawableGradient_endColor, 0); if (hasCenterColor) { - st.mColors = new int[3]; - st.mColors[0] = startColor; - st.mColors[1] = centerColor; - st.mColors[2] = endColor; + st.mGradientColors = new int[3]; + st.mGradientColors[0] = startColor; + st.mGradientColors[1] = centerColor; + st.mGradientColors[2] = endColor; st.mPositions = new float[3]; st.mPositions[0] = 0.0f; @@ -1357,9 +1359,9 @@ public class GradientDrawable extends Drawable { st.mPositions[1] = st.mCenterX != 0.5f ? st.mCenterX : st.mCenterY; st.mPositions[2] = 1f; } else { - st.mColors = new int[2]; - st.mColors[0] = startColor; - st.mColors[1] = endColor; + st.mGradientColors = new int[2]; + st.mGradientColors[0] = startColor; + st.mGradientColors[1] = endColor; } if (st.mGradient == LINEAR_GRADIENT) { @@ -1552,9 +1554,9 @@ public class GradientDrawable extends Drawable { public int mGradient = LINEAR_GRADIENT; public int mAngle = 0; public Orientation mOrientation; - public ColorStateList mColorStateList; - public ColorStateList mStrokeColorStateList; - public int[] mColors; + public ColorStateList mSolidColors; + public ColorStateList mStrokeColors; + public int[] mGradientColors; public int[] mTempColors; // no need to copy public float[] mTempPositions; // no need to copy public float[] mPositions; @@ -1593,9 +1595,9 @@ public class GradientDrawable extends Drawable { int[] mAttrCorners; int[] mAttrPadding; - public GradientState(Orientation orientation, int[] colors) { + public GradientState(Orientation orientation, int[] gradientColors) { mOrientation = orientation; - setColors(colors); + setGradientColors(gradientColors); } public GradientState(GradientState state) { @@ -1604,14 +1606,14 @@ public class GradientDrawable extends Drawable { mGradient = state.mGradient; mAngle = state.mAngle; mOrientation = state.mOrientation; - mColorStateList = state.mColorStateList; - if (state.mColors != null) { - mColors = state.mColors.clone(); + mSolidColors = state.mSolidColors; + if (state.mGradientColors != null) { + mGradientColors = state.mGradientColors.clone(); } if (state.mPositions != null) { mPositions = state.mPositions.clone(); } - mStrokeColorStateList = state.mStrokeColorStateList; + mStrokeColors = state.mStrokeColors; mStrokeWidth = state.mStrokeWidth; mStrokeDashWidth = state.mStrokeDashWidth; mStrokeDashGap = state.mStrokeDashGap; @@ -1655,8 +1657,8 @@ public class GradientDrawable extends Drawable { || mAttrSolid != null || mAttrStroke != null || mAttrCorners != null || mAttrPadding != null || (mTint != null && mTint.canApplyTheme()) - || (mStrokeColorStateList != null && mStrokeColorStateList.canApplyTheme()) - || (mColorStateList != null && mColorStateList.canApplyTheme()) + || (mStrokeColors != null && mStrokeColors.canApplyTheme()) + || (mSolidColors != null && mSolidColors.canApplyTheme()) || super.canApplyTheme(); } @@ -1672,7 +1674,10 @@ public class GradientDrawable extends Drawable { @Override public int getChangingConfigurations() { - return mChangingConfigurations; + return mChangingConfigurations + | (mStrokeColors != null ? mStrokeColors.getChangingConfigurations() : 0) + | (mSolidColors != null ? mSolidColors.getChangingConfigurations() : 0) + | (mTint != null ? mTint.getChangingConfigurations() : 0); } public void setShape(int shape) { @@ -1689,15 +1694,15 @@ public class GradientDrawable extends Drawable { mCenterY = y; } - public void setColors(int[] colors) { - mColors = colors; - mColorStateList = null; + public void setGradientColors(int[] colors) { + mGradientColors = colors; + mSolidColors = null; computeOpacity(); } - public void setColorStateList(ColorStateList colorStateList) { - mColors = null; - mColorStateList = colorStateList; + public void setSolidColors(ColorStateList colors) { + mGradientColors = null; + mSolidColors = colors; computeOpacity(); } @@ -1705,16 +1710,16 @@ public class GradientDrawable extends Drawable { mOpaqueOverBounds = false; mOpaqueOverShape = false; - if (mColors != null) { - for (int i = 0; i < mColors.length; i++) { - if (!isOpaque(mColors[i])) { + if (mGradientColors != null) { + for (int i = 0; i < mGradientColors.length; i++) { + if (!isOpaque(mGradientColors[i])) { return; } } } // An unfilled shape is not opaque over bounds or shape - if (mColors == null && mColorStateList == null) { + if (mGradientColors == null && mSolidColors == null) { return; } @@ -1726,10 +1731,9 @@ public class GradientDrawable extends Drawable { && mRadiusArray == null; } - public void setStroke( - int width, ColorStateList colorStateList, float dashWidth, float dashGap) { + public void setStroke(int width, ColorStateList colors, float dashWidth, float dashGap) { mStrokeWidth = width; - mStrokeColorStateList = colorStateList; + mStrokeColors = colors; mStrokeDashWidth = dashWidth; mStrokeDashGap = dashGap; computeOpacity(); @@ -1781,11 +1785,11 @@ public class GradientDrawable extends Drawable { private void updateLocalState(Resources res) { final GradientState state = mGradientState; - if (state.mColorStateList != null) { + if (state.mSolidColors != null) { final int[] currentState = getState(); - final int stateColor = state.mColorStateList.getColorForState(currentState, 0); + final int stateColor = state.mSolidColors.getColorForState(currentState, 0); mFillPaint.setColor(stateColor); - } else if (state.mColors == null) { + } else if (state.mGradientColors == null) { // If we don't have a solid color and we don't have a gradient, // the app is stroking the shape, set the color to the default // value of state.mSolidColor @@ -1802,9 +1806,9 @@ public class GradientDrawable extends Drawable { mStrokePaint.setStyle(Paint.Style.STROKE); mStrokePaint.setStrokeWidth(state.mStrokeWidth); - if (state.mStrokeColorStateList != null) { + if (state.mStrokeColors != null) { final int[] currentState = getState(); - final int strokeStateColor = state.mStrokeColorStateList.getColorForState( + final int strokeStateColor = state.mStrokeColors.getColorForState( currentState, 0); mStrokePaint.setColor(strokeStateColor); } diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java index 97f7105..e1ebdbb 100644 --- a/graphics/java/android/graphics/drawable/InsetDrawable.java +++ b/graphics/java/android/graphics/drawable/InsetDrawable.java @@ -242,26 +242,12 @@ public class InsetDrawable extends DrawableWrapper { } @Override - public ConstantState getConstantState() { - if (mState.canConstantState()) { - mState.mChangingConfigurations = getChangingConfigurations(); - return mState; - } - return null; - } - - @Override DrawableWrapperState mutateConstantState() { mState = new InsetState(mState); return mState; } static final class InsetState extends DrawableWrapper.DrawableWrapperState { - int[] mThemeAttrs; - int mChangingConfigurations; - - ConstantState mDrawableState; - int mInsetLeft = 0; int mInsetTop = 0; int mInsetRight = 0; diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 30fbe16..a2f71e5 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -296,6 +296,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { final Drawable d = layer.mDrawable; if (d.canApplyTheme()) { d.applyTheme(t); + + // Update cached mask of child changing configurations. + state.mChildrenChangingConfigurations |= d.getChangingConfigurations(); } } @@ -882,9 +885,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @Override public int getChangingConfigurations() { - return super.getChangingConfigurations() - | mLayerState.mChangingConfigurations - | mLayerState.mChildrenChangingConfigurations; + return super.getChangingConfigurations() | mLayerState.getChangingConfigurations(); } @Override @@ -1493,7 +1494,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @Override public int getChangingConfigurations() { - return mChangingConfigurations; + return mChangingConfigurations + | mChildrenChangingConfigurations; } public final int getOpacity() { diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 487162e..9bf33cf 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -266,7 +266,7 @@ public class NinePatchDrawable extends Drawable { @Override public int getChangingConfigurations() { - return super.getChangingConfigurations() | mNinePatchState.mChangingConfigurations; + return super.getChangingConfigurations() | mNinePatchState.getChangingConfigurations(); } @Override @@ -498,7 +498,7 @@ public class NinePatchDrawable extends Drawable { } if (state.mTint != null && state.mTint.canApplyTheme()) { - state.mTint.applyTheme(t); + state.mTint = state.mTint.obtainForTheme(t); } updateLocalState(t.getResources()); @@ -680,7 +680,8 @@ public class NinePatchDrawable extends Drawable { @Override public int getChangingConfigurations() { - return mChangingConfigurations; + return mChangingConfigurations + | (mTint != null ? mTint.getChangingConfigurations() : 0); } } diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 23f93fd..6731a17 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -479,7 +479,7 @@ public class RippleDrawable extends LayerDrawable { } if (state.mColor != null && state.mColor.canApplyTheme()) { - state.mColor.applyTheme(t); + state.mColor = state.mColor.obtainForTheme(t); } updateLocalState(); @@ -955,6 +955,12 @@ public class RippleDrawable extends LayerDrawable { public Drawable newDrawable(Resources res) { return new RippleDrawable(this, res); } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() + | (mColor != null ? mColor.getChangingConfigurations() : 0); + } } private RippleDrawable(RippleState state, Resources res) { diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java index 15e16f1..036a078 100644 --- a/graphics/java/android/graphics/drawable/RotateDrawable.java +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -89,9 +89,6 @@ public class RotateDrawable extends DrawableWrapper { final RotateState state = mState; - // Account for any configuration changes. - state.mChangingConfigurations |= a.getChangingConfigurations(); - // Extract the theme attributes, if any. state.mThemeAttrs = a.extractThemeAttrs(); diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java index fc88c15..532c51c 100644 --- a/graphics/java/android/graphics/drawable/ShapeDrawable.java +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -261,8 +261,7 @@ public class ShapeDrawable extends Drawable { @Override public int getChangingConfigurations() { - return super.getChangingConfigurations() - | mShapeState.mChangingConfigurations; + return super.getChangingConfigurations() | mShapeState.getChangingConfigurations(); } /** @@ -427,7 +426,7 @@ public class ShapeDrawable extends Drawable { // Apply theme to contained color state list. if (state.mTint != null && state.mTint.canApplyTheme()) { - state.mTint.applyTheme(t); + state.mTint = state.mTint.obtainForTheme(t); } // Update local properties. @@ -578,7 +577,8 @@ public class ShapeDrawable extends Drawable { @Override public int getChangingConfigurations() { - return mChangingConfigurations; + return mChangingConfigurations + | (mTint != null ? mTint.getChangingConfigurations() : 0); } } diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index b827682..a542feb 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -389,7 +389,7 @@ public class VectorDrawable extends Drawable { // Apply theme to contained color state list. if (state.mTint != null && state.mTint.canApplyTheme()) { - state.mTint.applyTheme(t); + state.mTint = state.mTint.obtainForTheme(t); } final VPathRenderer path = state.mVPathRenderer; @@ -625,7 +625,7 @@ public class VectorDrawable extends Drawable { @Override public int getChangingConfigurations() { - return super.getChangingConfigurations() | mVectorState.mChangingConfigurations; + return super.getChangingConfigurations() | mVectorState.getChangingConfigurations(); } void setAllowCaching(boolean allowCaching) { @@ -784,7 +784,8 @@ public class VectorDrawable extends Drawable { @Override public int getChangingConfigurations() { - return mChangingConfigurations; + return mChangingConfigurations + | (mTint != null ? mTint.getChangingConfigurations() : 0); } } diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index 4650d27..0bd1dbd 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -540,21 +540,23 @@ public class AndroidKeyStore extends KeyStoreSpi { KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster( params.getUserAuthenticators())); } + if (params.isInvalidatedOnNewFingerprintEnrolled()) { + // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports + // that. + } if (params.getUserAuthenticationValidityDurationSeconds() != null) { args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, params.getUserAuthenticationValidityDurationSeconds()); } - if (params.getKeyValidityStart() != null) { - args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart()); - } - if (params.getKeyValidityForOriginationEnd() != null) { - args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, - params.getKeyValidityForOriginationEnd()); - } - if (params.getKeyValidityForConsumptionEnd() != null) { - args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, - params.getKeyValidityForConsumptionEnd()); - } + args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, + (params.getKeyValidityStart() != null) + ? params.getKeyValidityStart() : new Date(0)); + args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, + (params.getKeyValidityForOriginationEnd() != null) + ? params.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE)); + args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, + (params.getKeyValidityForConsumptionEnd() != null) + ? params.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE)); // TODO: Remove this once keymaster does not require us to specify the size of imported key. args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8); diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java index 9122d8e..02b0816 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -59,6 +59,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private final Integer mMaxUsesPerBoot; private final Set<Integer> mUserAuthenticators; private final Integer mUserAuthenticationValidityDurationSeconds; + private final boolean mInvalidatedOnNewFingerprintEnrolled; private KeyGeneratorSpec( Context context, @@ -74,7 +75,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { Integer minSecondsBetweenOperations, Integer maxUsesPerBoot, Set<Integer> userAuthenticators, - Integer userAuthenticationValidityDurationSeconds) { + Integer userAuthenticationValidityDurationSeconds, + boolean invalidatedOnNewFingerprintEnrolled) { if (context == null) { throw new IllegalArgumentException("context == null"); } else if (TextUtils.isEmpty(keyStoreAlias)) { @@ -101,6 +103,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { ? new HashSet<Integer>(userAuthenticators) : Collections.<Integer>emptySet(); mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; + mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled; } /** @@ -239,6 +242,19 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** + * Returns {@code true} if this key must be permanently invalidated once a new fingerprint is + * enrolled. This constraint only has effect if fingerprint reader is one of the user + * authenticators protecting access to this key. + * + * @see #getUserAuthenticators() + * + * @hide + */ + public boolean isInvalidatedOnNewFingerprintEnrolled() { + return mInvalidatedOnNewFingerprintEnrolled; + } + + /** * Returns {@code true} if the key must be encrypted in the {@link java.security.KeyStore}. */ public boolean isEncryptionRequired() { @@ -260,6 +276,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private Integer mMaxUsesPerBoot; private Set<Integer> mUserAuthenticators; private Integer mUserAuthenticationValidityDurationSeconds; + private boolean mInvalidatedOnNewFingerprintEnrolled; /** * Creates a new instance of the {@code Builder} with the given {@code context}. The @@ -473,6 +490,22 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** + * Sets whether this key must be invalidated (permanently) once a new fingerprint is + * enrolled. This only has effect if fingerprint reader is one of the user authenticators + * protecting access to the key. + * + * <p>By default, enrolling a new fingerprint does not invalidate the key. + * + * @see #setUserAuthenticators(Set) + * + * @hide + */ + public Builder setInvalidatedOnNewFingerprintEnrolled(boolean invalidated) { + mInvalidatedOnNewFingerprintEnrolled = invalidated; + return this; + } + + /** * Builds a new instance instance of {@code KeyGeneratorSpec}. * * @throws IllegalArgumentException if a required field is missing or violates a constraint. @@ -481,7 +514,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { return new KeyGeneratorSpec(mContext, mKeystoreAlias, mFlags, mKeySize, mKeyValidityStart, mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, mPadding, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot, - mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); + mUserAuthenticators, mUserAuthenticationValidityDurationSeconds, + mInvalidatedOnNewFingerprintEnrolled); } } } diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java index c27ccb1..75034d1 100644 --- a/keystore/java/android/security/KeyStoreKeyConstraints.java +++ b/keystore/java/android/security/KeyStoreKeyConstraints.java @@ -537,6 +537,9 @@ public abstract class KeyStoreKeyConstraints { /** Lock screen. */ public static final int LOCK_SCREEN = 1; + /** Fingerprint reader/sensor. */ + public static final int FINGERPRINT_READER = 1 << 1; + /** * @hide */ @@ -544,6 +547,8 @@ public abstract class KeyStoreKeyConstraints { switch (userAuthenticator) { case LOCK_SCREEN: return LOCK_SCREEN; + case FINGERPRINT_READER: + return FINGERPRINT_READER; default: throw new IllegalArgumentException( "Unknown user authenticator: " + userAuthenticator); @@ -557,6 +562,8 @@ public abstract class KeyStoreKeyConstraints { switch (userAuthenticator) { case LOCK_SCREEN: return LOCK_SCREEN; + case FINGERPRINT_READER: + return FINGERPRINT_READER; default: throw new IllegalArgumentException( "Unknown user authenticator: " + userAuthenticator); @@ -600,6 +607,8 @@ public abstract class KeyStoreKeyConstraints { switch (userAuthenticator) { case LOCK_SCREEN: return "LOCK_SCREEN"; + case FINGERPRINT_READER: + return "FINGERPRINT_READER"; default: throw new IllegalArgumentException( "Unknown user authenticator: " + userAuthenticator); diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java index 09fee1b..8b0a3e9 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -23,6 +23,7 @@ import android.security.keymaster.KeymasterDefs; import java.security.InvalidAlgorithmParameterException; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; +import java.util.Date; import javax.crypto.KeyGeneratorSpi; import javax.crypto.SecretKey; @@ -140,21 +141,23 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster( spec.getUserAuthenticators())); } + if (spec.isInvalidatedOnNewFingerprintEnrolled()) { + // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports + // that. + } if (spec.getUserAuthenticationValidityDurationSeconds() != null) { args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, spec.getUserAuthenticationValidityDurationSeconds()); } - if (spec.getKeyValidityStart() != null) { - args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()); - } - if (spec.getKeyValidityForOriginationEnd() != null) { - args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, - spec.getKeyValidityForOriginationEnd()); - } - if (spec.getKeyValidityForConsumptionEnd() != null) { - args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, - spec.getKeyValidityForConsumptionEnd()); - } + args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, + (spec.getKeyValidityStart() != null) + ? spec.getKeyValidityStart() : new Date(0)); + args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, + (spec.getKeyValidityForOriginationEnd() != null) + ? spec.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE)); + args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, + (spec.getKeyValidityForConsumptionEnd() != null) + ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE)); if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) { diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java index ddeefbd..e5e5acc 100644 --- a/keystore/java/android/security/KeyStoreKeySpec.java +++ b/keystore/java/android/security/KeyStoreKeySpec.java @@ -45,6 +45,7 @@ public class KeyStoreKeySpec implements KeySpec { private final Set<Integer> mUserAuthenticators; private final Set<Integer> mTeeBackedUserAuthenticators; private final Integer mUserAuthenticationValidityDurationSeconds; + private final boolean mInvalidatedOnNewFingerprintEnrolled; /** @@ -63,7 +64,8 @@ public class KeyStoreKeySpec implements KeySpec { Integer maxUsesPerBoot, Set<Integer> userAuthenticators, Set<Integer> teeBackedUserAuthenticators, - Integer userAuthenticationValidityDurationSeconds) { + Integer userAuthenticationValidityDurationSeconds, + boolean invalidatedOnNewFingerprintEnrolled) { mKeystoreAlias = keystoreKeyAlias; mOrigin = origin; mKeySize = keySize; @@ -84,6 +86,7 @@ public class KeyStoreKeySpec implements KeySpec { ? new HashSet<Integer>(teeBackedUserAuthenticators) : Collections.<Integer>emptySet(); mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; + mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled; } /** @@ -223,4 +226,15 @@ public class KeyStoreKeySpec implements KeySpec { public Integer getUserAuthenticationValidityDurationSeconds() { return mUserAuthenticationValidityDurationSeconds; } + + /** + * Returns {@code true} if this key will be permanently invalidated once a new fingerprint is + * enrolled. This constraint only has effect if fingerprint reader is one of the user + * authenticators protecting access to this key. + * + * @see #getUserAuthenticators() + */ + public boolean isInvalidatedOnNewFingerprintEnrolled() { + return mInvalidatedOnNewFingerprintEnrolled; + } } diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index 2428c2a..b1b638f 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -59,6 +59,7 @@ public final class KeyStoreParameter implements ProtectionParameter { private final Integer mMaxUsesPerBoot; private final Set<Integer> mUserAuthenticators; private final Integer mUserAuthenticationValidityDurationSeconds; + private final boolean mInvalidatedOnNewFingerprintEnrolled; private KeyStoreParameter(int flags, Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, @@ -70,7 +71,8 @@ public final class KeyStoreParameter implements ProtectionParameter { Integer minSecondsBetweenOperations, Integer maxUsesPerBoot, Set<Integer> userAuthenticators, - Integer userAuthenticationValidityDurationSeconds) { + Integer userAuthenticationValidityDurationSeconds, + boolean invalidatedOnNewFingerprintEnrolled) { if ((userAuthenticationValidityDurationSeconds != null) && (userAuthenticationValidityDurationSeconds < 0)) { throw new IllegalArgumentException( @@ -92,6 +94,7 @@ public final class KeyStoreParameter implements ProtectionParameter { ? new HashSet<Integer>(userAuthenticators) : Collections.<Integer>emptySet(); mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; + mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled; } /** @@ -245,6 +248,19 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** + * Returns {@code true} if this key must be permanently invalidated once a new fingerprint is + * enrolled. This constraint only has effect if fingerprint reader is one of the user + * authenticators protecting access to this key. + * + * @see #getUserAuthenticators() + * + * @hide + */ + public boolean isInvalidatedOnNewFingerprintEnrolled() { + return mInvalidatedOnNewFingerprintEnrolled; + } + + /** * Builder class for {@link KeyStoreParameter} objects. * <p> * This will build protection parameters for use with the @@ -275,6 +291,7 @@ public final class KeyStoreParameter implements ProtectionParameter { private Integer mMaxUsesPerBoot; private Set<Integer> mUserAuthenticators; private Integer mUserAuthenticationValidityDurationSeconds; + private boolean mInvalidatedOnNewFingerprintEnrolled; /** * Creates a new instance of the {@code Builder} with the given @@ -496,6 +513,22 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** + * Sets whether this key must be invalidated (permanently) whenever a new fingerprint is + * enrolled. This only has effect if fingerprint reader is one of the user authenticators + * protecting access to the key. + * + * <p>By default, enrolling a new fingerprint does not invalidate the key. + * + * @see #setUserAuthenticators(Set) + * + * @hide + */ + public Builder setInvalidatedOnNewFingerprintEnrolled(boolean invalidated) { + mInvalidatedOnNewFingerprintEnrolled = invalidated; + return this; + } + + /** * Builds the instance of the {@code KeyStoreParameter}. * * @throws IllegalArgumentException if a required field is missing @@ -506,7 +539,8 @@ public final class KeyStoreParameter implements ProtectionParameter { mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, mAlgorithm, mPadding, mDigest, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot, mUserAuthenticators, - mUserAuthenticationValidityDurationSeconds); + mUserAuthenticationValidityDurationSeconds, + mInvalidatedOnNewFingerprintEnrolled); } } } diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java index 88255a8..c205d9d 100644 --- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java @@ -22,6 +22,7 @@ import android.security.keymaster.KeymasterDefs; import java.security.InvalidKeyException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; +import java.util.Date; import java.util.Set; import javax.crypto.SecretKey; @@ -112,6 +113,24 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { throw new InvalidKeySpecException("Unsupported key characteristic", e); } + Date keyValidityStart = + KeymasterUtils.getDate(keyCharacteristics, KeymasterDefs.KM_TAG_ACTIVE_DATETIME); + if ((keyValidityStart != null) && (keyValidityStart.getTime() <= 0)) { + keyValidityStart = null; + } + Date keyValidityForOriginationEnd = KeymasterUtils.getDate(keyCharacteristics, + KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); + if ((keyValidityForOriginationEnd != null) + && (keyValidityForOriginationEnd.getTime() == Long.MAX_VALUE)) { + keyValidityForOriginationEnd = null; + } + Date keyValidityForConsumptionEnd = KeymasterUtils.getDate(keyCharacteristics, + KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); + if ((keyValidityForConsumptionEnd != null) + && (keyValidityForConsumptionEnd.getTime() == Long.MAX_VALUE)) { + keyValidityForConsumptionEnd = null; + } + int swEnforcedUserAuthenticatorIds = keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); int hwEnforcedUserAuthenticatorIds = @@ -123,14 +142,15 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster( hwEnforcedUserAuthenticatorIds); + // TODO: Populate the value below from key characteristics once Keymaster is ready. + boolean invalidatedOnNewFingerprintEnrolled = false; + return new KeyStoreKeySpec(entryAlias, origin, keySize, - KeymasterUtils.getDate(keyCharacteristics, KeymasterDefs.KM_TAG_ACTIVE_DATETIME), - KeymasterUtils.getDate(keyCharacteristics, - KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME), - KeymasterUtils.getDate(keyCharacteristics, - KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME), + keyValidityStart, + keyValidityForOriginationEnd, + keyValidityForConsumptionEnd, purposes, algorithm, padding, @@ -141,7 +161,8 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT), userAuthenticators, teeBackedUserAuthenticators, - KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT)); + KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT), + invalidatedOnNewFingerprintEnrolled); } @Override diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java index 2645cf4..c3092d5 100644 --- a/keystore/java/android/security/KeymasterUtils.java +++ b/keystore/java/android/security/KeymasterUtils.java @@ -46,6 +46,9 @@ public abstract class KeymasterUtils { switch (e.getErrorCode()) { case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED: return new UserNotAuthenticatedException(); + // TODO: Handle TBD Keymaster error code "invalid key: new fingerprint enrolled" + // case KeymasterDefs.KM_ERROR_TBD + // return new NewFingerprintEnrolledException(); default: return new CryptoOperationException("Crypto operation failed", e); } diff --git a/keystore/java/android/security/NewFingerprintEnrolledException.java b/keystore/java/android/security/NewFingerprintEnrolledException.java new file mode 100644 index 0000000..6da4a2a --- /dev/null +++ b/keystore/java/android/security/NewFingerprintEnrolledException.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 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.security; + +/** + * Indicates that a cryptographic operation could not be performed because the key used by the + * operation is permanently invalid because a new fingerprint was enrolled. + * + * @hide + */ +public class NewFingerprintEnrolledException extends CryptoOperationException { + + /** + * Constructs a new {@code NewFingerprintEnrolledException} without detail message and cause. + */ + public NewFingerprintEnrolledException() { + super("Invalid key: new fingerprint enrolled"); + } + + /** + * Constructs a new {@code NewFingerprintEnrolledException} with the provided detail message and + * no cause. + */ + public NewFingerprintEnrolledException(String message) { + super(message); + } +} diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java index c05fd77..c49e8c2 100644 --- a/media/java/android/media/AudioPortEventHandler.java +++ b/media/java/android/media/AudioPortEventHandler.java @@ -40,6 +40,12 @@ class AudioPortEventHandler { private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3; private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4; + /** + * Accessed by native methods: JNI Callback context. + */ + @SuppressWarnings("unused") + private long mJniCallback; + void init() { synchronized (this) { if (mHandler != null) { @@ -63,9 +69,6 @@ class AudioPortEventHandler { listeners = mListeners; } } - if (listeners.isEmpty()) { - return; - } // reset audio port cache if the event corresponds to a change coming // from audio policy service or if mediaserver process died. if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED || @@ -73,6 +76,11 @@ class AudioPortEventHandler { msg.what == AUDIOPORT_EVENT_SERVICE_DIED) { AudioManager.resetAudioPortGeneration(); } + + if (listeners.isEmpty()) { + return; + } + ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) { diff --git a/packages/Keyguard/Android.mk b/packages/Keyguard/Android.mk index 96ed2e7..9083212 100644 --- a/packages/Keyguard/Android.mk +++ b/packages/Keyguard/Android.mk @@ -22,6 +22,8 @@ LOCAL_MODULE := Keyguard LOCAL_CERTIFICATE := platform +LOCAL_JAVA_LIBRARIES := SettingsLib + LOCAL_PRIVILEGED_MODULE := true LOCAL_PROGUARD_FLAG_FILES := proguard.flags @@ -30,4 +32,4 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res include $(BUILD_STATIC_JAVA_LIBRARY) -#include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file +#include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml index 5047330..9469ee7 100644 --- a/packages/Keyguard/res/values/strings.xml +++ b/packages/Keyguard/res/values/strings.xml @@ -300,4 +300,7 @@ <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">Switch input method button.</string> + <!-- Description of airplane mode --> + <string name="airplane_mode">Airplane mode</string> + </resources> diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java index 7d0b81d..4fbcc1e 100644 --- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java +++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java @@ -35,6 +35,7 @@ import android.widget.TextView; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.TelephonyIntents; +import com.android.settingslib.WirelessUtils; public class CarrierText extends TextView { private static final boolean DEBUG = KeyguardConstants.DEBUG; @@ -146,6 +147,9 @@ public class CarrierText extends TextView { getContext().getText(R.string.keyguard_missing_sim_message_short), text); } } + if (WirelessUtils.isAirplaneModeOn(mContext)) { + displayText = getContext().getString(R.string.airplane_mode); + } setText(displayText); } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index c9805ae..82dec30 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -53,10 +53,10 @@ import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; -import android.service.fingerprint.FingerprintManager; -import android.service.fingerprint.FingerprintManager.AuthenticationCallback; -import android.service.fingerprint.FingerprintUtils; -import android.service.fingerprint.FingerprintManager.AuthenticationResult; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; +import android.hardware.fingerprint.FingerprintUtils; +import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index c2462e0..8d2562d 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -19,7 +19,7 @@ import android.app.admin.DevicePolicyManager; import android.graphics.Bitmap; import android.media.AudioManager; import android.os.SystemClock; -import android.service.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintManager; import android.telephony.TelephonyManager; import android.view.WindowManagerPolicy; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index e1cb878..64b4452 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -104,6 +104,7 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe // See mConnectAttempted private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000; + private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000; /** Auto-connect after pairing only if locally initiated. */ private boolean mConnectAfterPairing; @@ -525,9 +526,11 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe */ void onUuidChanged() { updateProfiles(); + ParcelUuid[] uuids = mDevice.getUuids(); + long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT; if (DEBUG) { - Log.e(TAG, "onUuidChanged: Time since last connect" + Log.d(TAG, "onUuidChanged: Time since last connect" + (SystemClock.elapsedRealtime() - mConnectAttempted)); } @@ -535,9 +538,11 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe * If a connect was attempted earlier without any UUID, we will do the * connect now. */ + if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) { + timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT; + } if (!mProfiles.isEmpty() - && (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock - .elapsedRealtime()) { + && (mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) { connectWithoutResettingTimer(false); } dispatchAttributesChanged(); diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 2bd3f08..1c8142f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -100,9 +100,6 @@ <!-- Name of the button that links to the Wifi settings screen. [CHAR LIMIT=NONE] --> <string name="status_bar_settings_wifi_button">Wi-Fi</string> - <!-- Label in the system panel for airplane mode (all radios are turned off)[CHAR LIMIT=30] --> - <string name="status_bar_settings_airplane">Airplane mode</string> - <!-- Label in system panel saying the device will use the orientation sensor to rotate [CHAR LIMIT=30] --> <string name="status_bar_settings_auto_rotation">Auto-rotate screen</string> @@ -566,8 +563,6 @@ <!-- Textual description of Ethernet connections --> <string name="ethernet_label">Ethernet</string> - <!-- QuickSettings: Airplane mode [CHAR LIMIT=NONE] --> - <string name="quick_settings_airplane_mode_label">Airplane mode</string> <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] --> <string name="quick_settings_dnd_label">Do not disturb</string> <!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 2dd02a5..2bc31fc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -72,7 +72,7 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { final boolean airplaneMode = value != 0; state.value = airplaneMode; state.visible = true; - state.label = mContext.getString(R.string.quick_settings_airplane_mode_label); + state.label = mContext.getString(R.string.airplane_mode); if (airplaneMode) { state.icon = mEnable; state.contentDescription = mContext.getString( diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java index e6dc1c7..f106667 100644 --- a/services/core/java/com/android/server/AssetAtlasService.java +++ b/services/core/java/com/android/server/AssetAtlasService.java @@ -403,13 +403,13 @@ public class AssetAtlasService extends IAssetAtlas.Stub { if (cpuCount == 1) { new ComputeWorker(MIN_SIZE, MAX_SIZE, STEP, bitmaps, pixelCount, results, null).run(); } else { - int start = MIN_SIZE; - int end = MAX_SIZE - (cpuCount - 1) * STEP; + int start = MIN_SIZE + (cpuCount - 1) * STEP; + int end = MAX_SIZE; int step = STEP * cpuCount; final CountDownLatch signal = new CountDownLatch(cpuCount); - for (int i = 0; i < cpuCount; i++, start += STEP, end += STEP) { + for (int i = 0; i < cpuCount; i++, start -= STEP, end -= STEP) { ComputeWorker worker = new ComputeWorker(start, end, step, bitmaps, pixelCount, results, signal); new Thread(worker, "Atlas Worker #" + (i + 1)).start(); @@ -435,7 +435,8 @@ public class AssetAtlasService extends IAssetAtlas.Stub { if (DEBUG_ATLAS) { float delay = (System.nanoTime() - begin) / 1000.0f / 1000.0f / 1000.0f; - Log.d(LOG_TAG, String.format("Found best atlas configuration in %.2fs", delay)); + Log.d(LOG_TAG, String.format("Found best atlas configuration (out of %d) in %.2fs", + results.size(), delay)); } WorkerResult result = results.get(0); @@ -696,8 +697,8 @@ public class AssetAtlasService extends IAssetAtlas.Stub { Atlas.Entry entry = new Atlas.Entry(); for (Atlas.Type type : Atlas.Type.values()) { - for (int width = mStart; width < mEnd; width += mStep) { - for (int height = MIN_SIZE; height < MAX_SIZE; height += STEP) { + for (int width = mEnd; width > mStart; width -= mStep) { + for (int height = MAX_SIZE; height > MIN_SIZE; height -= STEP) { // If the atlas is not big enough, skip it if (width * height <= mThreshold) continue; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5858477..b0b410b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4194,17 +4194,13 @@ public final class ActivityManagerService extends ActivityManagerNative finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info); } - if (!restarting) { - if (!mStackSupervisor.resumeTopActivitiesLocked()) { - // If there was nothing to resume, and we are not already - // restarting this process, but there is a visible activity that - // is hosted by the process... then make sure all visible - // activities are running, taking care of restarting this - // process. - if (hasVisibleActivities) { - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0); - } - } + if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) { + // If there was nothing to resume, and we are not already + // restarting this process, but there is a visible activity that + // is hosted by the process... then make sure all visible + // activities are running, taking care of restarting this + // process. + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0); } } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index a8deea3..8299be8 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3496,6 +3496,9 @@ final class ActivityStack { if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Record #" + i + " " + r + ": app=" + r.app); if (r.app == app) { + if (r.visible) { + hasVisibleActivities = true; + } final boolean remove; if ((!r.haveState && !r.stateNotNeeded) || r.finishing) { // Don't currently have state for the activity, or @@ -3535,9 +3538,6 @@ final class ActivityStack { // it can be restarted later when needed. if (DEBUG_ALL) Slog.v( TAG, "Keeping entry, setting app to null"); - if (r.visible) { - hasVisibleActivities = true; - } if (DEBUG_APP) Slog.v(TAG, "Clearing app during removeHistory for activity " + r); r.app = null; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 16c88d3..669dff8 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3509,7 +3509,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0)); } - public void handleDisplayAddedLocked(int displayId) { + private void handleDisplayAdded(int displayId) { boolean newDisplay; synchronized (mService) { newDisplay = mActivityDisplays.get(displayId) == null; @@ -3527,7 +3527,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - public void handleDisplayRemovedLocked(int displayId) { + private void handleDisplayRemoved(int displayId) { synchronized (mService) { ActivityDisplay activityDisplay = mActivityDisplays.get(displayId); if (activityDisplay != null) { @@ -3541,7 +3541,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mWindowManager.onDisplayRemoved(displayId); } - public void handleDisplayChangedLocked(int displayId) { + private void handleDisplayChanged(int displayId) { synchronized (mService) { ActivityDisplay activityDisplay = mActivityDisplays.get(displayId); if (activityDisplay != null) { @@ -3551,7 +3551,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mWindowManager.onDisplayChanged(displayId); } - StackInfo getStackInfo(ActivityStack stack) { + private StackInfo getStackInfoLocked(ActivityStack stack) { StackInfo info = new StackInfo(); mWindowManager.getStackBounds(stack.mStackId, info.bounds); info.displayId = Display.DEFAULT_DISPLAY; @@ -3577,7 +3577,7 @@ public final class ActivityStackSupervisor implements DisplayListener { StackInfo getStackInfoLocked(int stackId) { ActivityStack stack = getStack(stackId); if (stack != null) { - return getStackInfo(stack); + return getStackInfoLocked(stack); } return null; } @@ -3587,7 +3587,7 @@ public final class ActivityStackSupervisor implements DisplayListener { for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; for (int ndx = stacks.size() - 1; ndx >= 0; --ndx) { - list.add(getStackInfo(stacks.get(ndx))); + list.add(getStackInfoLocked(stacks.get(ndx))); } } return list; @@ -3707,13 +3707,13 @@ public final class ActivityStackSupervisor implements DisplayListener { } } break; case HANDLE_DISPLAY_ADDED: { - handleDisplayAddedLocked(msg.arg1); + handleDisplayAdded(msg.arg1); } break; case HANDLE_DISPLAY_CHANGED: { - handleDisplayChangedLocked(msg.arg1); + handleDisplayChanged(msg.arg1); } break; case HANDLE_DISPLAY_REMOVED: { - handleDisplayRemovedLocked(msg.arg1); + handleDisplayRemoved(msg.arg1); } break; case CONTAINER_CALLBACK_VISIBILITY: { final ActivityContainer container = (ActivityContainer) msg.obj; diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index ab56b34..fd75077 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -23,16 +23,16 @@ import android.os.IBinder; import android.os.Looper; import android.os.MessageQueue; import android.os.RemoteException; -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.Fingerprint; -import android.service.fingerprint.IFingerprintService; -import android.service.fingerprint.IFingerprintServiceReceiver; +import android.hardware.fingerprint.FingerprintUtils; +import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.IFingerprintService; +import android.hardware.fingerprint.IFingerprintServiceReceiver; import static android.Manifest.permission.MANAGE_FINGERPRINT; import static android.Manifest.permission.USE_FINGERPRINT; @@ -363,7 +363,7 @@ public class FingerprintService extends SystemService { startEnroll(token, groupId, flags); } - @Override + @Override // Binder call public void authenticate(IBinder token, long sessionId, int groupId, int flags) { checkPermission(USE_FINGERPRINT); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 87cf06e..958caea 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3509,8 +3509,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public int getSystemDecorLayerLw() { - if (mStatusBar != null) return mStatusBar.getSurfaceLayer(); - if (mNavigationBar != null) return mNavigationBar.getSurfaceLayer(); + if (mStatusBar != null && mStatusBar.isVisibleLw()) { + return mStatusBar.getSurfaceLayer(); + } + + if (mNavigationBar != null && mNavigationBar.isVisibleLw()) { + return mNavigationBar.getSurfaceLayer(); + } + return 0; } diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java index a58f30d..55ec9fc 100644 --- a/services/core/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java @@ -242,7 +242,7 @@ public class AppWindowAnimator { } // This must be called while inside a transaction. - boolean stepAnimationLocked(long currentTime) { + boolean stepAnimationLocked(long currentTime, final int displayId) { if (mService.okToDisplay()) { // We will run animations as long as the display isn't frozen. @@ -292,7 +292,7 @@ public class AppWindowAnimator { } mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM, - "AppWindowToken"); + "AppWindowToken", displayId); clearAnimation(); animating = false; diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 46fa38a..897b865 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -159,13 +159,13 @@ public class WindowAnimator { final AppWindowAnimator appAnimator = tokens.get(tokenNdx).mAppAnimator; final boolean wasAnimating = appAnimator.animation != null && appAnimator.animation != AppWindowAnimator.sDummyAnimation; - if (appAnimator.stepAnimationLocked(mCurrentTime)) { + if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) { mAnimating = mAppWindowAnimating = true; } else if (wasAnimating) { // stopped animating, do one more pass through the layout setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER, - "appToken " + appAnimator.mAppToken + " done"); + "appToken " + appAnimator.mAppToken + " done", displayId); if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG, "updateWindowsApps...: done animating " + appAnimator.mAppToken); } @@ -178,12 +178,12 @@ public class WindowAnimator { final AppWindowAnimator appAnimator = exitingAppTokens.get(i).mAppAnimator; final boolean wasAnimating = appAnimator.animation != null && appAnimator.animation != AppWindowAnimator.sDummyAnimation; - if (appAnimator.stepAnimationLocked(mCurrentTime)) { + if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) { mAnimating = mAppWindowAnimating = true; } else if (wasAnimating) { // stopped animating, do one more pass through the layout setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER, - "exiting appToken " + appAnimator.mAppToken + " done"); + "exiting appToken " + appAnimator.mAppToken + " done", displayId); if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG, "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken); } @@ -575,11 +575,11 @@ public class WindowAnimator { // This will set mOrientationChangeComplete and cause a pass through layout. setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER, - "testTokenMayBeDrawnLocked: freezingScreen"); + "testTokenMayBeDrawnLocked: freezingScreen", displayId); } else { setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM, - "testTokenMayBeDrawnLocked"); + "testTokenMayBeDrawnLocked", displayId); // We can now show all of the drawn windows! if (!mService.mOpeningApps.contains(wtoken)) { @@ -792,28 +792,30 @@ public class WindowAnimator { if (displayId < 0) { return 0; } - return mService.getDisplayContentLocked(displayId).pendingLayoutChanges; + final DisplayContent displayContent = mService.getDisplayContentLocked(displayId); + return (displayContent != null) ? displayContent.pendingLayoutChanges : 0; } void setPendingLayoutChanges(final int displayId, final int changes) { - if (displayId >= 0) { - mService.getDisplayContentLocked(displayId).pendingLayoutChanges |= changes; + if (displayId < 0) { + return; + } + final DisplayContent displayContent = mService.getDisplayContentLocked(displayId); + if (displayContent != null) { + displayContent.pendingLayoutChanges |= changes; } } - void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String s) { - // Used to track which displays layout changes have been done. - SparseIntArray displays = new SparseIntArray(2); + void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String reason, + final int displayId) { WindowList windows = appAnimator.mAppToken.allAppWindows; for (int i = windows.size() - 1; i >= 0; i--) { - final int displayId = windows.get(i).getDisplayId(); - if (displayId >= 0 && displays.indexOfKey(displayId) < 0) { + if (displayId == windows.get(i).getDisplayId()) { setPendingLayoutChanges(displayId, changes); if (WindowManagerService.DEBUG_LAYOUT_REPEATS) { - mService.debugLayoutRepeats(s, getPendingLayoutChanges(displayId)); + mService.debugLayoutRepeats(reason, getPendingLayoutChanges(displayId)); } - // Keep from processing this display again. - displays.put(displayId, changes); + break; } } } diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index 7b74e91..6448de2 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -10,6 +10,7 @@ LOCAL_SRC_FILES += \ $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \ $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \ $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_fingerprint_FingerprintService.cpp \ $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \ $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \ $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \ diff --git a/core/jni/android_server_FingerprintManager.cpp b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp index 5d59234..5d59234 100644 --- a/core/jni/android_server_FingerprintManager.cpp +++ b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java index a232a6e..d41629d 100644 --- a/services/net/java/android/net/dhcp/DhcpPacket.java +++ b/services/net/java/android/net/dhcp/DhcpPacket.java @@ -611,10 +611,22 @@ abstract class DhcpPacket { /** * Reads a string of specified length from the buffer. */ - private static String readAsciiString(ByteBuffer buf, int byteCount) { + private static String readAsciiString(ByteBuffer buf, int byteCount, boolean nullOk) { byte[] bytes = new byte[byteCount]; buf.get(bytes); - return new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII); + int length = bytes.length; + if (!nullOk) { + // Stop at the first null byte. This is because some DHCP options (e.g., the domain + // name) are passed to netd via FrameworkListener, which refuses arguments containing + // null bytes. We don't do this by default because vendorInfo is an opaque string which + // could in theory contain null bytes. + for (length = 0; length < bytes.length; length++) { + if (bytes[length] == 0) { + break; + } + } + } + return new String(bytes, 0, length, StandardCharsets.US_ASCII); } /** @@ -797,7 +809,7 @@ abstract class DhcpPacket { break; case DHCP_HOST_NAME: expectedLen = optionLen; - hostName = readAsciiString(packet, optionLen); + hostName = readAsciiString(packet, optionLen, false); break; case DHCP_MTU: expectedLen = 2; @@ -805,7 +817,7 @@ abstract class DhcpPacket { break; case DHCP_DOMAIN_NAME: expectedLen = optionLen; - domainName = readAsciiString(packet, optionLen); + domainName = readAsciiString(packet, optionLen, false); break; case DHCP_BROADCAST_ADDRESS: bcAddr = readIpAddress(packet); @@ -834,7 +846,7 @@ abstract class DhcpPacket { break; case DHCP_MESSAGE: expectedLen = optionLen; - message = readAsciiString(packet, optionLen); + message = readAsciiString(packet, optionLen, false); break; case DHCP_MAX_MESSAGE_SIZE: expectedLen = 2; @@ -850,7 +862,7 @@ abstract class DhcpPacket { break; case DHCP_VENDOR_CLASS_ID: expectedLen = optionLen; - vendorId = readAsciiString(packet, optionLen); + vendorId = readAsciiString(packet, optionLen, true); break; case DHCP_CLIENT_IDENTIFIER: { // Client identifier byte[] id = new byte[optionLen]; diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk index f25fc62..33979b1 100644 --- a/services/tests/servicestests/Android.mk +++ b/services/tests/servicestests/Android.mk @@ -10,6 +10,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_STATIC_JAVA_LIBRARIES := \ services.core \ services.devicepolicy \ + services.net \ easymocklib \ guava \ mockito-target diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java new file mode 100644 index 0000000..2658937 --- /dev/null +++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 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.dhcp; + +import android.net.NetworkUtils; +import android.system.OsConstants; +import android.test.suitebuilder.annotation.SmallTest; +import junit.framework.TestCase; + +import java.net.Inet4Address; +import java.nio.ByteBuffer; + +import static android.net.dhcp.DhcpPacket.*; + + +public class DhcpPacketTest extends TestCase { + + private static Inet4Address SERVER_ADDR = + (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1"); + private static Inet4Address CLIENT_ADDR = + (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.234"); + private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; + + class TestDhcpPacket extends DhcpPacket { + private byte mType; + // TODO: Make this a map of option numbers to bytes instead. + private byte[] mDomainBytes, mVendorInfoBytes; + + public TestDhcpPacket(byte type, byte[] domainBytes, byte[] vendorInfoBytes) { + super(0xdeadbeef, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY, CLIENT_MAC, true); + mType = type; + mDomainBytes = domainBytes; + mVendorInfoBytes = vendorInfoBytes; + } + + public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) { + ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); + fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR, + DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false); + return result; + } + + public void finishPacket(ByteBuffer buffer) { + addTlv(buffer, DHCP_MESSAGE_TYPE, mType); + if (mDomainBytes != null) { + addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes); + } + if (mVendorInfoBytes != null) { + addTlv(buffer, DHCP_VENDOR_CLASS_ID, mVendorInfoBytes); + } + addTlvEnd(buffer); + } + + // Convenience method. + public ByteBuffer build() { + // ENCAP_BOOTP packets don't contain ports, so just pass in 0. + ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0); + pkt.flip(); + return pkt; + } + } + + private void assertDomainAndVendorInfoParses( + String expectedDomain, byte[] domainBytes, + String expectedVendorInfo, byte[] vendorInfoBytes) { + ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER, + domainBytes, vendorInfoBytes).build(); + DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + assertEquals(expectedDomain, offerPacket.mDomainName); + assertEquals(expectedVendorInfo, offerPacket.mVendorId); + } + + @SmallTest + public void testDomainName() throws Exception { + byte[] nullByte = new byte[] { 0x00 }; + byte[] twoNullBytes = new byte[] { 0x00, 0x00 }; + byte[] nonNullDomain = new byte[] { + (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l' + }; + byte[] trailingNullDomain = new byte[] { + (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00 + }; + byte[] embeddedNullsDomain = new byte[] { + (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l' + }; + byte[] metered = "ANDROID_METERED".getBytes("US-ASCII"); + + byte[] meteredEmbeddedNull = metered.clone(); + meteredEmbeddedNull[7] = (char) 0; + + byte[] meteredTrailingNull = metered.clone(); + meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0; + + assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte); + assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes); + assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered); + assertDomainAndVendorInfoParses("goo", embeddedNullsDomain, + "ANDROID\u0000METERED", meteredEmbeddedNull); + assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain, + "ANDROID_METERE\u0000", meteredTrailingNull); + } +} diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java index 8740e19..239c16a 100644 --- a/telephony/java/com/android/ims/ImsCallProfile.java +++ b/telephony/java/com/android/ims/ImsCallProfile.java @@ -270,7 +270,7 @@ public class ImsCallProfile implements Parcelable { return "{ serviceType=" + mServiceType + ", callType=" + mCallType + ", restrictCause=" + mRestrictCause + - ", callExtras=" + mCallExtras.toString() + + //", callExtras=" + mCallExtras.toString() + ", mediaProfile=" + mMediaProfile.toString() + " }"; } diff --git a/telephony/java/com/android/ims/internal/IImsUt.aidl b/telephony/java/com/android/ims/internal/IImsUt.aidl index 50a0169..c531ea5 100644 --- a/telephony/java/com/android/ims/internal/IImsUt.aidl +++ b/telephony/java/com/android/ims/internal/IImsUt.aidl @@ -79,12 +79,13 @@ interface IImsUt { /** * Updates the configuration of the call forward. */ - int updateCallForward(int action, int condition, String number, int timeSeconds); + int updateCallForward(int action, int condition, String number, + int serviceClass, int timeSeconds); /** * Updates the configuration of the call waiting. */ - int updateCallWaiting(boolean enable); + int updateCallWaiting(boolean enable, int serviceClass); /** * Updates the configuration of the CLIR supplementary service. diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java index 9284796..0f73342 100644 --- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java +++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java @@ -141,6 +141,7 @@ public final class WifiActivityEnergyInfo implements Parcelable { return (int)mControllerIdleTimeMs; } + /** * product of current(mA), voltage(V) and time(ms) * @return energy used diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 36fc96b..440ad61 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -235,8 +235,10 @@ public class WifiEnterpriseConfig implements Parcelable { public static final int SIM = 4; /** EAP-Authentication and Key Agreement */ public static final int AKA = 5; + /** EAP-Authentication and Key Agreement Prime */ + public static final int AKA_PRIME = 6; /** @hide */ - public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA" }; + public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'" }; /** Prevent initialization */ private Eap() {} @@ -286,6 +288,7 @@ public class WifiEnterpriseConfig implements Parcelable { case Eap.TTLS: case Eap.SIM: case Eap.AKA: + case Eap.AKA_PRIME: mFields.put(EAP_KEY, Eap.strings[eapMethod]); mFields.put(OPP_KEY_CACHING, "1"); break; diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 6371891..275c7d1 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -861,6 +861,14 @@ public class WifiManager { public static final int WIFI_FEATURE_TDLS_OFFCHANNEL = 0x2000; // Support for TDLS off channel /** @hide */ public static final int WIFI_FEATURE_EPR = 0x4000; // Enhanced power reporting + /** @hide */ + public static final int WIFI_FEATURE_AP_STA = 0x8000; // Support for AP STA Concurrency + /** @hide */ + public static final int WIFI_FEATURE_LINK_LAYER_STATS = 0x10000; // Link layer stats collection + /** @hide */ + public static final int WIFI_FEATURE_LOGGER = 0x20000; // WiFi Logger + /** @hide */ + public static final int WIFI_FEATURE_HAL_EPNO = 0x40000; // WiFi PNO enhanced private int getSupportedFeatures() { try { @@ -972,7 +980,7 @@ public class WifiManager { * @return true if this adapter supports advanced power/performance counters */ public boolean isEnhancedPowerReportingSupported() { - return isFeatureSupported(WIFI_FEATURE_EPR); + return isFeatureSupported(WIFI_FEATURE_LINK_LAYER_STATS); } /** |