diff options
19 files changed, 912 insertions, 93 deletions
diff --git a/api/current.txt b/api/current.txt index c7dbd495c75b..123d8cc03c12 100644 --- a/api/current.txt +++ b/api/current.txt @@ -529,6 +529,7 @@ package android { field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 field public static final int enabled = 16842766; // 0x101000e + field public static final int end = 16843997; // 0x10104dd field public static final int endColor = 16843166; // 0x101019e field public static final deprecated int endYear = 16843133; // 0x101017d field public static final int enterFadeDuration = 16843532; // 0x101030c @@ -1128,6 +1129,7 @@ package android { field public static final int stackFromBottom = 16843005; // 0x10100fd field public static final int stackViewStyle = 16843838; // 0x101043e field public static final int starStyle = 16842882; // 0x1010082 + field public static final int start = 16843996; // 0x10104dc field public static final int startColor = 16843165; // 0x101019d field public static final int startDelay = 16843746; // 0x10103e2 field public static final int startOffset = 16843198; // 0x10101be @@ -11946,6 +11948,9 @@ package android.graphics.drawable { method public android.graphics.drawable.Drawable findDrawableByLayerId(int); method public android.graphics.drawable.Drawable getDrawable(int); method public int getId(int); + method public int getLayerGravity(int); + method public int getLayerHeight(int); + method public int getLayerWidth(int); method public int getNumberOfLayers(); method public int getOpacity(); method public int getPaddingMode(); @@ -11955,7 +11960,10 @@ package android.graphics.drawable { method public void setColorFilter(android.graphics.ColorFilter); method public boolean setDrawableByLayerId(int, android.graphics.drawable.Drawable); method public void setId(int, int); + method public void setLayerGravity(int, int); method public void setLayerInset(int, int, int, int, int); + method public void setLayerInsetRelative(int, int, int, int, int); + method public void setLayerSize(int, int, int); method public void setOpacity(int); method public void setPaddingMode(int); method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable); @@ -12696,11 +12704,14 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES; field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE; field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP; + field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AE_LOCK_AVAILABLE; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AF_AVAILABLE_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_EFFECTS; + field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_SCENE_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES; + field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB; diff --git a/api/system-current.txt b/api/system-current.txt index e1a236fe7fbd..6224dfb44e9e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -601,6 +601,7 @@ package android { field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 field public static final int enabled = 16842766; // 0x101000e + field public static final int end = 16843997; // 0x10104dd field public static final int endColor = 16843166; // 0x101019e field public static final deprecated int endYear = 16843133; // 0x101017d field public static final int enterFadeDuration = 16843532; // 0x101030c @@ -1204,6 +1205,7 @@ package android { field public static final int stackFromBottom = 16843005; // 0x10100fd field public static final int stackViewStyle = 16843838; // 0x101043e field public static final int starStyle = 16842882; // 0x1010082 + field public static final int start = 16843996; // 0x10104dc field public static final int startColor = 16843165; // 0x101019d field public static final int startDelay = 16843746; // 0x10103e2 field public static final int startOffset = 16843198; // 0x10101be @@ -12214,6 +12216,9 @@ package android.graphics.drawable { method public android.graphics.drawable.Drawable findDrawableByLayerId(int); method public android.graphics.drawable.Drawable getDrawable(int); method public int getId(int); + method public int getLayerGravity(int); + method public int getLayerHeight(int); + method public int getLayerWidth(int); method public int getNumberOfLayers(); method public int getOpacity(); method public int getPaddingMode(); @@ -12223,7 +12228,10 @@ package android.graphics.drawable { method public void setColorFilter(android.graphics.ColorFilter); method public boolean setDrawableByLayerId(int, android.graphics.drawable.Drawable); method public void setId(int, int); + method public void setLayerGravity(int, int); method public void setLayerInset(int, int, int, int, int); + method public void setLayerInsetRelative(int, int, int, int, int); + method public void setLayerSize(int, int, int); method public void setOpacity(int); method public void setPaddingMode(int); method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable); @@ -12964,11 +12972,14 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES; field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE; field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP; + field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AE_LOCK_AVAILABLE; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AF_AVAILABLE_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_EFFECTS; + field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_SCENE_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES; + field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 5310071d26aa..16bc6a447904 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -422,6 +422,17 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Rational>("android.control.aeCompensationStep", Rational.class); /** + * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</p> + * <p>LIMITED or FULL devices will always list <code>true</code></p> + * <p>This key is available on all devices.</p> + * + * @see CaptureRequest#CONTROL_AE_LOCK + */ + @PublicKey + public static final Key<Boolean> CONTROL_AE_LOCK_AVAILABLE = + new Key<Boolean>("android.control.aeLockAvailable", boolean.class); + + /** * <p>List of auto-focus (AF) modes for {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} that are * supported by this camera device.</p> * <p>Not all the auto-focus modes may be supported by a @@ -469,6 +480,22 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.control.availableEffects", int[].class); /** + * <p>List of control modes for {@link CaptureRequest#CONTROL_MODE android.control.mode} that are supported by this camera + * device.</p> + * <p>This list contains control modes that can be set for the camera device. + * LEGACY mode devices will always support AUTO mode. LIMITED and FULL + * devices will always support OFF, AUTO modes.</p> + * <p><b>Range of valid values:</b><br> + * Any value listed in {@link CaptureRequest#CONTROL_MODE android.control.mode}</p> + * <p>This key is available on all devices.</p> + * + * @see CaptureRequest#CONTROL_MODE + */ + @PublicKey + public static final Key<int[]> CONTROL_AVAILABLE_MODES = + new Key<int[]>("android.control.availableModes", int[].class); + + /** * <p>List of scene modes for {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} that are supported by this camera * device.</p> * <p>This list contains scene modes that can be set for the camera device. @@ -530,6 +557,17 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.control.awbAvailableModes", int[].class); /** + * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</p> + * <p>LIMITED or FULL devices will always list <code>true</code></p> + * <p>This key is available on all devices.</p> + * + * @see CaptureRequest#CONTROL_AWB_LOCK + */ + @PublicKey + public static final Key<Boolean> CONTROL_AWB_LOCK_AVAILABLE = + new Key<Boolean>("android.control.awbLockAvailable", boolean.class); + + /** * <p>List of the maximum number of regions that can be used for metering in * auto-exposure (AE), auto-white balance (AWB), and auto-focus (AF); * this corresponds to the the maximum number of elements in diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index fb37ae560762..e1b14ccf15be 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -552,6 +552,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * in this matrix result metadata. The transform should keep the magnitude * of the output color values within <code>[0, 1.0]</code> (assuming input color * values is within the normalized range <code>[0, 1.0]</code>), or clipping may occur.</p> + * <p>The valid range of each matrix element varies on different devices, but + * values within [-1.5, 3.0] are guaranteed not to be clipped.</p> * <p><b>Units</b>: Unitless scale factors</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * <p><b>Full capability</b> - @@ -575,6 +577,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * TRANSFORM_MATRIX.</p> * <p>The gains in the result metadata are the gains actually * applied by the camera device to the current frame.</p> + * <p>The valid range of gains varies on different devices, but gains + * between [1.0, 3.0] are guaranteed not to be clipped. Even if a given + * device allows gains below 1.0, this is usually not recommended because + * this can create color artifacts.</p> * <p><b>Units</b>: Unitless gain factors</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * <p><b>Full capability</b> - @@ -1239,10 +1245,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * update, as if this frame is never captured. This mode can be used in the scenario * where the application doesn't want a 3A manual control capture to affect * the subsequent auto 3A capture results.</p> - * <p>LEGACY mode devices will only support AUTO and USE_SCENE_MODE modes. - * LIMITED mode devices will only support OFF and OFF_KEEP_STATE if they - * support the MANUAL_SENSOR and MANUAL_POST_PROCSESING capabilities. - * FULL mode devices will always support OFF and OFF_KEEP_STATE.</p> * <p><b>Possible values:</b> * <ul> * <li>{@link #CONTROL_MODE_OFF OFF}</li> @@ -1250,9 +1252,12 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <li>{@link #CONTROL_MODE_USE_SCENE_MODE USE_SCENE_MODE}</li> * <li>{@link #CONTROL_MODE_OFF_KEEP_STATE OFF_KEEP_STATE}</li> * </ul></p> + * <p><b>Available values for this device:</b><br> + * {@link CameraCharacteristics#CONTROL_AVAILABLE_MODES android.control.availableModes}</p> * <p>This key is available on all devices.</p> * * @see CaptureRequest#CONTROL_AF_MODE + * @see CameraCharacteristics#CONTROL_AVAILABLE_MODES * @see #CONTROL_MODE_OFF * @see #CONTROL_MODE_AUTO * @see #CONTROL_MODE_USE_SCENE_MODE diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 5642f6ff0e43..5bf5b2999882 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -403,6 +403,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * in this matrix result metadata. The transform should keep the magnitude * of the output color values within <code>[0, 1.0]</code> (assuming input color * values is within the normalized range <code>[0, 1.0]</code>), or clipping may occur.</p> + * <p>The valid range of each matrix element varies on different devices, but + * values within [-1.5, 3.0] are guaranteed not to be clipped.</p> * <p><b>Units</b>: Unitless scale factors</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * <p><b>Full capability</b> - @@ -426,6 +428,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * TRANSFORM_MATRIX.</p> * <p>The gains in the result metadata are the gains actually * applied by the camera device to the current frame.</p> + * <p>The valid range of gains varies on different devices, but gains + * between [1.0, 3.0] are guaranteed not to be clipped. Even if a given + * device allows gains below 1.0, this is usually not recommended because + * this can create color artifacts.</p> * <p><b>Units</b>: Unitless gain factors</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * <p><b>Full capability</b> - @@ -1855,10 +1861,6 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * update, as if this frame is never captured. This mode can be used in the scenario * where the application doesn't want a 3A manual control capture to affect * the subsequent auto 3A capture results.</p> - * <p>LEGACY mode devices will only support AUTO and USE_SCENE_MODE modes. - * LIMITED mode devices will only support OFF and OFF_KEEP_STATE if they - * support the MANUAL_SENSOR and MANUAL_POST_PROCSESING capabilities. - * FULL mode devices will always support OFF and OFF_KEEP_STATE.</p> * <p><b>Possible values:</b> * <ul> * <li>{@link #CONTROL_MODE_OFF OFF}</li> @@ -1866,9 +1868,12 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <li>{@link #CONTROL_MODE_USE_SCENE_MODE USE_SCENE_MODE}</li> * <li>{@link #CONTROL_MODE_OFF_KEEP_STATE OFF_KEEP_STATE}</li> * </ul></p> + * <p><b>Available values for this device:</b><br> + * {@link CameraCharacteristics#CONTROL_AVAILABLE_MODES android.control.availableModes}</p> * <p>This key is available on all devices.</p> * * @see CaptureRequest#CONTROL_AF_MODE + * @see CameraCharacteristics#CONTROL_AVAILABLE_MODES * @see #CONTROL_MODE_OFF * @see #CONTROL_MODE_AUTO * @see #CONTROL_MODE_USE_SCENE_MODE diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java index 6fc99ac1b9f6..33d539c25e73 100644 --- a/core/java/android/hardware/camera2/DngCreator.java +++ b/core/java/android/hardware/camera2/DngCreator.java @@ -530,9 +530,9 @@ public final class DngCreator implements AutoCloseable { int uPixStride = uPlane.getPixelStride(); byte[] yuvPixel = { 0, 0, 0 }; - byte[] yFullRow = new byte[yPixStride * width]; - byte[] uFullRow = new byte[uPixStride * width / 2]; - byte[] vFullRow = new byte[vPixStride * width / 2]; + byte[] yFullRow = new byte[yPixStride * (width - 1) + 1]; + byte[] uFullRow = new byte[uPixStride * (width / 2 - 1) + 1]; + byte[] vFullRow = new byte[vPixStride * (width / 2 - 1) + 1]; byte[] finalRow = new byte[BYTES_PER_RGB_PIX * width]; for (int i = 0; i < height; i++) { int halfH = i / 2; diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java index 347db05b930c..802b9389fad8 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java @@ -474,6 +474,15 @@ public class LegacyMetadataMapper { m.set(CONTROL_AE_COMPENSATION_STEP, ParamsUtils.createRational(step)); } + + /* + * control.aeLockAvailable + */ + { + boolean aeLockAvailable = p.isAutoExposureLockSupported(); + + m.set(CONTROL_AE_LOCK_AVAILABLE, aeLockAvailable); + } } @@ -571,6 +580,16 @@ public class LegacyMetadataMapper { Log.v(TAG, "mapControlAwb - control.awbAvailableModes set to " + ListUtils.listToString(awbAvail)); } + + + /* + * control.awbLockAvailable + */ + { + boolean awbLockAvailable = p.isAutoWhiteBalanceLockSupported(); + + m.set(CONTROL_AWB_LOCK_AVAILABLE, awbLockAvailable); + } } } @@ -618,17 +637,44 @@ public class LegacyMetadataMapper { /* * android.control.availableSceneModes */ + int maxNumDetectedFaces = p.getMaxNumDetectedFaces(); List<String> sceneModes = p.getSupportedSceneModes(); List<Integer> supportedSceneModes = ArrayUtils.convertStringListToIntList(sceneModes, sLegacySceneModes, sSceneModes); - if (supportedSceneModes == null) { // camera1 doesn't support scene mode settings - supportedSceneModes = new ArrayList<Integer>(); - supportedSceneModes.add(CONTROL_SCENE_MODE_DISABLED); // disabled is always available + + // Special case where the only scene mode listed is AUTO => no scene mode + if (sceneModes != null && sceneModes.size() == 1 && + sceneModes.get(0) == Parameters.SCENE_MODE_AUTO) { + supportedSceneModes = null; } - if (p.getMaxNumDetectedFaces() > 0) { // always supports FACE_PRIORITY when face detecting - supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY); + + boolean sceneModeSupported = true; + if (supportedSceneModes == null && maxNumDetectedFaces == 0) { + sceneModeSupported = false; } - m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes)); + + if (sceneModeSupported) { + if (supportedSceneModes == null) { + supportedSceneModes = new ArrayList<Integer>(); + } + if (maxNumDetectedFaces > 0) { // always supports FACE_PRIORITY when face detecting + supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY); + } + // Remove all DISABLED occurrences + if (supportedSceneModes.contains(CONTROL_SCENE_MODE_DISABLED)) { + while(supportedSceneModes.remove(new Integer(CONTROL_SCENE_MODE_DISABLED))) {} + } + m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes)); + } else { + m.set(CONTROL_AVAILABLE_SCENE_MODES, new int[] {CONTROL_SCENE_MODE_DISABLED}); + } + + /* + * android.control.availableModes + */ + m.set(CONTROL_AVAILABLE_MODES, sceneModeSupported ? + new int[] { CONTROL_MODE_AUTO, CONTROL_MODE_USE_SCENE_MODE } : + new int[] { CONTROL_MODE_AUTO }); } private static void mapLens(CameraMetadataNative m, Camera.Parameters p) { diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 2099c3f9b02f..bf3d9aac1da1 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -366,6 +366,7 @@ 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") @@ -384,6 +385,9 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } } return builder.toString(); + } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) { + ssp = null; + authority = "//" + getAuthority() + "/..."; } } // Not a sensitive scheme, but let's still be conservative about @@ -397,6 +401,9 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { if (ssp != null) { builder.append(ssp); } + if (authority != null) { + builder.append(authority); + } return builder.toString(); } @@ -1742,7 +1749,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * @return normalized Uri (never null) * @see {@link android.content.Intent#setData} - * @see {@link #setNormalizedData} + * @see {@link android.content.Intent#setDataAndNormalize} */ public Uri normalizeScheme() { String scheme = getScheme(); diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 24380716127a..a929f3dfd5a0 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -160,7 +160,22 @@ public class PopupWindow { private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() { @Override public Rect onGetEpicenter(Transition transition) { - return mAnchorBounds; + final View anchor = mAnchor.get(); + final View decor = mDecorView; + if (anchor == null || decor == null) { + return null; + } + + final Rect anchorBounds = mAnchorBounds; + final int[] anchorLocation = mAnchor.get().getLocationOnScreen(); + final int[] popupLocation = mDecorView.getLocationOnScreen(); + + // Compute the position of the anchor relative to the popup. + anchorBounds.set(0, 0, anchor.getWidth(), anchor.getHeight()); + anchorBounds.offset(anchorLocation[0] - popupLocation[0], + anchorLocation[1] - popupLocation[1]); + + return anchorBounds; } }; @@ -1494,10 +1509,6 @@ public class PopupWindow { p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL; - // Compute the position of the anchor relative to the popup. - mAnchorBounds.set(0, 0, anchorWidth, anchorHeight); - mAnchorBounds.offset(mDrawingLocation[0] - p.x, mDrawingLocation[1] - p.y); - return onTop; } @@ -1573,9 +1584,9 @@ public class PopupWindow { } /** - * <p>Dispose of the popup window. This method can be invoked only after - * {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling - * this method will have no effect.</p> + * Disposes of the popup window. This method can be invoked only after + * {@link #showAsDropDown(android.view.View)} has been executed. Failing + * that, calling this method will have no effect. * * @see #showAsDropDown(android.view.View) */ @@ -1589,6 +1600,9 @@ public class PopupWindow { mIsShowing = false; if (mExitTransition != null) { + // Cache the content view, since it may change without notice. + final View contentView = mContentView; + mExitTransition.addTarget(mBackgroundView); mExitTransition.addListener(new Transition.TransitionListenerAdapter() { @Override @@ -1596,7 +1610,7 @@ public class PopupWindow { transition.removeListener(this); transition.removeTarget(mBackgroundView); - dismissImmediate(); + dismissImmediate(contentView); } }); @@ -1605,7 +1619,11 @@ public class PopupWindow { // Transition to invisible. mBackgroundView.setVisibility(View.INVISIBLE); } else { - dismissImmediate(); + dismissImmediate(mContentView); + } + + if (mOnDismissListener != null) { + mOnDismissListener.onDismiss(); } } @@ -1613,21 +1631,17 @@ public class PopupWindow { * Removes the popup from the window manager and tears down the supporting * view hierarchy, if necessary. */ - private void dismissImmediate() { + private void dismissImmediate(View contentView) { try { mWindowManager.removeViewImmediate(mDecorView); } finally { mDecorView.removeView(mBackgroundView); mDecorView = null; - if (mBackgroundView != mContentView) { - ((ViewGroup) mBackgroundView).removeView(mContentView); + if (mBackgroundView != contentView) { + ((ViewGroup) mBackgroundView).removeView(contentView); } mBackgroundView = null; - - if (mOnDismissListener != null) { - mOnDismissListener.onDismiss(); - } } } diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 559d750e4340..d157cdc0a055 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5078,14 +5078,31 @@ <!-- Describes an item (or child) of a LayerDrawable. --> <declare-styleable name="LayerDrawableItem"> - <!-- Left coordinate of the layer. --> + <!-- Left inset to apply to the layer. --> <attr name="left" /> - <!-- Top coordinate of the layer. --> + <!-- Top inset to apply to the layer. --> <attr name="top" /> - <!-- Right coordinate of the layer. --> + <!-- Right inset to apply to the layer. --> <attr name="right" /> - <!-- Bottom coordinate of the layer. --> + <!-- Bottom inset to apply to the layer. --> <attr name="bottom" /> + <!-- Start inset to apply to the layer. Overrides {@code left} or + {@code right} depending on layout direction. --> + <attr name="start" format="dimension" /> + <!-- End inset to apply to the layer. Overrides {@code left} or + {@code right} depending on layout direction. --> + <attr name="end" format="dimension" /> + <!-- Width of the layer. Defaults to the layer's intrinsic width. --> + <attr name="width" /> + <!-- Height of the layer. Defaults to the layer's intrinsic height --> + <attr name="height" /> + <!-- Gravity used to align the layer within its container. If no value + is specified, the default behavior depends on whether an explicit + width or height has been set, If no dimension is set, gravity in + that direction defaults to {@code fill_horizontal} or + {@code fill_vertical}; otherwise, it defaults to {@code left} or + {@code top}. --> + <attr name="gravity" /> <!-- Drawable used to render the layer. --> <attr name="drawable" /> <!-- Identifier of the layer. This can be used to retrieve the layer diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 11cc18120074..3bf96318a019 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2611,6 +2611,8 @@ <public type="attr" name="trackTint" /> <public type="attr" name="trackTintMode" /> <public type="attr" name="resizeableActivity" /> + <public type="attr" name="start" /> + <public type="attr" name="end" /> <public type="style" name="Widget.Material.Button.Colored" /> diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index daf44271ef74..505bd1dacaf2 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -29,6 +29,8 @@ import android.graphics.PixelFormat; import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.LayoutDirection; +import android.view.Gravity; import android.view.View; import com.android.internal.R; @@ -54,6 +56,11 @@ import java.util.Collection; * @attr ref android.R.styleable#LayerDrawableItem_top * @attr ref android.R.styleable#LayerDrawableItem_right * @attr ref android.R.styleable#LayerDrawableItem_bottom + * @attr ref android.R.styleable#LayerDrawableItem_start + * @attr ref android.R.styleable#LayerDrawableItem_end + * @attr ref android.R.styleable#LayerDrawableItem_width + * @attr ref android.R.styleable#LayerDrawableItem_height + * @attr ref android.R.styleable#LayerDrawableItem_gravity * @attr ref android.R.styleable#LayerDrawableItem_drawable * @attr ref android.R.styleable#LayerDrawableItem_id */ @@ -73,6 +80,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { */ public static final int PADDING_MODE_STACK = 1; + /** Value used for undefined start and end insets. */ + private static final int UNDEFINED_INSET = Integer.MIN_VALUE; + LayerState mLayerState; private int mOpacityOverride = PixelFormat.UNKNOWN; @@ -231,6 +241,16 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { R.styleable.LayerDrawableItem_right, layer.mInsetR); layer.mInsetB = a.getDimensionPixelOffset( R.styleable.LayerDrawableItem_bottom, layer.mInsetB); + layer.mInsetS = a.getDimensionPixelOffset( + R.styleable.LayerDrawableItem_start, layer.mInsetS); + layer.mInsetE = a.getDimensionPixelOffset( + R.styleable.LayerDrawableItem_end, layer.mInsetE); + layer.mWidth = a.getDimensionPixelSize( + R.styleable.LayerDrawableItem_width, layer.mWidth); + layer.mHeight = a.getDimensionPixelSize( + R.styleable.LayerDrawableItem_height, layer.mHeight); + layer.mGravity = a.getInteger( + R.styleable.LayerDrawableItem_gravity, layer.mGravity); layer.mId = a.getResourceId(R.styleable.LayerDrawableItem_id, layer.mId); final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable); @@ -447,6 +467,89 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { } /** + * Sets an explicit size for the specified layer. + * <p> + * <strong>Note:</strong> Setting an explicit layer size changes the + * default layer gravity behavior. See {@link #setLayerGravity(int, int)} + * for more information. + * + * @param index the index of the drawable to adjust + * @param w width in pixels, or -1 to use the intrinsic width + * @param h height in pixels, or -1 to use the intrinsic height + * + * @see #getLayerWidth(int) + * @see #getLayerHeight(int) + * @attr ref android.R.styleable#LayerDrawableItem_width + * @attr ref android.R.styleable#LayerDrawableItem_height + */ + public void setLayerSize(int index, int w, int h) { + final ChildDrawable childDrawable = mLayerState.mChildren[index]; + childDrawable.mWidth = w; + childDrawable.mHeight = h; + } + + /** + * @param index the index of the drawable to adjust + * @return the explicit width of the layer, or -1 if not specified + * + * @see #setLayerSize(int, int, int) + * @attr ref android.R.styleable#LayerDrawableItem_width + */ + public int getLayerWidth(int index) { + final ChildDrawable childDrawable = mLayerState.mChildren[index]; + return childDrawable.mWidth; + } + + /** + * @param index the index of the drawable to adjust + * @return the explicit height of the layer, or -1 if not specified + * + * @see #setLayerSize(int, int, int) + * @attr ref android.R.styleable#LayerDrawableItem_height + */ + public int getLayerHeight(int index) { + final ChildDrawable childDrawable = mLayerState.mChildren[index]; + return childDrawable.mHeight; + } + + /** + * Sets the gravity used to position or stretch the specified layer within + * its container. Gravity is applied after any layer insets (see + * {@link #setLayerInset(int, int, int, int, int)}) or padding (see + * {@link #setPaddingMode(int)}). + * <p> + * If gravity is specified as {@link Gravity#NO_GRAVITY}, the default + * behavior depends on whether an explicit width or height has been set + * (see {@link #setLayerSize(int, int, int)}), If a dimension is not set, + * gravity in that direction defaults to {@link Gravity#FILL_HORIZONTAL} or + * {@link Gravity#FILL_VERTICAL}; otherwise, gravity in that direction + * defaults to {@link Gravity#LEFT} or {@link Gravity#TOP}. + * + * @param index the index of the drawable to adjust + * @param gravity the gravity to set for the layer + * + * @see #getLayerGravity(int) + * @attr ref android.R.styleable#LayerDrawableItem_gravity + */ + public void setLayerGravity(int index, int gravity) { + final ChildDrawable childDrawable = mLayerState.mChildren[index]; + childDrawable.mGravity = gravity; + } + + /** + * @param index the index of the layer + * @return the gravity used to position or stretch the specified layer + * within its container + * + * @see #setLayerGravity(int, int) + * @attr ref android.R.styleable#LayerDrawableItem_gravity + */ + public int getLayerGravity(int index) { + final ChildDrawable childDrawable = mLayerState.mChildren[index]; + return childDrawable.mGravity; + } + + /** * Specifies the insets in pixels for the drawable at the specified index. * * @param index the index of the drawable to adjust @@ -454,13 +557,43 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { * @param t number of pixels to add to the top bound * @param r number of pixels to subtract from the right bound * @param b number of pixels to subtract from the bottom bound + * + * @attr ref android.R.styleable#LayerDrawableItem_left + * @attr ref android.R.styleable#LayerDrawableItem_top + * @attr ref android.R.styleable#LayerDrawableItem_right + * @attr ref android.R.styleable#LayerDrawableItem_bottom */ public void setLayerInset(int index, int l, int t, int r, int b) { + setLayerInsetInternal(index, l, t, r, b, UNDEFINED_INSET, UNDEFINED_INSET); + } + + /** + * Specifies the relative insets in pixels for the drawable at the + * specified index. + * + * @param index the index of the drawable to adjust + * @param s number of pixels to inset from the start bound + * @param t number of pixels to inset from the top bound + * @param e number of pixels to inset from the end bound + * @param b number of pixels to inset from the bottom bound + * + * @attr ref android.R.styleable#LayerDrawableItem_start + * @attr ref android.R.styleable#LayerDrawableItem_top + * @attr ref android.R.styleable#LayerDrawableItem_end + * @attr ref android.R.styleable#LayerDrawableItem_bottom + */ + public void setLayerInsetRelative(int index, int s, int t, int e, int b) { + setLayerInsetInternal(index, 0, t, 0, b, s, e); + } + + private void setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e) { final ChildDrawable childDrawable = mLayerState.mChildren[index]; childDrawable.mInsetL = l; childDrawable.mInsetT = t; childDrawable.mInsetR = r; childDrawable.mInsetB = b; + childDrawable.mInsetS = s; + childDrawable.mInsetE = e; } /** @@ -770,7 +903,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { } if (paddingChanged) { - onBoundsChange(getBounds()); + updateLayerBounds(getBounds()); } return changed; @@ -795,7 +928,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { } if (paddingChanged) { - onBoundsChange(getBounds()); + updateLayerBounds(getBounds()); } return changed; @@ -803,18 +936,50 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @Override protected void onBoundsChange(Rect bounds) { + updateLayerBounds(bounds); + } + + private void updateLayerBounds(Rect bounds) { int padL = 0; int padT = 0; int padR = 0; int padB = 0; + final Rect outRect = mTmpRect; + final int layoutDirection = getLayoutDirection(); final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST; final ChildDrawable[] array = mLayerState.mChildren; final int N = mLayerState.mNum; for (int i = 0; i < N; i++) { final ChildDrawable r = array[i]; - r.mDrawable.setBounds(bounds.left + r.mInsetL + padL, bounds.top + r.mInsetT + padT, - bounds.right - r.mInsetR - padR, bounds.bottom - r.mInsetB - padB); + final Drawable d = r.mDrawable; + final Rect container = d.getBounds(); + + // Take the resolved layout direction into account. If start / end + // padding are defined, they will be resolved (hence overriding) to + // left / right or right / left depending on the resolved layout + // direction. If start / end padding are not defined, use the + // left / right ones. + final int insetL, insetR; + if (layoutDirection == LayoutDirection.RTL) { + insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE; + insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS; + } else { + insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS; + insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE; + } + + // Establish containing region based on aggregate padding and + // requested insets for the current layer. + container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT, + bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB); + + // Apply resolved gravity to drawable based on resolved size. + final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight); + final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth; + final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight; + Gravity.apply(gravity, w, h, container, outRect, layoutDirection); + d.setBounds(outRect); if (nest) { padL += mPaddingL[i]; @@ -825,6 +990,38 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { } } + /** + * Resolves layer gravity given explicit gravity and dimensions. + * <p> + * If the client hasn't specified a gravity but has specified an explicit + * dimension, defaults to START or TOP. Otherwise, defaults to FILL to + * preserve legacy behavior. + * + * @param gravity + * @param width + * @param height + * @return + */ + private int resolveGravity(int gravity, int width, int height) { + if (!Gravity.isHorizontal(gravity)) { + if (width < 0) { + gravity |= Gravity.FILL_HORIZONTAL; + } else { + gravity |= Gravity.START; + } + } + + if (!Gravity.isVertical(gravity)) { + if (height < 0) { + gravity |= Gravity.FILL_VERTICAL; + } else { + gravity |= Gravity.TOP; + } + } + + return gravity; + } + @Override public int getIntrinsicWidth() { int width = -1; @@ -836,7 +1033,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { final int N = mLayerState.mNum; for (int i = 0; i < N; i++) { final ChildDrawable r = array[i]; - final int w = r.mDrawable.getIntrinsicWidth() + r.mInsetL + r.mInsetR + padL + padR; + final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth; + final int w = minWidth + r.mInsetL + r.mInsetR + padL + padR; if (w > width) { width = w; } @@ -861,7 +1059,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { final int N = mLayerState.mNum; for (int i = 0; i < N; i++) { final ChildDrawable r = array[i]; - int h = r.mDrawable.getIntrinsicHeight() + r.mInsetT + r.mInsetB + padT + padB; + final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight; + final int h = minHeight + r.mInsetT + r.mInsetB + padT + padB; if (h > height) { height = h; } @@ -948,18 +1147,24 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { /** @hide */ @Override public void setLayoutDirection(int layoutDirection) { + super.setLayoutDirection(layoutDirection); final ChildDrawable[] array = mLayerState.mChildren; final int N = mLayerState.mNum; for (int i = 0; i < N; i++) { array[i].mDrawable.setLayoutDirection(layoutDirection); } - super.setLayoutDirection(layoutDirection); + updateLayerBounds(getBounds()); } static class ChildDrawable { public Drawable mDrawable; public int[] mThemeAttrs; public int mInsetL, mInsetT, mInsetR, mInsetB; + public int mInsetS = UNDEFINED_INSET; + public int mInsetE = UNDEFINED_INSET; + public int mWidth = -1; + public int mHeight = -1; + public int mGravity = Gravity.NO_GRAVITY; public int mId = View.NO_ID; ChildDrawable() { @@ -981,6 +1186,11 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { mInsetT = orig.mInsetT; mInsetR = orig.mInsetR; mInsetB = orig.mInsetB; + mInsetS = orig.mInsetS; + mInsetE = orig.mInsetE; + mWidth = orig.mWidth; + mHeight = orig.mHeight; + mGravity = orig.mGravity; mId = orig.mId; } } diff --git a/location/java/android/location/GpsSatellite.java b/location/java/android/location/GpsSatellite.java index 17af4a647461..820f574653a9 100644 --- a/location/java/android/location/GpsSatellite.java +++ b/location/java/android/location/GpsSatellite.java @@ -40,13 +40,17 @@ public final class GpsSatellite { * cached GpsStatus instance to the client's copy. */ void setStatus(GpsSatellite satellite) { - mValid = satellite.mValid; - mHasEphemeris = satellite.mHasEphemeris; - mHasAlmanac = satellite.mHasAlmanac; - mUsedInFix = satellite.mUsedInFix; - mSnr = satellite.mSnr; - mElevation = satellite.mElevation; - mAzimuth = satellite.mAzimuth; + if (satellite == null) { + mValid = false; + } else { + mValid = satellite.mValid; + mHasEphemeris = satellite.mHasEphemeris; + mHasAlmanac = satellite.mHasAlmanac; + mUsedInFix = satellite.mUsedInFix; + mSnr = satellite.mSnr; + mElevation = satellite.mElevation; + mAzimuth = satellite.mAzimuth; + } } /** diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java index 4af55a65b0e3..323f3269368e 100644 --- a/location/java/android/location/GpsStatus.java +++ b/location/java/android/location/GpsStatus.java @@ -16,6 +16,8 @@ package android.location; +import android.util.SparseArray; + import java.util.Iterator; import java.util.NoSuchElementException; @@ -29,20 +31,24 @@ public final class GpsStatus { /* These package private values are modified by the LocationManager class */ private int mTimeToFirstFix; - private GpsSatellite mSatellites[] = new GpsSatellite[NUM_SATELLITES]; + private final SparseArray<GpsSatellite> mSatellites = new SparseArray<>(); private final class SatelliteIterator implements Iterator<GpsSatellite> { - private GpsSatellite[] mSatellites; - int mIndex = 0; + private final SparseArray<GpsSatellite> mSatellites; + private final int mSatellitesCount; + + private int mIndex = 0; - SatelliteIterator(GpsSatellite[] satellites) { + SatelliteIterator(SparseArray<GpsSatellite> satellites) { mSatellites = satellites; + mSatellitesCount = satellites.size(); } public boolean hasNext() { - for (int i = mIndex; i < mSatellites.length; i++) { - if (mSatellites[i].mValid) { + for (; mIndex < mSatellitesCount; ++mIndex) { + GpsSatellite satellite = mSatellites.valueAt(mIndex); + if (satellite.mValid) { return true; } } @@ -50,8 +56,9 @@ public final class GpsStatus { } public GpsSatellite next() { - while (mIndex < mSatellites.length) { - GpsSatellite satellite = mSatellites[mIndex++]; + while (mIndex < mSatellitesCount) { + GpsSatellite satellite = mSatellites.valueAt(mIndex); + ++mIndex; if (satellite.mValid) { return satellite; } @@ -106,7 +113,7 @@ public final class GpsStatus { * <li> {@link GpsStatus#GPS_EVENT_SATELLITE_STATUS} * </ul> * - * When this method is called, the client should call + * When this method is called, the client should call * {@link LocationManager#getGpsStatus} to get additional * status information. * @@ -127,11 +134,8 @@ public final class GpsStatus { void onNmeaReceived(long timestamp, String nmea); } - GpsStatus() { - for (int i = 0; i < mSatellites.length; i++) { - mSatellites[i] = new GpsSatellite(i + 1); - } - } + // For API-compat a public ctor() is not available + GpsStatus() {} /** * Used internally within {@link LocationManager} to copy GPS status @@ -141,18 +145,17 @@ public final class GpsStatus { synchronized void setStatus(int svCount, int[] prns, float[] snrs, float[] elevations, float[] azimuths, int ephemerisMask, int almanacMask, int usedInFixMask) { - int i; + clearSatellites(); + for (int i = 0; i < svCount; i++) { + int prn = prns[i]; + int prnShift = (1 << (prn - 1)); + if (prn > 0 && prn <= NUM_SATELLITES) { + GpsSatellite satellite = mSatellites.get(prn); + if (satellite == null) { + satellite = new GpsSatellite(prn); + mSatellites.put(prn, satellite); + } - for (i = 0; i < mSatellites.length; i++) { - mSatellites[i].mValid = false; - } - - for (i = 0; i < svCount; i++) { - int prn = prns[i] - 1; - int prnShift = (1 << prn); - if (prn >= 0 && prn < mSatellites.length) { - GpsSatellite satellite = mSatellites[prn]; - satellite.mValid = true; satellite.mSnr = snrs[i]; satellite.mElevation = elevations[i]; @@ -172,10 +175,38 @@ public final class GpsStatus { */ void setStatus(GpsStatus status) { mTimeToFirstFix = status.getTimeToFirstFix(); + clearSatellites(); + + SparseArray<GpsSatellite> otherSatellites = status.mSatellites; + int otherSatellitesCount = otherSatellites.size(); + int satelliteIndex = 0; + // merge both sparse arrays, note that we have already invalidated the elements in the + // receiver array + for (int i = 0; i < otherSatellitesCount; ++i) { + GpsSatellite otherSatellite = otherSatellites.valueAt(i); + int otherSatellitePrn = otherSatellite.getPrn(); + + int satellitesCount = mSatellites.size(); + while (satelliteIndex < satellitesCount + && mSatellites.valueAt(satelliteIndex).getPrn() < otherSatellitePrn) { + ++satelliteIndex; + } - for (int i = 0; i < mSatellites.length; i++) { - mSatellites[i].setStatus(status.mSatellites[i]); - } + if (satelliteIndex < mSatellites.size()) { + GpsSatellite satellite = mSatellites.valueAt(satelliteIndex); + if (satellite.getPrn() == otherSatellitePrn) { + satellite.setStatus(otherSatellite); + } else { + satellite = new GpsSatellite(otherSatellitePrn); + satellite.setStatus(otherSatellite); + mSatellites.put(otherSatellitePrn, satellite); + } + } else { + GpsSatellite satellite = new GpsSatellite(otherSatellitePrn); + satellite.setStatus(otherSatellite); + mSatellites.append(otherSatellitePrn, satellite); + } + } } void setTimeToFirstFix(int ttff) { @@ -183,7 +214,7 @@ public final class GpsStatus { } /** - * Returns the time required to receive the first fix since the most recent + * Returns the time required to receive the first fix since the most recent * restart of the GPS engine. * * @return time to first fix in milliseconds @@ -211,4 +242,12 @@ public final class GpsStatus { public int getMaxSatellites() { return NUM_SATELLITES; } + + private void clearSatellites() { + int satellitesCount = mSatellites.size(); + for (int i = 0; i < satellitesCount; i++) { + GpsSatellite satellite = mSatellites.valueAt(i); + satellite.mValid = false; + } + } } diff --git a/location/tests/locationtests/src/android/location/GpsStatusTest.java b/location/tests/locationtests/src/android/location/GpsStatusTest.java new file mode 100644 index 000000000000..4808fafa272e --- /dev/null +++ b/location/tests/locationtests/src/android/location/GpsStatusTest.java @@ -0,0 +1,356 @@ +/* + * 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.location; + +import junit.framework.TestCase; + +import android.test.suitebuilder.annotation.SmallTest; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Random; +import java.util.Set; + +/** + * Unit tests for {@link GpsStatus}. + */ +@SmallTest +public class GpsStatusTest extends TestCase { + + private static final int MAX_VALUE = 250; + + private final Random mRandom = new Random(); + + private GpsStatus mStatus; + private int mCount; + private int[] mPrns; + private float[] mSnrs; + private float[] mElevations; + private float[] mAzimuth; + private int mEphemerisMask; + private int mAlmanacMask; + private int mUsedInFixMask; + + public void setUp() throws Exception { + super.setUp(); + mStatus = createGpsStatus(); + generateSatellitesData(generateInt()); + } + + public void testEmptyGpsStatus() throws Exception { + verifyIsEmpty(mStatus); + } + + public void testGpsStatusIterator() throws Exception { + generateSatellitesData(2); + setSatellites(mStatus); + Iterator<GpsSatellite> iterator = mStatus.getSatellites().iterator(); + assertTrue("hasNext(1)", iterator.hasNext()); + assertTrue("hasNext(1) does not overflow", iterator.hasNext()); + GpsSatellite satellite1 = iterator.next(); + assertNotNull("satellite", satellite1); + assertTrue("hasNext(2)", iterator.hasNext()); + assertTrue("hasNext(2) does not overflow", iterator.hasNext()); + GpsSatellite satellite2 = iterator.next(); + assertNotNull("satellite", satellite2); + assertFalse("hasNext() no elements", iterator.hasNext()); + } + + public void testTtff() throws Exception { + int testTtff = generateInt(); + set(mStatus, testTtff); + verifyTtff(mStatus, testTtff); + } + + public void testCopyTtff() throws Exception { + int testTtff = generateInt(); + verifyTtff(mStatus, 0); + + GpsStatus otherStatus = createGpsStatus(); + set(otherStatus, testTtff); + verifyTtff(otherStatus, testTtff); + + set(mStatus, otherStatus); + verifyTtff(mStatus, testTtff); + } + + public void testSetSatellites() throws Exception { + setSatellites(mStatus); + verifySatellites(mStatus); + } + + public void testCopySatellites() throws Exception { + verifyIsEmpty(mStatus); + + GpsStatus otherStatus = createGpsStatus(); + setSatellites(otherStatus); + verifySatellites(otherStatus); + + set(mStatus, otherStatus); + verifySatellites(mStatus); + } + + public void testOverrideSatellites() throws Exception { + setSatellites(mStatus); + verifySatellites(mStatus); + + GpsStatus otherStatus = createGpsStatus(); + generateSatellitesData(mCount, true /* reusePrns */); + setSatellites(otherStatus); + verifySatellites(otherStatus); + + set(mStatus, otherStatus); + verifySatellites(mStatus); + } + + public void testAddSatellites() throws Exception { + int count = 10; + generateSatellitesData(count); + setSatellites(mStatus); + verifySatellites(mStatus); + + GpsStatus otherStatus = createGpsStatus(); + generateSatellitesData(count); + setSatellites(otherStatus); + verifySatellites(otherStatus); + + set(mStatus, otherStatus); + verifySatellites(mStatus); + } + + public void testAddMoreSatellites() throws Exception { + int count = 25; + generateSatellitesData(count); + setSatellites(mStatus); + verifySatellites(mStatus); + + GpsStatus otherStatus = createGpsStatus(); + generateSatellitesData(count * 2); + setSatellites(otherStatus); + verifySatellites(otherStatus); + + set(mStatus, otherStatus); + verifySatellites(mStatus); + } + + public void testAddLessSatellites() throws Exception { + int count = 25; + generateSatellitesData(count * 2); + setSatellites(mStatus); + verifySatellites(mStatus); + + GpsStatus otherStatus = createGpsStatus(); + generateSatellitesData(count); + setSatellites(otherStatus); + verifySatellites(otherStatus); + + set(mStatus, otherStatus); + verifySatellites(mStatus); + } + + private static void verifyIsEmpty(GpsStatus status) { + verifySatelliteCount(status, 0); + verifyTtff(status, 0); + } + + private static void verifySatelliteCount(GpsStatus status, int expectedCount) { + int satellites = 0; + for (GpsSatellite s : status.getSatellites()) { + ++satellites; + } + assertEquals("GpsStatus::SatelliteCount", expectedCount, satellites); + } + + private void verifySatellites(GpsStatus status) { + verifySatelliteCount(status, mCount); + verifySatellites(status, mCount, mPrns, mSnrs, mElevations, mAzimuth, mEphemerisMask, + mAlmanacMask, mUsedInFixMask); + } + + private static void verifySatellites( + GpsStatus status, + int count, + int[] prns, + float[] snrs, + float[] elevations, + float[] azimuth, + int ephemerisMask, + int almanacMask, + int usedInFixMask) { + for (int i = 0; i < count; ++i) { + int prn = prns[i]; + GpsSatellite satellite = getSatellite(status, prn); + assertNotNull(getSatelliteAssertInfo(i, prn, "non-null"), satellite); + assertEquals(getSatelliteAssertInfo(i, prn, "Snr"), snrs[i], satellite.getSnr()); + assertEquals( + getSatelliteAssertInfo(i, prn, "Elevation"), + elevations[i], + satellite.getElevation()); + assertEquals( + getSatelliteAssertInfo(i, prn, "Azimuth"), + azimuth[i], + satellite.getAzimuth()); + int prnShift = 1 << (prn - 1); + assertEquals( + getSatelliteAssertInfo(i, prn, "ephemeris"), + (ephemerisMask & prnShift) != 0, + satellite.hasEphemeris()); + assertEquals( + getSatelliteAssertInfo(i, prn, "almanac"), + (almanacMask & prnShift) != 0, + satellite.hasAlmanac()); + assertEquals( + getSatelliteAssertInfo(i, prn, "usedInFix"), + (usedInFixMask & prnShift) != 0, + satellite.usedInFix()); + } + } + + private static void verifyTtff(GpsStatus status, int expectedTtff) { + assertEquals("GpsStatus::TTFF", expectedTtff, status.getTimeToFirstFix()); + } + + private static GpsStatus createGpsStatus() throws Exception { + Constructor<GpsStatus> ctor = GpsStatus.class.getDeclaredConstructor(); + ctor.setAccessible(true); + return ctor.newInstance(); + } + + private static void set(GpsStatus status, int ttff) throws Exception { + Class<?> statusClass = status.getClass(); + Method setTtff = statusClass.getDeclaredMethod("setTimeToFirstFix", Integer.TYPE); + setTtff.setAccessible(true); + setTtff.invoke(status, ttff); + } + + private static void set(GpsStatus status, GpsStatus statusToSet) throws Exception { + Class<?> statusClass = status.getClass(); + Method setStatus = statusClass.getDeclaredMethod("setStatus", statusClass); + setStatus.setAccessible(true); + setStatus.invoke(status, statusToSet); + } + + private void setSatellites(GpsStatus status) throws Exception { + set(status, mCount, mPrns, mSnrs, mElevations, mAzimuth, mEphemerisMask, mAlmanacMask, + mUsedInFixMask); + } + + private static void set( + GpsStatus status, + int count, + int[] prns, + float[] snrs, + float[] elevations, + float[] azimuth, + int ephemerisMask, + int almanacMask, + int usedInFixMask) throws Exception { + Class<?> statusClass = status.getClass(); + Class<?> intClass = Integer.TYPE; + Class<?> floatArrayClass = Class.forName("[F"); + Method setStatus = statusClass.getDeclaredMethod( + "setStatus", + intClass, + Class.forName("[I"), + floatArrayClass, + floatArrayClass, + floatArrayClass, + intClass, + intClass, + intClass); + setStatus.setAccessible(true); + setStatus.invoke( + status, + count, + prns, + snrs, + elevations, + azimuth, + ephemerisMask, + almanacMask, + usedInFixMask); + } + + private int generateInt() { + return mRandom.nextInt(MAX_VALUE) + 1; + } + + private int[] generateIntArray(int count) { + Set<Integer> generatedPrns = new HashSet<>(); + int[] array = new int[count]; + for(int i = 0; i < count; ++i) { + int generated; + do { + generated = generateInt(); + } while (generatedPrns.contains(generated)); + array[i] = generated; + generatedPrns.add(generated); + } + return array; + } + + private float[] generateFloatArray(int count) { + float[] array = new float[count]; + for(int i = 0; i < count; ++i) { + array[i] = generateInt(); + } + return array; + } + + private int generateMask(int[] prns) { + int mask = 0; + int prnsLength = prns.length; + for (int i = 0; i < prnsLength; ++i) { + if (mRandom.nextBoolean()) { + mask |= 1 << (prns[i] - 1); + } + } + return mask; + } + + private void generateSatellitesData(int count) { + generateSatellitesData(count, false /* reusePrns */); + } + + private void generateSatellitesData(int count, boolean reusePrns) { + mCount = count; + if (!reusePrns) { + mPrns = generateIntArray(count); + } + mSnrs = generateFloatArray(count); + mElevations = generateFloatArray(count); + mAzimuth = generateFloatArray(count); + mEphemerisMask = generateMask(mPrns); + mAlmanacMask = generateMask(mPrns); + mUsedInFixMask = generateMask(mPrns); + } + + private static GpsSatellite getSatellite(GpsStatus status, int prn) { + for (GpsSatellite satellite : status.getSatellites()) { + if (satellite.getPrn() == prn) { + return satellite; + } + } + return null; + } + + private static String getSatelliteAssertInfo(int index, int prn, String param) { + return String.format("Satellite::%s [i=%d, prn=%d]", param, index, prn); + } +} diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 489f5524d934..ca242e4bcbe0 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -709,7 +709,13 @@ public final class AudioAttributes implements Parcelable { } } - /** @hide */ + /** + * @hide + * Only use to get which stream type should be used for volume control, NOT for audio playback + * (all audio playback APIs are supposed to take AudioAttributes as input parameters) + * @param aa non-null AudioAttributes. + * @return a valid stream type for volume control that matches the attributes. + */ public static int toLegacyStreamType(AudioAttributes aa) { // flags to stream type mapping if ((aa.getFlags() & FLAG_AUDIBILITY_ENFORCED) == FLAG_AUDIBILITY_ENFORCED) { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index f448dc235ccd..986ae46edb93 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -46,9 +46,9 @@ import android.provider.Settings; import android.util.Log; import android.view.KeyEvent; -import java.util.HashMap; import java.util.ArrayList; - +import java.util.HashMap; +import java.util.Iterator; /** * AudioManager provides access to volume and ringer mode control. @@ -3598,11 +3598,13 @@ public class AudioManager { newPorts.clear(); status = AudioSystem.listAudioPorts(newPorts, portGeneration); if (status != SUCCESS) { + Log.w(TAG, "updateAudioPortCache: listAudioPorts failed"); return status; } newPatches.clear(); status = AudioSystem.listAudioPatches(newPatches, patchGeneration); if (status != SUCCESS) { + Log.w(TAG, "updateAudioPortCache: listAudioPatches failed"); return status; } } while (patchGeneration[0] != portGeneration[0]); @@ -3611,18 +3613,33 @@ public class AudioManager { for (int j = 0; j < newPatches.get(i).sources().length; j++) { AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j], newPorts); - if (portCfg == null) { - return ERROR; - } newPatches.get(i).sources()[j] = portCfg; } for (int j = 0; j < newPatches.get(i).sinks().length; j++) { AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j], newPorts); + newPatches.get(i).sinks()[j] = portCfg; + } + } + for (Iterator<AudioPatch> i = newPatches.iterator(); i.hasNext(); ) { + AudioPatch newPatch = i.next(); + boolean hasInvalidPort = false; + for (AudioPortConfig portCfg : newPatch.sources()) { if (portCfg == null) { - return ERROR; + hasInvalidPort = true; + break; } - newPatches.get(i).sinks()[j] = portCfg; + } + for (AudioPortConfig portCfg : newPatch.sinks()) { + if (portCfg == null) { + hasInvalidPort = true; + break; + } + } + if (hasInvalidPort) { + // Temporarily remove patches with invalid ports. One who created the patch + // is responsible for dealing with the port change. + i.remove(); } } diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 5b922664e15e..bc9722e264b4 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -756,8 +756,9 @@ public final class TvContract { * </p><p> * Note that this sub-directory also supports opening the logo as an asset file in write * mode. Callers can create or replace the primary logo associated with this channel by - * opening the asset file and writing the full-size photo contents into it. When the file - * is closed, the image will be parsed, sized down if necessary, and stored. + * opening the asset file and writing the full-size photo contents into it. (Make sure there + * is no padding around the logo image.) When the file is closed, the image will be parsed, + * sized down if necessary, and stored. * </p><p> * Usage example: * <pre> diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index f900d0db0308..87f78c1bb448 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -28,6 +28,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; +import android.net.ProxyInfo; import android.net.TrafficStats; import android.net.Uri; import android.net.wifi.WifiInfo; @@ -656,12 +657,36 @@ public class NetworkMonitor extends StateMachine { int httpResponseCode = 599; try { URL url = new URL("http", mServer, "/generate_204"); + // On networks with a PAC instead of fetching a URL that should result in a 204 + // reponse, we instead simply fetch the PAC script. This is done for a few reasons: + // 1. At present our PAC code does not yet handle multiple PACs on multiple networks + // until something like https://android-review.googlesource.com/#/c/115180/ lands. + // Network.openConnection() will ignore network-specific PACs and instead fetch + // using NO_PROXY. If a PAC is in place, the only fetch we know will succeed with + // NO_PROXY is the fetch of the PAC itself. + // 2. To proxy the generate_204 fetch through a PAC would require a number of things + // happen before the fetch can commence, namely: + // a) the PAC script be fetched + // b) a PAC script resolver service be fired up and resolve mServer + // Network validation could be delayed until these prerequisities are satisifed or + // could simply be left to race them. Neither is an optimal solution. + // 3. PAC scripts are sometimes used to block or restrict Internet access and may in + // fact block fetching of the generate_204 URL which would lead to false negative + // results for network validation. + boolean fetchPac = false; + { + final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy(); + if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) { + url = new URL(proxyInfo.getPacFileUrl().toString()); + fetchPac = true; + } + } if (DBG) { log("Checking " + url.toString() + " on " + mNetworkAgentInfo.networkInfo.getExtraInfo()); } urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url); - urlConnection.setInstanceFollowRedirects(false); + urlConnection.setInstanceFollowRedirects(fetchPac); urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); urlConnection.setUseCaches(false); @@ -695,6 +720,11 @@ public class NetworkMonitor extends StateMachine { httpResponseCode = 204; } + if (httpResponseCode == 200 && fetchPac) { + if (DBG) log("PAC fetch 200 response interpreted as 204 response."); + httpResponseCode = 204; + } + sendNetworkConditionsBroadcast(true /* response received */, httpResponseCode != 204 /* isCaptivePortal */, requestTimestamp, responseTimestamp); |