Merge "Fix alpha animations in ViewPropAnimRT"
diff --git a/Android.mk b/Android.mk
index 56e9df6..8f7779e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -175,6 +175,7 @@
core/java/android/nfc/INfcAdapterExtras.aidl \
core/java/android/nfc/INfcTag.aidl \
core/java/android/nfc/INfcCardEmulation.aidl \
+ core/java/android/nfc/INfcLockscreenDispatch.aidl \
core/java/android/os/IBatteryPropertiesListener.aidl \
core/java/android/os/IBatteryPropertiesRegistrar.aidl \
core/java/android/os/ICancellationSignal.aidl \
diff --git a/api/current.txt b/api/current.txt
index ddde2f7..a263946 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2525,7 +2525,14 @@
public static final class R.transition {
ctor public R.transition();
+ field public static final int explode = 17760259; // 0x10f0003
+ field public static final int fade = 17760258; // 0x10f0002
+ field public static final int move = 17760257; // 0x10f0001
field public static final int no_transition = 17760256; // 0x10f0000
+ field public static final int slide_bottom = 17760260; // 0x10f0004
+ field public static final int slide_left = 17760263; // 0x10f0007
+ field public static final int slide_right = 17760262; // 0x10f0006
+ field public static final int slide_top = 17760261; // 0x10f0005
}
public static final class R.xml {
@@ -13798,6 +13805,7 @@
method public deprecated void setWiredHeadsetOn(boolean);
method public deprecated boolean shouldVibrate(int);
method public void startBluetoothSco();
+ method public void startBluetoothScoVirtualCall();
method public void stopBluetoothSco();
method public void unloadSoundEffects();
method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
@@ -14482,6 +14490,11 @@
method public java.lang.String getDefaultUrl();
}
+ public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
+ ctor public MediaDrm.MediaDrmStateException(int, java.lang.String);
+ method public int getErrorCode();
+ }
+
public static abstract interface MediaDrm.OnEventListener {
method public abstract void onEvent(android.media.MediaDrm, byte[], int, int, byte[]);
}
@@ -15350,6 +15363,19 @@
ctor public UnsupportedSchemeException(java.lang.String);
}
+ public abstract class VolumeProvider {
+ ctor public VolumeProvider(int, int);
+ method public final int getMaxVolume();
+ method public final int getVolumeControl();
+ method public final void notifyVolumeChanged();
+ method public void onAdjustVolumeBy(int);
+ method public abstract int onGetCurrentVolume();
+ method public void onSetVolumeTo(int);
+ field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+ field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+ field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+ }
+
}
package android.media.audiofx {
@@ -15716,6 +15742,7 @@
method public android.media.session.PlaybackState getPlaybackState();
method public int getRatingType();
method public android.media.session.MediaController.TransportControls getTransportControls();
+ method public android.media.session.MediaController.VolumeInfo getVolumeInfo();
method public void removeCallback(android.media.session.MediaController.Callback);
method public void sendControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
}
@@ -15739,6 +15766,14 @@
method public void stop();
}
+ public static final class MediaController.VolumeInfo {
+ method public int getAudioStream();
+ method public int getCurrentVolume();
+ method public int getMaxVolume();
+ method public int getVolumeControl();
+ method public int getVolumeType();
+ }
+
public final class MediaSession {
method public void addCallback(android.media.session.MediaSession.Callback);
method public void addCallback(android.media.session.MediaSession.Callback, android.os.Handler);
@@ -15756,9 +15791,11 @@
method public void setMetadata(android.media.MediaMetadata);
method public void setPlaybackState(android.media.session.PlaybackState);
method public void setPlaybackToLocal(int);
- method public void setPlaybackToRemote(android.media.session.RemoteVolumeProvider);
+ method public void setPlaybackToRemote(android.media.VolumeProvider);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+ field public static final int VOLUME_TYPE_LOCAL = 1; // 0x1
+ field public static final int VOLUME_TYPE_REMOTE = 2; // 0x2
}
public static abstract class MediaSession.Callback {
@@ -15829,19 +15866,6 @@
field public static final int STATE_STOPPED = 1; // 0x1
}
- public abstract class RemoteVolumeProvider {
- ctor public RemoteVolumeProvider(int, int);
- method public final int getMaxVolume();
- method public final int getVolumeControl();
- method public final void notifyVolumeChanged();
- method public void onAdjustVolumeBy(int);
- method public abstract int onGetCurrentVolume();
- method public void onSetVolumeTo(int);
- field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
- field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
- field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
- }
-
}
package android.media.tv {
@@ -17640,6 +17664,7 @@
method public boolean invokeBeam(android.app.Activity);
method public boolean isEnabled();
method public boolean isNdefPushEnabled();
+ method public boolean registerLockscreenDispatch(android.nfc.NfcAdapter.NfcLockscreenDispatch, int[]);
method public void setBeamPushUris(android.net.Uri[], android.app.Activity);
method public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
@@ -17675,6 +17700,10 @@
method public abstract android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
}
+ public static abstract interface NfcAdapter.NfcLockscreenDispatch {
+ method public abstract boolean onTagDetected(android.nfc.Tag);
+ }
+
public static abstract interface NfcAdapter.OnNdefPushCompleteCallback {
method public abstract void onNdefPushComplete(android.nfc.NfcEvent);
}
@@ -27797,7 +27826,6 @@
field public static final java.lang.String ACTION_CALL_SERVICE;
field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
field public static final java.lang.String ACTION_CALL_SERVICE_SELECTOR;
- field public static final java.lang.String ACTION_CHANGE_DEFAULT_PHONE = "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
@@ -27805,7 +27833,6 @@
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
- field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 6296dd1..6b2a0e2 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -52,13 +52,13 @@
);
}
-static SkBitmap::Config flinger2skia(PixelFormat f)
+static SkColorType flinger2skia(PixelFormat f)
{
switch (f) {
case PIXEL_FORMAT_RGB_565:
- return SkBitmap::kRGB_565_Config;
+ return kRGB_565_SkColorType;
default:
- return SkBitmap::kARGB_8888_Config;
+ return kN32_SkColorType;
}
}
@@ -174,9 +174,10 @@
if (base) {
if (png) {
+ const SkImageInfo info = SkImageInfo::Make(w, h, flinger2skia(f),
+ kPremul_SkAlphaType);
SkBitmap b;
- b.setConfig(flinger2skia(f), w, h, s*bytesPerPixel(f));
- b.setPixels((void*)base);
+ b.installPixels(info, const_cast<void*>(base), s*bytesPerPixel(f));
SkDynamicMemoryWStream stream;
SkImageEncoder::EncodeStream(&stream, b,
SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b78b9c9..c583998 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -188,6 +188,9 @@
endPerformanceSnapshot();
}
if (mPerfMetrics != null) {
+ if (results == null) {
+ results = new Bundle();
+ }
results.putAll(mPerfMetrics);
}
if (mUiAutomation != null) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3dfa78b..e24dc84 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -55,6 +55,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.Set;
/**
@@ -6821,72 +6822,12 @@
if (other == null) {
return false;
}
- if (mAction != other.mAction) {
- if (mAction != null) {
- if (!mAction.equals(other.mAction)) {
- return false;
- }
- } else {
- if (!other.mAction.equals(mAction)) {
- return false;
- }
- }
- }
- if (mData != other.mData) {
- if (mData != null) {
- if (!mData.equals(other.mData)) {
- return false;
- }
- } else {
- if (!other.mData.equals(mData)) {
- return false;
- }
- }
- }
- if (mType != other.mType) {
- if (mType != null) {
- if (!mType.equals(other.mType)) {
- return false;
- }
- } else {
- if (!other.mType.equals(mType)) {
- return false;
- }
- }
- }
- if (mPackage != other.mPackage) {
- if (mPackage != null) {
- if (!mPackage.equals(other.mPackage)) {
- return false;
- }
- } else {
- if (!other.mPackage.equals(mPackage)) {
- return false;
- }
- }
- }
- if (mComponent != other.mComponent) {
- if (mComponent != null) {
- if (!mComponent.equals(other.mComponent)) {
- return false;
- }
- } else {
- if (!other.mComponent.equals(mComponent)) {
- return false;
- }
- }
- }
- if (mCategories != other.mCategories) {
- if (mCategories != null) {
- if (!mCategories.equals(other.mCategories)) {
- return false;
- }
- } else {
- if (!other.mCategories.equals(mCategories)) {
- return false;
- }
- }
- }
+ if (!Objects.equals(this.mAction, other.mAction)) return false;
+ if (!Objects.equals(this.mData, other.mData)) return false;
+ if (!Objects.equals(this.mType, other.mType)) return false;
+ if (!Objects.equals(this.mPackage, other.mPackage)) return false;
+ if (!Objects.equals(this.mComponent, other.mComponent)) return false;
+ if (!Objects.equals(this.mCategories, other.mCategories)) return false;
return true;
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 44fc3b6..cc8503b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -167,7 +167,7 @@
private boolean mOneShot;
private boolean mWithBuffer;
private boolean mFaceDetectionRunning = false;
- private Object mAutoFocusCallbackLock = new Object();
+ private final Object mAutoFocusCallbackLock = new Object();
private static final int NO_ERROR = 0;
private static final int EACCESS = -13;
@@ -202,14 +202,14 @@
/**
* A constant meaning the normal camera connect/open will be used.
- * @hide
*/
- public static final int CAMERA_HAL_API_VERSION_NORMAL_OPEN = -2;
+ private static final int CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -2;
/**
* Used to indicate HAL version un-specified.
*/
private static final int CAMERA_HAL_API_VERSION_UNSPECIFIED = -1;
+
/**
* Hardware face detection. It does not use much CPU.
*/
@@ -376,18 +376,25 @@
*
* @param cameraId The hardware camera to access, between 0 and
* {@link #getNumberOfCameras()}-1.
- * @param halVersion The HAL API version this camera device to be opened as. When
- * it is {@value #CAMERA_HAL_API_VERSION_NORMAL_OPEN}, the methods will be equivalent
- * to {@link #open}, but more detailed error information will be returned to managed code.
+ * @param halVersion The HAL API version this camera device to be opened as.
* @return a new Camera object, connected, locked and ready for use.
+ *
+ * @throws IllegalArgumentException if the {@code halVersion} is invalid
+ *
* @throws RuntimeException if opening the camera fails (for example, if the
* camera is in use by another process or device policy manager has disabled
* the camera).
+ *
* @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
+ * @see #CAMERA_HAL_API_VERSION_1_0
*
* @hide
*/
public static Camera openLegacy(int cameraId, int halVersion) {
+ if (halVersion < CAMERA_HAL_API_VERSION_1_0) {
+ throw new IllegalArgumentException("Invalid HAL version " + halVersion);
+ }
+
return new Camera(cameraId, halVersion);
}
@@ -399,7 +406,7 @@
* @param halVersion The HAL API version this camera device to be opened as.
*/
private Camera(int cameraId, int halVersion) {
- int err = cameraInit(cameraId, halVersion);
+ int err = cameraInitVersion(cameraId, halVersion);
if (checkInitErrors(err)) {
switch(err) {
case EACCESS:
@@ -428,13 +435,7 @@
}
}
- private int cameraInit(int cameraId, int halVersion) {
- // This function should be only called by Camera(int cameraId, int halVersion).
- if (halVersion < CAMERA_HAL_API_VERSION_1_0 &&
- halVersion != CAMERA_HAL_API_VERSION_NORMAL_OPEN) {
- throw new IllegalArgumentException("Invalid HAL version " + halVersion);
- }
-
+ private int cameraInitVersion(int cameraId, int halVersion) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
@@ -457,8 +458,31 @@
return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
}
+ private int cameraInitNormal(int cameraId) {
+ return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
+ }
+
+ /**
+ * Connect to the camera service using #connectLegacy
+ *
+ * <p>
+ * This acts the same as normal except that it will return
+ * the detailed error code if open fails instead of
+ * converting everything into {@code NO_INIT}.</p>
+ *
+ * <p>Intended to use by the camera2 shim only, do <i>not</i> use this for other code.</p>
+ *
+ * @return a detailed errno error code, or {@code NO_ERROR} on success
+ *
+ * @hide
+ */
+ public int cameraInitUnspecified(int cameraId) {
+ return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_UNSPECIFIED);
+ }
+
+ /** used by Camera#open, Camera#open(int) */
Camera(int cameraId) {
- int err = cameraInit(cameraId);
+ int err = cameraInitNormal(cameraId);
if (checkInitErrors(err)) {
switch(err) {
case EACCESS:
@@ -472,32 +496,6 @@
}
}
- /**
- * @hide
- */
- public int cameraInit(int cameraId) {
- mShutterCallback = null;
- mRawImageCallback = null;
- mJpegCallback = null;
- mPreviewCallback = null;
- mPostviewCallback = null;
- mUsingPreviewAllocation = false;
- mZoomListener = null;
-
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else {
- mEventHandler = null;
- }
-
- String packageName = ActivityThread.currentPackageName();
-
- return native_setup(new WeakReference<Camera>(this), cameraId,
- CAMERA_HAL_API_VERSION_UNSPECIFIED, packageName);
- }
/**
* @hide
@@ -519,6 +517,7 @@
Camera() {
}
+ @Override
protected void finalize() {
release();
}
@@ -1056,7 +1055,7 @@
private class EventHandler extends Handler
{
- private Camera mCamera;
+ private final Camera mCamera;
public EventHandler(Camera c, Looper looper) {
super(looper);
@@ -2337,6 +2336,7 @@
* @hide
* @deprecated
*/
+ @Deprecated
public void dump() {
Log.e(TAG, "dump: size=" + mMap.size());
for (String k : mMap.keySet()) {
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4a87680..e2f88eb 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -579,6 +579,7 @@
* of the lens that can be focused correctly.</p>
* <p>If the lens is fixed-focus, this should be
* 0.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*/
public static final Key<Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE =
new Key<Float>("android.lens.info.minimumFocusDistance", float.class);
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 73188ff..9a3d806 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -278,14 +278,19 @@
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
int id = Integer.parseInt(cameraId);
try {
- mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
- USE_CALLING_UID, holder);
- cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
- } catch (CameraRuntimeException e) {
- if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+ if (supportsCamera2Api(cameraId)) {
+ // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
+ mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
+ USE_CALLING_UID, holder);
+ cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
+ } else {
// Use legacy camera implementation for HAL1 devices
Log.i(TAG, "Using legacy camera HAL.");
cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
+ }
+ } catch (CameraRuntimeException e) {
+ if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+ throw new AssertionError("Should've gone down the shim path");
} else if (e.getReason() == CameraAccessException.CAMERA_IN_USE ||
e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE ||
e.getReason() == CameraAccessException.CAMERA_DISABLED ||
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 2cfb611..5bc59dc 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -18,6 +18,7 @@
import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.TypeReference;
import android.os.Parcel;
import android.os.Parcelable;
@@ -280,7 +281,7 @@
@Override
public int hashCode() {
- return mSettings.hashCode();
+ return HashCodeHelpers.hashCode(mSettings, mSurfaceSet, mUserTag);
}
public static final Parcelable.Creator<CaptureRequest> CREATOR =
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d9f3af4..2e59eee 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -28,6 +28,8 @@
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
+import android.hardware.camera2.utils.CloseableLock;
+import android.hardware.camera2.utils.CloseableLock.ScopedLock;
import android.hardware.camera2.utils.LongParcelable;
import android.os.Handler;
import android.os.IBinder;
@@ -57,13 +59,14 @@
// TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
private ICameraDeviceUser mRemoteDevice;
- private final Object mLock = new Object();
+ private final CloseableLock mCloseLock;
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
private final StateListener mDeviceListener;
private volatile StateListener mSessionStateListener;
private final Handler mDeviceHandler;
+ private volatile boolean mClosing = false;
private boolean mInError = false;
private boolean mIdle = true;
@@ -100,7 +103,9 @@
private final Runnable mCallOnOpened = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onOpened(CameraDeviceImpl.this);
@@ -113,7 +118,9 @@
private final Runnable mCallOnUnconfigured = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onUnconfigured(CameraDeviceImpl.this);
@@ -126,7 +133,9 @@
private final Runnable mCallOnActive = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onActive(CameraDeviceImpl.this);
@@ -139,7 +148,9 @@
private final Runnable mCallOnBusy = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onBusy(CameraDeviceImpl.this);
@@ -150,20 +161,29 @@
};
private final Runnable mCallOnClosed = new Runnable() {
+ private boolean mClosedOnce = false;
+
@Override
public void run() {
+ if (mClosedOnce) {
+ throw new AssertionError("Don't post #onClosed more than once");
+ }
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onClosed(CameraDeviceImpl.this);
}
mDeviceListener.onClosed(CameraDeviceImpl.this);
+ mClosedOnce = true;
}
};
private final Runnable mCallOnIdle = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onIdle(CameraDeviceImpl.this);
@@ -176,7 +196,9 @@
private final Runnable mCallOnDisconnected = new Runnable() {
@Override
public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
StateListener sessionListener = mSessionStateListener;
if (sessionListener != null) {
sessionListener.onDisconnected(CameraDeviceImpl.this);
@@ -195,6 +217,7 @@
mDeviceListener = listener;
mDeviceHandler = handler;
mCharacteristics = characteristics;
+ mCloseLock = new CloseableLock(/*name*/"CD-" + mCameraId);
final int MAX_TAG_LEN = 23;
String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -210,8 +233,8 @@
}
public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
// TODO: Move from decorator to direct binder-mediated exceptions
- synchronized(mLock) {
// If setRemoteFailure already called, do nothing
if (mInError) return;
@@ -254,7 +277,9 @@
}
final int code = failureCode;
final boolean isError = failureIsError;
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed, can't go to error state
+
mInError = true;
mDeviceHandler.post(new Runnable() {
@Override
@@ -280,7 +305,7 @@
if (outputs == null) {
outputs = new ArrayList<Surface>();
}
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
@@ -344,7 +369,7 @@
public void createCaptureSession(List<Surface> outputs,
CameraCaptureSession.StateListener listener, Handler handler)
throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
if (DEBUG) {
Log.d(TAG, "createCaptureSession");
}
@@ -389,7 +414,7 @@
@Override
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
CameraMetadataNative templatedRequest = new CameraMetadataNative();
@@ -435,7 +460,7 @@
* starting and stopping repeating request and flushing.
*
* <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
- * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
+ * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
* If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
* is added to the list mFrameNumberRequestPairs.</p>
*
@@ -446,7 +471,7 @@
private void checkEarlyTriggerSequenceComplete(
final int requestId, final long lastFrameNumber) {
// lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
- // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
+ // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
final CaptureListenerHolder holder;
int index = mCaptureListenerMap.indexOfKey(requestId);
@@ -463,7 +488,7 @@
if (holder != null) {
if (DEBUG) {
- Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
+ Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
+ " request did not reach HAL");
}
@@ -480,10 +505,9 @@
|| lastFrameNumber > Integer.MAX_VALUE) {
throw new AssertionError(lastFrameNumber + " cannot be cast to int");
}
- holder.getListener().onCaptureSequenceCompleted(
+ holder.getListener().onCaptureSequenceAborted(
CameraDeviceImpl.this,
- requestId,
- lastFrameNumber);
+ requestId);
}
}
};
@@ -509,7 +533,7 @@
handler = checkHandler(handler);
}
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
int requestId;
@@ -581,7 +605,7 @@
@Override
public void stopRepeating() throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
if (mRepeatingRequestId != REQUEST_ID_NONE) {
@@ -612,7 +636,7 @@
private void waitUntilIdle() throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
if (mRepeatingRequestId != REQUEST_ID_NONE) {
throw new IllegalStateException("Active repeating request ongoing");
@@ -633,7 +657,7 @@
@Override
public void flush() throws CameraAccessException {
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
mDeviceHandler.post(mCallOnBusy);
@@ -656,8 +680,15 @@
@Override
public void close() {
- synchronized (mLock) {
+ mClosing = true;
+ // Acquire exclusive lock, close, release (idempotent)
+ mCloseLock.close();
+ /*
+ * The rest of this is safe, since no other methods will be able to execute
+ * (they will throw ISE instead; the callbacks will get dropped)
+ */
+ {
try {
if (mRemoteDevice != null) {
mRemoteDevice.disconnect();
@@ -805,7 +836,12 @@
// remove request from mCaptureListenerMap
final int requestId = frameNumberRequestPair.getValue();
final CaptureListenerHolder holder;
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) {
+ Log.w(TAG, "Camera closed while checking sequences");
+ return;
+ }
+
int index = mCaptureListenerMap.indexOfKey(requestId);
holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
: null;
@@ -890,9 +926,12 @@
@Override
public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
Runnable r = null;
- if (isClosed()) return;
- synchronized(mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) {
+ return; // Camera already closed
+ }
+
mInError = true;
switch (errorCode) {
case ERROR_CAMERA_DISCONNECTED:
@@ -914,25 +953,24 @@
break;
}
CameraDeviceImpl.this.mDeviceHandler.post(r);
- }
- // Fire onCaptureSequenceCompleted
- if (DEBUG) {
- Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+ // Fire onCaptureSequenceCompleted
+ if (DEBUG) {
+ Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+ }
+ mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
+ checkAndFireSequenceComplete();
}
- mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
- checkAndFireSequenceComplete();
-
}
@Override
public void onCameraIdle() {
- if (isClosed()) return;
-
if (DEBUG) {
Log.d(TAG, "Camera now idle");
}
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
if (!CameraDeviceImpl.this.mIdle) {
CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
}
@@ -948,30 +986,33 @@
}
final CaptureListenerHolder holder;
- // Get the listener for this frame ID, if there is one
- synchronized (mLock) {
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
+
+ // Get the listener for this frame ID, if there is one
holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
- }
- if (holder == null) {
- return;
- }
+ if (holder == null) {
+ return;
+ }
- if (isClosed()) return;
+ if (isClosed()) return;
- // Dispatch capture start notice
- holder.getHandler().post(
- new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
- holder.getListener().onCaptureStarted(
- CameraDeviceImpl.this,
- holder.getRequest(resultExtras.getSubsequenceId()),
- timestamp);
+ // Dispatch capture start notice
+ holder.getHandler().post(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ holder.getListener().onCaptureStarted(
+ CameraDeviceImpl.this,
+ holder.getRequest(resultExtras.getSubsequenceId()),
+ timestamp);
+ }
}
- }
- });
+ });
+
+ }
}
@Override
@@ -984,88 +1025,91 @@
+ requestId);
}
+ try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+ if (scopedLock == null) return; // Camera already closed
- // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
- result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
- getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
+ // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
+ result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
+ getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
- final CaptureListenerHolder holder;
- synchronized (mLock) {
- holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
- }
+ final CaptureListenerHolder holder =
+ CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
- Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
- boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
+ Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
+ boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
- // Update tracker (increment counter) when it's not a partial result.
- if (!quirkIsPartialResult) {
- mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
- }
-
- // Check if we have a listener for this
- if (holder == null) {
- if (DEBUG) {
- Log.d(TAG,
- "holder is null, early return at frame "
- + resultExtras.getFrameNumber());
+ // Update tracker (increment counter) when it's not a partial result.
+ if (!quirkIsPartialResult) {
+ mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(),
+ /*error*/false);
}
- return;
- }
- if (isClosed()) {
- if (DEBUG) {
- Log.d(TAG,
- "camera is closed, early return at frame "
- + resultExtras.getFrameNumber());
+ // Check if we have a listener for this
+ if (holder == null) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "holder is null, early return at frame "
+ + resultExtras.getFrameNumber());
+ }
+ return;
}
- return;
- }
- final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
-
-
- Runnable resultDispatch = null;
-
- // Either send a partial result or the final capture completed result
- if (quirkIsPartialResult) {
- final CaptureResult resultAsCapture =
- new CaptureResult(result, request, requestId);
-
- // Partial result
- resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()){
- holder.getListener().onCapturePartial(
- CameraDeviceImpl.this,
- request,
- resultAsCapture);
- }
+ if (isClosed()) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "camera is closed, early return at frame "
+ + resultExtras.getFrameNumber());
}
- };
- } else {
- final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, requestId);
+ return;
+ }
- // Final capture result
- resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()){
- holder.getListener().onCaptureCompleted(
- CameraDeviceImpl.this,
- request,
- resultAsCapture);
+ final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
+
+
+ Runnable resultDispatch = null;
+
+ // Either send a partial result or the final capture completed result
+ if (quirkIsPartialResult) {
+ final CaptureResult resultAsCapture =
+ new CaptureResult(result, request, requestId);
+
+ // Partial result
+ resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getListener().onCapturePartial(
+ CameraDeviceImpl.this,
+ request,
+ resultAsCapture);
+ }
}
- }
- };
- }
+ };
+ } else {
+ final TotalCaptureResult resultAsCapture =
+ new TotalCaptureResult(result, request, requestId);
- holder.getHandler().post(resultDispatch);
+ // Final capture result
+ resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getListener().onCaptureCompleted(
+ CameraDeviceImpl.this,
+ request,
+ resultAsCapture);
+ }
+ }
+ };
+ }
- // Fire onCaptureSequenceCompleted
- if (!quirkIsPartialResult) {
- checkAndFireSequenceComplete();
+ holder.getHandler().post(resultDispatch);
+
+ // Fire onCaptureSequenceCompleted
+ if (!quirkIsPartialResult) {
+ checkAndFireSequenceComplete();
+ }
+
}
}
@@ -1101,10 +1145,9 @@
}
}
+ /** Whether the camera device has started to close (may not yet have finished) */
private boolean isClosed() {
- synchronized(mLock) {
- return (mRemoteDevice == null);
- }
+ return mClosing;
}
private CameraCharacteristics getCharacteristics() {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index aa2f026..abd69c1 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -76,13 +76,11 @@
}
// TODO: Move open/init into LegacyCameraDevice thread when API is switched to async.
Camera legacyCamera = Camera.openUninitialized();
- int initErrors = legacyCamera.cameraInit(cameraId);
+ int initErrors = legacyCamera.cameraInitUnspecified(cameraId);
+
// Check errors old HAL initialization
- if (Camera.checkInitErrors(initErrors)) {
- // TODO: Map over old camera error codes. This likely involves improving the error
- // reporting in the HAL1 connect path.
- throw new CameraRuntimeException(CameraAccessException.CAMERA_DISCONNECTED);
- }
+ CameraBinderDecorator.throwOnError(initErrors);
+
LegacyCameraDevice device = new LegacyCameraDevice(cameraId, legacyCamera, callbacks);
return new CameraDeviceUserShim(cameraId, device);
}
@@ -169,6 +167,7 @@
}
int numSurfaces = mSurfaces.size();
if (numSurfaces > 0) {
+ surfaces = new ArrayList<>();
for (int i = 0; i < numSurfaces; ++i) {
surfaces.add(mSurfaces.valueAt(i));
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 3f9c6ed..50515a2 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -23,7 +23,6 @@
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.utils.LongParcelable;
import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -34,7 +33,8 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
+
+import static android.hardware.camera2.utils.CameraBinderDecorator.*;
/**
* This class emulates the functionality of a Camera2 device using a the old Camera class.
@@ -54,6 +54,7 @@
private final int mCameraId;
private final ICameraDeviceCallbacks mDeviceCallbacks;
private final CameraDeviceState mDeviceState = new CameraDeviceState();
+ private List<Surface> mConfiguredSurfaces;
private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
@@ -213,16 +214,35 @@
/**
* Configure the device with a set of output surfaces.
*
+ * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
+ *
+ * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
+ *
* @param outputs a list of surfaces to set.
- * @return an error code for this binder operation, or {@link CameraBinderDecorator.NO_ERROR}
+ * @return an error code for this binder operation, or {@link NO_ERROR}
* on success.
*/
public int configureOutputs(List<Surface> outputs) {
+ if (outputs != null) {
+ for (Surface output : outputs) {
+ if (output == null) {
+ Log.e(TAG, "configureOutputs - null outputs are not allowed");
+ return BAD_VALUE;
+ }
+ }
+ }
+
int error = mDeviceState.setConfiguring();
- if (error == CameraBinderDecorator.NO_ERROR) {
+ if (error == NO_ERROR) {
mRequestThreadManager.configure(outputs);
error = mDeviceState.setIdle();
}
+
+ // TODO: May also want to check the surfaces more deeply (e.g. state, formats, sizes..)
+ if (error == NO_ERROR) {
+ mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
+ }
+
return error;
}
@@ -239,7 +259,35 @@
*/
public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
/*out*/LongParcelable frameNumber) {
- // TODO: validate request here
+ if (requestList == null || requestList.isEmpty()) {
+ Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
+ return BAD_VALUE;
+ }
+
+ // Make sure that there all requests have at least 1 surface; all surfaces are non-null
+ for (CaptureRequest request : requestList) {
+ if (request.getTargets().isEmpty()) {
+ Log.e(TAG, "submitRequestList - "
+ + "Each request must have at least one Surface target");
+ return BAD_VALUE;
+ }
+
+ for (Surface surface : request.getTargets()) {
+ if (surface == null) {
+ Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
+ return BAD_VALUE;
+ } else if (mConfiguredSurfaces == null) {
+ Log.e(TAG, "submitRequestList - must configure " +
+ " device with valid surfaces before submitting requests");
+ return INVALID_OPERATION;
+ } else if (!mConfiguredSurfaces.contains(surface)) {
+ Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
+ return BAD_VALUE;
+ }
+ }
+ }
+
+ // TODO: further validation of request here
mIdle.close();
return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
frameNumber);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index e675f87..048878c 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -21,12 +21,16 @@
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Size;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
import android.util.Log;
+import android.util.Range;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import static com.android.internal.util.Preconditions.*;
@@ -44,6 +48,9 @@
private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
+ // for metadata
+ private static final float LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS = 0.0f;
+
private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // ms
private static final long APPROXIMATE_SENSOR_AREA = (1 << 20); // 8mp
private static final long APPROXIMATE_JPEG_ENCODE_TIME = 600; // ms
@@ -90,8 +97,12 @@
}
private static void mapCameraParameters(CameraMetadataNative m, Camera.Parameters p) {
+ m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
mapStreamConfigs(m, p);
-
+ mapAeConfig(m, p);
+ mapCapabilities(m, p);
+ mapLens(m, p);
+ mapFlash(m, p);
// TODO: map other fields
}
@@ -137,12 +148,85 @@
StreamConfigurationDuration[] jpegStalls =
new StreamConfigurationDuration[jpegSizes.size()];
int i = 0;
+ long longestStallDuration = -1;
for (Camera.Size s : jpegSizes) {
+ long stallDuration = calculateJpegStallDuration(s);
jpegStalls[i++] = new StreamConfigurationDuration(HAL_PIXEL_FORMAT_BLOB, s.width,
- s.height, calculateJpegStallDuration(s));
+ s.height, stallDuration);
+ if (longestStallDuration < stallDuration) {
+ longestStallDuration = stallDuration;
+ }
}
// Set stall durations for jpeg, other formats use default stall duration
m.set(SCALER_AVAILABLE_STALL_DURATIONS, jpegStalls);
+
+ m.set(SENSOR_INFO_MAX_FRAME_DURATION, longestStallDuration);
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private static void mapAeConfig(CameraMetadataNative m, Camera.Parameters p) {
+
+ List<int[]> fpsRanges = p.getSupportedPreviewFpsRange();
+ if (fpsRanges == null) {
+ throw new AssertionError("Supported FPS ranges cannot be null.");
+ }
+ int rangesSize = fpsRanges.size();
+ if (rangesSize <= 0) {
+ throw new AssertionError("At least one FPS range must be supported.");
+ }
+ Range<Integer>[] ranges = new Range[rangesSize];
+ int i = 0;
+ for (int[] r : fpsRanges) {
+ ranges[i++] = Range.create(r[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ r[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ }
+ m.set(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, ranges);
+
+ List<String> antiBandingModes = p.getSupportedAntibanding();
+ int antiBandingModesSize = antiBandingModes.size();
+ if (antiBandingModesSize > 0) {
+ int[] modes = new int[antiBandingModesSize];
+ int j = 0;
+ for (String mode : antiBandingModes) {
+ int convertedMode = convertAntiBandingMode(mode);
+ if (convertedMode == -1) {
+ Log.w(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
+ " not supported, skipping...");
+ } else {
+ modes[j++] = convertedMode;
+ }
+ }
+ m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, Arrays.copyOf(modes, j));
+ }
+ }
+
+ private static void mapCapabilities(CameraMetadataNative m, Camera.Parameters p) {
+ int[] capabilities = { REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE };
+ m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
+ }
+
+ private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
+ /*
+ * We can tell if the lens is fixed focus;
+ * but if it's not, we can't tell the minimum focus distance, so leave it null then.
+ */
+ if (p.getFocusMode() == Camera.Parameters.FOCUS_MODE_FIXED) {
+ m.set(LENS_INFO_MINIMUM_FOCUS_DISTANCE, LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS);
+ }
+ }
+
+ private static void mapFlash(CameraMetadataNative m, Camera.Parameters p) {
+ boolean flashAvailable = false;
+ List<String> supportedFlashModes = p.getSupportedFlashModes();
+ if (supportedFlashModes != null) {
+ // If only 'OFF' is available, we don't really have flash support
+ if (!(supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_OFF) &&
+ supportedFlashModes.size() == 1)) {
+ flashAvailable = true;
+ }
+ }
+
+ m.set(FLASH_INFO_AVAILABLE, flashAvailable);
}
private static void appendStreamConfig(
@@ -155,6 +239,63 @@
}
/**
+ * Returns -1 if the anti-banding mode string is null, or not supported.
+ */
+ private static int convertAntiBandingMode(final String mode) {
+ if (mode == null) {
+ return -1;
+ }
+ switch(mode) {
+ case Camera.Parameters.ANTIBANDING_OFF: {
+ return CONTROL_AE_ANTIBANDING_MODE_OFF;
+ }
+ case Camera.Parameters.ANTIBANDING_50HZ: {
+ return CONTROL_AE_ANTIBANDING_MODE_50HZ;
+ }
+ case Camera.Parameters.ANTIBANDING_60HZ: {
+ return CONTROL_AE_ANTIBANDING_MODE_60HZ;
+ }
+ case Camera.Parameters.ANTIBANDING_AUTO: {
+ return CONTROL_AE_ANTIBANDING_MODE_AUTO;
+ }
+ default: {
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * Returns null if the anti-banding mode enum is not supported.
+ */
+ private static String convertAntiBandingModeToLegacy(int mode) {
+ switch(mode) {
+ case CONTROL_AE_ANTIBANDING_MODE_OFF: {
+ return Camera.Parameters.ANTIBANDING_OFF;
+ }
+ case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
+ return Camera.Parameters.ANTIBANDING_50HZ;
+ }
+ case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
+ return Camera.Parameters.ANTIBANDING_60HZ;
+ }
+ case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
+ return Camera.Parameters.ANTIBANDING_AUTO;
+ }
+ default: {
+ return null;
+ }
+ }
+ }
+
+
+ private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
+ int[] legacyFps = new int[2];
+ legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
+ legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
+ return legacyFps;
+ }
+
+ /**
* Return the stall duration for a given output jpeg size in nanoseconds.
*
* <p>An 8mp image is chosen to have a stall duration of 0.8 seconds.</p>
@@ -166,4 +307,45 @@
APPROXIMATE_SENSOR_AREA; // 600ms stall for 8mp
return baseDuration + area * stallPerArea;
}
+
+ /**
+ * Generate capture result metadata from legacy camera parameters.
+ *
+ * @param params a {@link Camera.Parameters} object to generate metadata from.
+ * @param request the {@link CaptureRequest} used for this result.
+ * @param timestamp the timestamp to use for this result in nanoseconds.
+ * @return a {@link CameraMetadataNative} object containing result metadata.
+ */
+ public static CameraMetadataNative convertResultMetadata(Camera.Parameters params,
+ CaptureRequest request,
+ long timestamp) {
+ CameraMetadataNative result = new CameraMetadataNative();
+ result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
+ result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
+
+ // TODO: Remaining result metadata tags conversions.
+ return result;
+ }
+
+ /**
+ * Set the legacy parameters using the request metadata.
+ *
+ * @param request a {@link CaptureRequest} object to generate parameters from.
+ * @param params the a {@link Camera.Parameters} to set parameters in.
+ */
+ public static void convertRequestMetadata(CaptureRequest request,
+ /*out*/Camera.Parameters params) {
+ Integer antiBandingMode = request.get(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE);
+ if (antiBandingMode != null) {
+ String legacyMode = convertAntiBandingModeToLegacy(antiBandingMode);
+ if (legacyMode != null) params.setAntibanding(legacyMode);
+ }
+
+ Range<Integer> aeFpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+ if (aeFpsRange != null) {
+ int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
+ params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ }
+ }
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index bf250a1..e0f3429 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -16,11 +16,9 @@
package android.hardware.camera2.legacy;
-import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.utils.LongParcelable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.os.ConditionVariable;
@@ -76,8 +74,8 @@
private volatile RequestHolder mInFlightPreview;
private volatile RequestHolder mInFlightJpeg;
- private List<Surface> mPreviewOutputs = new ArrayList<Surface>();
- private List<Surface> mCallbackOutputs = new ArrayList<Surface>();
+ private final List<Surface> mPreviewOutputs = new ArrayList<Surface>();
+ private final List<Surface> mCallbackOutputs = new ArrayList<Surface>();
private GLThreadManager mGLThreadManager;
private SurfaceTexture mPreviewTexture;
private Camera.Parameters mParams;
@@ -85,6 +83,7 @@
private Size mIntermediateBufferSize;
private final RequestQueue mRequestQueue = new RequestQueue();
+ private CaptureRequest mLastRequest = null;
private SurfaceTexture mDummyTexture;
private Surface mDummySurface;
@@ -315,16 +314,17 @@
mInFlightPreview = null;
mInFlightJpeg = null;
-
- for (Surface s : outputs) {
- int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
- switch (format) {
- case CameraMetadataNative.NATIVE_JPEG_FORMAT:
- mCallbackOutputs.add(s);
- break;
- default:
- mPreviewOutputs.add(s);
- break;
+ if (outputs != null) {
+ for (Surface s : outputs) {
+ int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
+ switch (format) {
+ case CameraMetadataNative.NATIVE_JPEG_FORMAT:
+ mCallbackOutputs.add(s);
+ break;
+ default:
+ mPreviewOutputs.add(s);
+ break;
+ }
}
}
mParams = mCamera.getParameters();
@@ -370,8 +370,6 @@
}
}
-
-
// TODO: Detect and optimize single-output paths here to skip stream teeing.
if (mGLThreadManager == null) {
mGLThreadManager = new GLThreadManager(mCameraId);
@@ -432,7 +430,6 @@
private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
private boolean mCleanup = false;
- private List<RequestHolder> mRepeating = null;
@SuppressWarnings("unchecked")
@Override
@@ -447,7 +444,8 @@
switch (msg.what) {
case MSG_CONFIGURE_OUTPUTS:
ConfigureHolder config = (ConfigureHolder) msg.obj;
- Log.i(TAG, "Configure outputs: " + config.surfaces.size() +
+ int sizes = config.surfaces != null ? config.surfaces.size() : 0;
+ Log.i(TAG, "Configure outputs: " + sizes +
" surfaces configured.");
try {
configureOutputs(config.surfaces);
@@ -475,6 +473,13 @@
List<RequestHolder> requests =
nextBurst.first.produceRequestHolders(nextBurst.second);
for (RequestHolder holder : requests) {
+ CaptureRequest request = holder.getRequest();
+ if (mLastRequest == null || mLastRequest != request) {
+ mLastRequest = request;
+ LegacyMetadataMapper.convertRequestMetadata(mLastRequest,
+ /*out*/mParams);
+ mCamera.setParameters(mParams);
+ }
mDeviceState.setCaptureStart(holder);
long timestamp = 0;
try {
@@ -502,8 +507,8 @@
// TODO: err handling
throw new IOError(e);
}
- CameraMetadataNative result = convertResultMetadata(mParams,
- holder.getRequest(), timestamp);
+ CameraMetadataNative result = LegacyMetadataMapper.convertResultMetadata(mParams,
+ request, timestamp);
mDeviceState.setCaptureResult(holder, result);
}
if (DEBUG) {
@@ -527,17 +532,6 @@
}
};
- private CameraMetadataNative convertResultMetadata(Camera.Parameters params,
- CaptureRequest request,
- long timestamp) {
- CameraMetadataNative result = new CameraMetadataNative();
- result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
- result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
-
- // TODO: Remaining result metadata tags conversions.
- return result;
- }
-
/**
* Create a new RequestThreadManager.
*
@@ -620,12 +614,14 @@
/**
- * Configure with the current output Surfaces.
+ * Configure with the current list of output Surfaces.
*
* <p>
* This operation blocks until the configuration is complete.
* </p>
*
+ * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
+ *
* @param outputs a {@link java.util.Collection} of outputs to configure.
*/
public void configure(Collection<Surface> outputs) {
diff --git a/core/java/android/hardware/camera2/utils/CloseableLock.java b/core/java/android/hardware/camera2/utils/CloseableLock.java
new file mode 100644
index 0000000..af55055
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/CloseableLock.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.utils;
+
+import android.util.Log;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Implement a shared/exclusive lock that can be closed.
+ *
+ * <p>A shared lock can be acquired if any other shared locks are also acquired. An
+ * exclusive lock acquire will block until all shared locks have been released.</p>
+ *
+ * <p>Locks are re-entrant; trying to acquire another lock (of the same type)
+ * while a lock is already held will immediately succeed.</p>
+ *
+ * <p>Acquiring to acquire a shared lock while holding an exclusive lock or vice versa is not
+ * supported; attempting it will throw an {@link IllegalStateException}.</p>
+ *
+ * <p>If the lock is closed, all future and current acquires will immediately return {@code null}.
+ * </p>
+ */
+public class CloseableLock implements AutoCloseable {
+
+ private static final boolean VERBOSE = false;
+
+ private final String TAG = "CloseableLock";
+ private final String mName;
+
+ private volatile boolean mClosed = false;
+
+ /** If an exclusive lock is acquired by some thread. */
+ private boolean mExclusive = false;
+ /**
+ * How many shared locks are acquired by any thread:
+ *
+ * <p>Reentrant locking increments this. If an exclusive lock is held,
+ * this value will stay at 0.</p>
+ */
+ private int mSharedLocks = 0;
+
+ private final ReentrantLock mLock = new ReentrantLock();
+ /** This condition automatically releases mLock when waiting; re-acquiring it after notify */
+ private final Condition mCondition = mLock.newCondition();
+
+ /** How many times the current thread is holding the lock */
+ private final ThreadLocal<Integer> mLockCount =
+ new ThreadLocal<Integer>() {
+ @Override protected Integer initialValue() {
+ return 0;
+ }
+ };
+
+ /**
+ * Helper class to release a lock at the end of a try-with-resources statement.
+ */
+ public class ScopedLock implements AutoCloseable {
+ private ScopedLock() {}
+
+ /** Release the lock with {@link CloseableLock#releaseLock}. */
+ @Override
+ public void close() {
+ releaseLock();
+ }
+ }
+
+ /**
+ * Create a new instance; starts out with 0 locks acquired.
+ */
+ public CloseableLock() {
+ mName = "";
+ }
+
+ /**
+ * Create a new instance; starts out with 0 locks acquired.
+ *
+ * @param name set an optional name for logging functionality
+ */
+ public CloseableLock(String name) {
+ mName = name;
+ }
+
+ /**
+ * Acquires the lock exclusively (blocking), marks it as closed, then releases the lock.
+ *
+ * <p>Marking a lock as closed will fail all further acquisition attempts;
+ * it will also immediately unblock all other threads currently trying to acquire a lock.</p>
+ *
+ * <p>This operation is idempotent; calling it more than once has no effect.</p>
+ *
+ * @throws IllegalStateException
+ * if an attempt is made to {@code close} while this thread has a lock acquired
+ */
+ @Override
+ public void close() {
+ if (mClosed) {
+ log("close - already closed; ignoring");
+ return;
+ }
+
+ ScopedLock scoper = acquireExclusiveLock();
+ // Already closed by another thread?
+ if (scoper == null) {
+ return;
+ } else if (mLockCount.get() != 1) {
+ // Future: may want to add a #releaseAndClose to allow this.
+ throw new IllegalStateException(
+ "Cannot close while one or more acquired locks are being held by this " +
+ "thread; release all other locks first");
+ }
+
+ try {
+ mLock.lock();
+
+ mClosed = true;
+ mExclusive = false;
+ mSharedLocks = 0;
+ mLockCount.remove();
+
+ // Notify all threads that are waiting to unblock and return immediately
+ mCondition.signalAll();
+ } finally {
+ mLock.unlock();
+ }
+
+ log("close - completed");
+ }
+
+ /**
+ * Try to acquire the lock non-exclusively, blocking until the operation completes.
+ *
+ * <p>If the lock has already been closed, or being closed before this operation returns,
+ * the call will immediately return {@code false}.</p>
+ *
+ * <p>If other threads hold a non-exclusive lock (and the lock is not yet closed),
+ * this operation will return immediately. If another thread holds an exclusive lock,
+ * this thread will block until the exclusive lock has been released.</p>
+ *
+ * <p>This lock is re-entrant; acquiring more than one non-exclusive lock per thread is
+ * supported, and must be matched by an equal number of {@link #releaseLock} calls.</p>
+ *
+ * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock
+ * was already closed.
+ *
+ * @throws IllegalStateException if this thread is already holding an exclusive lock
+ */
+ public ScopedLock acquireLock() {
+
+ int ownedLocks;
+
+ try {
+ mLock.lock();
+
+ // Lock is already closed, all further acquisitions will fail
+ if (mClosed) {
+ log("acquire lock early aborted (already closed)");
+ return null;
+ }
+
+ ownedLocks = mLockCount.get();
+
+ // This thread is already holding an exclusive lock
+ if (mExclusive && ownedLocks > 0) {
+ throw new IllegalStateException(
+ "Cannot acquire shared lock while holding exclusive lock");
+ }
+
+ // Is another thread holding the exclusive lock? Block until we can get in.
+ while (mExclusive) {
+ mCondition.awaitUninterruptibly();
+
+ // Did another thread #close while we were waiting? Unblock immediately.
+ if (mClosed) {
+ log("acquire lock unblocked aborted (already closed)");
+ return null;
+ }
+ }
+
+ mSharedLocks++;
+
+ ownedLocks = mLockCount.get() + 1;
+ mLockCount.set(ownedLocks);
+ } finally {
+ mLock.unlock();
+ }
+
+ log("acquired lock (local own count = " + ownedLocks + "");
+ return new ScopedLock();
+ }
+
+ /**
+ * Try to acquire the lock exclusively, blocking until all other threads release their locks.
+ *
+ * <p>If the lock has already been closed, or being closed before this operation returns,
+ * the call will immediately return {@code false}.</p>
+ *
+ * <p>If any other threads are holding a lock, this thread will block until all
+ * other locks are released.</p>
+ *
+ * <p>This lock is re-entrant; acquiring more than one exclusive lock per thread is supported,
+ * and must be matched by an equal number of {@link #releaseLock} calls.</p>
+ *
+ * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock
+ * was already closed.
+ *
+ * @throws IllegalStateException
+ * if an attempt is made to acquire an exclusive lock while already holding a lock
+ */
+ public ScopedLock acquireExclusiveLock() {
+
+ int ownedLocks;
+
+ try {
+ mLock.lock();
+
+ // Lock is already closed, all further acquisitions will fail
+ if (mClosed) {
+ log("acquire exclusive lock early aborted (already closed)");
+ return null;
+ }
+
+ ownedLocks = mLockCount.get();
+
+ // This thread is already holding a shared lock
+ if (!mExclusive && ownedLocks > 0) {
+ throw new IllegalStateException(
+ "Cannot acquire exclusive lock while holding shared lock");
+ }
+
+ /*
+ * Is another thread holding the lock? Block until we can get in.
+ *
+ * If we are already holding the lock, always let it through since
+ * we are just reentering the exclusive lock.
+ */
+ while (ownedLocks == 0 && (mExclusive || mSharedLocks > 0)) {
+ mCondition.awaitUninterruptibly();
+
+ // Did another thread #close while we were waiting? Unblock immediately.
+ if (mClosed) {
+ log("acquire exclusive lock unblocked aborted (already closed)");
+ return null;
+ }
+ }
+
+ mExclusive = true;
+
+ ownedLocks = mLockCount.get() + 1;
+ mLockCount.set(ownedLocks);
+ } finally {
+ mLock.unlock();
+ }
+
+ log("acquired exclusive lock (local own count = " + ownedLocks + "");
+ return new ScopedLock();
+ }
+
+ /**
+ * Release a single lock that was acquired.
+ *
+ * <p>Any other other that is blocked and trying to acquire a lock will get a chance
+ * to acquire the lock.</p>
+ *
+ * @throws IllegalStateException if no locks were acquired, or if the lock was already closed
+ */
+ public void releaseLock() {
+ if (mLockCount.get() <= 0) {
+ throw new IllegalStateException(
+ "Cannot release lock that was not acquired by this thread");
+ }
+
+ int ownedLocks;
+
+ try {
+ mLock.lock();
+
+ // Lock is already closed, it couldn't have been acquired in the first place
+ if (mClosed) {
+ throw new IllegalStateException("Do not release after the lock has been closed");
+ }
+
+ if (!mExclusive) {
+ mSharedLocks--;
+ } else {
+ if (mSharedLocks != 0) {
+ throw new AssertionError("Too many shared locks " + mSharedLocks);
+ }
+ }
+
+ ownedLocks = mLockCount.get() - 1;
+ mLockCount.set(ownedLocks);
+
+ if (ownedLocks == 0 && mExclusive) {
+ // Wake up any threads that might be waiting for the exclusive lock to be released
+ mExclusive = false;
+ mCondition.signalAll();
+ } else if (ownedLocks == 0 && mSharedLocks == 0) {
+ // Wake up any threads that might be trying to get the exclusive lock
+ mCondition.signalAll();
+ }
+ } finally {
+ mLock.unlock();
+ }
+
+ log("released lock (local lock count " + ownedLocks + ")");
+ }
+
+ private void log(String what) {
+ if (VERBOSE) {
+ Log.v(TAG + "[" + mName + "]", what);
+ }
+ }
+
+}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 9218c11..53e87a6 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -25,6 +25,7 @@
import android.nfc.INfcAdapterExtras;
import android.nfc.INfcTag;
import android.nfc.INfcCardEmulation;
+import android.nfc.INfcLockscreenDispatch;
import android.os.Bundle;
/**
@@ -52,4 +53,6 @@
void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
void setP2pModes(int initatorModes, int targetModes);
+
+ void registerLockscreenDispatch(INfcLockscreenDispatch lockscreenDispatch, in int[] techList);
}
diff --git a/core/java/android/nfc/INfcLockscreenDispatch.aidl b/core/java/android/nfc/INfcLockscreenDispatch.aidl
new file mode 100644
index 0000000..27e9a8c
--- /dev/null
+++ b/core/java/android/nfc/INfcLockscreenDispatch.aidl
@@ -0,0 +1,12 @@
+package android.nfc;
+
+import android.nfc.Tag;
+
+/**
+ * @hide
+ */
+interface INfcLockscreenDispatch {
+
+ boolean onTagDetected(in Tag tag);
+
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index dd8e41c..8991066 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -380,6 +380,16 @@
public Uri[] createBeamUris(NfcEvent event);
}
+
+ /**
+ * A callback to be invoked when an application has registered for receiving
+ * tags at the lockscreen.
+ */
+ public interface NfcLockscreenDispatch {
+ public boolean onTagDetected(Tag tag);
+ }
+
+
/**
* Helper to check if this device has FEATURE_NFC, but without using
* a context.
@@ -1417,6 +1427,23 @@
}
}
+ public boolean registerLockscreenDispatch(final NfcLockscreenDispatch lockscreenDispatch,
+ int[] techList) {
+ try {
+ sService.registerLockscreenDispatch(new INfcLockscreenDispatch.Stub() {
+ @Override
+ public boolean onTagDetected(Tag tag) throws RemoteException {
+ return lockscreenDispatch.onTagDetected(tag);
+ }
+ }, techList);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+
+ return true;
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 06c05ee..2a5e9fd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4537,12 +4537,6 @@
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
- * Specifies the package name currently configured to be the primary phone application
- * @hide
- */
- public static final String PHONE_DEFAULT_APPLICATION = "phone_default_application";
-
- /**
* Name of a package that the current user has explicitly allowed to see all of that
* user's notifications.
*
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 5b8e854..9a8380d 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -570,8 +570,8 @@
mTrackDrawable.getPadding(mTempRect);
- final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
- + mThumbTextPadding * 2;
+ final int maxTextWidth = mShowText ? Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+ + mThumbTextPadding * 2 : 0;
mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
final int switchWidth = Math.max(mSwitchMinWidth,
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 9998995..c139c9d 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -258,16 +258,16 @@
// can return NULL
static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
- switch (src.config()) {
- case SkBitmap::kARGB_8888_Config:
+ switch (src.colorType()) {
+ case kN32_SkColorType:
if (src.isOpaque()) return ToColor_S32_Opaque;
return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
if (src.isOpaque()) return ToColor_S4444_Opaque;
return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
return ToColor_S565;
- case SkBitmap::kIndex8_Config:
+ case kIndex_8_SkColorType:
if (src.getColorTable() == NULL) {
return NULL;
}
@@ -291,7 +291,7 @@
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
jint offset, jint stride, jint width, jint height,
jint configHandle, jboolean isMutable) {
- SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+ SkColorType colorType = SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(configHandle));
if (NULL != jColors) {
size_t n = env->GetArrayLength(jColors);
if (n < SkAbs32(stride) * (size_t)height) {
@@ -301,12 +301,12 @@
}
// ARGB_4444 is a deprecated format, convert automatically to 8888
- if (config == SkBitmap::kARGB_4444_Config) {
- config = SkBitmap::kARGB_8888_Config;
+ if (colorType == kARGB_4444_SkColorType) {
+ colorType = kN32_SkColorType;
}
SkBitmap bitmap;
- bitmap.setConfig(config, width, height);
+ bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType));
jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
if (NULL == buff) {
@@ -515,28 +515,29 @@
android::Parcel* p = android::parcelForJavaObject(env, parcel);
- const bool isMutable = p->readInt32() != 0;
- const SkBitmap::Config config = (SkBitmap::Config)p->readInt32();
- const int width = p->readInt32();
- const int height = p->readInt32();
- const int rowBytes = p->readInt32();
- const int density = p->readInt32();
+ const bool isMutable = p->readInt32() != 0;
+ const SkColorType colorType = (SkColorType)p->readInt32();
+ const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
+ const int width = p->readInt32();
+ const int height = p->readInt32();
+ const int rowBytes = p->readInt32();
+ const int density = p->readInt32();
- if (SkBitmap::kARGB_8888_Config != config &&
- SkBitmap::kRGB_565_Config != config &&
- SkBitmap::kARGB_4444_Config != config &&
- SkBitmap::kIndex8_Config != config &&
- SkBitmap::kA8_Config != config) {
- SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
+ if (kN32_SkColorType != colorType &&
+ kRGB_565_SkColorType != colorType &&
+ kARGB_4444_SkColorType != colorType &&
+ kIndex_8_SkColorType != colorType &&
+ kAlpha_8_SkColorType != colorType) {
+ SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType);
return NULL;
}
SkBitmap* bitmap = new SkBitmap;
- bitmap->setConfig(config, width, height, rowBytes);
+ bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType), rowBytes);
SkColorTable* ctable = NULL;
- if (config == SkBitmap::kIndex8_Config) {
+ if (colorType == kIndex_8_SkColorType) {
int count = p->readInt32();
if (count > 0) {
size_t size = count * sizeof(SkPMColor);
@@ -587,13 +588,14 @@
android::Parcel* p = android::parcelForJavaObject(env, parcel);
p->writeInt32(isMutable);
- p->writeInt32(bitmap->config());
+ p->writeInt32(bitmap->colorType());
+ p->writeInt32(bitmap->alphaType());
p->writeInt32(bitmap->width());
p->writeInt32(bitmap->height());
p->writeInt32(bitmap->rowBytes());
p->writeInt32(density);
- if (bitmap->config() == SkBitmap::kIndex8_Config) {
+ if (bitmap->colorType() == kIndex_8_SkColorType) {
SkColorTable* ctable = bitmap->getColorTable();
if (ctable != NULL) {
int count = ctable->count();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 5106f0d..86ed677 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -386,7 +386,7 @@
// FIXME: If the alphaType is kUnpremul and the image has alpha, the
// colors may not be correct, since Skia does not yet support drawing
// to/from unpremultiplied bitmaps.
- outputBitmap->setConfig(SkImageInfo::Make(scaledWidth, scaledHeight,
+ outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
colorType, decodingBitmap.alphaType()));
if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
return nullObjectReturn("allocation failed for scaled bitmap");
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 935e3a0..9e09280 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -703,10 +703,11 @@
jboolean hasAlpha, jlong paintHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkImageInfo info = SkImageInfo::Make(width, height,
+ hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+ kPremul_SkAlphaType);
SkBitmap bitmap;
- bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config :
- SkBitmap::kRGB_565_Config, width, height);
- if (!bitmap.allocPixels()) {
+ if (!bitmap.allocPixels(info)) {
return;
}
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 4c9feca..f8bab24 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -36,6 +36,11 @@
using namespace android;
+enum {
+ // Keep up to date with Camera.java
+ CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -2,
+};
+
struct fields_t {
jfieldID context;
jfieldID facing;
@@ -475,8 +480,8 @@
env->ReleaseStringChars(clientPackageName, rawClientName);
sp<Camera> camera;
- if (halVersion == ICameraService::CAMERA_HAL_API_VERSION_UNSPECIFIED) {
- // Default path: hal version is unspecified, do normal camera open.
+ if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
+ // Default path: hal version is don't care, do normal camera connect.
camera = Camera::connect(cameraId, clientName,
Camera::USE_CALLING_UID);
} else {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index ee4c619..849531c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -287,6 +287,8 @@
env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz,
"errorCallbackFromNative","(I)V"),
check_AudioSystem_Command(err));
+
+ env->DeleteLocalRef(clazz);
}
static jint
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index dc1ea06..4362018 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -388,11 +388,11 @@
static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
jlong rendererPtr, jintArray colors, jint offset, jint stride,
jfloat left, jfloat top, jint width, jint height, jboolean hasAlpha, jlong paintPtr) {
+ const SkImageInfo info = SkImageInfo::Make(width, height,
+ hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+ kPremul_SkAlphaType);
SkBitmap* bitmap = new SkBitmap;
- bitmap->setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config : SkBitmap::kRGB_565_Config,
- width, height);
-
- if (!bitmap->allocPixels()) {
+ if (!bitmap->allocPixels(info)) {
delete bitmap;
return;
}
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 0210bd9..5ebed9c 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -142,16 +142,16 @@
// Canvas management
// ----------------------------------------------------------------------------
-static inline SkBitmap::Config convertPixelFormat(int32_t format) {
+static inline SkColorType convertPixelFormat(int32_t format) {
switch (format) {
case PIXEL_FORMAT_RGBA_8888:
- return SkBitmap::kARGB_8888_Config;
+ return kN32_SkColorType;
case PIXEL_FORMAT_RGBX_8888:
- return SkBitmap::kARGB_8888_Config;
+ return kN32_SkColorType;
case PIXEL_FORMAT_RGB_565:
- return SkBitmap::kRGB_565_Config;
+ return kRGB_565_SkColorType;
default:
- return SkBitmap::kNo_Config;
+ return kUnknown_SkColorType;
}
}
@@ -188,8 +188,10 @@
ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
SkBitmap bitmap;
- bitmap.setConfig(convertPixelFormat(buffer->getPixelFormat()),
- buffer->getWidth(), buffer->getHeight(), bytesCount);
+ bitmap.setInfo(SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
+ convertPixelFormat(buffer->getPixelFormat()),
+ kPremul_SkAlphaType),
+ bytesCount);
if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
bitmap.setPixels(bits);
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3d14aaf..7018751 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -173,17 +173,17 @@
return value;
}
-static inline SkBitmap::Config convertPixelFormat(PixelFormat format) {
+static inline SkColorType convertPixelFormat(PixelFormat format) {
/* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then
- we can map to SkBitmap::kARGB_8888_Config, and optionally call
+ we can map to kN32_SkColorType, and optionally call
bitmap.setAlphaType(kOpaque_SkAlphaType) on the resulting SkBitmap
(as an accelerator)
*/
switch (format) {
- case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config;
- case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config;
- case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config;
- default: return SkBitmap::kNo_Config;
+ case PIXEL_FORMAT_RGBX_8888: return kN32_SkColorType;
+ case PIXEL_FORMAT_RGBA_8888: return kN32_SkColorType;
+ case PIXEL_FORMAT_RGB_565: return kRGB_565_SkColorType;
+ default: return kUnknown_SkColorType;
}
}
@@ -220,12 +220,16 @@
// Associate a SkCanvas object to this surface
env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format);
+ SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
+ convertPixelFormat(outBuffer.format),
+ kPremul_SkAlphaType);
+ if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
+ info.fAlphaType = kOpaque_SkAlphaType;
+ }
+
SkBitmap bitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
- bitmap.setConfig(convertPixelFormat(outBuffer.format), outBuffer.width, outBuffer.height, bpr);
- if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
- bitmap.setAlphaType(kOpaque_SkAlphaType);
- }
+ bitmap.setInfo(info, bpr);
if (outBuffer.width > 0 && outBuffer.height > 0) {
bitmap.setPixels(outBuffer.bits);
} else {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index cfc8eb8..9783e91 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -175,7 +175,7 @@
screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
SkBitmap* bitmap = new SkBitmap();
- bitmap->setConfig(screenshotInfo, (size_t)rowBytes);
+ bitmap->setInfo(screenshotInfo, (size_t)rowBytes);
if (screenshotInfo.fWidth > 0 && screenshotInfo.fHeight > 0) {
// takes ownership of ScreenshotClient
SkMallocPixelRef* pixels = SkMallocPixelRef::NewWithProc(screenshotInfo,
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index c1ab515..5c04a78 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -73,17 +73,28 @@
// Native layer
// ----------------------------------------------------------------------------
-static inline SkBitmap::Config convertPixelFormat(int32_t format) {
- switch (format) {
+// FIXME: consider exporting this to share (e.g. android_view_Surface.cpp)
+static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) {
+ SkImageInfo info;
+ info.fWidth = buffer.width;
+ info.fHeight = buffer.height;
+ switch (buffer.format) {
case WINDOW_FORMAT_RGBA_8888:
- return SkBitmap::kARGB_8888_Config;
+ info.fColorType = kN32_SkColorType;
+ info.fAlphaType = kPremul_SkAlphaType;
+ break;
case WINDOW_FORMAT_RGBX_8888:
- return SkBitmap::kARGB_8888_Config;
+ info.fColorType = kN32_SkColorType;
+ info.fAlphaType = kOpaque_SkAlphaType;
case WINDOW_FORMAT_RGB_565:
- return SkBitmap::kRGB_565_Config;
+ info.fColorType = kRGB_565_SkColorType;
+ info.fAlphaType = kOpaque_SkAlphaType;
default:
- return SkBitmap::kNo_Config;
+ info.fColorType = kUnknown_SkColorType;
+ info.fAlphaType = kIgnore_SkAlphaType;
+ break;
}
+ return info;
}
/**
@@ -148,11 +159,7 @@
ssize_t bytesCount = buffer.stride * bytesPerPixel(buffer.format);
SkBitmap bitmap;
- bitmap.setConfig(convertPixelFormat(buffer.format), buffer.width, buffer.height, bytesCount);
-
- if (buffer.format == WINDOW_FORMAT_RGBX_8888) {
- bitmap.setAlphaType(kOpaque_SkAlphaType);
- }
+ bitmap.setInfo(convertPixelFormat(buffer), bytesCount);
if (buffer.width > 0 && buffer.height > 0) {
bitmap.setPixels(buffer.bits);
diff --git a/core/res/res/transition/explode.xml b/core/res/res/transition/explode.xml
new file mode 100644
index 0000000..fe22284
--- /dev/null
+++ b/core/res/res/transition/explode.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<explode xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/core/res/res/transition/slide_bottom.xml b/core/res/res/transition/slide_bottom.xml
new file mode 100644
index 0000000..46dc0d6
--- /dev/null
+++ b/core/res/res/transition/slide_bottom.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="bottom"/>
diff --git a/core/res/res/transition/slide_left.xml b/core/res/res/transition/slide_left.xml
new file mode 100644
index 0000000..997bd97
--- /dev/null
+++ b/core/res/res/transition/slide_left.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="left"/>
diff --git a/core/res/res/transition/slide_right.xml b/core/res/res/transition/slide_right.xml
new file mode 100644
index 0000000..98f8f6a
--- /dev/null
+++ b/core/res/res/transition/slide_right.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="right"/>
diff --git a/core/res/res/transition/slide_top.xml b/core/res/res/transition/slide_top.xml
new file mode 100644
index 0000000..07ab945
--- /dev/null
+++ b/core/res/res/transition/slide_top.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="top"/>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 07a1e51..c3e4d94 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2461,5 +2461,26 @@
<!-- An interpolator which accelerates fast and keeps accelerating until the end. -->
<public type="interpolator" name="fast_out_linear_in" />
+ <!-- Used for Activity Transitions, this transition indicates that no Transition
+ should be used. -->
<public type="transition" name="no_transition" id="0x010f0000"/>
+ <!-- A transition that moves and resizes a view -->
+ <public type="transition" name="move"/>
+ <!-- A transition that fades views in and out. -->
+ <public type="transition" name="fade"/>
+ <!-- A transition that moves views in or out of the scene to or from the edges when
+ a view visibility changes. -->
+ <public type="transition" name="explode"/>
+ <!-- A transition that moves views in or out of the scene to or from the bottom edge when
+ a view visibility changes. -->
+ <public type="transition" name="slide_bottom"/>
+ <!-- A transition that moves views in or out of the scene to or from the top edge when
+ a view visibility changes. -->
+ <public type="transition" name="slide_top"/>
+ <!-- A transition that moves views in or out of the scene to or from the right edge when
+ a view visibility changes. -->
+ <public type="transition" name="slide_right"/>
+ <!-- A transition that moves views in or out of the scene to or from the left edge when
+ a view visibility changes. -->
+ <public type="transition" name="slide_left"/>
</resources>
diff --git a/docs/html/preview/images/l-dev-prev.png b/docs/html/preview/images/l-dev-prev.png
index 95bad8c..eae6ede 100644
--- a/docs/html/preview/images/l-dev-prev.png
+++ b/docs/html/preview/images/l-dev-prev.png
Binary files differ
diff --git a/docs/html/preview/index.html b/docs/html/preview/index.html
index 368db84..d4610b5 100644
--- a/docs/html/preview/index.html
+++ b/docs/html/preview/index.html
@@ -163,8 +163,8 @@
platform officially launches.
</div>
- <img src="/preview/images/l-dev-prev.png" style=" margin:10px 0 0 100px" width="700px"/>
- <div class="col-6" style="margin-left:630px; margin-top:-40px">
+ <img src="/preview/images/l-dev-prev.png" style=" margin:0px 0 0 40px" width="860px"/>
+ <div class="col-6" style="margin-left:660px; margin-top:-105px">
<a href="/preview/setup-sdk.html" class="landing-button landing-secondary" style="position:absolute;z-index:100;float:right;margin-top: 0px; background-color:#09c">Get Started</a><!--
<p>Set up your environment and check out all the docs to get up and running.</p>-->
@@ -176,7 +176,7 @@
-<div class="landing-section landing-gray-background" style="margin-top:-80px; padding-bottom:20px">
+<div class="landing-section landing-gray-background" style="margin-top:-135px; padding-bottom:20px">
<div class="wrap">
<div class="cols">
<div class="landing-body" style="margin-top:-80px" >
@@ -186,14 +186,14 @@
<p>A New UI Design</p>
<p class="landing-small">
Create a consistent experience across mobile and the web with
- material design, the new Google-wide standard.
+ <b>material design</b>, the new Google-wide standard.
</p>
<p class="landing-small">
<a href="/preview/material/index.html">Learn about material</a>
</p>
</div>
<div class="col-4">
- <p>A Rehauled Runtime</p>
+ <p>A New Runtime</p>
<p class="landing-small">
Test your apps and get them ready for <b>ART</b> (<b>A</b>ndroid <b>R</b>un<b>t</b>ime),
the default runtime in the next release.
@@ -205,21 +205,21 @@
<div class="col-4">
<p style="width:230px">Enhanced Notifications</p>
<p class="landing-small">
- Get more control over where notifications appear,
- how they look, and automatic syncing to non-handheld devices.
+ Get control over where notifications appear,
+ how they look, and how they sync to non-handheld devices.
</p>
<p class="landing-small">
- <a href="/preview/api-overview.html#UI">Learn more</a>
+ <a href="/preview/api-overview.html#UI">Learn about notifications</a>
</p>
</div>
<div class="col-4">
- <p>Project Volta</p>
+ <p>Increased Efficiency</p>
<p class="landing-small">
- We've tuned the platform to be more energy efficient and
+ <b>Project Volta</b> is our effort to make the platform energy efficient and
to give you more control over resource usage.
</p>
<p class="landing-small">
- <a href="/preview/api-overview.html#Power">Learn more</a>
+ <a href="/preview/api-overview.html#Power">Learn about Project Volta</a>
</p>
</div>
</div>
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index 1f84b86..fc0e8a0 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -112,6 +112,10 @@
int i1 = (int) ipart;
int i2 = MathUtils::min(i1 + 1, mSize - 1);
+ LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!"
+ " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f",
+ i1, i2, input, lutpos, mSize, mValues, ipart, weight);
+
float v1 = mValues[i1];
float v2 = mValues[i2];
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 9dd5aa5..97eb583 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -104,8 +104,7 @@
}
static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
- bitmap.setConfig(SkBitmap::kA8_Config, width, height);
- bitmap.allocPixels();
+ bitmap.allocPixels(SkImageInfo::MakeA8(width, height));
bitmap.eraseColor(0);
}
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 1001cae0..9212d0a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -333,8 +333,7 @@
void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
uint32_t width, uint32_t height) {
SkBitmap rgbaBitmap;
- rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType());
- rgbaBitmap.allocPixels();
+ rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType()));
rgbaBitmap.eraseColor(0);
SkCanvas canvas(rgbaBitmap);
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 3f6ccc9..063383b 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -206,9 +206,8 @@
} else {
SkBitmap surfaceBitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
- surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
- outBuffer.width, outBuffer.height, bpr);
- surfaceBitmap.setPixels(outBuffer.bits);
+ surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
+ outBuffer.bits, bpr);
SkCanvas surfaceCanvas(surfaceBitmap);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d35225a..fb19242 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1254,11 +1254,6 @@
* call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
* <p>Even if a SCO connection is established, the following restrictions apply on audio
* output streams so that they can be routed to SCO headset:
- * <p>NOTE: up to and including API version
- * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
- * voice call to the bluetooth headset.
- * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
- * connection is established.
* <ul>
* <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
* <li> the format must be mono </li>
@@ -1274,6 +1269,11 @@
* it will be ignored. Similarly, if a call is received or sent while an application
* is using the SCO connection, the connection will be lost for the application and NOT
* returned automatically when the call ends.
+ * <p>NOTE: up to and including API version
+ * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
+ * voice call to the bluetooth headset.
+ * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
+ * connection is established.
* @see #stopBluetoothSco()
* @see #ACTION_SCO_AUDIO_STATE_UPDATED
*/
@@ -1287,13 +1287,38 @@
}
/**
+ * Start bluetooth SCO audio connection in virtual call mode.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ * <p>Similar to {@link #startBluetoothSco()} with explicit selection of virtual call mode.
+ * Telephony and communication applications (VoIP, Video Chat) should preferably select
+ * virtual call mode.
+ * Applications using voice input for search or commands should first try raw audio connection
+ * with {@link #startBluetoothSco()} and fall back to startBluetoothScoVirtualCall() in case of
+ * failure.
+ * @see #startBluetoothSco()
+ * @see #stopBluetoothSco()
+ * @see #ACTION_SCO_AUDIO_STATE_UPDATED
+ */
+ public void startBluetoothScoVirtualCall() {
+ IAudioService service = getService();
+ try {
+ service.startBluetoothScoVirtualCall(mICallBack);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in startBluetoothScoVirtualCall", e);
+ }
+ }
+
+ /**
* Stop bluetooth SCO audio connection.
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
* <p>This method must be called by applications having requested the use of
- * bluetooth SCO audio with {@link #startBluetoothSco()}
- * when finished with the SCO connection or if connection fails.
+ * bluetooth SCO audio with {@link #startBluetoothSco()} or
+ * {@link #startBluetoothScoVirtualCall()} when finished with the SCO connection or
+ * if connection fails.
* @see #startBluetoothSco()
+ * @see #startBluetoothScoVirtualCall()
*/
public void stopBluetoothSco(){
IAudioService service = getService();
@@ -2169,48 +2194,8 @@
Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
return;
}
- IAudioService service = getService();
- try {
- // pi != null, this is currently still needed to support across
- // reboot launching of the last app.
- service.registerMediaButtonIntent(pi, eventReceiver,
- eventReceiver == null ? mToken : null);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
- }
MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.addMediaButtonListener(pi, mContext);
- }
-
- /**
- * @hide
- * Used internally by telephony package to register an intent receiver for ACTION_MEDIA_BUTTON.
- * @param eventReceiver the component that will receive the media button key events,
- * no-op if eventReceiver is null
- */
- public void registerMediaButtonEventReceiverForCalls(ComponentName eventReceiver) {
- if (eventReceiver == null) {
- return;
- }
- IAudioService service = getService();
- try {
- // eventReceiver != null
- service.registerMediaButtonEventReceiverForCalls(eventReceiver);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerMediaButtonEventReceiverForCalls", e);
- }
- }
-
- /**
- * @hide
- */
- public void unregisterMediaButtonEventReceiverForCalls() {
- IAudioService service = getService();
- try {
- service.unregisterMediaButtonEventReceiverForCalls();
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiverForCalls", e);
- }
+ helper.addMediaButtonListener(pi, eventReceiver, mContext);
}
/**
@@ -2247,12 +2232,6 @@
* @hide
*/
public void unregisterMediaButtonIntent(PendingIntent pi) {
- IAudioService service = getService();
- try {
- service.unregisterMediaButtonIntent(pi);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
- }
MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
helper.removeMediaButtonListener(pi);
}
@@ -2418,46 +2397,6 @@
}
/**
- * @hide
- * Request the user of a RemoteControlClient to seek to the given playback position.
- * @param generationId the RemoteControlClient generation counter for which this request is
- * issued. Requests for an older generation than current one will be ignored.
- * @param timeMs the time in ms to seek to, must be positive.
- */
- public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- if (timeMs < 0) {
- return;
- }
- IAudioService service = getService();
- try {
- service.setRemoteControlClientPlaybackPosition(generationId, timeMs);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setRccPlaybackPosition("+ generationId + ", "
- + timeMs + ")", e);
- }
- }
-
- /**
- * @hide
- * Notify the user of a RemoteControlClient that it should update its metadata with the
- * new value for the given key.
- * @param generationId the RemoteControlClient generation counter for which this request is
- * issued. Requests for an older generation than current one will be ignored.
- * @param key the metadata key for which a new value exists
- * @param value the new metadata value
- */
- public void updateRemoteControlClientMetadata(int generationId, int key,
- Rating value) {
- IAudioService service = getService();
- try {
- service.updateRemoteControlClientMetadata(generationId, key, value);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in updateRemoteControlClientMetadata("+ generationId + ", "
- + key +", " + value + ")", e);
- }
- }
-
- /**
* @hide
* Reload audio settings. This method is called by Settings backup
* agent when audio settings are restored and causes the AudioService
@@ -2873,12 +2812,9 @@
* @hide
*/
public int getRemoteStreamVolume() {
- try {
- return getService().getRemoteStreamVolume();
- } catch (RemoteException e) {
- Log.w(TAG, "Error getting remote stream volume", e);
- return 0;
- }
+ // TODO STOPSHIP switch callers to use media sessions instead
+ Log.e(TAG, "Need to implement new Remote Volume!");
+ return 0;
}
/**
@@ -2886,12 +2822,9 @@
* @hide
*/
public int getRemoteStreamMaxVolume() {
- try {
- return getService().getRemoteStreamMaxVolume();
- } catch (RemoteException e) {
- Log.w(TAG, "Error getting remote stream max volume", e);
- return 0;
- }
+ // TODO STOPSHIP switch callers to use media sessions instead
+ Log.e(TAG, "Need to implement new Remote Volume!");
+ return 0;
}
/**
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 48479a4..0c224a6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -2056,7 +2056,19 @@
}
/** @see AudioManager#startBluetoothSco() */
- public void startBluetoothSco(IBinder cb, int targetSdkVersion){
+ public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
+ int scoAudioMode =
+ (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
+ SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
+ startBluetoothScoInt(cb, scoAudioMode);
+ }
+
+ /** @see AudioManager#startBluetoothScoVirtualCall() */
+ public void startBluetoothScoVirtualCall(IBinder cb) {
+ startBluetoothScoInt(cb, SCO_MODE_VIRTUAL_CALL);
+ }
+
+ void startBluetoothScoInt(IBinder cb, int scoAudioMode){
if (!checkAudioSettingsPermission("startBluetoothSco()") ||
!mSystemReady) {
return;
@@ -2068,7 +2080,7 @@
// The caller identity must be cleared after getScoClient() because it is needed if a new
// client is created.
final long ident = Binder.clearCallingIdentity();
- client.incCount(targetSdkVersion);
+ client.incCount(scoAudioMode);
Binder.restoreCallingIdentity(ident);
}
@@ -2114,9 +2126,9 @@
}
}
- public void incCount(int targetSdkVersion) {
+ public void incCount(int scoAudioMode) {
synchronized(mScoClients) {
- requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, targetSdkVersion);
+ requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
if (mStartcount == 0) {
try {
mCb.linkToDeath(this, 0);
@@ -2186,7 +2198,7 @@
}
}
- private void requestScoState(int state, int targetSdkVersion) {
+ private void requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
if (totalCount() == 0) {
if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
@@ -2201,9 +2213,7 @@
(mScoAudioState == SCO_STATE_INACTIVE ||
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_INACTIVE) {
- mScoAudioMode =
- (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
- SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
+ mScoAudioMode = scoAudioMode;
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
boolean status;
if (mScoAudioMode == SCO_MODE_RAW) {
@@ -4402,68 +4412,12 @@
mMediaFocusControl.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
}
- public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
- mMediaFocusControl.registerMediaButtonEventReceiverForCalls(c);
- }
-
- public void unregisterMediaButtonEventReceiverForCalls() {
- mMediaFocusControl.unregisterMediaButtonEventReceiverForCalls();
- }
-
- public void registerMediaButtonIntent(PendingIntent pi, ComponentName c, IBinder token) {
- mMediaFocusControl.registerMediaButtonIntent(pi, c, token);
- }
-
- public void unregisterMediaButtonIntent(PendingIntent pi) {
- mMediaFocusControl.unregisterMediaButtonIntent(pi);
- }
-
- public int registerRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient, String callingPckg) {
- return mMediaFocusControl.registerRemoteControlClient(mediaIntent, rcClient, callingPckg);
- }
-
- public void unregisterRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient) {
- mMediaFocusControl.unregisterRemoteControlClient(mediaIntent, rcClient);
- }
-
- public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- mMediaFocusControl.setRemoteControlClientPlaybackPosition(generationId, timeMs);
- }
-
- public void updateRemoteControlClientMetadata(int generationId, int key, Rating value) {
- mMediaFocusControl.updateRemoteControlClientMetadata(generationId, key, value);
- }
-
- public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
- mMediaFocusControl.registerRemoteVolumeObserverForRcc(rccId, rvo);
- }
-
- @Override
- public int getRemoteStreamVolume() {
- return mMediaFocusControl.getRemoteStreamVolume();
- }
-
- @Override
- public int getRemoteStreamMaxVolume() {
- return mMediaFocusControl.getRemoteStreamMaxVolume();
- }
-
@Override
public void setRemoteStreamVolume(int index) {
enforceSelfOrSystemUI("set the remote stream volume");
mMediaFocusControl.setRemoteStreamVolume(index);
}
- public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
- mMediaFocusControl.setPlaybackStateForRcc(rccId, state, timeMs, speed);
- }
-
- public void setPlaybackInfoForRcc(int rccId, int what, int value) {
- mMediaFocusControl.setPlaybackInfoForRcc(rccId, what, value);
- }
-
//==========================================================================================
// Audio Focus
//==========================================================================================
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 169631e..4dcdd19 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -122,12 +122,6 @@
int getCurrentAudioFocus();
- void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c, IBinder token);
- oneway void unregisterMediaButtonIntent(in PendingIntent pi);
-
- oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
- oneway void unregisterMediaButtonEventReceiverForCalls();
-
/**
* Register an IRemoteControlDisplay.
* Success of registration is subject to a check on
@@ -180,43 +174,9 @@
*/
oneway void remoteControlDisplayWantsPlaybackPositionSync(in IRemoteControlDisplay rcd,
boolean wantsSync);
- /**
- * Request the user of a RemoteControlClient to seek to the given playback position.
- * @param generationId the RemoteControlClient generation counter for which this request is
- * issued. Requests for an older generation than current one will be ignored.
- * @param timeMs the time in ms to seek to, must be positive.
- */
- void setRemoteControlClientPlaybackPosition(int generationId, long timeMs);
- /**
- * Notify the user of a RemoteControlClient that it should update its metadata with the
- * new value for the given key.
- * @param generationId the RemoteControlClient generation counter for which this request is
- * issued. Requests for an older generation than current one will be ignored.
- * @param key the metadata key for which a new value exists
- * @param value the new metadata value
- */
- void updateRemoteControlClientMetadata(int generationId, int key, in Rating value);
-
- /**
- * Do not use directly, use instead
- * {@link android.media.AudioManager#registerRemoteControlClient(RemoteControlClient)}
- */
- int registerRemoteControlClient(in PendingIntent mediaIntent,
- in IRemoteControlClient rcClient, in String callingPackageName);
- /**
- * Do not use directly, use instead
- * {@link android.media.AudioManager#unregisterRemoteControlClient(RemoteControlClient)}
- */
- oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
- in IRemoteControlClient rcClient);
-
- oneway void setPlaybackInfoForRcc(int rccId, int what, int value);
- void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed);
- int getRemoteStreamMaxVolume();
- int getRemoteStreamVolume();
- oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo);
void startBluetoothSco(IBinder cb, int targetSdkVersion);
+ void startBluetoothScoVirtualCall(IBinder cb);
void stopBluetoothSco(IBinder cb);
void forceVolumeControlStream(int streamType, IBinder cb);
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 440653a..6559bc5 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -181,6 +181,27 @@
}
/**
+ * Thrown when an unrecoverable failure occurs during a MediaDrm operation.
+ * Extends java.lang.IllegalStateException with the addition of an error
+ * code that may be useful in diagnosing the failure.
+ */
+ public static final class MediaDrmStateException extends java.lang.IllegalStateException {
+ private final int mErrorCode;
+
+ public MediaDrmStateException(int errorCode, String detailMessage) {
+ super(detailMessage);
+ mErrorCode = errorCode;
+ }
+
+ /**
+ * Retrieve the associated error code
+ */
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+ }
+
+ /**
* Register a callback to be invoked when an event occurs
*
* @param listener the callback that will be run
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index 1c73c05..a4a7c4e 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -379,32 +379,11 @@
onReevaluateRemote();
break;
- case MSG_RCC_NEW_PLAYBACK_INFO:
- onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
- ((Integer)msg.obj).intValue() /* value */);
- break;
-
case MSG_RCC_NEW_VOLUME_OBS:
onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
(IRemoteVolumeObserver)msg.obj /* rvo */);
break;
- case MSG_RCC_NEW_PLAYBACK_STATE:
- onNewPlaybackStateForRcc(msg.arg1 /* rccId */,
- msg.arg2 /* state */,
- (PlayerRecord.RccPlaybackState)msg.obj /* newState */);
- break;
-
- case MSG_RCC_SEEK_REQUEST:
- onSetRemoteControlClientPlaybackPosition(
- msg.arg1 /* generationId */, ((Long)msg.obj).longValue() /* timeMs */);
- break;
-
- case MSG_RCC_UPDATE_METADATA:
- onUpdateRemoteControlClientMetadata(msg.arg1 /*genId*/, msg.arg2 /*key*/,
- (Rating) msg.obj /* value */);
- break;
-
case MSG_RCDISPLAY_INIT_INFO:
// msg.obj is guaranteed to be non null
onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
@@ -2003,217 +1982,6 @@
}
}
- protected void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- // ignore position change requests if invalid generation ID
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if (mCurrentRcClientGen != generationId) {
- return;
- }
- }
- }
- // discard any unprocessed seek request in the message queue, and replace with latest
- sendMsg(mEventHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
- 0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
- }
-
- private void onSetRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- if(DEBUG_RC) Log.d(TAG, "onSetRemoteControlClientPlaybackPosition(genId=" + generationId +
- ", timeMs=" + timeMs + ")");
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if ((mCurrentRcClient != null) && (mCurrentRcClientGen == generationId)) {
- // tell the current client to seek to the requested location
- try {
- mCurrentRcClient.seekTo(generationId, timeMs);
- } catch (RemoteException e) {
- Log.e(TAG, "Current valid remote client is dead: "+e);
- mCurrentRcClient = null;
- }
- }
- }
- }
- }
-
- protected void updateRemoteControlClientMetadata(int genId, int key, Rating value) {
- sendMsg(mEventHandler, MSG_RCC_UPDATE_METADATA, SENDMSG_QUEUE,
- genId /* arg1 */, key /* arg2 */, value /* obj */, 0 /* delay */);
- }
-
- private void onUpdateRemoteControlClientMetadata(int genId, int key, Rating value) {
- if(DEBUG_RC) Log.d(TAG, "onUpdateRemoteControlClientMetadata(genId=" + genId +
- ", what=" + key + ",rating=" + value + ")");
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if ((mCurrentRcClient != null) && (mCurrentRcClientGen == genId)) {
- try {
- switch (key) {
- case MediaMetadataEditor.RATING_KEY_BY_USER:
- mCurrentRcClient.updateMetadata(genId, key, value);
- break;
- default:
- Log.e(TAG, "unhandled metadata key " + key + " update for RCC "
- + genId);
- break;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Current valid remote client is dead", e);
- mCurrentRcClient = null;
- }
- }
- }
- }
- }
-
- protected void setPlaybackInfoForRcc(int rccId, int what, int value) {
- sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
- rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
- }
-
- // handler for MSG_RCC_NEW_PLAYBACK_INFO
- private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
- if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
- ", what=" + key + ",val=" + value + ")");
- synchronized(mPRStack) {
- // iterating from top of stack as playback information changes are more likely
- // on entries at the top of the remote control stack
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if (prse.getRccId() == rccId) {
- switch (key) {
- case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
- prse.mPlaybackType = value;
- postReevaluateRemote();
- break;
- case RemoteControlClient.PLAYBACKINFO_VOLUME:
- prse.mPlaybackVolume = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemote.mVolume = value;
- mVolumeController.postHasNewRemotePlaybackInfo();
- }
- }
- break;
- case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
- prse.mPlaybackVolumeMax = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemote.mVolumeMax = value;
- mVolumeController.postHasNewRemotePlaybackInfo();
- }
- }
- break;
- case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
- prse.mPlaybackVolumeHandling = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemote.mVolumeHandling = value;
- mVolumeController.postHasNewRemotePlaybackInfo();
- }
- }
- break;
- case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
- prse.mPlaybackStream = value;
- break;
- default:
- Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
- break;
- }
- return;
- }
- }//for
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index mPRStack on onNewPlaybackInfoForRcc, lock error? ", e);
- }
- }
- }
-
- protected void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
- sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
- rccId /* arg1 */, state /* arg2 */,
- new PlayerRecord.RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
- }
-
- private void onNewPlaybackStateForRcc(int rccId, int state,
- PlayerRecord.RccPlaybackState newState) {
- if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
- + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
- synchronized(mPRStack) {
- if (mPRStack.empty()) {
- return;
- }
- PlayerRecord oldTopPrse = mPRStack.lastElement(); // top of the stack before any changes
- PlayerRecord prse = null;
- int lastPlayingIndex = mPRStack.size();
- int inStackIndex = -1;
- try {
- // go through the stack from the top to figure out who's playing, and the position
- // of this RemoteControlClient (note that it may not be in the stack)
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- prse = mPRStack.elementAt(index);
- if (prse.getRccId() == rccId) {
- inStackIndex = index;
- prse.mPlaybackState = newState;
- }
- if (prse.isPlaybackActive()) {
- lastPlayingIndex = index;
- }
- }
-
- if (inStackIndex != -1) {
- // is in the stack
- prse = mPRStack.elementAt(inStackIndex);
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemoteIsActive = isPlaystateActive(state);
- postReevaluateRemote();
- }
- }
- if (mPRStack.size() > 1) { // no need to remove and add if stack contains only 1
- // remove it from its old location in the stack
- mPRStack.removeElementAt(inStackIndex);
- if (prse.isPlaybackActive()) {
- // and put it at the top
- mPRStack.push(prse);
- } else {
- // and put it after the ones with active playback
- if (inStackIndex > lastPlayingIndex) {
- mPRStack.add(lastPlayingIndex, prse);
- } else {
- mPRStack.add(lastPlayingIndex - 1, prse);
- }
- }
- }
-
- if (oldTopPrse != mPRStack.lastElement()) {
- // the top of the stack changed:
- final ComponentName target =
- mPRStack.lastElement().getMediaButtonReceiver();
- if (target != null) {
- // post message to persist the default media button receiver
- mEventHandler.sendMessage( mEventHandler.obtainMessage(
- MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
- }
- // reevaluate the display
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification or bad index
- Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
- + " size=" + mPRStack.size()
- + "accessing PlayerRecord stack in onNewPlaybackStateForRcc", e);
- }
- }
- }
-
- protected void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
- sendMsg(mEventHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
- rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
- }
-
// handler for MSG_RCC_NEW_VOLUME_OBS
private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
synchronized(mPRStack) {
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index ddd5b72..3336694 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -29,7 +29,6 @@
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
import android.media.session.MediaSession;
-import android.media.session.RemoteVolumeProvider;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -2204,10 +2203,10 @@
return;
}
if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
- int volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_FIXED;
+ int volumeControl = VolumeProvider.VOLUME_CONTROL_FIXED;
switch (mVolumeHandling) {
case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
- volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
break;
case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
default:
@@ -2226,7 +2225,7 @@
}
}
- class SessionVolumeProvider extends RemoteVolumeProvider {
+ class SessionVolumeProvider extends VolumeProvider {
public SessionVolumeProvider(int volumeControl, int maxVolume) {
super(volumeControl, maxVolume);
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 76c7299..be96398 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -361,18 +361,10 @@
if (timeMs < 0) {
throw new IllegalArgumentException("illegal negative time value");
}
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- if (mCurrentSession != null) {
- mCurrentSession.getTransportControls().seekTo(timeMs);
- }
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ mCurrentSession.getTransportControls().seekTo(timeMs);
}
- } else {
- final int genId;
- synchronized (mGenLock) {
- genId = mClientGenerationIdCurrent;
- }
- mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs);
}
return true;
}
@@ -534,34 +526,15 @@
if (!mMetadataChanged) {
return;
}
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- if (mCurrentSession != null) {
- if (mEditorMetadata.containsKey(
- String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
- Rating rating = (Rating) getObject(
- MediaMetadataEditor.RATING_KEY_BY_USER, null);
- if (rating != null) {
- mCurrentSession.getTransportControls().setRating(rating);
- }
- }
- }
- }
- } else {
- final int genId;
- synchronized(mGenLock) {
- genId = mClientGenerationIdCurrent;
- }
- synchronized(mInfoLock) {
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
if (mEditorMetadata.containsKey(
String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
Rating rating = (Rating) getObject(
MediaMetadataEditor.RATING_KEY_BY_USER, null);
- mAudioManager.updateRemoteControlClientMetadata(genId,
- MediaMetadataEditor.RATING_KEY_BY_USER,
- rating);
- } else {
- Log.e(TAG, "no metadata to apply");
+ if (rating != null) {
+ mCurrentSession.getTransportControls().setRating(rating);
+ }
}
}
}
diff --git a/media/java/android/media/session/RemoteVolumeProvider.java b/media/java/android/media/VolumeProvider.java
similarity index 92%
rename from media/java/android/media/session/RemoteVolumeProvider.java
rename to media/java/android/media/VolumeProvider.java
index 606b1d7..7d93b40 100644
--- a/media/java/android/media/session/RemoteVolumeProvider.java
+++ b/media/java/android/media/VolumeProvider.java
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
+package android.media;
+import android.media.session.MediaSession;
import android.os.RemoteException;
import android.util.Log;
@@ -24,8 +25,8 @@
* You can set a volume provider on a session by calling
* {@link MediaSession#setPlaybackToRemote}.
*/
-public abstract class RemoteVolumeProvider {
- private static final String TAG = "RemoteVolumeProvider";
+public abstract class VolumeProvider {
+ private static final String TAG = "VolumeProvider";
/**
* The volume is fixed and can not be modified. Requests to change volume
@@ -60,7 +61,7 @@
* this provider.
* @param maxVolume The maximum allowed volume.
*/
- public RemoteVolumeProvider(int volumeControl, int maxVolume) {
+ public VolumeProvider(int volumeControl, int maxVolume) {
mControlType = volumeControl;
mMaxVolume = maxVolume;
}
@@ -117,7 +118,7 @@
/**
* @hide
*/
- void setSession(MediaSession session) {
+ public void setSession(MediaSession session) {
mSession = session;
}
}
\ No newline at end of file
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 1cfc5bc..5bc0de4 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -15,6 +15,7 @@
package android.media.session;
+import android.content.ComponentName;
import android.media.MediaMetadata;
import android.media.session.ISessionController;
import android.media.session.RouteOptions;
@@ -33,6 +34,7 @@
ISessionController getController();
void setFlags(int flags);
void setActive(boolean active);
+ void setMediaButtonReceiver(in ComponentName mbr);
void destroy();
// These commands are for setting up and communicating with routes
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index f0cd785..b4c11f6 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -20,6 +20,7 @@
import android.media.Rating;
import android.media.session.ISessionControllerCallback;
import android.media.session.MediaSessionInfo;
+import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -38,6 +39,9 @@
void showRoutePicker();
MediaSessionInfo getSessionInfo();
long getFlags();
+ ParcelableVolumeInfo getVolumeAttributes();
+ void adjustVolumeBy(int delta, int flags);
+ void setVolumeTo(int value, int flags);
// These commands are for the TransportController
void play();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 87a43e4..84dad25 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -18,6 +18,7 @@
import android.media.MediaMetadata;
import android.media.Rating;
+import android.media.VolumeProvider;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -183,6 +184,23 @@
}
/**
+ * Get the current volume info for this session.
+ *
+ * @return The current volume info or null.
+ */
+ public VolumeInfo getVolumeInfo() {
+ try {
+ ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
+ return new VolumeInfo(result.volumeType, result.audioStream, result.controlType,
+ result.maxVolume, result.currentVolume);
+
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getVolumeInfo.", e);
+ }
+ return null;
+ }
+
+ /**
* Adds a callback to receive updates from the Session. Updates will be
* posted on the caller's thread.
*
@@ -509,6 +527,85 @@
}
}
+ /**
+ * Holds information about the way volume is handled for this session.
+ */
+ public static final class VolumeInfo {
+ private final int mVolumeType;
+ private final int mAudioStream;
+ private final int mVolumeControl;
+ private final int mMaxVolume;
+ private final int mCurrentVolume;
+
+ /**
+ * @hide
+ */
+ public VolumeInfo(int type, int stream, int control, int max, int current) {
+ mVolumeType = type;
+ mAudioStream = stream;
+ mVolumeControl = control;
+ mMaxVolume = max;
+ mCurrentVolume = current;
+ }
+
+ /**
+ * Get the type of volume handling, either local or remote. One of:
+ * <ul>
+ * <li>{@link MediaSession#VOLUME_TYPE_LOCAL}</li>
+ * <li>{@link MediaSession#VOLUME_TYPE_REMOTE}</li>
+ * </ul>
+ *
+ * @return The type of volume handling this session is using.
+ */
+ public int getVolumeType() {
+ return mVolumeType;
+ }
+
+ /**
+ * Get the stream this is currently controlling volume on. When the volume
+ * type is {@link MediaSession#VOLUME_TYPE_REMOTE} this value does not
+ * have meaning and should be ignored.
+ *
+ * @return The stream this session is playing on.
+ */
+ public int getAudioStream() {
+ return mAudioStream;
+ }
+
+ /**
+ * Get the type of volume control that can be used. One of:
+ * <ul>
+ * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
+ * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
+ * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
+ * </ul>
+ *
+ * @return The type of volume control that may be used with this
+ * session.
+ */
+ public int getVolumeControl() {
+ return mVolumeControl;
+ }
+
+ /**
+ * Get the maximum volume that may be set for this session.
+ *
+ * @return The maximum allowed volume where this session is playing.
+ */
+ public int getMaxVolume() {
+ return mMaxVolume;
+ }
+
+ /**
+ * Get the current volume for this session.
+ *
+ * @return The current volume where this session is playing.
+ */
+ public int getCurrentVolume() {
+ return mCurrentVolume;
+ }
+ }
+
private final static class CallbackStub extends ISessionControllerCallback.Stub {
private final WeakReference<MediaController> mController;
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 4ba1351..406b1c3 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -19,10 +19,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
+import android.media.VolumeProvider;
import android.media.session.ISessionController;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
@@ -125,18 +127,12 @@
public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
/**
- * The session uses local playback. Used for configuring volume handling
- * with the system.
- *
- * @hide
+ * The session uses local playback.
*/
public static final int VOLUME_TYPE_LOCAL = 1;
/**
- * The session uses remote playback. Used for configuring volume handling
- * with the system.
- *
- * @hide
+ * The session uses remote playback.
*/
public static final int VOLUME_TYPE_REMOTE = 2;
@@ -155,7 +151,7 @@
= new ArrayMap<String, RouteInterface.EventListener>();
private Route mRoute;
- private RemoteVolumeProvider mVolumeProvider;
+ private VolumeProvider mVolumeProvider;
private boolean mActive = false;;
@@ -232,6 +228,21 @@
}
/**
+ * Set a media button event receiver component to use to restart playback
+ * after an app has been stopped.
+ *
+ * @param mbr The receiver component to send the media button event to.
+ * @hide
+ */
+ public void setMediaButtonReceiver(ComponentName mbr) {
+ try {
+ mBinder.setMediaButtonReceiver(mbr);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
+ }
+ }
+
+ /**
* Set any flags for the session.
*
* @param flags The flags to set for this session.
@@ -272,7 +283,7 @@
* @param volumeProvider The provider that will handle volume changes. May
* not be null.
*/
- public void setPlaybackToRemote(RemoteVolumeProvider volumeProvider) {
+ public void setPlaybackToRemote(VolumeProvider volumeProvider) {
if (volumeProvider == null) {
throw new IllegalArgumentException("volumeProvider may not be null!");
}
@@ -524,12 +535,12 @@
}
/**
- * Notify the system that the remove volume changed.
+ * Notify the system that the remote volume changed.
*
* @param provider The provider that is handling volume changes.
* @hide
*/
- void notifyRemoteVolumeChanged(RemoteVolumeProvider provider) {
+ public void notifyRemoteVolumeChanged(VolumeProvider provider) {
if (provider == null || provider != mVolumeProvider) {
Log.w(TAG, "Received update from stale volume provider");
return;
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 801844f..838b857 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.MediaMetadata;
@@ -214,7 +215,7 @@
}
}
- public void addMediaButtonListener(PendingIntent pi,
+ public void addMediaButtonListener(PendingIntent pi, ComponentName mbrComponent,
Context context) {
if (pi == null) {
Log.w(TAG, "Pending intent was null, can't addMediaButtonListener.");
@@ -238,6 +239,7 @@
holder.mMediaButtonReceiver = new MediaButtonReceiver(pi, context);
holder.mSession.addCallback(holder.mMediaButtonReceiver, mHandler);
+ holder.mSession.setMediaButtonReceiver(mbrComponent);
if (DEBUG) {
Log.d(TAG, "addMediaButtonListener added " + pi);
}
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/media/java/android/media/session/ParcelableVolumeInfo.aidl
new file mode 100644
index 0000000..c4250f0
--- /dev/null
+++ b/media/java/android/media/session/ParcelableVolumeInfo.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media.session;
+
+parcelable ParcelableVolumeInfo;
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java
new file mode 100644
index 0000000..166ccd3
--- /dev/null
+++ b/media/java/android/media/session/ParcelableVolumeInfo.java
@@ -0,0 +1,78 @@
+/* Copyright 2014, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+package android.media.session;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Convenience class for passing information about the audio configuration of a
+ * session. The public implementation is {@link MediaController.VolumeInfo}.
+ *
+ * @hide
+ */
+public class ParcelableVolumeInfo implements Parcelable {
+ public int volumeType;
+ public int audioStream;
+ public int controlType;
+ public int maxVolume;
+ public int currentVolume;
+
+ public ParcelableVolumeInfo(int volumeType, int audioStream, int controlType, int maxVolume,
+ int currentVolume) {
+ this.volumeType = volumeType;
+ this.audioStream = audioStream;
+ this.controlType = controlType;
+ this.maxVolume = maxVolume;
+ this.currentVolume = currentVolume;
+ }
+
+ public ParcelableVolumeInfo(Parcel from) {
+ volumeType = from.readInt();
+ audioStream = from.readInt();
+ controlType = from.readInt();
+ maxVolume = from.readInt();
+ currentVolume = from.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(volumeType);
+ dest.writeInt(audioStream);
+ dest.writeInt(controlType);
+ dest.writeInt(maxVolume);
+ dest.writeInt(currentVolume);
+ }
+
+
+ public static final Parcelable.Creator<ParcelableVolumeInfo> CREATOR
+ = new Parcelable.Creator<ParcelableVolumeInfo>() {
+ @Override
+ public ParcelableVolumeInfo createFromParcel(Parcel in) {
+ return new ParcelableVolumeInfo(in);
+ }
+
+ @Override
+ public ParcelableVolumeInfo[] newArray(int size) {
+ return new ParcelableVolumeInfo[size];
+ }
+ };
+}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 4fbd2a4..5f27b16 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -110,6 +110,11 @@
jfieldID certificateData;
};
+struct StateExceptionFields {
+ jmethodID init;
+ jclass classId;
+};
+
struct fields_t {
jfieldID context;
jmethodID post_event;
@@ -121,6 +126,7 @@
IteratorFields iterator;
EntryFields entry;
CertificateFields certificate;
+ StateExceptionFields stateException;
jclass certificateClassId;
jclass hashmapClassId;
jclass arraylistClassId;
@@ -212,6 +218,14 @@
}
}
+static void throwStateException(JNIEnv *env, const char *msg, status_t err) {
+ ALOGE("Illegal state exception: %s (%d)", msg, err);
+
+ jobject exception = env->NewObject(gFields.stateException.classId,
+ gFields.stateException.init, static_cast<int>(err),
+ env->NewStringUTF(msg));
+ env->Throw(static_cast<jthrowable>(exception));
+}
static bool throwExceptionAsNecessary(
JNIEnv *env, status_t err, const char *msg = NULL) {
@@ -275,8 +289,7 @@
msg = errbuf.string();
}
}
- ALOGE("Illegal state exception: %s", msg);
- jniThrowException(env, "java/lang/IllegalStateException", msg);
+ throwStateException(env, msg, err);
return true;
}
return false;
@@ -608,6 +621,10 @@
FIND_CLASS(clazz, "java/util/ArrayList");
gFields.arraylistClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+ FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
+ GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+ gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
}
static void android_media_MediaDrm_native_setup(
diff --git a/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml b/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
index b769a0c..acb7767 100644
--- a/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
+++ b/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
@@ -125,6 +125,11 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/audiomanagertwo" />
+ <CheckBox
+ android:id="@+id/useVirtualCallCheckBox"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/useVirtualCallCheckText" />
</LinearLayout>
diff --git a/media/tests/ScoAudioTest/res/values/strings.xml b/media/tests/ScoAudioTest/res/values/strings.xml
index c3ff6d5..b0284e2 100644
--- a/media/tests/ScoAudioTest/res/values/strings.xml
+++ b/media/tests/ScoAudioTest/res/values/strings.xml
@@ -10,5 +10,5 @@
<string name="tts_speak">Speak TTS</string>
<string name="tts_to_file">TTS to file</string>
<string name="audiomanagertwo">Use different AudioManager for starting SCO</string>
-
+ <string name="useVirtualCallCheckText">Use Virtual Call</string>
</resources>
diff --git a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
index 0304640..7e21876 100644
--- a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
+++ b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
@@ -207,16 +207,25 @@
if (mForceScoOn != isChecked) {
mForceScoOn = isChecked;
AudioManager mngr = mAudioManager;
+ boolean useVirtualCall = false;
CheckBox box = (CheckBox) findViewById(R.id.useSecondAudioManager);
if (box.isChecked()) {
Log.i(TAG, "Using 2nd audio manager");
mngr = mAudioManager2;
}
+ box = (CheckBox) findViewById(R.id.useVirtualCallCheckBox);
+ useVirtualCall = box.isChecked();
if (mForceScoOn) {
- Log.e(TAG, "startBluetoothSco() IN");
- mngr.startBluetoothSco();
- Log.e(TAG, "startBluetoothSco() OUT");
+ if (useVirtualCall) {
+ Log.e(TAG, "startBluetoothScoVirtualCall() IN");
+ mngr.startBluetoothScoVirtualCall();
+ Log.e(TAG, "startBluetoothScoVirtualCall() OUT");
+ } else {
+ Log.e(TAG, "startBluetoothSco() IN");
+ mngr.startBluetoothSco();
+ Log.e(TAG, "startBluetoothSco() OUT");
+ }
} else {
Log.e(TAG, "stopBluetoothSco() IN");
mngr.stopBluetoothSco();
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
index a4d292a..b8b6c04 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
@@ -169,15 +169,10 @@
return true;
}
-void OmxJpegImageDecoder::configBitmapSize(SkBitmap* bm, SkBitmap::Config pref,
+void OmxJpegImageDecoder::configBitmapSize(SkBitmap* bm, SkColorType pref,
int width, int height) {
- bm->setConfig(getColorSpaceConfig(pref), width, height, 0, kOpaque_SkAlphaType);
-}
-
-SkBitmap::Config OmxJpegImageDecoder::getColorSpaceConfig(
- SkBitmap::Config pref) {
-
- // Set the color space to ARGB_8888 for now
+ // Set the color space to ARGB_8888 for now (ignoring pref)
// because of limitation in hardware support.
- return SkBitmap::kARGB_8888_Config;
+ bm->setInfo(SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType);
}
+
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
index e431e72..e487245 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
@@ -49,9 +49,7 @@
sp<MediaSource> getDecoder(OMXClient* client, const sp<MediaSource>& source);
bool decodeSource(sp<MediaSource> decoder, const sp<MediaSource>& source,
SkBitmap* bm);
- void configBitmapSize(SkBitmap* bm, SkBitmap::Config pref, int width,
- int height);
- SkBitmap::Config getColorSpaceConfig(SkBitmap::Config pref);
+ void configBitmapSize(SkBitmap* bm, SkColorType, int width, int height);
OMXClient mClient;
};
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index d2bf30c..ab18271 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -71,6 +71,7 @@
private AppWidgetHost mAppWidgetHost;
private AppWidgetManager mAppWidgetManager;
private KeyguardWidgetPager mAppWidgetContainer;
+ // TODO remove transport control references, these don't exist anymore
private KeyguardTransportControlView mTransportControl;
private int mAppWidgetToShow;
@@ -235,36 +236,6 @@
mKeyguardMultiUserSelectorView.finalizeActiveUserView(true);
}
}
- @Override
- public void onMusicClientIdChanged(
- int clientGeneration, boolean clearing, android.app.PendingIntent intent) {
- // Set transport state to invisible until we know music is playing (below)
- if (DEBUGXPORT && (mClientGeneration != clientGeneration || clearing)) {
- Log.v(TAG, (clearing ? "hide" : "show") + " transport, gen:" + clientGeneration);
- }
- mClientGeneration = clientGeneration;
- final int newState = (clearing ? TRANSPORT_GONE
- : (mTransportState == TRANSPORT_VISIBLE ?
- TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
- if (newState != mTransportState) {
- mTransportState = newState;
- if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
- KeyguardHostView.this.post(mSwitchPageRunnable);
- }
- }
- @Override
- public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
- if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
- if (mTransportState != TRANSPORT_GONE) {
- final int newState = (isMusicPlaying(playbackState) ?
- TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
- if (newState != mTransportState) {
- mTransportState = newState;
- if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
- KeyguardHostView.this.post(mSwitchPageRunnable);
- }
- }
- }
};
private static final boolean isMusicPlaying(int playbackState) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 668e1ef..bf34705 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -91,8 +91,6 @@
private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
protected static final int MSG_BOOT_COMPLETED = 313;
private static final int MSG_USER_SWITCH_COMPLETE = 314;
- private static final int MSG_SET_CURRENT_CLIENT_ID = 315;
- protected static final int MSG_SET_PLAYBACK_STATE = 316;
protected static final int MSG_USER_INFO_CHANGED = 317;
protected static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
private static final int MSG_SCREEN_TURNED_ON = 319;
@@ -184,12 +182,6 @@
case MSG_BOOT_COMPLETED:
handleBootCompleted();
break;
- case MSG_SET_CURRENT_CLIENT_ID:
- handleSetGenerationId(msg.arg1, msg.arg2 != 0, (PendingIntent) msg.obj);
- break;
- case MSG_SET_PLAYBACK_STATE:
- handleSetPlaybackState(msg.arg1, msg.arg2, (Long) msg.obj);
- break;
case MSG_USER_INFO_CHANGED:
handleUserInfoChanged(msg.arg1);
break;
@@ -206,8 +198,6 @@
}
};
- private AudioManager mAudioManager;
-
private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
@Override
@@ -257,49 +247,6 @@
private DisplayClientState mDisplayClientState = new DisplayClientState();
- /**
- * This currently implements the bare minimum required to enable showing and hiding
- * KeyguardTransportControl. There's a lot of client state to maintain which is why
- * KeyguardTransportControl maintains an independent connection while it's showing.
- */
- private final IRemoteControlDisplay.Stub mRemoteControlDisplay =
- new IRemoteControlDisplay.Stub() {
-
- public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
- long currentPosMs, float speed) {
- Message msg = mHandler.obtainMessage(MSG_SET_PLAYBACK_STATE,
- generationId, state, stateChangeTimeMs);
- mHandler.sendMessage(msg);
- }
-
- public void setMetadata(int generationId, Bundle metadata) {
-
- }
-
- public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
-
- }
-
- public void setArtwork(int generationId, Bitmap bitmap) {
-
- }
-
- public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
-
- }
-
- public void setEnabled(boolean enabled) {
- // no-op: this RemoteControlDisplay is not subject to being disabled.
- }
-
- public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
- boolean clearing) throws RemoteException {
- Message msg = mHandler.obtainMessage(MSG_SET_CURRENT_CLIENT_ID,
- clientGeneration, (clearing ? 1 : 0), mediaIntent);
- mHandler.sendMessage(msg);
- }
- };
-
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
@@ -501,38 +448,6 @@
}
}
- protected void handleSetGenerationId(int clientGeneration, boolean clearing, PendingIntent p) {
- mDisplayClientState.clientGeneration = clientGeneration;
- mDisplayClientState.clearing = clearing;
- mDisplayClientState.intent = p;
- if (DEBUG)
- Log.v(TAG, "handleSetGenerationId(g=" + clientGeneration + ", clear=" + clearing + ")");
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onMusicClientIdChanged(clientGeneration, clearing, p);
- }
- }
- }
-
- protected void handleSetPlaybackState(int generationId, int playbackState, long eventTime) {
- if (DEBUG)
- Log.v(TAG, "handleSetPlaybackState(gen=" + generationId
- + ", state=" + playbackState + ", t=" + eventTime + ")");
- mDisplayClientState.playbackState = playbackState;
- mDisplayClientState.playbackEventTime = eventTime;
- if (generationId == mDisplayClientState.clientGeneration) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onMusicPlaybackStateChanged(playbackState, eventTime);
- }
- }
- } else {
- Log.w(TAG, "Ignoring generation id " + generationId + " because it's not current");
- }
- }
-
private void handleUserInfoChanged(int userId) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -694,8 +609,6 @@
protected void handleBootCompleted() {
if (mBootCompleted) return;
mBootCompleted = true;
- mAudioManager = new AudioManager(mContext);
- mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1013,12 +926,6 @@
callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
callback.onClockVisibilityChanged();
callback.onSimStateChanged(mSimState);
- callback.onMusicClientIdChanged(
- mDisplayClientState.clientGeneration,
- mDisplayClientState.clearing,
- mDisplayClientState.intent);
- callback.onMusicPlaybackStateChanged(mDisplayClientState.playbackState,
- mDisplayClientState.playbackEventTime);
}
public void sendKeyguardVisibilityChanged(boolean showing) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index bcdf18f..01600d2 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -144,18 +144,6 @@
public void onBootCompleted() { }
/**
- * Called when audio client attaches or detaches from AudioManager.
- */
- public void onMusicClientIdChanged(int clientGeneration, boolean clearing, PendingIntent intent) { }
-
- /**
- * Called when the audio playback state changes.
- * @param playbackState
- * @param eventTime
- */
- public void onMusicPlaybackStateChanged(int playbackState, long eventTime) { }
-
- /**
* Called when the emergency call button is pressed.
*/
void onEmergencyCallAction() { }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index bc2671011..637061d04 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -624,6 +624,7 @@
public void onWakeUp() {
synchronized (mLock) {
if (shouldEnableWakeGestureLp()) {
+ performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
mPowerManager.wakeUp(SystemClock.uptimeMillis());
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9fff329..986321d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -335,11 +335,14 @@
// Was previously in list.
numFullscreen--;
}
- updateEffectiveIntent();
if (r.isPersistable()) {
mService.notifyTaskPersisterLocked(this, false);
}
- return mActivities.size() == 0;
+ if (mActivities.isEmpty()) {
+ return true;
+ }
+ updateEffectiveIntent();
+ return false;
}
boolean autoRemoveFromRecents() {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 15a6b25..53337c4 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -203,7 +203,8 @@
@Override
public String toString() {
return String.valueOf(hashCode()).substring(0, 3) + ".."
- + ":[" + job.getService().getPackageName() + ",jId=" + job.getId()
+ + ":[" + job.getService()
+ + ",jId=" + job.getId()
+ ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")"
+ ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging()
+ ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 9d61493..1264741 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -17,6 +17,8 @@
package com.android.server.media;
import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.routeprovider.RouteRequest;
@@ -25,7 +27,6 @@
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.MediaController;
-import android.media.session.RemoteVolumeProvider;
import android.media.session.RouteCommand;
import android.media.session.RouteInfo;
import android.media.session.RouteOptions;
@@ -34,9 +35,11 @@
import android.media.session.MediaSessionInfo;
import android.media.session.RouteInterface;
import android.media.session.PlaybackState;
+import android.media.session.ParcelableVolumeInfo;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
+import android.media.VolumeProvider;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
@@ -107,6 +110,7 @@
// TODO define a RouteState class with relevant info
private int mRouteState;
private long mFlags;
+ private ComponentName mMediaButtonReceiver;
// TransportPerformer fields
@@ -117,9 +121,10 @@
// End TransportPerformer fields
// Volume handling fields
- private int mPlaybackType = MediaSession.VOLUME_TYPE_LOCAL;
+ private AudioManager mAudioManager;
+ private int mVolumeType = MediaSession.VOLUME_TYPE_LOCAL;
private int mAudioStream = AudioManager.STREAM_MUSIC;
- private int mVolumeControlType = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
private int mMaxVolume = 0;
private int mCurrentVolume = 0;
// End volume handling fields
@@ -140,6 +145,7 @@
mSessionCb = new SessionCb(cb);
mService = service;
mHandler = new MessageHandler(handler.getLooper());
+ mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
}
/**
@@ -187,6 +193,10 @@
return mSessionInfo;
}
+ public ComponentName getMediaButtonReceiver() {
+ return mMediaButtonReceiver;
+ }
+
/**
* Get this session's flags.
*
@@ -265,20 +275,42 @@
*
* @param delta The amount to adjust the volume by.
*/
- public void adjustVolumeBy(int delta) {
- if (mVolumeControlType == RemoteVolumeProvider.VOLUME_CONTROL_FIXED) {
- // Nothing to do, the volume cannot be changed
- return;
+ public void adjustVolumeBy(int delta, int flags) {
+ if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+ if (delta == 0) {
+ mAudioManager.adjustStreamVolume(mAudioStream, delta, flags);
+ } else {
+ int direction = 0;
+ int steps = delta;
+ if (delta > 0) {
+ direction = 1;
+ } else if (delta < 0) {
+ direction = -1;
+ steps = -delta;
+ }
+ for (int i = 0; i < steps; i++) {
+ mAudioManager.adjustStreamVolume(mAudioStream, direction, flags);
+ }
+ }
+ } else {
+ if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
+ // Nothing to do, the volume cannot be changed
+ return;
+ }
+ mSessionCb.adjustVolumeBy(delta);
}
- mSessionCb.adjustVolumeBy(delta);
}
- public void setVolumeTo(int value) {
- if (mVolumeControlType != RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
- // Nothing to do. The volume can't be set directly.
- return;
+ public void setVolumeTo(int value, int flags) {
+ if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+ mAudioManager.setStreamVolume(mAudioStream, value, flags);
+ } else {
+ if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
+ // Nothing to do. The volume can't be set directly.
+ return;
+ }
+ mSessionCb.setVolumeTo(value);
}
- mSessionCb.setVolumeTo(value);
}
/**
@@ -352,7 +384,7 @@
* @return The current type of playback.
*/
public int getPlaybackType() {
- return mPlaybackType;
+ return mVolumeType;
}
/**
@@ -683,6 +715,11 @@
}
@Override
+ public void setMediaButtonReceiver(ComponentName mbr) {
+ mMediaButtonReceiver = mbr;
+ }
+
+ @Override
public void setMetadata(MediaMetadata metadata) {
mMetadata = metadata;
mHandler.post(MessageHandler.MSG_UPDATE_METADATA);
@@ -754,7 +791,7 @@
public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
switch(type) {
case MediaSession.VOLUME_TYPE_LOCAL:
- mPlaybackType = type;
+ mVolumeType = type;
int audioStream = arg1;
if (isValidStream(audioStream)) {
mAudioStream = audioStream;
@@ -764,7 +801,7 @@
}
break;
case MediaSession.VOLUME_TYPE_REMOTE:
- mPlaybackType = type;
+ mVolumeType = type;
mVolumeControlType = arg1;
mMaxVolume = arg2;
break;
@@ -985,6 +1022,35 @@
}
@Override
+ public ParcelableVolumeInfo getVolumeAttributes() {
+ synchronized (mLock) {
+ int type;
+ int max;
+ int current;
+ if (mVolumeType == MediaSession.VOLUME_TYPE_REMOTE) {
+ type = mVolumeControlType;
+ max = mMaxVolume;
+ current = mCurrentVolume;
+ } else {
+ type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ max = mAudioManager.getStreamMaxVolume(mAudioStream);
+ current = mAudioManager.getStreamVolume(mAudioStream);
+ }
+ return new ParcelableVolumeInfo(mVolumeType, mAudioStream, type, max, current);
+ }
+ }
+
+ @Override
+ public void adjustVolumeBy(int delta, int flags) {
+ MediaSessionRecord.this.adjustVolumeBy(delta, flags);
+ }
+
+ @Override
+ public void setVolumeTo(int value, int flags) {
+ MediaSessionRecord.this.setVolumeTo(value, flags);
+ }
+
+ @Override
public void play() throws RemoteException {
mSessionCb.play();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 67065ba..685717f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -23,6 +23,7 @@
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -88,6 +89,7 @@
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
+ private ContentResolver mContentResolver;
private MediaSessionRecord mPrioritySession;
private int mCurrentUserId = -1;
@@ -115,6 +117,7 @@
mKeyguardManager =
(KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
mAudioService = getAudioService();
+ mContentResolver = getContext().getContentResolver();
}
private IAudioService getAudioService() {
@@ -381,8 +384,7 @@
return false;
}
if (compName != null) {
- final String enabledNotifListeners = Settings.Secure.getStringForUser(
- getContext().getContentResolver(),
+ final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
userId);
if (enabledNotifListeners != null) {
@@ -485,6 +487,9 @@
synchronized (mLock) {
List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
int size = records.size();
+ if (size > 0) {
+ persistMediaButtonReceiverLocked(records.get(0));
+ }
ArrayList<MediaSessionToken> tokens = new ArrayList<MediaSessionToken>();
for (int i = 0; i < size; i++) {
tokens.add(new MediaSessionToken(records.get(i).getControllerBinder()));
@@ -504,6 +509,16 @@
}
}
+ private void persistMediaButtonReceiverLocked(MediaSessionRecord record) {
+ ComponentName receiver = record.getMediaButtonReceiver();
+ if (receiver != null) {
+ Settings.System.putStringForUser(mContentResolver,
+ Settings.System.MEDIA_BUTTON_RECEIVER,
+ receiver == null ? "" : receiver.flattenToString(),
+ UserHandle.USER_CURRENT);
+ }
+ }
+
private MediaRouteProviderProxy.RoutesListener mRoutesCallback
= new MediaRouteProviderProxy.RoutesListener() {
@Override
@@ -881,14 +896,6 @@
private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags,
MediaSessionRecord session) {
- int direction = 0;
- int steps = delta;
- if (delta > 0) {
- direction = 1;
- } else if (delta < 0) {
- direction = -1;
- steps = -delta;
- }
if (DEBUG) {
String sessionInfo = session == null ? null : session.getSessionInfo().toString();
Log.d(TAG, "Adjusting session " + sessionInfo + " by " + delta + ". flags=" + flags
@@ -901,6 +908,14 @@
mAudioService.adjustSuggestedStreamVolume(delta, suggestedStream, flags,
getContext().getOpPackageName());
} else {
+ int direction = 0;
+ int steps = delta;
+ if (delta > 0) {
+ direction = 1;
+ } else if (delta < 0) {
+ direction = -1;
+ steps = -delta;
+ }
for (int i = 0; i < steps; i++) {
mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
flags, getContext().getOpPackageName());
@@ -910,26 +925,7 @@
Log.e(TAG, "Error adjusting default volume.", e);
}
} else {
- if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) {
- try {
- if (delta == 0) {
- mAudioService.adjustSuggestedStreamVolume(delta,
- session.getAudioStream(), flags,
- getContext().getOpPackageName());
- } else {
- for (int i = 0; i < steps; i++) {
- mAudioService.adjustSuggestedStreamVolume(direction,
- session.getAudioStream(), flags,
- getContext().getOpPackageName());
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error adjusting volume for stream "
- + session.getAudioStream(), e);
- }
- } else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) {
- session.adjustVolumeBy(delta);
- }
+ session.adjustVolumeBy(delta, flags);
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 2a7b4f6..1d53016 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -34,9 +34,9 @@
public class BackgroundDexOptService extends JobService {
static final String TAG = "BackgroundDexOptService";
- static final int BACKGROUND_DEXOPT_JOB = 808;
+ static final int BACKGROUND_DEXOPT_JOB = 800;
private static ComponentName sDexoptServiceName = new ComponentName(
- BackgroundDexOptService.class.getPackage().getName(),
+ "android",
BackgroundDexOptService.class.getName());
final AtomicBoolean mIdleTime = new AtomicBoolean(false);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 99b5b03..6798f3f 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -106,11 +106,9 @@
mLogHandler = new LogHandler(IoThread.get().getLooper());
mTvInputHardwareManager = new TvInputHardwareManager(context);
- registerBroadcastReceivers();
synchronized (mLock) {
mUserStates.put(mCurrentUserId, new UserState());
- buildTvInputListLocked(mCurrentUserId);
}
}
@@ -119,6 +117,16 @@
publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
}
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ registerBroadcastReceivers();
+ synchronized (mLock) {
+ buildTvInputListLocked(mCurrentUserId);
+ }
+ }
+ }
+
private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
@Override
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 9a5079d..3696e24 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -61,8 +61,7 @@
jobject canvas, jint width, jint height) {
SkBitmap* bitmap = new SkBitmap;
- bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
- bitmap->allocPixels();
+ bitmap->allocN32Pixels(width, height);
bitmap->eraseColor(0);
INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap));
diff --git a/telecomm/java/android/telecomm/PhoneApplication.java b/telecomm/java/android/telecomm/PhoneApplication.java
deleted file mode 100644
index 1da54e0..0000000
--- a/telecomm/java/android/telecomm/PhoneApplication.java
+++ /dev/null
@@ -1,183 +0,0 @@
-package android.telecomm;
-
-import android.annotation.SystemApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.telecomm.ITelecommService;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class for managing the primary phone application that will receive incoming calls, and be allowed
- * to make emergency outgoing calls.
- *
- * @hide
- */
-public class PhoneApplication {
- private static final String TAG = PhoneApplication.class.getSimpleName();
- private static final String TELECOMM_SERVICE_NAME = "telecomm";
-
- /**
- * Sets the specified package name as the default phone application. The caller of this method
- * needs to have permission to write to secure settings.
- *
- * @hide
- * */
- @SystemApi
- public static void setDefaultPhoneApplication(String packageName, Context context) {
- // Get old package name
- String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
- Settings.Secure.PHONE_DEFAULT_APPLICATION);
-
- if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
- // No change
- return;
- }
-
- // Only make the change if the new package belongs to a valid phone application
- List<ComponentName> componentNames = getInstalledPhoneApplications(context);
- ComponentName foundComponent = null;
- for (ComponentName componentName : componentNames) {
- if (TextUtils.equals(componentName.getPackageName(), packageName)) {
- foundComponent = componentName;
- break;
- }
- }
-
- if (foundComponent != null) {
- // Update the secure setting.
- Settings.Secure.putString(context.getContentResolver(),
- Settings.Secure.PHONE_DEFAULT_APPLICATION, foundComponent.getPackageName());
- }
- }
-
- /**
- * Returns the installed phone application that will be used to receive incoming calls, and is
- * allowed to make emergency calls.
- *
- * The application will be returned in order of preference:
- * 1) User selected phone application (if still installed)
- * 2) Pre-installed system dialer (if not disabled)
- * 3) Null
- *
- * @hide
- * */
- @SystemApi
- public static ComponentName getDefaultPhoneApplication(Context context) {
- String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
- Settings.Secure.PHONE_DEFAULT_APPLICATION);
-
- final List<ComponentName> componentNames = getInstalledPhoneApplications(context);
- if (!TextUtils.isEmpty(defaultPackageName)) {
- for (ComponentName componentName : componentNames) {
- if (TextUtils.equals(componentName.getPackageName(), defaultPackageName)) {
- return componentName;
- }
- }
- }
-
- // No user-set dialer found, fallback to system dialer
- ComponentName systemDialer = null;
- try {
- systemDialer = getTelecommService().getSystemPhoneApplication();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecommService#getSystemPhoneApplication", e);
- return null;
- }
-
- if (systemDialer == null) {
- // No system dialer configured at build time
- return null;
- }
-
- // Verify that the system dialer has not been disabled.
- return getComponentName(componentNames, systemDialer.getPackageName());
- }
-
- /**
- * Returns a list of installed and available phone applications.
- *
- * In order to appear in the list, a phone application must implement an intent-filter with
- * the DIAL intent for the following schemes:
- *
- * 1) Empty scheme
- * 2) tel Uri scheme
- *
- * @hide
- **/
- @SystemApi
- public static List<ComponentName> getInstalledPhoneApplications(Context context) {
- PackageManager packageManager = context.getPackageManager();
-
- // Get the list of apps registered for the DIAL intent with empty scheme
- Intent intent = new Intent(Intent.ACTION_DIAL);
- List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
-
- List<ComponentName> componentNames = new ArrayList<ComponentName> ();
-
- for (ResolveInfo resolveInfo : resolveInfoList) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
- }
- final ComponentName componentName =
- new ComponentName(activityInfo.packageName, activityInfo.name);
- componentNames.add(componentName);
- }
-
- // TODO: Filter for apps that don't handle DIAL intent with tel scheme
- return componentNames;
- }
-
- /**
- * Returns the {@link ComponentName} for the installed phone application for a given package
- * name.
- *
- * @param context A valid context.
- * @param packageName to retrieve the {@link ComponentName} for.
- *
- * @return The {@link ComponentName} for the installed phone application corresponding to the
- * package name, or null if none is found.
- *
- * @hide
- */
- @SystemApi
- public static ComponentName getPhoneApplicationForPackageName(Context context,
- String packageName) {
- return getComponentName(getInstalledPhoneApplications(context), packageName);
- }
-
- /**
- * Returns the component from a list of application components that corresponds to the package
- * name.
- *
- * @param componentNames A list of component names
- * @param packageName The package name to look for
- * @return The {@link ComponentName} that matches the provided packageName, or null if not
- * found.
- */
- private static ComponentName getComponentName(List<ComponentName> componentNames,
- String packageName) {
- for (ComponentName componentName : componentNames) {
- if (TextUtils.equals(packageName, componentName.getPackageName())) {
- return componentName;
- }
- }
- return null;
- }
-
- private static ITelecommService getTelecommService() {
- return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
- }
-}
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 0952097..0a12c08 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -56,21 +56,6 @@
public static final String ACTION_CALL_SERVICE_SELECTOR = CallServiceSelector.class.getName();
/**
- * Activity action: Ask the user to change the default phone application. This will show a
- * dialog that asks the user whether they want to replace the current default phone application
- * with the one defined in {@link #EXTRA_PACKAGE_NAME}.
- */
- public static final String ACTION_CHANGE_DEFAULT_PHONE =
- "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
-
- /**
- * The PackageName string passed in as an extra for {@link #ACTION_CHANGE_DEFAULT_PHONE}.
- *
- * @see #ACTION_CHANGE_DEFAULT_PHONE
- */
- public static final String EXTRA_PACKAGE_NAME = "package";
-
- /**
* Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
* the speakerphone should be automatically turned on for an outgoing call.
*/
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 989c2cd..a97e7e4 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -16,9 +16,7 @@
package android.telecomm;
-import android.content.ComponentName;
import android.content.Context;
-import android.os.RemoteException;
import com.android.internal.telecomm.ITelecommService;
@@ -47,14 +45,4 @@
public static TelecommManager from(Context context) {
return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE);
}
-
- /** {@hide} */
- public ComponentName getSystemPhoneApplication() {
- try {
- return mService.getSystemPhoneApplication();
- } catch (RemoteException e) {
- Log.e(TAG, e, "Error calling ITelecommService#getSystemPhoneApplication");
- return null;
- }
- }
}
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index dc2b869..c758c6d 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -17,7 +17,6 @@
package com.android.internal.telecomm;
import android.telecomm.Subscription;
-import android.content.ComponentName;
/**
* Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
@@ -43,11 +42,6 @@
void showCallScreen(boolean showDialpad);
/**
- * Returns the component name of the phone application installed on the system partition.
- */
- ComponentName getSystemPhoneApplication();
-
- /**
* Gets a list of Subscriptions.
*/
List<Subscription> getSubscriptions();