Merge "CameraService: Handle ActivityManager death for UidPolicy" into pi-dev
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 940ac5f..346761c 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2257,7 +2257,7 @@
* from the main sensor along the +X axis (to the right from the user's perspective) will
* report <code>(0.03, 0, 0)</code>.</p>
* <p>To transform a pixel coordinates between two cameras facing the same direction, first
- * the source camera ACAMERA_LENS_RADIAL_DISTORTION must be corrected for. Then the source
+ * the source camera ACAMERA_LENS_DISTORTION must be corrected for. Then the source
* camera ACAMERA_LENS_INTRINSIC_CALIBRATION needs to be applied, followed by the
* ACAMERA_LENS_POSE_ROTATION of the source camera, the translation of the source camera
* relative to the destination camera, the ACAMERA_LENS_POSE_ROTATION of the destination
@@ -2269,10 +2269,10 @@
* <p>When ACAMERA_LENS_POSE_REFERENCE is GYROSCOPE, then this position is relative to
* the center of the primary gyroscope on the device.</p>
*
+ * @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_LENS_INTRINSIC_CALIBRATION
* @see ACAMERA_LENS_POSE_REFERENCE
* @see ACAMERA_LENS_POSE_ROTATION
- * @see ACAMERA_LENS_RADIAL_DISTORTION
*/
ACAMERA_LENS_POSE_TRANSLATION = // float[3]
ACAMERA_LENS_START + 7,
@@ -2382,7 +2382,7 @@
* where <code>(0,0)</code> is the top-left of the
* preCorrectionActiveArraySize rectangle. Once the pose and
* intrinsic calibration transforms have been applied to a
- * world point, then the ACAMERA_LENS_RADIAL_DISTORTION
+ * world point, then the ACAMERA_LENS_DISTORTION
* transform needs to be applied, and the result adjusted to
* be in the ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE coordinate
* system (where <code>(0, 0)</code> is the top-left of the
@@ -2390,56 +2390,15 @@
* coordinate of the world point for processed (non-RAW)
* output buffers.</p>
*
+ * @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_LENS_POSE_ROTATION
* @see ACAMERA_LENS_POSE_TRANSLATION
- * @see ACAMERA_LENS_RADIAL_DISTORTION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
ACAMERA_LENS_INTRINSIC_CALIBRATION = // float[5]
ACAMERA_LENS_START + 10,
- /**
- * <p>The correction coefficients to correct for this camera device's
- * radial and tangential lens distortion.</p>
- *
- * <p>Type: float[6]</p>
- *
- * <p>This tag may appear in:
- * <ul>
- * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
- * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
- * </ul></p>
- *
- * <p>Four radial distortion coefficients <code>[kappa_0, kappa_1, kappa_2,
- * kappa_3]</code> and two tangential distortion coefficients
- * <code>[kappa_4, kappa_5]</code> that can be used to correct the
- * lens's geometric distortion with the mapping equations:</p>
- * <pre><code> x_c = x_i * ( kappa_0 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
- * kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
- * y_c = y_i * ( kappa_0 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
- * kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
- * </code></pre>
- * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
- * input image that correspond to the pixel values in the
- * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
- * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
- * </code></pre>
- * <p>The pixel coordinates are defined in a normalized
- * coordinate system related to the
- * ACAMERA_LENS_INTRINSIC_CALIBRATION calibration fields.
- * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code> have <code>(0,0)</code> at the
- * lens optical center <code>[c_x, c_y]</code>. The maximum magnitudes
- * of both x and y coordinates are normalized to be 1 at the
- * edge further from the optical center, so the range
- * for both dimensions is <code>-1 <= x <= 1</code>.</p>
- * <p>Finally, <code>r</code> represents the radial distance from the
- * optical center, <code>r^2 = x_i^2 + y_i^2</code>, and its magnitude
- * is therefore no larger than <code>|r| <= sqrt(2)</code>.</p>
- * <p>The distortion model used is the Brown-Conrady model.</p>
- *
- * @see ACAMERA_LENS_INTRINSIC_CALIBRATION
- */
- ACAMERA_LENS_RADIAL_DISTORTION = // float[6]
+ ACAMERA_LENS_RADIAL_DISTORTION = // Deprecated! DO NOT USE
ACAMERA_LENS_START + 11,
/**
* <p>The origin for ACAMERA_LENS_POSE_TRANSLATION.</p>
@@ -2458,6 +2417,51 @@
*/
ACAMERA_LENS_POSE_REFERENCE = // byte (acamera_metadata_enum_android_lens_pose_reference_t)
ACAMERA_LENS_START + 12,
+ /**
+ * <p>The correction coefficients to correct for this camera device's
+ * radial and tangential lens distortion.</p>
+ * <p>Replaces the deprecated ACAMERA_LENS_RADIAL_DISTORTION field, which was
+ * inconsistently defined.</p>
+ *
+ * @see ACAMERA_LENS_RADIAL_DISTORTION
+ *
+ * <p>Type: float[5]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * </ul></p>
+ *
+ * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2,
+ * kappa_3]</code> and two tangential distortion coefficients
+ * <code>[kappa_4, kappa_5]</code> that can be used to correct the
+ * lens's geometric distortion with the mapping equations:</p>
+ * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+ * kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
+ * y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+ * kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
+ * </code></pre>
+ * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
+ * input image that correspond to the pixel values in the
+ * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
+ * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
+ * </code></pre>
+ * <p>The pixel coordinates are defined in a coordinate system
+ * related to the ACAMERA_LENS_INTRINSIC_CALIBRATION
+ * calibration fields; see that entry for details of the mapping stages.
+ * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code>
+ * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and
+ * the range of the coordinates depends on the focal length
+ * terms of the intrinsic calibration.</p>
+ * <p>Finally, <code>r</code> represents the radial distance from the
+ * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p>
+ * <p>The distortion model used is the Brown-Conrady model.</p>
+ *
+ * @see ACAMERA_LENS_INTRINSIC_CALIBRATION
+ */
+ ACAMERA_LENS_DISTORTION = // float[5]
+ ACAMERA_LENS_START + 13,
ACAMERA_LENS_END,
/**
@@ -4212,7 +4216,7 @@
* ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.</p>
* <p>The currently supported fields that correct for geometric distortion are:</p>
* <ol>
- * <li>ACAMERA_LENS_RADIAL_DISTORTION.</li>
+ * <li>ACAMERA_LENS_DISTORTION.</li>
* </ol>
* <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same
* as the post-distortion-corrected rectangle given in
@@ -4224,7 +4228,7 @@
* full array may include black calibration pixels or other inactive regions.</p>
* <p>The data representation is <code>int[4]</code>, which maps to <code>(left, top, width, height)</code>.</p>
*
- * @see ACAMERA_LENS_RADIAL_DISTORTION
+ * @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
@@ -6941,7 +6945,7 @@
* <li>ACAMERA_LENS_POSE_TRANSLATION</li>
* <li>ACAMERA_LENS_POSE_ROTATION</li>
* <li>ACAMERA_LENS_INTRINSIC_CALIBRATION</li>
- * <li>ACAMERA_LENS_RADIAL_DISTORTION</li>
+ * <li>ACAMERA_LENS_DISTORTION</li>
* </ul>
* </li>
* <li>The ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE entry is listed by this device.</li>
@@ -6959,12 +6963,12 @@
* rate, including depth stall time.</p>
*
* @see ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE
+ * @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_LENS_FACING
* @see ACAMERA_LENS_INTRINSIC_CALIBRATION
* @see ACAMERA_LENS_POSE_REFERENCE
* @see ACAMERA_LENS_POSE_ROTATION
* @see ACAMERA_LENS_POSE_TRANSLATION
- * @see ACAMERA_LENS_RADIAL_DISTORTION
*/
ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8,
@@ -6994,7 +6998,7 @@
* <li>ACAMERA_LENS_POSE_ROTATION</li>
* <li>ACAMERA_LENS_POSE_TRANSLATION</li>
* <li>ACAMERA_LENS_INTRINSIC_CALIBRATION</li>
- * <li>ACAMERA_LENS_RADIAL_DISTORTION</li>
+ * <li>ACAMERA_LENS_DISTORTION</li>
* </ul>
* </li>
* <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
@@ -7020,11 +7024,11 @@
* not slow down the frame rate of the capture, as long as the minimum frame duration
* of the physical and logical streams are the same.</p>
*
+ * @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_LENS_INTRINSIC_CALIBRATION
* @see ACAMERA_LENS_POSE_REFERENCE
* @see ACAMERA_LENS_POSE_ROTATION
* @see ACAMERA_LENS_POSE_TRANSLATION
- * @see ACAMERA_LENS_RADIAL_DISTORTION
* @see ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
*/
ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11,
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index 6b0201a..300c688 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <stdio.h>
+#include <inttypes.h>
#include "DrmPlugin.h"
#include "ClearKeyDrmProperties.h"
@@ -26,6 +27,7 @@
#include "TypeConvert.h"
namespace {
+const int kSecureStopIdStart = 100;
const std::string kStreaming("Streaming");
const std::string kOffline("Offline");
const std::string kTrue("True");
@@ -36,7 +38,18 @@
// Value: "True" or "False"
const std::string kQueryKeyRenewAllowed("RenewAllowed");
// Value: "True" or "False"
-};
+
+const int kSecureStopIdSize = 10;
+
+std::vector<uint8_t> uint32ToVector(uint32_t value) {
+ // 10 bytes to display max value 4294967295 + one byte null terminator
+ char buffer[kSecureStopIdSize];
+ memset(buffer, 0, kSecureStopIdSize);
+ snprintf(buffer, kSecureStopIdSize, "%" PRIu32, value);
+ return std::vector<uint8_t>(buffer, buffer + sizeof(buffer));
+}
+
+}; // unnamed namespace
namespace android {
namespace hardware {
@@ -48,9 +61,11 @@
: mSessionLibrary(sessionLibrary),
mOpenSessionOkCount(0),
mCloseSessionOkCount(0),
- mCloseSessionNotOpenedCount(0) {
+ mCloseSessionNotOpenedCount(0),
+ mNextSecureStopId(kSecureStopIdStart) {
mPlayPolicy.clear();
initProperties();
+ mSecureStops.clear();
}
void DrmPlugin::initProperties() {
@@ -73,6 +88,18 @@
mByteArrayProperties[kMetricsKey] = valueVector;
}
+// The secure stop in ClearKey implementation is not installed securely.
+// This function merely creates a test environment for testing secure stops APIs.
+// The content in this secure stop is implementation dependent, the clearkey
+// secureStop does not serve as a reference implementation.
+void DrmPlugin::installSecureStop(const hidl_vec<uint8_t>& sessionId) {
+ ClearkeySecureStop clearkeySecureStop;
+ clearkeySecureStop.id = uint32ToVector(++mNextSecureStopId);
+ clearkeySecureStop.data.assign(sessionId.begin(), sessionId.end());
+
+ mSecureStops.insert(std::pair<std::vector<uint8_t>, ClearkeySecureStop>(
+ clearkeySecureStop.id, clearkeySecureStop));
+}
Return<void> DrmPlugin::openSession(openSession_cb _hidl_cb) {
sp<Session> session = mSessionLibrary->createSession();
@@ -209,6 +236,7 @@
_hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>());
return Void();
}
+
sp<Session> session = mSessionLibrary->findSession(toVector(scope));
if (!session.get()) {
_hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>());
@@ -224,6 +252,8 @@
keySetId.clear();
}
+ installSecureStop(scope);
+
// Returns status and empty keySetId
_hidl_cb(status, toHidlVec(keySetId));
return Void();
@@ -435,7 +465,106 @@
return Void();
}
+Return<void> DrmPlugin::getSecureStops(getSecureStops_cb _hidl_cb) {
+ std::vector<SecureStop> stops;
+ for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) {
+ ClearkeySecureStop clearkeyStop = itr->second;
+ std::vector<uint8_t> stopVec;
+ stopVec.insert(stopVec.end(), clearkeyStop.id.begin(), clearkeyStop.id.end());
+ stopVec.insert(stopVec.end(), clearkeyStop.data.begin(), clearkeyStop.data.end());
+ SecureStop stop;
+ stop.opaqueData = toHidlVec(stopVec);
+ stops.push_back(stop);
+ }
+ _hidl_cb(Status::OK, stops);
+ return Void();
+}
+
+Return<void> DrmPlugin::getSecureStop(const hidl_vec<uint8_t>& secureStopId,
+ getSecureStop_cb _hidl_cb) {
+ SecureStop stop;
+ auto itr = mSecureStops.find(toVector(secureStopId));
+ if (itr != mSecureStops.end()) {
+ ClearkeySecureStop clearkeyStop = itr->second;
+ std::vector<uint8_t> stopVec;
+ stopVec.insert(stopVec.end(), clearkeyStop.id.begin(), clearkeyStop.id.end());
+ stopVec.insert(stopVec.end(), clearkeyStop.data.begin(), clearkeyStop.data.end());
+
+ stop.opaqueData = toHidlVec(stopVec);
+ _hidl_cb(Status::OK, stop);
+ } else {
+ _hidl_cb(Status::BAD_VALUE, stop);
+ }
+
+ return Void();
+}
+
+Return<Status> DrmPlugin::releaseSecureStop(const hidl_vec<uint8_t>& secureStopId) {
+ return removeSecureStop(secureStopId);
+}
+
+Return<Status> DrmPlugin::releaseAllSecureStops() {
+ return removeAllSecureStops();
+}
+
+Return<void> DrmPlugin::getSecureStopIds(getSecureStopIds_cb _hidl_cb) {
+ std::vector<SecureStopId> ids;
+ for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) {
+ ids.push_back(itr->first);
+ }
+
+ _hidl_cb(Status::OK, toHidlVec(ids));
+ return Void();
+}
+
+Return<Status> DrmPlugin::releaseSecureStops(const SecureStopRelease& ssRelease) {
+ if (ssRelease.opaqueData.size() == 0) {
+ return Status::BAD_VALUE;
+ }
+
+ Status status = Status::OK;
+ std::vector<uint8_t> input = toVector(ssRelease.opaqueData);
+
+ // The format of opaqueData is shared between the server
+ // and the drm service. The clearkey implementation consists of:
+ // count - number of secure stops
+ // list of fixed length secure stops
+ size_t countBufferSize = sizeof(uint32_t);
+ uint32_t count = 0;
+ sscanf(reinterpret_cast<char*>(input.data()), "%04" PRIu32, &count);
+
+ // Avoid divide by 0 below.
+ if (count == 0) {
+ return Status::BAD_VALUE;
+ }
+
+ size_t secureStopSize = (input.size() - countBufferSize) / count;
+ uint8_t buffer[secureStopSize];
+ size_t offset = countBufferSize; // skip the count
+ for (size_t i = 0; i < count; ++i, offset += secureStopSize) {
+ memcpy(buffer, input.data() + offset, secureStopSize);
+ std::vector<uint8_t> id(buffer, buffer + kSecureStopIdSize);
+
+ status = removeSecureStop(toHidlVec(id));
+ if (Status::OK != status) break;
+ }
+
+ return status;
+}
+
+Return<Status> DrmPlugin::removeSecureStop(const hidl_vec<uint8_t>& secureStopId) {
+ if (1 != mSecureStops.erase(toVector(secureStopId))) {
+ return Status::BAD_VALUE;
+ }
+ return Status::OK;
+}
+
+Return<Status> DrmPlugin::removeAllSecureStops() {
+ mSecureStops.clear();
+ mNextSecureStopId = kSecureStopIdStart;
+ return Status::OK;
+}
} // namespace clearkey
} // namespace V1_1
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index 19baf0b..fb0695a 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -17,9 +17,11 @@
#ifndef CLEARKEY_DRM_PLUGIN_H_
#define CLEARKEY_DRM_PLUGIN_H_
-
#include <android/hardware/drm/1.1/IDrmPlugin.h>
+#include <stdio.h>
+#include <map>
+
#include "SessionLibrary.h"
#include "Utils.h"
@@ -36,6 +38,7 @@
using ::android::hardware::drm::V1_0::KeyValue;
using ::android::hardware::drm::V1_0::SecureStop;
using ::android::hardware::drm::V1_0::SecureStopId;
+using ::android::hardware::drm::V1_0::SessionId;
using ::android::hardware::drm::V1_0::Status;
using ::android::hardware::drm::V1_1::DrmMetricGroup;
using ::android::hardware::drm::V1_1::IDrmPlugin;
@@ -124,34 +127,6 @@
return Void();
}
- Return<void> getSecureStops(getSecureStops_cb _hidl_cb) {
- _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<SecureStop>());
- return Void();
- }
-
- Return<void> getSecureStop(
- const hidl_vec<uint8_t>& secureStopId,
- getSecureStop_cb _hidl_cb) {
-
- if (secureStopId.size() == 0) {
- _hidl_cb(Status::BAD_VALUE, SecureStop());
- return Void();
- }
- _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, SecureStop());
- return Void();
- }
-
- Return<Status> releaseSecureStop(const hidl_vec<uint8_t>& ssRelease) {
- if (ssRelease.size() == 0) {
- return Status::BAD_VALUE;
- }
- return Status::ERROR_DRM_CANNOT_HANDLE;
- }
-
- Return<Status> releaseAllSecureStops() {
- return Status::ERROR_DRM_CANNOT_HANDLE;
- }
-
Return<void> getHdcpLevels(getHdcpLevels_cb _hidl_cb) {
HdcpLevel connectedLevel = HdcpLevel::HDCP_NONE;
HdcpLevel maxLevel = HdcpLevel::HDCP_NO_OUTPUT;
@@ -305,31 +280,26 @@
return Void();
}
- Return<void> getSecureStopIds(getSecureStopIds_cb _hidl_cb) {
- _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<SecureStopId>());
- return Void();
- }
+ Return<void> getSecureStops(getSecureStops_cb _hidl_cb);
- Return<Status> releaseSecureStops(const SecureStopRelease& ssRelease) {
- if (ssRelease.opaqueData.size() == 0) {
- return Status::BAD_VALUE;
- }
- return Status::ERROR_DRM_CANNOT_HANDLE;
- }
+ Return<void> getSecureStop(const hidl_vec<uint8_t>& secureStopId,
+ getSecureStop_cb _hidl_cb);
- Return<Status> removeSecureStop(const hidl_vec<uint8_t>& secureStopId) {
- if (secureStopId.size() == 0) {
- return Status::BAD_VALUE;
- }
- return Status::ERROR_DRM_CANNOT_HANDLE;
- }
+ Return<Status> releaseSecureStop(const hidl_vec<uint8_t>& ssRelease);
- Return<Status> removeAllSecureStops() {
- return Status::ERROR_DRM_CANNOT_HANDLE;
- }
+ Return<Status> releaseAllSecureStops();
+
+ Return<void> getSecureStopIds(getSecureStopIds_cb _hidl_cb);
+
+ Return<Status> releaseSecureStops(const SecureStopRelease& ssRelease);
+
+ Return<Status> removeSecureStop(const hidl_vec<uint8_t>& secureStopId);
+
+ Return<Status> removeAllSecureStops();
private:
void initProperties();
+ void installSecureStop(const hidl_vec<uint8_t>& sessionId);
void setPlayPolicy();
Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId,
@@ -344,6 +314,12 @@
KeyRequestType *getKeyRequestType,
std::string *defaultUrl);
+ struct ClearkeySecureStop {
+ std::vector<uint8_t> id;
+ std::vector<uint8_t> data;
+ };
+
+ std::map<std::vector<uint8_t>, ClearkeySecureStop> mSecureStops;
std::vector<KeyValue> mPlayPolicy;
std::map<std::string, std::string> mStringProperties;
std::map<std::string, std::vector<uint8_t> > mByteArrayProperties;
@@ -353,6 +329,7 @@
int64_t mOpenSessionOkCount;
int64_t mCloseSessionOkCount;
int64_t mCloseSessionNotOpenedCount;
+ uint32_t mNextSecureStopId;
CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin);
};
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index c082fc1..666d68a 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -1471,8 +1471,8 @@
// point image to the first tile for grid size and HVCC
image = &mItemIdToItemMap.editValueAt(tileItemIndex);
- meta->setInt32(kKeyGridWidth, image->width);
- meta->setInt32(kKeyGridHeight, image->height);
+ meta->setInt32(kKeyTileWidth, image->width);
+ meta->setInt32(kKeyTileHeight, image->height);
meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
}
diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp
index 2cdb44e..6f56d0c 100644
--- a/media/libmedia/NdkWrapper.cpp
+++ b/media/libmedia/NdkWrapper.cpp
@@ -56,10 +56,8 @@
AMEDIAFORMAT_KEY_COLOR_TRANSFER,
AMEDIAFORMAT_KEY_COMPLEXITY,
AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL,
- AMEDIAFORMAT_KEY_GRID_COLS,
- AMEDIAFORMAT_KEY_GRID_HEIGHT,
+ AMEDIAFORMAT_KEY_GRID_COLUMNS,
AMEDIAFORMAT_KEY_GRID_ROWS,
- AMEDIAFORMAT_KEY_GRID_WIDTH,
AMEDIAFORMAT_KEY_HEIGHT,
AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD,
AMEDIAFORMAT_KEY_IS_ADTS,
@@ -84,6 +82,8 @@
AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
AMEDIAFORMAT_KEY_DISPLAY_WIDTH,
AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID,
+ AMEDIAFORMAT_KEY_TILE_HEIGHT,
+ AMEDIAFORMAT_KEY_TILE_WIDTH,
AMEDIAFORMAT_KEY_TRACK_INDEX,
};
diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp
index 9323d6d..03700bf 100644
--- a/media/libmedia/TypeConverter.cpp
+++ b/media/libmedia/TypeConverter.cpp
@@ -226,8 +226,12 @@
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1_BACK),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1POINT2),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1POINT4),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_6POINT1),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1POINT2),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1POINT4),
TERMINATOR
};
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
index 6010af4..18e63f2 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -211,11 +211,11 @@
kKeyTemporalLayerId = 'iLyr', // int32_t, temporal layer-id. 0-based (0 => base layer)
kKeyTemporalLayerCount = 'cLyr', // int32_t, number of temporal layers encoded
- kKeyGridWidth = 'grdW', // int32_t, HEIF grid width
- kKeyGridHeight = 'grdH', // int32_t, HEIF grid height
+ kKeyTileWidth = 'tilW', // int32_t, HEIF tile width
+ kKeyTileHeight = 'tilH', // int32_t, HEIF tile height
kKeyGridRows = 'grdR', // int32_t, HEIF grid rows
kKeyGridCols = 'grdC', // int32_t, HEIF grid columns
- kKeyIccProfile = 'prof', // raw data, ICC prifile data
+ kKeyIccProfile = 'prof', // raw data, ICC profile data
kKeyIsPrimaryImage = 'prim', // bool (int32_t), image track is the primary image
kKeyFrameCount = 'nfrm', // int32_t, total number of frame in video track
};
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 3570aaf..a8c6d15 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -4384,9 +4384,9 @@
status_t ACodec::configureImageGrid(
const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
- int32_t gridWidth, gridHeight, gridRows, gridCols;
- if (!msg->findInt32("grid-width", &gridWidth) ||
- !msg->findInt32("grid-height", &gridHeight) ||
+ int32_t tileWidth, tileHeight, gridRows, gridCols;
+ if (!msg->findInt32("tile-width", &tileWidth) ||
+ !msg->findInt32("tile-height", &tileHeight) ||
!msg->findInt32("grid-rows", &gridRows) ||
!msg->findInt32("grid-cols", &gridCols)) {
return OK;
@@ -4396,8 +4396,8 @@
InitOMXParams(&gridType);
gridType.nPortIndex = kPortIndexOutput;
gridType.bEnabled = OMX_TRUE;
- gridType.nGridWidth = gridWidth;
- gridType.nGridHeight = gridHeight;
+ gridType.nTileWidth = tileWidth;
+ gridType.nTileHeight = tileHeight;
gridType.nGridRows = gridRows;
gridType.nGridCols = gridCols;
@@ -4422,8 +4422,8 @@
&gridType, sizeof(gridType));
if (err == OK && gridType.bEnabled) {
- outputFormat->setInt32("grid-width", gridType.nGridWidth);
- outputFormat->setInt32("grid-height", gridType.nGridHeight);
+ outputFormat->setInt32("tile-width", gridType.nTileWidth);
+ outputFormat->setInt32("tile-height", gridType.nTileHeight);
outputFormat->setInt32("grid-rows", gridType.nGridRows);
outputFormat->setInt32("grid-cols", gridType.nGridCols);
}
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index 9748a8b..3d0aad1 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -495,27 +495,27 @@
mGridRows = mGridCols = 1;
if (overrideMeta == NULL) {
// check if we're dealing with a tiled heif
- int32_t gridWidth, gridHeight, gridRows, gridCols;
- if (trackMeta()->findInt32(kKeyGridWidth, &gridWidth) && gridWidth > 0
- && trackMeta()->findInt32(kKeyGridHeight, &gridHeight) && gridHeight > 0
+ int32_t tileWidth, tileHeight, gridRows, gridCols;
+ if (trackMeta()->findInt32(kKeyTileWidth, &tileWidth) && tileWidth > 0
+ && trackMeta()->findInt32(kKeyTileHeight, &tileHeight) && tileHeight > 0
&& trackMeta()->findInt32(kKeyGridRows, &gridRows) && gridRows > 0
&& trackMeta()->findInt32(kKeyGridCols, &gridCols) && gridCols > 0) {
int32_t width, height;
CHECK(trackMeta()->findInt32(kKeyWidth, &width));
CHECK(trackMeta()->findInt32(kKeyHeight, &height));
- if (width <= gridWidth * gridCols && height <= gridHeight * gridRows) {
- ALOGV("grid: %dx%d, size: %dx%d, picture size: %dx%d",
- gridCols, gridRows, gridWidth, gridHeight, width, height);
+ if (width <= tileWidth * gridCols && height <= tileHeight * gridRows) {
+ ALOGV("grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
+ gridCols, gridRows, tileWidth, tileHeight, width, height);
overrideMeta = new MetaData(*(trackMeta()));
- overrideMeta->setInt32(kKeyWidth, gridWidth);
- overrideMeta->setInt32(kKeyHeight, gridHeight);
+ overrideMeta->setInt32(kKeyWidth, tileWidth);
+ overrideMeta->setInt32(kKeyHeight, tileHeight);
mGridCols = gridCols;
mGridRows = gridRows;
} else {
- ALOGE("bad grid: %dx%d, size: %dx%d, picture size: %dx%d",
- gridCols, gridRows, gridWidth, gridHeight, width, height);
+ ALOGE("bad grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
+ gridCols, gridRows, tileWidth, tileHeight, width, height);
}
}
if (overrideMeta == NULL) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 768df26..a3261d7 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -358,7 +358,7 @@
Vector<uint16_t> mDimgRefs;
int32_t mIsPrimary;
int32_t mWidth, mHeight;
- int32_t mGridWidth, mGridHeight;
+ int32_t mTileWidth, mTileHeight;
int32_t mGridRows, mGridCols;
size_t mNumTiles, mTileIndex;
@@ -1770,8 +1770,8 @@
mIsPrimary(0),
mWidth(0),
mHeight(0),
- mGridWidth(0),
- mGridHeight(0),
+ mTileWidth(0),
+ mTileHeight(0),
mGridRows(0),
mGridCols(0),
mNumTiles(1),
@@ -1802,13 +1802,13 @@
CHECK(mMeta->findInt32(kKeyWidth, &mWidth) && (mWidth > 0));
CHECK(mMeta->findInt32(kKeyHeight, &mHeight) && (mHeight > 0));
- int32_t gridWidth, gridHeight, gridRows, gridCols;
- if (mMeta->findInt32(kKeyGridWidth, &gridWidth) && (gridWidth > 0) &&
- mMeta->findInt32(kKeyGridHeight, &gridHeight) && (gridHeight > 0) &&
+ int32_t tileWidth, tileHeight, gridRows, gridCols;
+ if (mMeta->findInt32(kKeyTileWidth, &tileWidth) && (tileWidth > 0) &&
+ mMeta->findInt32(kKeyTileHeight, &tileHeight) && (tileHeight > 0) &&
mMeta->findInt32(kKeyGridRows, &gridRows) && (gridRows > 0) &&
mMeta->findInt32(kKeyGridCols, &gridCols) && (gridCols > 0)) {
- mGridWidth = gridWidth;
- mGridHeight = gridHeight;
+ mTileWidth = tileWidth;
+ mTileHeight = tileHeight;
mGridRows = gridRows;
mGridCols = gridCols;
mNumTiles = gridRows * gridCols;
@@ -1978,8 +1978,8 @@
mProperties.push_back(mOwner->addProperty_l({
.type = FOURCC('i', 's', 'p', 'e'),
- .width = hasGrid ? mGridWidth : mWidth,
- .height = hasGrid ? mGridHeight : mHeight,
+ .width = hasGrid ? mTileWidth : mWidth,
+ .height = hasGrid ? mTileHeight : mHeight,
}));
if (!hasGrid && heifRotation > 0) {
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 4a93051..0c6e988 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -665,13 +665,13 @@
}
if (!strncasecmp("image/", mime, 6)) {
- int32_t gridWidth, gridHeight, gridRows, gridCols;
- if (meta->findInt32(kKeyGridWidth, &gridWidth)
- && meta->findInt32(kKeyGridHeight, &gridHeight)
+ int32_t tileWidth, tileHeight, gridRows, gridCols;
+ if (meta->findInt32(kKeyTileWidth, &tileWidth)
+ && meta->findInt32(kKeyTileHeight, &tileHeight)
&& meta->findInt32(kKeyGridRows, &gridRows)
&& meta->findInt32(kKeyGridCols, &gridCols)) {
- msg->setInt32("grid-width", gridWidth);
- msg->setInt32("grid-height", gridHeight);
+ msg->setInt32("tile-width", tileWidth);
+ msg->setInt32("tile-height", tileHeight);
msg->setInt32("grid-rows", gridRows);
msg->setInt32("grid-cols", gridCols);
}
@@ -1341,12 +1341,12 @@
if (msg->findInt32("is-default", &isPrimary) && isPrimary) {
meta->setInt32(kKeyTrackIsDefault, 1);
}
- int32_t gridWidth, gridHeight, gridRows, gridCols;
- if (msg->findInt32("grid-width", &gridWidth)) {
- meta->setInt32(kKeyGridWidth, gridWidth);
+ int32_t tileWidth, tileHeight, gridRows, gridCols;
+ if (msg->findInt32("tile-width", &tileWidth)) {
+ meta->setInt32(kKeyTileWidth, tileWidth);
}
- if (msg->findInt32("grid-height", &gridHeight)) {
- meta->setInt32(kKeyGridHeight, gridHeight);
+ if (msg->findInt32("tile-height", &tileHeight)) {
+ meta->setInt32(kKeyTileHeight, tileHeight);
}
if (msg->findInt32("grid-rows", &gridRows)) {
meta->setInt32(kKeyGridRows, gridRows);
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
index 4bdd20f..0e35f11 100644
--- a/media/libstagefright/codec2/include/C2Buffer.h
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -655,7 +655,8 @@
* (Re)creates a 1D allocation from a native |handle|. If successful, the allocation is stored
* in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
*
- * \param handle the handle for the existing allocation
+ * \param handle the handle for the existing allocation. On success, the allocation will
+ * take ownership of |handle|.
* \param allocation pointer to where the allocation shall be stored on success. nullptr
* will be stored here on failure
*
@@ -712,7 +713,8 @@
* (Re)creates a 2D allocation from a native handle. If successful, the allocation is stored
* in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
*
- * \param handle the handle for the existing allocation
+ * \param handle the handle for the existing allocation. On success, the allocation will
+ * take ownership of |handle|.
* \param allocation pointer to where the allocation shall be stored on success. nullptr
* will be stored here on failure
*
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
index a90e094..96b52a1 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
@@ -255,6 +255,7 @@
mHidlHandle(std::move(hidlHandle)),
mHandle(handle),
mBuffer(nullptr),
+ mLockedHandle(nullptr),
mLocked(false),
mAllocatorId(allocatorId) {
}
@@ -269,6 +270,8 @@
unmap(addr, C2Rect(), nullptr);
}
mMapper->freeBuffer(const_cast<native_handle_t *>(mBuffer));
+ native_handle_delete(const_cast<native_handle_t*>(
+ reinterpret_cast<const native_handle_t*>(mHandle)));
}
c2_status_t C2AllocationGralloc::map(
@@ -608,8 +611,8 @@
return C2_BAD_VALUE;
}
- hidl_handle hidlHandle = C2HandleGralloc::UnwrapNativeHandle(grallocHandle);
-
+ hidl_handle hidlHandle;
+ hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true);
allocation->reset(new C2AllocationGralloc(info, mMapper, hidlHandle, grallocHandle, mTraits->id));
return C2_OK;
}
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
index 664c09e..c5fb8d9 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
@@ -150,7 +150,6 @@
* so that we can capture the error.
*
* \param ionFd ion client (ownership transferred to created object)
- * \param owned whehter native_handle_t is owned by an allocation or not.
* \param capacity size of allocation
* \param bufferFd buffer handle (ownership transferred to created object). Must be
* invalid if err is not 0.
@@ -158,9 +157,8 @@
* invalid if err is not 0.
* \param err errno during buffer allocation or import
*/
- Impl(int ionFd, bool owned, size_t capacity, int bufferFd, ion_user_handle_t buffer, C2Allocator::id_t id, int err)
+ Impl(int ionFd, size_t capacity, int bufferFd, ion_user_handle_t buffer, C2Allocator::id_t id, int err)
: mIonFd(ionFd),
- mHandleOwned(owned),
mHandle(bufferFd, capacity),
mBuffer(buffer),
mId(id),
@@ -191,7 +189,7 @@
static Impl *Import(int ionFd, size_t capacity, int bufferFd, C2Allocator::id_t id) {
ion_user_handle_t buffer = -1;
int ret = ion_import(ionFd, bufferFd, &buffer);
- return new Impl(ionFd, false, capacity, bufferFd, buffer, id, ret);
+ return new Impl(ionFd, capacity, bufferFd, buffer, id, ret);
}
/**
@@ -218,7 +216,7 @@
buffer = -1;
}
}
- return new Impl(ionFd, true, size, bufferFd, buffer, id, ret);
+ return new Impl(ionFd, size, bufferFd, buffer, id, ret);
}
c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) {
@@ -311,13 +309,11 @@
}
if (mInit == C2_OK) {
(void)ion_free(mIonFd, mBuffer);
+ native_handle_close(&mHandle);
}
if (mIonFd >= 0) {
close(mIonFd);
}
- if (mHandleOwned) {
- native_handle_close(&mHandle);
- }
}
c2_status_t status() const {
@@ -338,7 +334,6 @@
private:
int mIonFd;
- bool mHandleOwned;
C2HandleIon mHandle;
ion_user_handle_t mBuffer;
C2Allocator::id_t mId;
@@ -481,6 +476,8 @@
c2_status_t ret = alloc->status();
if (ret == C2_OK) {
*allocation = alloc;
+ native_handle_delete(const_cast<native_handle_t*>(
+ reinterpret_cast<const native_handle_t*>(handle)));
}
return ret;
}
diff --git a/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp
index 7cb4ba9..dda8e39 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp
@@ -135,7 +135,8 @@
bool mInvalidated; // TODO: implement
int64_t mExpireUs;
bool mHasCache;
- _C2BlockPoolData mBuffer;
+ BufferId mId;
+ native_handle_t *mHandle;
std::weak_ptr<_C2BlockPoolData> mCache;
void updateExpire() {
@@ -144,11 +145,18 @@
public:
ClientBuffer(BufferId id, native_handle_t *handle)
- : mInvalidated(false), mHasCache(false), mBuffer(id, handle) {
+ : mInvalidated(false), mHasCache(false), mId(id), mHandle(handle) {
(void)mInvalidated;
mExpireUs = getTimestampNow() + kCacheTtlUs;
}
+ ~ClientBuffer() {
+ if (mHandle) {
+ native_handle_close(mHandle);
+ native_handle_delete(mHandle);
+ }
+ }
+
bool expire() const {
int64_t now = getTimestampNow();
return now >= mExpireUs;
@@ -175,7 +183,7 @@
// Allocates a raw ptr in order to avoid sending #postBufferRelease
// from deleter, in case of native_handle_clone failure.
_C2BlockPoolData *ptr = new _C2BlockPoolData(
- mBuffer.mId, native_handle_clone(mBuffer.mHandle));
+ mId, native_handle_clone(mHandle));
if (ptr && ptr->mHandle != NULL) {
std::shared_ptr<_C2BlockPoolData>
cache(ptr, BlockPoolDataDtor(impl));
diff --git a/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
index 0cf023c..7f4223c 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
+++ b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
@@ -25,6 +25,7 @@
struct __attribute__((visibility("hidden"))) _C2BlockPoolData {
uint32_t mId; //BufferId
+ // Handle should be copied to somewhere else, and should be managed there.
native_handle_t *mHandle;
_C2BlockPoolData() : mId(0), mHandle(NULL) {}
@@ -33,10 +34,6 @@
: mId(id), mHandle(handle) {}
~_C2BlockPoolData() {
- if (mHandle != NULL) {
- native_handle_close(mHandle);
- native_handle_delete(mHandle);
- }
}
};
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 9bf450c..f32b83e 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -296,10 +296,8 @@
EXPORT const char* AMEDIAFORMAT_KEY_DURATION = "durationUs";
EXPORT const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
EXPORT const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate";
-EXPORT const char* AMEDIAFORMAT_KEY_GRID_COLS = "grid-cols";
-EXPORT const char* AMEDIAFORMAT_KEY_GRID_HEIGHT = "grid-height";
+EXPORT const char* AMEDIAFORMAT_KEY_GRID_COLUMNS = "grid-cols";
EXPORT const char* AMEDIAFORMAT_KEY_GRID_ROWS = "grid-rows";
-EXPORT const char* AMEDIAFORMAT_KEY_GRID_WIDTH = "grid-width";
EXPORT const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO = "hdr-static-info";
EXPORT const char* AMEDIAFORMAT_KEY_HEIGHT = "height";
EXPORT const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
@@ -329,6 +327,8 @@
EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID = "temporal-layer-id";
EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING = "ts-schema";
+EXPORT const char* AMEDIAFORMAT_KEY_TILE_HEIGHT = "tile-height";
+EXPORT const char* AMEDIAFORMAT_KEY_TILE_WIDTH = "tile-width";
EXPORT const char* AMEDIAFORMAT_KEY_TIME_US = "timeUs";
EXPORT const char* AMEDIAFORMAT_KEY_TRACK_ID = "track-id";
EXPORT const char* AMEDIAFORMAT_KEY_TRACK_INDEX = "track-index";
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 1da9197..687054e 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -110,10 +110,8 @@
extern const char* AMEDIAFORMAT_KEY_DURATION;
extern const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL;
extern const char* AMEDIAFORMAT_KEY_FRAME_RATE;
-extern const char* AMEDIAFORMAT_KEY_GRID_COLS;
-extern const char* AMEDIAFORMAT_KEY_GRID_HEIGHT;
+extern const char* AMEDIAFORMAT_KEY_GRID_COLUMNS;
extern const char* AMEDIAFORMAT_KEY_GRID_ROWS;
-extern const char* AMEDIAFORMAT_KEY_GRID_WIDTH;
extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO;
extern const char* AMEDIAFORMAT_KEY_HEIGHT;
extern const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD;
@@ -143,6 +141,8 @@
extern const char* AMEDIAFORMAT_KEY_STRIDE;
extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID;
extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING;
+extern const char* AMEDIAFORMAT_KEY_TILE_HEIGHT;
+extern const char* AMEDIAFORMAT_KEY_TILE_WIDTH;
extern const char* AMEDIAFORMAT_KEY_TIME_US;
extern const char* AMEDIAFORMAT_KEY_TRACK_ID;
extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX;
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index b0d8e7d..b6480a2 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -40,6 +40,9 @@
LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
+# TODO: Enable proguard (b/74090741)
+LOCAL_PROGUARD_ENABLED := disabled
+
LOCAL_MULTILIB := first
LOCAL_JAVA_LIBRARIES += android-support-annotations
diff --git a/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java b/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java
new file mode 100644
index 0000000..6805dad
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlayerBase;
+import android.media.MediaPlaylistAgent;
+import android.media.MediaSession2;
+import android.media.MediaSession2.OnDataSourceMissingHelper;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class SessionPlaylistAgent extends MediaPlaylistAgent {
+ private static final String TAG = "SessionPlaylistAgent";
+ @VisibleForTesting
+ static final int END_OF_PLAYLIST = -1;
+ @VisibleForTesting
+ static final int NO_VALID_ITEMS = -2;
+
+ private final PlayItem mEopPlayItem = new PlayItem(END_OF_PLAYLIST, null);
+
+ private final Object mLock = new Object();
+ private final MediaSession2 mSession;
+
+ // TODO: Set data sources properly into mPlayer (b/74090741)
+ @GuardedBy("mLock")
+ private MediaPlayerBase mPlayer;
+ @GuardedBy("mLock")
+ private OnDataSourceMissingHelper mDsdHelper;
+
+ // TODO: Check if having the same item is okay (b/74090741)
+ @GuardedBy("mLock")
+ private ArrayList<MediaItem2> mPlaylist = new ArrayList<>();
+ @GuardedBy("mLock")
+ private ArrayList<MediaItem2> mShuffledList = new ArrayList<>();
+ @GuardedBy("mLock")
+ private Map<MediaItem2, DataSourceDesc> mItemDsdMap = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private MediaMetadata2 mMetadata;
+ @GuardedBy("mLock")
+ private int mRepeatMode;
+ @GuardedBy("mLock")
+ private int mShuffleMode;
+ @GuardedBy("mLock")
+ private PlayItem mCurrent;
+
+ private class PlayItem {
+ int shuffledIdx;
+ DataSourceDesc dsd;
+ MediaItem2 mediaItem;
+
+ PlayItem(int shuffledIdx) {
+ this(shuffledIdx, null);
+ }
+
+ PlayItem(int shuffledIdx, DataSourceDesc dsd) {
+ this.shuffledIdx = shuffledIdx;
+ if (shuffledIdx >= 0) {
+ this.mediaItem = mShuffledList.get(shuffledIdx);
+ if (dsd == null) {
+ synchronized (mLock) {
+ this.dsd = retrieveDataSourceDescLocked(this.mediaItem);
+ }
+ } else {
+ this.dsd = dsd;
+ }
+ }
+ }
+
+ boolean isValid() {
+ if (this == mEopPlayItem) {
+ return true;
+ }
+ if (mediaItem == null) {
+ return false;
+ }
+ if (dsd == null) {
+ return false;
+ }
+ if (shuffledIdx >= mShuffledList.size()) {
+ return false;
+ }
+ if (mediaItem != mShuffledList.get(shuffledIdx)) {
+ return false;
+ }
+ if (mediaItem.getDataSourceDesc() != null
+ && !mediaItem.getDataSourceDesc().equals(dsd)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public SessionPlaylistAgent(@NonNull Context context, @NonNull MediaSession2 session,
+ @NonNull MediaPlayerBase player) {
+ super(context);
+ if (session == null) {
+ throw new IllegalArgumentException("session shouldn't be null");
+ }
+ if (player == null) {
+ throw new IllegalArgumentException("player shouldn't be null");
+ }
+ mSession = session;
+ mPlayer = player;
+ }
+
+ public void setPlayer(MediaPlayerBase player) {
+ if (player == null) {
+ throw new IllegalArgumentException("player shouldn't be null");
+ }
+ synchronized (mLock) {
+ mPlayer = player;
+ }
+ }
+
+ public void setOnDataSourceMissingHelper(OnDataSourceMissingHelper helper) {
+ synchronized (mLock) {
+ mDsdHelper = helper;
+ }
+ }
+
+ @Override
+ public @Nullable List<MediaItem2> getPlaylist() {
+ synchronized (mLock) {
+ return Collections.unmodifiableList(mPlaylist);
+ }
+ }
+
+ @Override
+ public void setPlaylist(@NonNull List<MediaItem2> list,
+ @Nullable MediaMetadata2 metadata) {
+ if (list == null) {
+ throw new IllegalArgumentException("list shouldn't be null");
+ }
+
+ synchronized (mLock) {
+ mItemDsdMap.clear();
+
+ mPlaylist.clear();
+ mPlaylist.addAll(list);
+ applyShuffleModeLocked();
+
+ mMetadata = metadata;
+ mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+ }
+ notifyPlaylistChanged();
+ }
+
+ @Override
+ public @Nullable MediaMetadata2 getPlaylistMetadata() {
+ return mMetadata;
+ }
+
+ @Override
+ public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
+ synchronized (mLock) {
+ if (metadata == mMetadata) {
+ return;
+ }
+ mMetadata = metadata;
+ }
+ notifyPlaylistMetadataChanged();
+ }
+
+ @Override
+ public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
+ if (item == null) {
+ throw new IllegalArgumentException("item shouldn't be null");
+ }
+ synchronized (mLock) {
+ index = clamp(index, mPlaylist.size());
+ int shuffledIdx = index;
+ mPlaylist.add(index, item);
+ if (mShuffleMode == MediaPlaylistAgent.SHUFFLE_MODE_NONE) {
+ mShuffledList.add(index, item);
+ } else {
+ // Add the item in random position of mShuffledList.
+ shuffledIdx = ThreadLocalRandom.current().nextInt(mShuffledList.size() + 1);
+ mShuffledList.add(shuffledIdx, item);
+ }
+ if (!hasValidItem()) {
+ mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+ } else {
+ updateCurrentIfNeededLocked();
+ }
+ }
+ notifyPlaylistChanged();
+ }
+
+ @Override
+ public void removePlaylistItem(@NonNull MediaItem2 item) {
+ if (item == null) {
+ throw new IllegalArgumentException("item shouldn't be null");
+ }
+ synchronized (mLock) {
+ if (!mPlaylist.remove(item)) {
+ return;
+ }
+ mShuffledList.remove(item);
+ mItemDsdMap.remove(item);
+ updateCurrentIfNeededLocked();
+ }
+ notifyPlaylistChanged();
+ }
+
+ @Override
+ public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
+ if (item == null) {
+ throw new IllegalArgumentException("item shouldn't be null");
+ }
+ synchronized (mLock) {
+ if (mPlaylist.size() <= 0) {
+ return;
+ }
+ index = clamp(index, mPlaylist.size() - 1);
+ int shuffledIdx = mShuffledList.indexOf(mPlaylist.get(index));
+ mItemDsdMap.remove(mShuffledList.get(shuffledIdx));
+ mShuffledList.set(shuffledIdx, item);
+ mPlaylist.set(index, item);
+ if (!hasValidItem()) {
+ mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+ } else {
+ updateCurrentIfNeededLocked();
+ }
+ }
+ notifyPlaylistChanged();
+ }
+
+ @Override
+ public void skipToPlaylistItem(@NonNull MediaItem2 item) {
+ if (item == null) {
+ throw new IllegalArgumentException("item shouldn't be null");
+ }
+ synchronized (mLock) {
+ if (!hasValidItem() || item.equals(mCurrent.mediaItem)) {
+ return;
+ }
+ int shuffledIdx = mShuffledList.indexOf(item);
+ if (shuffledIdx < 0) {
+ return;
+ }
+ mCurrent = new PlayItem(shuffledIdx);
+ updateCurrentIfNeededLocked();
+ }
+ }
+
+ @Override
+ public void skipToPreviousItem() {
+ synchronized (mLock) {
+ if (!hasValidItem()) {
+ return;
+ }
+ PlayItem prev = getNextValidPlayItemLocked(mCurrent.shuffledIdx, -1);
+ if (prev != mEopPlayItem) {
+ mCurrent = prev;
+ }
+ updateCurrentIfNeededLocked();
+ }
+ }
+
+ @Override
+ public void skipToNextItem() {
+ synchronized (mLock) {
+ if (!hasValidItem()) {
+ return;
+ }
+ PlayItem next = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1);
+ if (next != mEopPlayItem) {
+ mCurrent = next;
+ }
+ updateCurrentIfNeededLocked();
+ }
+ }
+
+ @Override
+ public int getRepeatMode() {
+ return mRepeatMode;
+ }
+
+ @Override
+ public void setRepeatMode(int repeatMode) {
+ if (repeatMode < MediaPlaylistAgent.REPEAT_MODE_NONE
+ || repeatMode > MediaPlaylistAgent.REPEAT_MODE_GROUP) {
+ return;
+ }
+ synchronized (mLock) {
+ if (mRepeatMode == repeatMode) {
+ return;
+ }
+ mRepeatMode = repeatMode;
+ }
+ notifyRepeatModeChanged();
+ }
+
+ @Override
+ public int getShuffleMode() {
+ return mShuffleMode;
+ }
+
+ @Override
+ public void setShuffleMode(int shuffleMode) {
+ if (shuffleMode < MediaPlaylistAgent.SHUFFLE_MODE_NONE
+ || shuffleMode > MediaPlaylistAgent.SHUFFLE_MODE_GROUP) {
+ return;
+ }
+ synchronized (mLock) {
+ if (mShuffleMode == shuffleMode) {
+ return;
+ }
+ mShuffleMode = shuffleMode;
+ applyShuffleModeLocked();
+ }
+ notifyShuffleModeChanged();
+ }
+
+ @VisibleForTesting
+ int getCurShuffledIndex() {
+ return hasValidItem() ? mCurrent.shuffledIdx : NO_VALID_ITEMS;
+ }
+
+ private boolean hasValidItem() {
+ return mCurrent != null;
+ }
+
+ private DataSourceDesc retrieveDataSourceDescLocked(MediaItem2 item) {
+ DataSourceDesc dsd = item.getDataSourceDesc();
+ if (dsd != null) {
+ mItemDsdMap.put(item, dsd);
+ return dsd;
+ }
+ dsd = mItemDsdMap.get(item);
+ if (dsd != null) {
+ return dsd;
+ }
+ OnDataSourceMissingHelper helper = mDsdHelper;
+ if (helper != null) {
+ // TODO: Do not call onDataSourceMissing with the lock (b/74090741).
+ dsd = helper.onDataSourceMissing(mSession, item);
+ if (dsd != null) {
+ mItemDsdMap.put(item, dsd);
+ }
+ }
+ return dsd;
+ }
+
+ private PlayItem getNextValidPlayItemLocked(int curShuffledIdx, int direction) {
+ int size = mPlaylist.size();
+ if (curShuffledIdx == END_OF_PLAYLIST) {
+ curShuffledIdx = (direction > 0) ? -1 : size;
+ }
+ for (int i = 0; i < size; i++) {
+ curShuffledIdx += direction;
+ if (curShuffledIdx < 0 || curShuffledIdx >= mPlaylist.size()) {
+ if (mRepeatMode == REPEAT_MODE_NONE) {
+ return (i == size - 1) ? null : mEopPlayItem;
+ } else {
+ curShuffledIdx = curShuffledIdx < 0 ? mPlaylist.size() - 1 : 0;
+ }
+ }
+ DataSourceDesc dsd = retrieveDataSourceDescLocked(mShuffledList.get(curShuffledIdx));
+ if (dsd != null) {
+ return new PlayItem(curShuffledIdx, dsd);
+ }
+ }
+ return null;
+ }
+
+ private void updateCurrentIfNeededLocked() {
+ if (!hasValidItem() || mCurrent.isValid()) {
+ return;
+ }
+ int shuffledIdx = mShuffledList.indexOf(mCurrent.mediaItem);
+ if (shuffledIdx >= 0) {
+ // Added an item.
+ mCurrent.shuffledIdx = shuffledIdx;
+ return;
+ }
+
+ if (mCurrent.shuffledIdx >= mShuffledList.size()) {
+ mCurrent = getNextValidPlayItemLocked(mShuffledList.size() - 1, 1);
+ } else {
+ mCurrent.mediaItem = mShuffledList.get(mCurrent.shuffledIdx);
+ if (retrieveDataSourceDescLocked(mCurrent.mediaItem) == null) {
+ mCurrent = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1);
+ }
+ }
+ return;
+ }
+
+ private void applyShuffleModeLocked() {
+ mShuffledList.clear();
+ mShuffledList.addAll(mPlaylist);
+ if (mShuffleMode == MediaPlaylistAgent.SHUFFLE_MODE_ALL
+ || mShuffleMode == MediaPlaylistAgent.SHUFFLE_MODE_GROUP) {
+ Collections.shuffle(mShuffledList);
+ }
+ }
+
+ // Clamps value to [0, size]
+ private static int clamp(int value, int size) {
+ if (value < 0) {
+ return 0;
+ }
+ return (value > size) ? size : value;
+ }
+}
diff --git a/packages/MediaComponents/tests/Android.mk b/packages/MediaComponents/tests/Android.mk
new file mode 100644
index 0000000..bf81efb
--- /dev/null
+++ b/packages/MediaComponents/tests/Android.mk
@@ -0,0 +1,35 @@
+# Copyright 2018 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android.test.runner.stubs \
+ android.test.base.stubs \
+ junit
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := MediaComponentsTest
+
+LOCAL_INSTRUMENTATION_FOR := MediaComponents
+
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/packages/MediaComponents/tests/AndroidManifest.xml b/packages/MediaComponents/tests/AndroidManifest.xml
new file mode 100644
index 0000000..7255265
--- /dev/null
+++ b/packages/MediaComponents/tests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.media.tests">
+
+ <application android:label="Media API Test">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!--
+ To run the tests use the command:
+ "adb shell am instrument -w com.android.media.tests/android.test.InstrumentationTestRunner"
+ -->
+ <instrumentation
+ android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.media.update"
+ android:label="Media API test" />
+
+</manifest>
diff --git a/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java b/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java
new file mode 100644
index 0000000..80370ec
--- /dev/null
+++ b/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.content.Context;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlayer2;
+import android.media.MediaPlayerBase;
+import android.media.MediaPlaylistAgent;
+import android.media.MediaSession2;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Tests {@link SessionPlaylistAgent}.
+ */
+public class SessionPlaylistAgentTest extends AndroidTestCase {
+ private static final String TAG = "SessionPlaylistAgentTest";
+ private static final int WAIT_TIME_MS = 1000;
+ private static final int INVALID_REPEAT_MODE = -100;
+ private static final int INVALID_SHUFFLE_MODE = -100;
+
+ private Handler mHandler;
+ private Executor mHandlerExecutor;
+
+ private Object mWaitLock = new Object();
+ private Context mContext;
+ private MediaSession2 mSession;
+ private MediaPlayerBase mPlayer;
+ private SessionPlaylistAgent mAgent;
+ private MyDataSourceHelper mDataSourceHelper;
+ private MyPlaylistEventCallback mEventCallback;
+
+ public class MyPlaylistEventCallback extends MediaPlaylistAgent.PlaylistEventCallback {
+ boolean onPlaylistChangedCalled;
+ boolean onPlaylistMetadataChangedCalled;
+ boolean onRepeatModeChangedCalled;
+ boolean onShuffleModeChangedCalled;
+
+ private Object mWaitLock;
+
+ public MyPlaylistEventCallback(Object waitLock) {
+ mWaitLock = waitLock;
+ }
+
+ public void clear() {
+ onPlaylistChangedCalled = false;
+ onPlaylistMetadataChangedCalled = false;
+ onRepeatModeChangedCalled = false;
+ onShuffleModeChangedCalled = false;
+ }
+
+ public void onPlaylistChanged(MediaPlaylistAgent playlistAgent, List<MediaItem2> list,
+ MediaMetadata2 metadata) {
+ synchronized (mWaitLock) {
+ onPlaylistChangedCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ public void onPlaylistMetadataChanged(MediaPlaylistAgent playlistAgent,
+ MediaMetadata2 metadata) {
+ synchronized (mWaitLock) {
+ onPlaylistMetadataChangedCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ public void onRepeatModeChanged(MediaPlaylistAgent playlistAgent, int repeatMode) {
+ synchronized (mWaitLock) {
+ onRepeatModeChangedCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ public void onShuffleModeChanged(MediaPlaylistAgent playlistAgent, int shuffleMode) {
+ synchronized (mWaitLock) {
+ onShuffleModeChangedCalled = true;
+ mWaitLock.notify();
+ }
+ }
+ }
+
+ public class MyDataSourceHelper implements MediaSession2.OnDataSourceMissingHelper {
+ @Override
+ public DataSourceDesc onDataSourceMissing(MediaSession2 session, MediaItem2 item) {
+ if (item.getMediaId().contains("WITHOUT_DSD")) {
+ return null;
+ }
+ return new DataSourceDesc.Builder()
+ .setDataSource(getContext(), Uri.parse("dsd://test"))
+ .setMediaId(item.getMediaId())
+ .build();
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HandlerThread handlerThread = new HandlerThread("SessionPlaylistAgent");
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper());
+ mHandlerExecutor = (runnable) -> {
+ mHandler.post(runnable);
+ };
+
+ mContext = getContext();
+ mPlayer = MediaPlayer2.create();
+ mSession = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(mHandlerExecutor,
+ new MediaSession2.SessionCallback(mContext) {})
+ .setId(TAG).build();
+ mDataSourceHelper = new MyDataSourceHelper();
+ mAgent = new SessionPlaylistAgent(mContext, mSession, mPlayer);
+ mAgent.setOnDataSourceMissingHelper(mDataSourceHelper);
+ mEventCallback = new MyPlaylistEventCallback(mWaitLock);
+ mAgent.registerPlaylistEventCallback(mHandlerExecutor, mEventCallback);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSession.close();
+ mPlayer.close();
+ mHandler.getLooper().quitSafely();
+ mHandler = null;
+ mHandlerExecutor = null;
+ }
+
+ @Test
+ public void testSetAndGetShuflleMode() throws Exception {
+ int shuffleMode = mAgent.getShuffleMode();
+ if (shuffleMode != MediaPlaylistAgent.SHUFFLE_MODE_NONE) {
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setShuffleMode(MediaPlaylistAgent.SHUFFLE_MODE_NONE);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onShuffleModeChangedCalled);
+ }
+ assertEquals(MediaPlaylistAgent.SHUFFLE_MODE_NONE, mAgent.getShuffleMode());
+ }
+
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setShuffleMode(MediaPlaylistAgent.SHUFFLE_MODE_ALL);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onShuffleModeChangedCalled);
+ }
+ assertEquals(MediaPlaylistAgent.SHUFFLE_MODE_ALL, mAgent.getShuffleMode());
+
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setShuffleMode(MediaPlaylistAgent.SHUFFLE_MODE_GROUP);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onShuffleModeChangedCalled);
+ }
+ assertEquals(MediaPlaylistAgent.SHUFFLE_MODE_GROUP, mAgent.getShuffleMode());
+
+ // INVALID_SHUFFLE_MODE will not change the shuffle mode.
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setShuffleMode(INVALID_SHUFFLE_MODE);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertFalse(mEventCallback.onShuffleModeChangedCalled);
+ }
+ assertEquals(MediaPlaylistAgent.SHUFFLE_MODE_GROUP, mAgent.getShuffleMode());
+ }
+
+ @Test
+ public void testSetAndGetRepeatMode() throws Exception {
+ int repeatMode = mAgent.getRepeatMode();
+ if (repeatMode != MediaPlaylistAgent.REPEAT_MODE_NONE) {
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_NONE);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onRepeatModeChangedCalled);
+ }
+ assertEquals(MediaPlaylistAgent.REPEAT_MODE_NONE, mAgent.getRepeatMode());
+ }
+
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ONE);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onRepeatModeChangedCalled);
+ }
+ assertEquals(MediaPlaylistAgent.REPEAT_MODE_ONE, mAgent.getRepeatMode());
+
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ALL);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onRepeatModeChangedCalled);
+ }
+ assertEquals(MediaPlaylistAgent.REPEAT_MODE_ALL, mAgent.getRepeatMode());
+
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_GROUP);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onRepeatModeChangedCalled);
+ }
+ assertEquals(MediaPlaylistAgent.REPEAT_MODE_GROUP, mAgent.getRepeatMode());
+
+ // INVALID_SHUFFLE_MODE will not change the shuffle mode.
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setRepeatMode(INVALID_REPEAT_MODE);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertFalse(mEventCallback.onRepeatModeChangedCalled);
+ }
+ assertEquals(MediaPlaylistAgent.REPEAT_MODE_GROUP, mAgent.getRepeatMode());
+ }
+
+ @Test
+ public void testSetPlaylist() throws Exception {
+ int listSize = 10;
+ createAndSetPlaylist(10);
+ assertEquals(listSize, mAgent.getPlaylist().size());
+ assertEquals(0, mAgent.getCurShuffledIndex());
+ }
+
+ @Test
+ public void testSkipItems() throws Exception {
+ int listSize = 5;
+ List<MediaItem2> playlist = createAndSetPlaylist(listSize);
+
+ mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_NONE);
+ // Test skipToPlaylistItem
+ for (int i = listSize - 1; i >= 0; --i) {
+ mAgent.skipToPlaylistItem(playlist.get(i));
+ assertEquals(i, mAgent.getCurShuffledIndex());
+ }
+
+ // Test skipToNextItem
+ // curPlayPos = 0
+ for (int curPlayPos = 0; curPlayPos < listSize - 1; ++curPlayPos) {
+ mAgent.skipToNextItem();
+ assertEquals(curPlayPos + 1, mAgent.getCurShuffledIndex());
+ }
+ mAgent.skipToNextItem();
+ assertEquals(listSize - 1, mAgent.getCurShuffledIndex());
+
+ // Test skipToPrevious
+ // curPlayPos = listSize - 1
+ for (int curPlayPos = listSize - 1; curPlayPos > 0; --curPlayPos) {
+ mAgent.skipToPreviousItem();
+ assertEquals(curPlayPos - 1, mAgent.getCurShuffledIndex());
+ }
+ mAgent.skipToPreviousItem();
+ assertEquals(0, mAgent.getCurShuffledIndex());
+
+ mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ALL);
+ // Test skipToPrevious with repeat mode all
+ // curPlayPos = 0
+ mAgent.skipToPreviousItem();
+ assertEquals(listSize - 1, mAgent.getCurShuffledIndex());
+
+ // Test skipToNext with repeat mode all
+ // curPlayPos = listSize - 1
+ mAgent.skipToNextItem();
+ assertEquals(0, mAgent.getCurShuffledIndex());
+
+ mAgent.skipToPreviousItem();
+ // curPlayPos = listSize - 1, nextPlayPos = 0
+ // Test next play pos after setting repeat mode none.
+ mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_NONE);
+ assertEquals(listSize - 1, mAgent.getCurShuffledIndex());
+ }
+
+ @Test
+ public void testEditPlaylist() throws Exception {
+ int listSize = 5;
+ List<MediaItem2> playlist = createAndSetPlaylist(listSize);
+
+ // Test add item: [0 (cur), 1, 2, 3, 4] -> [0 (cur), 1, 5, 2, 3, 4]
+ mEventCallback.clear();
+ MediaItem2 item_5 = generateMediaItem(5);
+ synchronized (mWaitLock) {
+ playlist.add(2, item_5);
+ mAgent.addPlaylistItem(2, item_5);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+
+ mEventCallback.clear();
+ // Move current: [0 (cur), 1, 5, 2, 3, 4] -> [0, 1, 5 (cur), 2, 3, 4]
+ mAgent.skipToPlaylistItem(item_5);
+ // Remove current item: [0, 1, 5 (cur), 2, 3, 4] -> [0, 1, 2 (cur), 3, 4]
+ synchronized (mWaitLock) {
+ playlist.remove(item_5);
+ mAgent.removePlaylistItem(item_5);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(2, mAgent.getCurShuffledIndex());
+
+ // Remove previous item: [0, 1, 2 (cur), 3, 4] -> [0, 2 (cur), 3, 4]
+ mEventCallback.clear();
+ MediaItem2 previousItem = playlist.get(1);
+ synchronized (mWaitLock) {
+ playlist.remove(previousItem);
+ mAgent.removePlaylistItem(previousItem);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(1, mAgent.getCurShuffledIndex());
+
+ // Remove next item: [0, 2 (cur), 3, 4] -> [0, 2 (cur), 4]
+ mEventCallback.clear();
+ MediaItem2 nextItem = playlist.get(2);
+ synchronized (mWaitLock) {
+ playlist.remove(nextItem);
+ mAgent.removePlaylistItem(nextItem);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(1, mAgent.getCurShuffledIndex());
+
+ // Replace item: [0, 2 (cur), 4] -> [0, 2 (cur), 5]
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ playlist.set(2, item_5);
+ mAgent.replacePlaylistItem(2, item_5);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(1, mAgent.getCurShuffledIndex());
+
+ // Move last and remove the last item: [0, 2 (cur), 5] -> [0, 2, 5 (cur)] -> [0, 2 (cur)]
+ MediaItem2 lastItem = playlist.get(1);
+ mAgent.skipToPlaylistItem(lastItem);
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ playlist.remove(lastItem);
+ mAgent.removePlaylistItem(lastItem);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(1, mAgent.getCurShuffledIndex());
+
+ // Remove all items
+ for (int i = playlist.size() - 1; i >= 0; --i) {
+ MediaItem2 item = playlist.get(i);
+ mAgent.skipToPlaylistItem(item);
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ playlist.remove(item);
+ mAgent.removePlaylistItem(item);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ }
+ assertEquals(SessionPlaylistAgent.NO_VALID_ITEMS, mAgent.getCurShuffledIndex());
+ }
+
+
+ @Test
+ public void testPlaylistWithInvalidItem() throws Exception {
+ int listSize = 2;
+ List<MediaItem2> playlist = createAndSetPlaylist(listSize);
+
+ // Add item: [0 (cur), 1] -> [0 (cur), 3 (no_dsd), 1]
+ mEventCallback.clear();
+ MediaItem2 invalidItem2 = generateMediaItemWithoutDataSourceDesc(2);
+ synchronized (mWaitLock) {
+ playlist.add(1, invalidItem2);
+ mAgent.addPlaylistItem(1, invalidItem2);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(0, mAgent.getCurShuffledIndex());
+
+ // Test skip to next item: [0 (cur), 2 (no_dsd), 1] -> [0, 2 (no_dsd), 1 (cur)]
+ mAgent.skipToNextItem();
+ assertEquals(2, mAgent.getCurShuffledIndex());
+
+ // Test skip to previous item: [0, 2 (no_dsd), 1 (cur)] -> [0 (cur), 2 (no_dsd), 1]
+ mAgent.skipToPreviousItem();
+ assertEquals(0, mAgent.getCurShuffledIndex());
+
+ // Remove current item: [0 (cur), 2 (no_dsd), 1] -> [2 (no_dsd), 1 (cur)]
+ mEventCallback.clear();
+ MediaItem2 item = playlist.get(0);
+ synchronized (mWaitLock) {
+ playlist.remove(item);
+ mAgent.removePlaylistItem(item);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(1, mAgent.getCurShuffledIndex());
+
+ // Remove current item: [2 (no_dsd), 1 (cur)] -> [2 (no_dsd)]
+ mEventCallback.clear();
+ item = playlist.get(1);
+ synchronized (mWaitLock) {
+ playlist.remove(item);
+ mAgent.removePlaylistItem(item);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(SessionPlaylistAgent.NO_VALID_ITEMS, mAgent.getCurShuffledIndex());
+
+ // Add invalid item: [2 (no_dsd)] -> [0 (no_dsd), 2 (no_dsd)]
+ MediaItem2 invalidItem0 = generateMediaItemWithoutDataSourceDesc(0);
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ playlist.add(0, invalidItem0);
+ mAgent.addPlaylistItem(0, invalidItem0);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(SessionPlaylistAgent.NO_VALID_ITEMS, mAgent.getCurShuffledIndex());
+
+ // Add valid item: [0 (no_dsd), 2 (no_dsd)] -> [0 (no_dsd), 1, 2 (no_dsd)]
+ MediaItem2 invalidItem1 = generateMediaItem(1);
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ playlist.add(1, invalidItem1);
+ mAgent.addPlaylistItem(1, invalidItem1);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(1, mAgent.getCurShuffledIndex());
+
+ // Replace the valid item with an invalid item:
+ // [0 (no_dsd), 1 (cur), 2 (no_dsd)] -> [0 (no_dsd), 3 (no_dsd), 2 (no_dsd)]
+ MediaItem2 invalidItem3 = generateMediaItemWithoutDataSourceDesc(3);
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ playlist.set(1, invalidItem3);
+ mAgent.replacePlaylistItem(1, invalidItem3);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ assertPlaylistEquals(playlist, mAgent.getPlaylist());
+ assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex());
+ }
+
+ private List<MediaItem2> createAndSetPlaylist(int listSize) throws Exception {
+ List<MediaItem2> items = new ArrayList<>();
+ for (int i = 0; i < listSize; ++i) {
+ items.add(generateMediaItem(i));
+ }
+ mEventCallback.clear();
+ synchronized (mWaitLock) {
+ mAgent.setPlaylist(items, null);
+ mWaitLock.wait(WAIT_TIME_MS);
+ assertTrue(mEventCallback.onPlaylistChangedCalled);
+ }
+ return items;
+ }
+
+ private void assertPlaylistEquals(List<MediaItem2> expected, List<MediaItem2> actual) {
+ if (expected == actual) {
+ return;
+ }
+ assertTrue(expected != null && actual != null);
+ assertEquals(expected.size(), actual.size());
+ for (int i = 0; i < expected.size(); ++i) {
+ assertTrue(expected.get(i).equals(actual.get(i)));
+ }
+ }
+
+ private MediaItem2 generateMediaItemWithoutDataSourceDesc(int key) {
+ return new MediaItem2.Builder(mContext, 0)
+ .setMediaId("TEST_MEDIA_ID_WITHOUT_DSD_" + key)
+ .build();
+ }
+
+ private MediaItem2 generateMediaItem(int key) {
+ return new MediaItem2.Builder(mContext, 0)
+ .setMediaId("TEST_MEDIA_ID_" + key)
+ .build();
+ }
+}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 8033382..ea06b6c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3139,16 +3139,21 @@
bool pinned = (sessionId > AUDIO_SESSION_OUTPUT_MIX) && isSessionAcquired_l(sessionId);
handle = thread->createEffect_l(client, effectClient, priority, sessionId,
&desc, enabled, &lStatus, pinned);
- if (handle != 0 && id != NULL) {
- *id = handle->id();
- }
- if (handle == 0) {
+ if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
// remove local strong reference to Client with mClientLock held
Mutex::Autolock _cl(mClientLock);
client.clear();
+ } else {
+ // handle must be valid here, but check again to be safe.
+ if (handle.get() != nullptr && id != nullptr) *id = handle->id();
}
}
+ if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+ // handle must be cleared outside lock.
+ handle.clear();
+ }
+
Exit:
*status = lStatus;
return handle;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 20447d8..62e9fe7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1360,7 +1360,7 @@
if (chainCreated) {
removeEffectChain_l(chain);
}
- handle.clear();
+ // handle must be cleared by caller to avoid deadlock.
}
*status = lStatus;
@@ -8524,7 +8524,11 @@
audio_devices_t outDevice, audio_devices_t inDevice, bool systemReady)
: MmapThread(audioFlinger, id, hwDev, output->stream, outDevice, inDevice, systemReady),
mStreamType(AUDIO_STREAM_MUSIC),
- mStreamVolume(1.0), mStreamMute(false), mOutput(output)
+ mStreamVolume(1.0),
+ mStreamMute(false),
+ mHalVolFloat(-1.0f), // Initialize to illegal value so it always gets set properly later.
+ mNoCallbackWarningCount(0),
+ mOutput(output)
{
snprintf(mThreadName, kThreadNameLength, "AudioMmapOut_%X", id);
mChannelCount = audio_channel_count_from_out_mask(mChannelMask);
@@ -8632,7 +8636,6 @@
}
if (volume != mHalVolFloat) {
- mHalVolFloat = volume;
// Convert volumes from float to 8.24
uint32_t vol = (uint32_t)(volume * (1 << 24));
@@ -8645,7 +8648,10 @@
volume = (float)vol / (1 << 24);
}
// Try to use HW volume control and fall back to SW control if not implemented
- if (mOutput->stream->setVolume(volume, volume) != NO_ERROR) {
+ if (mOutput->stream->setVolume(volume, volume) == NO_ERROR) {
+ mHalVolFloat = volume; // HW volume control worked, so update value.
+ mNoCallbackWarningCount = 0;
+ } else {
sp<MmapStreamCallback> callback = mCallback.promote();
if (callback != 0) {
int channelCount;
@@ -8659,8 +8665,13 @@
values.add(volume);
}
callback->onVolumeChanged(mChannelMask, values);
+ mHalVolFloat = volume; // SW volume control worked, so update value.
+ mNoCallbackWarningCount = 0;
} else {
- ALOGW("Could not set MMAP stream volume: no volume callback!");
+ if (mNoCallbackWarningCount < kMaxNoCallbackWarnings) {
+ ALOGW("Could not set MMAP stream volume: no volume callback!");
+ mNoCallbackWarningCount++;
+ }
}
}
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index ae14ac1..7cd46a7 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1666,6 +1666,8 @@
bool mMasterMute;
bool mStreamMute;
float mHalVolFloat;
+ int32_t mNoCallbackWarningCount;
+ static constexpr int32_t kMaxNoCallbackWarnings = 5;
AudioStreamOut* mOutput;
};
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 74ca902..73d92ac 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1266,6 +1266,11 @@
// We do not introduce additional delay here.
}
+ if (stream == AUDIO_STREAM_ENFORCED_AUDIBLE &&
+ mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
+ setStrategyMute(STRATEGY_SONIFICATION, true, outputDesc);
+ }
+
return NO_ERROR;
}
@@ -1366,6 +1371,12 @@
// update the outputs if stopping one with a stream that can affect notification routing
handleNotificationRoutingForStream(stream);
}
+
+ if (stream == AUDIO_STREAM_ENFORCED_AUDIBLE &&
+ mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
+ setStrategyMute(STRATEGY_SONIFICATION, false, outputDesc);
+ }
+
if (stream == AUDIO_STREAM_MUSIC) {
selectOutputForMusicEffects();
}