Merge "Move CCodec and c2.google.* components out of framework" 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/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 40b31b9..4a2a0a8 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -597,16 +597,21 @@
}
int32_t AAudioConvert_framesToBytes(int32_t numFrames,
- int32_t bytesPerFrame,
- int32_t *sizeInBytes) {
- // TODO implement more elegantly
- const int32_t maxChannels = 256; // ridiculously large
- const int32_t maxBytesPerFrame = maxChannels * sizeof(float);
- // Prevent overflow by limiting multiplicands.
- if (bytesPerFrame > maxBytesPerFrame || numFrames > (0x3FFFFFFF / maxBytesPerFrame)) {
+ int32_t bytesPerFrame,
+ int32_t *sizeInBytes) {
+ *sizeInBytes = 0;
+
+ if (numFrames < 0 || bytesPerFrame < 0) {
+ ALOGE("negative size, numFrames = %d, frameSize = %d", numFrames, bytesPerFrame);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+
+ // Prevent numeric overflow.
+ if (numFrames > (INT32_MAX / bytesPerFrame)) {
ALOGE("size overflow, numFrames = %d, frameSize = %d", numFrames, bytesPerFrame);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
+
*sizeInBytes = numFrames * bytesPerFrame;
return AAUDIO_OK;
}
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index cea88fb..4b975e8 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -196,14 +196,16 @@
/**
* Calculate the number of bytes and prevent numeric overflow.
+ * The *sizeInBytes will be set to zero if there is an error.
+ *
* @param numFrames frame count
* @param bytesPerFrame size of a frame in bytes
- * @param sizeInBytes total size in bytes
+ * @param sizeInBytes pointer to a variable to receive total size in bytes
* @return AAUDIO_OK or negative error, eg. AAUDIO_ERROR_OUT_OF_RANGE
*/
int32_t AAudioConvert_framesToBytes(int32_t numFrames,
- int32_t bytesPerFrame,
- int32_t *sizeInBytes);
+ int32_t bytesPerFrame,
+ int32_t *sizeInBytes);
audio_format_t AAudioConvert_aaudioToAndroidDataFormat(aaudio_format_t aaudio_format);
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/bqhelper/Android.bp b/media/libstagefright/bqhelper/Android.bp
index 388ed6b..9febe12 100644
--- a/media/libstagefright/bqhelper/Android.bp
+++ b/media/libstagefright/bqhelper/Android.bp
@@ -42,8 +42,9 @@
],
export_shared_lib_headers: [
- "libstagefright_foundation",
+ "libgui",
"libhidlmemory",
+ "libstagefright_foundation",
],
cflags: [
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/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index 95e7c39..8966933 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -5,10 +5,12 @@
"internal",
],
- vendor_available: false,
vndk: {
enabled: true,
},
+
+ // TODO: Remove this when this module is moved back to frameworks/av.
+ vendor_available: true,
}
cc_library_shared {
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/proguard.cfg b/packages/MediaComponents/proguard.cfg
index 43f2e63..d7bf730 100644
--- a/packages/MediaComponents/proguard.cfg
+++ b/packages/MediaComponents/proguard.cfg
@@ -16,5 +16,5 @@
# Keep entry point for updatable Java classes
-keep public class com.android.media.update.ApiFactory {
- public static java.lang.Object initialize(android.content.res.Resources, android.content.res.Resources$Theme);
+ public static com.android.media.update.ApiFactory initialize(android.content.pm.ApplicationInfo);
}
diff --git a/packages/MediaComponents/res/layout/settings_list_item.xml b/packages/MediaComponents/res/layout/embedded_settings_list_item.xml
similarity index 63%
copy from packages/MediaComponents/res/layout/settings_list_item.xml
copy to packages/MediaComponents/res/layout/embedded_settings_list_item.xml
index 81b3275..1156dca 100644
--- a/packages/MediaComponents/res/layout/settings_list_item.xml
+++ b/packages/MediaComponents/res/layout/embedded_settings_list_item.xml
@@ -15,49 +15,49 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/MediaControlView2_settings_width"
- android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/mcv2_embedded_settings_height"
android:orientation="horizontal"
- android:background="@color/black_transparent_70">
+ android:background="@color/black_opacity_70">
<LinearLayout
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_settings_height"
- android:paddingRight="2dp"
+ android:layout_height="@dimen/mcv2_embedded_settings_height"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
- android:layout_width="@dimen/MediaControlView2_settings_icon_size"
- android:layout_height="@dimen/MediaControlView2_settings_icon_size"
- android:gravity="center"
- android:paddingLeft="2dp"/>
+ android:layout_width="@dimen/mcv2_embedded_settings_icon_size"
+ android:layout_height="@dimen/mcv2_embedded_settings_icon_size"
+ android:layout_margin="8dp"
+ android:gravity="center" />
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:layout_height="@dimen/mcv2_embedded_settings_height"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/main_text"
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_text_width"
+ android:layout_height="@dimen/mcv2_embedded_settings_text_height"
+ android:gravity="center"
android:paddingLeft="2dp"
android:textColor="@color/white"
- android:textSize="@dimen/MediaControlView2_settings_main_text_size"/>
+ android:textSize="@dimen/mcv2_embedded_settings_main_text_size"/>
<TextView
android:id="@+id/sub_text"
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_text_width"
+ android:layout_height="@dimen/mcv2_embedded_settings_text_height"
android:layout_below="@id/main_text"
+ android:gravity="center"
android:paddingLeft="2dp"
- android:textColor="@color/white_transparent_70"
- android:textSize="@dimen/MediaControlView2_settings_sub_text_size"/>
+ android:textColor="@color/white_opacity_70"
+ android:textSize="@dimen/mcv2_embedded_settings_sub_text_size"/>
</RelativeLayout>
-
</LinearLayout>
diff --git a/packages/MediaComponents/res/layout/sub_settings_list_item.xml b/packages/MediaComponents/res/layout/embedded_sub_settings_list_item.xml
similarity index 66%
copy from packages/MediaComponents/res/layout/sub_settings_list_item.xml
copy to packages/MediaComponents/res/layout/embedded_sub_settings_list_item.xml
index 9de7f2b..5947a72 100644
--- a/packages/MediaComponents/res/layout/sub_settings_list_item.xml
+++ b/packages/MediaComponents/res/layout/embedded_sub_settings_list_item.xml
@@ -15,40 +15,39 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/MediaControlView2_settings_width"
- android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/mcv2_embedded_settings_height"
android:orientation="horizontal"
- android:background="@color/black_transparent_70">
+ android:background="@color/black_opacity_70">
<LinearLayout
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_settings_height"
- android:paddingRight="2dp"
+ android:layout_height="@dimen/mcv2_embedded_settings_height"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/check"
- android:layout_width="@dimen/MediaControlView2_settings_icon_size"
- android:layout_height="@dimen/MediaControlView2_settings_icon_size"
+ android:layout_width="@dimen/mcv2_embedded_settings_icon_size"
+ android:layout_height="@dimen/mcv2_embedded_settings_icon_size"
+ android:layout_margin="8dp"
android:gravity="center"
- android:paddingLeft="2dp"
android:src="@drawable/ic_check"/>
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:layout_height="@dimen/mcv2_embedded_settings_height"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_text_width"
+ android:layout_height="@dimen/mcv2_embedded_settings_text_height"
+ android:gravity="center"
android:paddingLeft="2dp"
android:textColor="@color/white"
- android:textSize="@dimen/MediaControlView2_settings_main_text_size"/>
+ android:textSize="@dimen/mcv2_embedded_settings_main_text_size"/>
</RelativeLayout>
-
</LinearLayout>
diff --git a/packages/MediaComponents/res/layout/embedded_transport_controls.xml b/packages/MediaComponents/res/layout/embedded_transport_controls.xml
new file mode 100644
index 0000000..a3a5957
--- /dev/null
+++ b/packages/MediaComponents/res/layout/embedded_transport_controls.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:paddingLeft="@dimen/mcv2_transport_controls_padding"
+ android:paddingRight="@dimen/mcv2_transport_controls_padding"
+ android:visibility="visible">
+
+ <ImageButton android:id="@+id/prev" style="@style/EmbeddedTransportControlsButton.Previous" />
+ <ImageButton android:id="@+id/rew" style="@style/EmbeddedTransportControlsButton.Rew" />
+ <ImageButton android:id="@+id/pause" style="@style/EmbeddedTransportControlsButton.Pause" />
+ <ImageButton android:id="@+id/ffwd" style="@style/EmbeddedTransportControlsButton.Ffwd" />
+ <ImageButton android:id="@+id/next" style="@style/EmbeddedTransportControlsButton.Next" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/settings_list_item.xml b/packages/MediaComponents/res/layout/full_settings_list_item.xml
similarity index 63%
rename from packages/MediaComponents/res/layout/settings_list_item.xml
rename to packages/MediaComponents/res/layout/full_settings_list_item.xml
index 81b3275..f92ea5e 100644
--- a/packages/MediaComponents/res/layout/settings_list_item.xml
+++ b/packages/MediaComponents/res/layout/full_settings_list_item.xml
@@ -15,49 +15,48 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/MediaControlView2_settings_width"
- android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/mcv2_full_settings_height"
android:orientation="horizontal"
- android:background="@color/black_transparent_70">
+ android:background="@color/black_opacity_70">
<LinearLayout
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_settings_height"
- android:paddingRight="2dp"
+ android:layout_height="@dimen/mcv2_full_settings_height"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
- android:layout_width="@dimen/MediaControlView2_settings_icon_size"
- android:layout_height="@dimen/MediaControlView2_settings_icon_size"
- android:gravity="center"
- android:paddingLeft="2dp"/>
+ android:layout_width="@dimen/mcv2_full_settings_icon_size"
+ android:layout_height="@dimen/mcv2_full_settings_icon_size"
+ android:layout_margin="8dp"
+ android:gravity="center"/>
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:layout_height="@dimen/mcv2_full_settings_height"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/main_text"
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_text_width"
+ android:layout_height="@dimen/mcv2_full_settings_text_height"
android:paddingLeft="2dp"
+ android:gravity="center"
android:textColor="@color/white"
- android:textSize="@dimen/MediaControlView2_settings_main_text_size"/>
+ android:textSize="@dimen/mcv2_full_settings_main_text_size"/>
<TextView
android:id="@+id/sub_text"
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_text_width"
+ android:layout_height="@dimen/mcv2_full_settings_text_height"
android:layout_below="@id/main_text"
+ android:gravity="center"
android:paddingLeft="2dp"
- android:textColor="@color/white_transparent_70"
- android:textSize="@dimen/MediaControlView2_settings_sub_text_size"/>
+ android:textColor="@color/white_opacity_70"
+ android:textSize="@dimen/mcv2_full_settings_sub_text_size"/>
</RelativeLayout>
-
</LinearLayout>
-
diff --git a/packages/MediaComponents/res/layout/sub_settings_list_item.xml b/packages/MediaComponents/res/layout/full_sub_settings_list_item.xml
similarity index 66%
rename from packages/MediaComponents/res/layout/sub_settings_list_item.xml
rename to packages/MediaComponents/res/layout/full_sub_settings_list_item.xml
index 9de7f2b..49128d0 100644
--- a/packages/MediaComponents/res/layout/sub_settings_list_item.xml
+++ b/packages/MediaComponents/res/layout/full_sub_settings_list_item.xml
@@ -15,40 +15,39 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/MediaControlView2_settings_width"
- android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/mcv2_full_settings_height"
android:orientation="horizontal"
- android:background="@color/black_transparent_70">
+ android:background="@color/black_opacity_70">
<LinearLayout
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_settings_height"
- android:paddingRight="2dp"
+ android:layout_height="@dimen/mcv2_full_settings_height"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/check"
- android:layout_width="@dimen/MediaControlView2_settings_icon_size"
- android:layout_height="@dimen/MediaControlView2_settings_icon_size"
+ android:layout_width="@dimen/mcv2_full_settings_icon_size"
+ android:layout_height="@dimen/mcv2_full_settings_icon_size"
+ android:layout_margin="8dp"
android:gravity="center"
- android:paddingLeft="2dp"
android:src="@drawable/ic_check"/>
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:layout_height="@dimen/mcv2_full_settings_height"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
- android:layout_height="@dimen/MediaControlView2_text_width"
+ android:layout_height="@dimen/mcv2_full_settings_text_height"
+ android:gravity="center"
android:paddingLeft="2dp"
android:textColor="@color/white"
- android:textSize="@dimen/MediaControlView2_settings_main_text_size"/>
+ android:textSize="@dimen/mcv2_full_settings_main_text_size"/>
</RelativeLayout>
-
-</LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/full_transport_controls.xml b/packages/MediaComponents/res/layout/full_transport_controls.xml
new file mode 100644
index 0000000..dd41a1f
--- /dev/null
+++ b/packages/MediaComponents/res/layout/full_transport_controls.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:paddingLeft="@dimen/mcv2_transport_controls_padding"
+ android:paddingRight="@dimen/mcv2_transport_controls_padding"
+ android:visibility="visible">
+
+ <ImageButton android:id="@+id/prev" style="@style/FullTransportControlsButton.Previous" />
+ <ImageButton android:id="@+id/rew" style="@style/FullTransportControlsButton.Rew" />
+ <ImageButton android:id="@+id/pause" style="@style/FullTransportControlsButton.Pause" />
+ <ImageButton android:id="@+id/ffwd" style="@style/FullTransportControlsButton.Ffwd" />
+ <ImageButton android:id="@+id/next" style="@style/FullTransportControlsButton.Next" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index eab2309..c5fbee8 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -111,19 +111,11 @@
</RelativeLayout>
<LinearLayout
+ android:id="@+id/center_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:gravity="center"
- android:paddingTop="4dp"
- android:orientation="horizontal">
-
- <ImageButton android:id="@+id/prev" style="@style/TransportControlsButton.Previous" />
- <ImageButton android:id="@+id/rew" style="@style/TransportControlsButton.Rew" />
- <ImageButton android:id="@+id/pause" style="@style/TransportControlsButton.Pause" />
- <ImageButton android:id="@+id/ffwd" style="@style/TransportControlsButton.Ffwd" />
- <ImageButton android:id="@+id/next" style="@style/TransportControlsButton.Next" />
-
+ android:gravity="center">
</LinearLayout>
<SeekBar
@@ -137,103 +129,108 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="44dp"
- android:paddingLeft="15dp"
android:orientation="horizontal">
- <TextView
- android:id="@+id/time_current"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_alignParentStart="true"
- android:paddingEnd="4dp"
- android:paddingStart="4dp"
- android:textSize="14sp"
- android:textStyle="bold"
- android:textColor="#FFFFFF" />
-
- <TextView
- android:id="@+id/time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_toRightOf="@id/time_current"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:textSize="14sp"
- android:textStyle="bold"
- android:textColor="#BBBBBB" />
-
- <TextView
- android:id="@+id/ad_skip_time"
- android:gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:textSize="12sp"
- android:textColor="#FFFFFF"
- android:visibility="gone" />
-
<LinearLayout
- android:id="@+id/basic_controls"
- android:gravity="center"
- android:layout_alignParentRight="true"
+ android:id="@+id/bottom_bar_left"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:orientation="horizontal" >
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true">
<TextView
- android:id="@+id/ad_remaining"
+ android:id="@+id/ad_skip_time"
android:gravity="center"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="4dp"
android:textSize="12sp"
android:textColor="#FFFFFF"
android:visibility="gone" />
-
- <ImageButton
- android:id="@+id/mute"
- style="@style/BottomBarButton.Mute" />
- <ImageButton
- android:id="@+id/subtitle"
- android:scaleType="fitCenter"
- android:visibility="gone"
- style="@style/BottomBarButton.CC" />
- <ImageButton
- android:id="@+id/fullscreen"
- style="@style/BottomBarButton.FullScreen"/>
- <ImageButton
- android:id="@+id/overflow_right"
- style="@style/BottomBarButton.OverflowRight"/>
</LinearLayout>
<LinearLayout
- android:id="@+id/extra_controls"
- android:layout_alignParentRight="true"
+ android:id="@+id/time"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:visibility="gone"
- android:orientation="horizontal"
- android:gravity="center">
+ android:layout_height="match_parent"
+ android:layout_toRightOf="@id/bottom_bar_left"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:gravity="center" >
- <LinearLayout
- android:id="@+id/custom_buttons"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <ImageButton
- android:id="@+id/video_quality"
- style="@style/BottomBarButton.VideoQuality" />
- <ImageButton
- android:id="@+id/settings"
- style="@style/BottomBarButton.Settings" />
- <ImageButton
- android:id="@+id/overflow_left"
- style="@style/BottomBarButton.OverflowLeft"/>
+ <TextView
+ android:id="@+id/time_current"
+ style="@style/TimeText.Current"/>
+ <TextView
+ android:id="@+id/time_interpunct"
+ style="@style/TimeText.Interpunct"/>
+ <TextView
+ android:id="@+id/time_end"
+ style="@style/TimeText.End"/>
</LinearLayout>
+ <LinearLayout
+ android:id="@+id/bottom_bar_right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:gravity="right">
+
+ <LinearLayout
+ android:id="@+id/basic_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/ad_remaining"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textSize="12sp"
+ android:textColor="#FFFFFF"
+ android:visibility="gone" />
+
+ <ImageButton
+ android:id="@+id/mute"
+ style="@style/BottomBarButton.Mute" />
+ <ImageButton
+ android:id="@+id/subtitle"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ style="@style/BottomBarButton.CC" />
+ <ImageButton
+ android:id="@+id/fullscreen"
+ style="@style/BottomBarButton.FullScreen"/>
+ <ImageButton
+ android:id="@+id/overflow_right"
+ style="@style/BottomBarButton.OverflowRight"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/extra_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+
+ <LinearLayout
+ android:id="@+id/custom_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <ImageButton
+ android:id="@+id/video_quality"
+ style="@style/BottomBarButton.VideoQuality" />
+ <ImageButton
+ android:id="@+id/settings"
+ style="@style/BottomBarButton.Settings" />
+ <ImageButton
+ android:id="@+id/overflow_left"
+ style="@style/BottomBarButton.OverflowLeft"/>
+ </LinearLayout>
+ </LinearLayout>
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/minimal_transport_controls.xml b/packages/MediaComponents/res/layout/minimal_transport_controls.xml
new file mode 100644
index 0000000..9ca3721
--- /dev/null
+++ b/packages/MediaComponents/res/layout/minimal_transport_controls.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:visibility="visible">
+
+ <ImageButton android:id="@+id/pause" style="@style/MinimalTransportControlsButton.Pause" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/settings_list.xml b/packages/MediaComponents/res/layout/settings_list.xml
index 37a3a60..ea30538 100644
--- a/packages/MediaComponents/res/layout/settings_list.xml
+++ b/packages/MediaComponents/res/layout/settings_list.xml
@@ -15,8 +15,8 @@
-->
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/MediaControlView2_settings_width"
- android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:layout_width="@dimen/mcv2_embedded_settings_width"
+ android:layout_height="@dimen/mcv2_embedded_settings_height"
android:divider="@null"
android:dividerHeight="0dp">
</ListView>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/values/colors.xml b/packages/MediaComponents/res/values/colors.xml
index 8ba069c..6a65ef5 100644
--- a/packages/MediaComponents/res/values/colors.xml
+++ b/packages/MediaComponents/res/values/colors.xml
@@ -17,6 +17,6 @@
<resources>
<color name="gray">#808080</color>
<color name="white">#ffffff</color>
- <color name="white_transparent_70">#B3ffffff</color>
- <color name="black_transparent_70">#B3000000</color>
+ <color name="white_opacity_70">#B3ffffff</color>
+ <color name="black_opacity_70">#B3000000</color>
</resources>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/values/dimens.xml b/packages/MediaComponents/res/values/dimens.xml
index b5ef626..8d72d40 100644
--- a/packages/MediaComponents/res/values/dimens.xml
+++ b/packages/MediaComponents/res/values/dimens.xml
@@ -41,12 +41,26 @@
<!-- Group list fade out animation duration. -->
<integer name="mr_controller_volume_group_list_fade_out_duration_ms">200</integer>
- <dimen name="MediaControlView2_settings_width">200dp</dimen>
- <dimen name="MediaControlView2_settings_height">36dp</dimen>
- <dimen name="MediaControlView2_settings_icon_size">28dp</dimen>
- <dimen name="MediaControlView2_settings_offset">8dp</dimen>
- <dimen name="MediaControlView2_text_width">18dp</dimen>
+ <dimen name="mcv2_embedded_settings_width">150dp</dimen>
+ <dimen name="mcv2_embedded_settings_height">36dp</dimen>
+ <dimen name="mcv2_embedded_settings_icon_size">20dp</dimen>
+ <dimen name="mcv2_embedded_settings_text_height">18dp</dimen>
+ <dimen name="mcv2_embedded_settings_main_text_size">12sp</dimen>
+ <dimen name="mcv2_embedded_settings_sub_text_size">10sp</dimen>
+ <dimen name="mcv2_full_settings_width">225dp</dimen>
+ <dimen name="mcv2_full_settings_height">54dp</dimen>
+ <dimen name="mcv2_full_settings_icon_size">30dp</dimen>
+ <dimen name="mcv2_full_settings_text_height">27dp</dimen>
+ <dimen name="mcv2_full_settings_main_text_size">16sp</dimen>
+ <dimen name="mcv2_full_settings_sub_text_size">13sp</dimen>
+ <dimen name="mcv2_settings_offset">8dp</dimen>
- <dimen name="MediaControlView2_settings_main_text_size">12sp</dimen>
- <dimen name="MediaControlView2_settings_sub_text_size">10sp</dimen>
+ <dimen name="mcv2_transport_controls_padding">4dp</dimen>
+ <dimen name="mcv2_pause_icon_size">36dp</dimen>
+ <dimen name="mcv2_full_icon_size">28dp</dimen>
+ <dimen name="mcv2_embedded_icon_size">24dp</dimen>
+ <dimen name="mcv2_minimal_icon_size">24dp</dimen>
+ <dimen name="mcv2_icon_margin">10dp</dimen>
+
+ <!-- TODO: adjust bottom bar view -->
</resources>
diff --git a/packages/MediaComponents/res/values/strings.xml b/packages/MediaComponents/res/values/strings.xml
index c80aaf3..69e9dff 100644
--- a/packages/MediaComponents/res/values/strings.xml
+++ b/packages/MediaComponents/res/values/strings.xml
@@ -122,6 +122,8 @@
<item>1.5x</item>
<item>2x</item>
</string-array>
+ <!-- Placeholder text for displaying time. Used to calculate which size layout to use. -->
+ <string name="MediaControlView2_time_placeholder">00:00:00</string>
<!-- Text for displaying subtitle track number. -->
<string name="MediaControlView2_subtitle_track_number_text">
diff --git a/packages/MediaComponents/res/values/style.xml b/packages/MediaComponents/res/values/style.xml
index e6ed039..b1da137 100644
--- a/packages/MediaComponents/res/values/style.xml
+++ b/packages/MediaComponents/res/values/style.xml
@@ -1,30 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <style name="TransportControlsButton">
+ <style name="FullTransportControlsButton">
<item name="android:background">@null</item>
- <item name="android:layout_width">70dp</item>
- <item name="android:layout_height">40dp</item>
- <item name="android:visibility">gone</item>
+ <item name="android:layout_margin">@dimen/mcv2_icon_margin</item>
+ <item name="android:scaleType">fitXY</item>
</style>
- <style name="TransportControlsButton.Previous">
+ <style name="FullTransportControlsButton.Previous">
<item name="android:src">@drawable/ic_skip_previous</item>
+ <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
</style>
- <style name="TransportControlsButton.Next">
+ <style name="FullTransportControlsButton.Next">
<item name="android:src">@drawable/ic_skip_next</item>
+ <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
</style>
- <style name="TransportControlsButton.Pause">
+ <style name="FullTransportControlsButton.Pause">
<item name="android:src">@drawable/ic_pause_circle_filled</item>
+ <item name="android:layout_width">@dimen/mcv2_pause_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_pause_icon_size</item>
</style>
- <style name="TransportControlsButton.Ffwd">
+ <style name="FullTransportControlsButton.Ffwd">
<item name="android:src">@drawable/ic_forward_30</item>
+ <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
</style>
- <style name="TransportControlsButton.Rew">
+ <style name="FullTransportControlsButton.Rew">
<item name="android:src">@drawable/ic_rewind_10</item>
+ <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
+ </style>
+
+ <style name="EmbeddedTransportControlsButton">
+ <item name="android:background">@null</item>
+ <item name="android:layout_margin">@dimen/mcv2_icon_margin</item>
+ <item name="android:scaleType">fitXY</item>
+ </style>
+
+ <style name="EmbeddedTransportControlsButton.Previous">
+ <item name="android:src">@drawable/ic_skip_previous</item>
+ <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
+ </style>
+
+ <style name="EmbeddedTransportControlsButton.Next">
+ <item name="android:src">@drawable/ic_skip_next</item>
+ <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
+ </style>
+
+ <style name="EmbeddedTransportControlsButton.Pause">
+ <item name="android:src">@drawable/ic_pause_circle_filled</item>
+ <item name="android:layout_width">@dimen/mcv2_pause_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_pause_icon_size</item>
+ </style>
+
+ <style name="EmbeddedTransportControlsButton.Ffwd">
+ <item name="android:src">@drawable/ic_forward_30</item>
+ <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
+ </style>
+
+ <style name="EmbeddedTransportControlsButton.Rew">
+ <item name="android:src">@drawable/ic_rewind_10</item>
+ <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
+ </style>
+
+ <style name="MinimalTransportControlsButton">
+ <item name="android:background">@null</item>
+ <item name="android:scaleType">fitXY</item>
+ </style>
+
+ <style name="MinimalTransportControlsButton.Pause">
+ <item name="android:src">@drawable/ic_pause_circle_filled</item>
+ <item name="android:layout_width">@dimen/mcv2_minimal_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_minimal_icon_size</item>
</style>
<style name="TitleBar">
@@ -45,14 +101,38 @@
<item name="android:src">@drawable/ic_launch</item>
</style>
+ <style name="TimeText">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:paddingStart">4dp</item>
+ <item name="android:paddingEnd">4dp</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="TimeText.Current">
+ <item name="android:textColor">@color/white</item>
+ <item name="android:text">@string/MediaControlView2_time_placeholder</item>
+ </style>
+
+ <style name="TimeText.Interpunct">
+ <item name="android:textColor">@color/white_opacity_70</item>
+ <item name="android:text">·</item>
+ </style>
+
+ <style name="TimeText.End">
+ <item name="android:textColor">@color/white_opacity_70</item>
+ <item name="android:text">@string/MediaControlView2_time_placeholder</item>
+ </style>
+
<style name="BottomBarButton">
<item name="android:background">@null</item>
- <item name="android:layout_width">44dp</item>
- <item name="android:layout_height">34dp</item>
- <item name="android:layout_marginTop">5dp</item>
- <item name="android:layout_marginBottom">5dp</item>
- <item name="android:paddingLeft">5dp</item>
- <item name="android:paddingRight">5dp</item>
+ <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
+ <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
+ <item name="android:layout_margin">@dimen/mcv2_icon_margin</item>
+ <item name="android:gravity">center_horizontal</item>
+ <item name="android:scaleType">fitXY</item>
</style>
<style name="BottomBarButton.CC">
diff --git a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl b/packages/MediaComponents/src/com/android/media/IMediaController2.aidl
index d6c8e21..0488b70 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaController2.aidl
@@ -37,12 +37,14 @@
void onBufferedPositionChanged(long bufferedPositionMs);
void onPlaylistChanged(in List<Bundle> playlist, in Bundle metadata);
void onPlaylistMetadataChanged(in Bundle metadata);
- void onPlaylistParamsChanged(in Bundle params);
void onPlaybackInfoChanged(in Bundle playbackInfo);
+ void onRepeatModeChanged(int repeatMode);
+ void onShuffleModeChanged(int shuffleMode);
+ void onError(int errorCode, in Bundle extras);
void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup,
int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed,
- long bufferedPositionMs, in Bundle playbackInfo, in Bundle params,
+ long bufferedPositionMs, in Bundle playbackInfo, int repeatMode, int shuffleMode,
in List<Bundle> playlist, in PendingIntent sessionActivity);
void onDisconnected();
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index 7317b44..664467d 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -67,6 +67,8 @@
void skipToPlaylistItem(IMediaController2 caller, in Bundle mediaItem);
void skipToPreviousItem(IMediaController2 caller);
void skipToNextItem(IMediaController2 caller);
+ void setRepeatMode(IMediaController2 caller, int repeatMode);
+ void setShuffleMode(IMediaController2 caller, int shuffleMode);
//////////////////////////////////////////////////////////////////////////////////////////////
// library service specific
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 15922f7..0091816 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -22,6 +22,8 @@
import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM;
import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_LIST;
import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE;
import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID;
import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH;
import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_URI;
@@ -40,11 +42,12 @@
import android.media.MediaController2.PlaybackInfo;
import android.media.MediaItem2;
import android.media.MediaMetadata2;
+import android.media.MediaPlaylistAgent.RepeatMode;
+import android.media.MediaPlaylistAgent.ShuffleMode;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
-import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSessionService2;
import android.media.Rating2;
import android.media.SessionToken2;
@@ -87,7 +90,9 @@
@GuardedBy("mLock")
private MediaMetadata2 mPlaylistMetadata;
@GuardedBy("mLock")
- private PlaylistParams mPlaylistParams;
+ private @RepeatMode int mRepeatMode;
+ @GuardedBy("mLock")
+ private @ShuffleMode int mShuffleMode;
@GuardedBy("mLock")
private int mPlayerState;
@GuardedBy("mLock")
@@ -706,9 +711,41 @@
}
@Override
- public PlaylistParams getPlaylistParams_impl() {
- synchronized (mLock) {
- return mPlaylistParams;
+ public int getShuffleMode_impl() {
+ return mShuffleMode;
+ }
+
+ @Override
+ public void setShuffleMode_impl(int shuffleMode) {
+ final IMediaSession2 binder = getSessionBinderIfAble(
+ COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE);
+ if (binder != null) {
+ try {
+ binder.setShuffleMode(mControllerStub, shuffleMode);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
+ }
+
+ @Override
+ public int getRepeatMode_impl() {
+ return mRepeatMode;
+ }
+
+ @Override
+ public void setRepeatMode_impl(int repeatMode) {
+ final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE);
+ if (binder != null) {
+ try {
+ binder.setRepeatMode(mControllerStub, repeatMode);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
}
}
@@ -719,19 +756,6 @@
}
}
- // TODO(jaewan): Remove (b/74116823)
- @Override
- public void setPlaylistParams_impl(PlaylistParams params) {
- if (params == null) {
- throw new IllegalArgumentException("params shouldn't be null");
- }
- /*
- Bundle args = new Bundle();
- args.putBundle(MediaSession2Stub.ARGUMENT_KEY_PLAYLIST_PARAMS, params.toBundle());
- sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS, args);
- */
- }
-
@Override
public int getPlayerState_impl() {
synchronized (mLock) {
@@ -817,18 +841,6 @@
});
}
- void pushPlaylistParamsChanges(final PlaylistParams params) {
- synchronized (mLock) {
- mPlaylistParams = params;
- }
- mCallbackExecutor.execute(() -> {
- if (!mInstance.isConnected()) {
- return;
- }
- mCallback.onPlaylistParamsChanged(mInstance, params);
- });
- }
-
void pushPlaybackInfoChanges(final PlaybackInfo info) {
synchronized (mLock) {
mPlaybackInfo = info;
@@ -855,7 +867,7 @@
});
}
- public void pushPlaylistMetadataChanges(MediaMetadata2 metadata) {
+ void pushPlaylistMetadataChanges(MediaMetadata2 metadata) {
synchronized (mLock) {
mPlaylistMetadata = metadata;
}
@@ -868,6 +880,41 @@
});
}
+ void pushShuffleModeChanges(int shuffleMode) {
+ synchronized (mLock) {
+ mShuffleMode = shuffleMode;
+ }
+ mCallbackExecutor.execute(() -> {
+ if (!mInstance.isConnected()) {
+ return;
+ }
+ // TODO(jaewan): Fix public API not to take playlistAgent.
+ mCallback.onShuffleModeChanged(mInstance, null, shuffleMode);
+ });
+ }
+
+ void pushRepeatModeChanges(int repeatMode) {
+ synchronized (mLock) {
+ mRepeatMode = repeatMode;
+ }
+ mCallbackExecutor.execute(() -> {
+ if (!mInstance.isConnected()) {
+ return;
+ }
+ // TODO(jaewan): Fix public API not to take playlistAgent.
+ mCallback.onRepeatModeChanged(mInstance, null, repeatMode);
+ });
+ }
+
+ void pushError(int errorCode, Bundle extras) {
+ mCallbackExecutor.execute(() -> {
+ if (!mInstance.isConnected()) {
+ return;
+ }
+ mCallback.onError(mInstance, errorCode, extras);
+ });
+ }
+
// Should be used without a lock to prevent potential deadlock.
void onConnectedNotLocked(IMediaSession2 sessionBinder,
final CommandGroup allowedCommands,
@@ -877,7 +924,9 @@
final float playbackSpeed,
final long bufferedPositionMs,
final PlaybackInfo info,
- final PlaylistParams params, final List<MediaItem2> playlist,
+ final int repeatMode,
+ final int shuffleMode,
+ final List<MediaItem2> playlist,
final PendingIntent sessionActivity) {
if (DEBUG) {
Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder
@@ -907,7 +956,8 @@
mPlaybackSpeed = playbackSpeed;
mBufferedPositionMs = bufferedPositionMs;
mPlaybackInfo = info;
- mPlaylistParams = params;
+ mRepeatMode = repeatMode;
+ mShuffleMode = shuffleMode;
mPlaylist = playlist;
mSessionActivity = sessionActivity;
mSessionBinder = sessionBinder;
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
index 99bdfce..721c963 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
@@ -24,7 +24,6 @@
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
-import android.media.MediaSession2.PlaylistParams;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.text.TextUtils;
@@ -169,7 +168,7 @@
}
@Override
- public void onPlaylistParamsChanged(Bundle paramsBundle) throws RuntimeException {
+ public void onRepeatModeChanged(int repeatMode) {
final MediaController2Impl controller;
try {
controller = getController();
@@ -177,12 +176,7 @@
Log.w(TAG, "Don't fail silently here. Highly likely a bug");
return;
}
- PlaylistParams params = PlaylistParams.fromBundle(controller.getContext(), paramsBundle);
- if (params == null) {
- Log.w(TAG, "onPlaylistParamsChanged(): Ignoring null playlistParams");
- return;
- }
- controller.pushPlaylistParamsChanges(params);
+ controller.pushRepeatModeChanges(repeatMode);
}
@Override
@@ -208,9 +202,33 @@
}
@Override
+ public void onShuffleModeChanged(int shuffleMode) {
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ controller.pushShuffleModeChanges(shuffleMode);
+ }
+
+ @Override
+ public void onError(int errorCode, Bundle extras) {
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ controller.pushError(errorCode, extras);
+ }
+
+ @Override
public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed,
- long bufferedPositionMs, Bundle playbackInfo, Bundle playlistParams,
+ long bufferedPositionMs, Bundle playbackInfo, int shuffleMode, int repeatMode,
List<Bundle> itemBundleList, PendingIntent sessionActivity) {
final MediaController2Impl controller = mController.get();
if (controller == null) {
@@ -233,8 +251,7 @@
controller.onConnectedNotLocked(sessionBinder,
CommandGroup.fromBundle(context, commandGroup),
playerState, positionEventTimeMs, positionMs, playbackSpeed, bufferedPositionMs,
- PlaybackInfoImpl.fromBundle(context, playbackInfo),
- PlaylistParams.fromBundle(context, playlistParams),
+ PlaybackInfoImpl.fromBundle(context, playbackInfo), repeatMode, shuffleMode,
itemList, sessionActivity);
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java b/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java
index bab29b7..ab6b167 100644
--- a/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java
@@ -20,6 +20,7 @@
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.MediaPlaylistAgent;
@@ -48,6 +49,7 @@
mInstance = instance;
}
+ @Override
final public void registerPlaylistEventCallback_impl(
@NonNull @CallbackExecutor Executor executor, @NonNull PlaylistEventCallback callback) {
if (executor == null) {
@@ -66,6 +68,7 @@
}
}
+ @Override
final public void unregisterPlaylistEventCallback_impl(
@NonNull PlaylistEventCallback callback) {
if (callback == null) {
@@ -76,6 +79,7 @@
}
}
+ @Override
final public void notifyPlaylistChanged_impl() {
ArrayMap<PlaylistEventCallback, Executor> callbacks = getCallbacks();
List<MediaItem2> playlist= mInstance.getPlaylist();
@@ -88,6 +92,7 @@
}
}
+ @Override
final public void notifyPlaylistMetadataChanged_impl() {
ArrayMap<PlaylistEventCallback, Executor> callbacks = getCallbacks();
for (int i = 0; i < callbacks.size(); i++) {
@@ -98,6 +103,7 @@
}
}
+ @Override
final public void notifyShuffleModeChanged_impl() {
ArrayMap<PlaylistEventCallback, Executor> callbacks = getCallbacks();
for (int i = 0; i < callbacks.size(); i++) {
@@ -108,6 +114,7 @@
}
}
+ @Override
final public void notifyRepeatModeChanged_impl() {
ArrayMap<PlaylistEventCallback, Executor> callbacks = getCallbacks();
for (int i = 0; i < callbacks.size(); i++) {
@@ -118,66 +125,98 @@
}
}
+ @Override
public @Nullable List<MediaItem2> getPlaylist_impl() {
// empty implementation
return null;
}
+ @Override
public void setPlaylist_impl(@NonNull List<MediaItem2> list,
@Nullable MediaMetadata2 metadata) {
// empty implementation
}
+ @Override
public @Nullable MediaMetadata2 getPlaylistMetadata_impl() {
// empty implementation
return null;
}
+ @Override
public void updatePlaylistMetadata_impl(@Nullable MediaMetadata2 metadata) {
// empty implementation
}
+ @Override
public void addPlaylistItem_impl(int index, @NonNull MediaItem2 item) {
// empty implementation
}
+ @Override
public void removePlaylistItem_impl(@NonNull MediaItem2 item) {
// empty implementation
}
+ @Override
public void replacePlaylistItem_impl(int index, @NonNull MediaItem2 item) {
// empty implementation
}
+ @Override
public void skipToPlaylistItem_impl(@NonNull MediaItem2 item) {
// empty implementation
}
+ @Override
public void skipToPreviousItem_impl() {
// empty implementation
}
+ @Override
public void skipToNextItem_impl() {
// empty implementation
}
+ @Override
public int getRepeatMode_impl() {
return MediaPlaylistAgent.REPEAT_MODE_NONE;
}
+ @Override
public void setRepeatMode_impl(int repeatMode) {
// empty implementation
}
+ @Override
public int getShuffleMode_impl() {
// empty implementation
return MediaPlaylistAgent.SHUFFLE_MODE_NONE;
}
+ @Override
public void setShuffleMode_impl(int shuffleMode) {
// empty implementation
}
+ @Override
+ public @Nullable MediaItem2 getMediaItem_impl(@NonNull DataSourceDesc dsd) {
+ if (dsd == null) {
+ throw new IllegalArgumentException("dsd shouldn't be null");
+ }
+ List<MediaItem2> itemList = mInstance.getPlaylist();
+ if (itemList == null) {
+ return null;
+ }
+ for (int i = 0; i < itemList.size(); i++) {
+ MediaItem2 item = itemList.get(i);
+ if (item != null && item.getDataSourceDesc() == dsd) {
+ return item;
+ }
+ }
+ return null;
+ }
+
private ArrayMap<PlaylistEventCallback, Executor> getCallbacks() {
ArrayMap<PlaylistEventCallback, Executor> callbacks = new ArrayMap<>();
synchronized (mLock) {
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 8957ea8..2b7c72a 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -39,6 +39,7 @@
import android.media.MediaMetadata2;
import android.media.MediaPlayerBase;
import android.media.MediaPlayerBase.PlayerEventCallback;
+import android.media.MediaPlayerBase.PlayerState;
import android.media.MediaPlaylistAgent;
import android.media.MediaPlaylistAgent.PlaylistEventCallback;
import android.media.MediaSession2;
@@ -47,9 +48,6 @@
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.PlaylistParams;
-import android.media.MediaSession2.PlaylistParams.RepeatMode;
-import android.media.MediaSession2.PlaylistParams.ShuffleMode;
import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.SessionToken2;
@@ -71,6 +69,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.concurrent.Executor;
public class MediaSession2Impl implements MediaSession2Provider {
@@ -87,7 +86,6 @@
private final MediaSession2Stub mSessionStub;
private final SessionToken2 mSessionToken;
private final AudioManager mAudioManager;
- private final ArrayMap<PlayerEventCallback, Executor> mCallbacks = new ArrayMap<>();
private final PendingIntent mSessionActivity;
private final PlayerEventCallback mPlayerEventCallback;
private final PlaylistEventCallback mPlaylistEventCallback;
@@ -254,6 +252,7 @@
mSessionStub.notifyPlaybackInfoChanged(info);
notifyPlayerUpdatedNotLocked(oldPlayer);
}
+ // TODO(jaewan): Repeat the same thing for the playlist agent.
}
private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) {
@@ -430,41 +429,6 @@
mSessionStub.notifyCustomLayoutNotLocked(controller, layout);
}
- @Override
- public void setPlaylistParams_impl(PlaylistParams params) {
- if (params == null) {
- throw new IllegalArgumentException("params shouldn't be null");
- }
- ensureCallingThread();
- // TODO: Uncomment or remove
- /*
- final MediaPlayerBase player = mPlayer;
- if (player != null) {
- // TODO implement
- //player.setPlaylistParams(params);
- mSessionStub.notifyPlaylistParamsChanged(params);
- }
- */
- }
-
- @Override
- public PlaylistParams getPlaylistParams_impl() {
- // TODO: Uncomment or remove
- /*
- final MediaPlayerBase player = mPlayer;
- if (player != null) {
- // TODO(jaewan): Is it safe to be called on any thread?
- // Otherwise MediaSession2 should cache parameter of setPlaylistParams.
- // TODO implement
- //return player.getPlaylistParams();
- return null;
- } else if (DEBUG) {
- Log.d(TAG, "API calls after the close()", new IllegalStateException());
- }
- */
- return null;
- }
-
//////////////////////////////////////////////////////////////////////////////////////
// TODO(jaewan): Implement follows
//////////////////////////////////////////////////////////////////////////////////////
@@ -598,6 +562,48 @@
}
@Override
+ public int getRepeatMode_impl() {
+ final MediaPlaylistAgent agent = mPlaylistAgent;
+ if (agent != null) {
+ return agent.getRepeatMode();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
+ return MediaPlaylistAgent.REPEAT_MODE_NONE;
+ }
+
+ @Override
+ public void setRepeatMode_impl(int repeatMode) {
+ final MediaPlaylistAgent agent = mPlaylistAgent;
+ if (agent != null) {
+ agent.setRepeatMode(repeatMode);
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
+ }
+
+ @Override
+ public int getShuffleMode_impl() {
+ final MediaPlaylistAgent agent = mPlaylistAgent;
+ if (agent != null) {
+ return agent.getShuffleMode();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
+ return MediaPlaylistAgent.SHUFFLE_MODE_NONE;
+ }
+
+ @Override
+ public void setShuffleMode_impl(int shuffleMode) {
+ final MediaPlaylistAgent agent = mPlaylistAgent;
+ if (agent != null) {
+ agent.setShuffleMode(shuffleMode);
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
+ }
+
+ @Override
public void prepare_impl() {
ensureCallingThread();
final MediaPlayerBase player = mPlayer;
@@ -611,31 +617,23 @@
@Override
public void fastForward_impl() {
ensureCallingThread();
- // TODO: Uncomment or remove
- /*
final MediaPlayerBase player = mPlayer;
if (player != null) {
- // TODO implement
- //player.fastForward();
+ player.fastForward();
} else if (DEBUG) {
Log.d(TAG, "API calls after the close()", new IllegalStateException());
}
- */
}
@Override
public void rewind_impl() {
ensureCallingThread();
- // TODO: Uncomment or remove
- /*
final MediaPlayerBase player = mPlayer;
if (player != null) {
- // TODO implement
- //player.rewind();
+ player.rewind();
} else if (DEBUG) {
Log.d(TAG, "API calls after the close()", new IllegalStateException());
}
- */
}
@Override
@@ -650,33 +648,41 @@
}
@Override
- public void registerPlayerEventCallback_impl(Executor executor, PlayerEventCallback callback) {
- if (executor == null) {
- throw new IllegalArgumentException("executor shouldn't be null");
+ public @PlayerState int getPlayerState_impl() {
+ final MediaPlayerBase player = mPlayer;
+ if (player != null) {
+ return mPlayer.getPlayerState();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
}
- if (callback == null) {
- throw new IllegalArgumentException("callback shouldn't be null");
- }
- ensureCallingThread();
- if (mCallbacks.get(callback) != null) {
- Log.w(TAG, "callback is already added. Ignoring.");
- return;
- }
- mCallbacks.put(callback, executor);
+ return MediaPlayerBase.PLAYER_STATE_ERROR;
}
@Override
- public void unregisterPlayerEventCallback_impl(PlayerEventCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback shouldn't be null");
+ public long getPosition_impl() {
+ final MediaPlayerBase player = mPlayer;
+ if (player != null) {
+ return mPlayer.getCurrentPosition();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
}
- ensureCallingThread();
- mCallbacks.remove(callback);
+ return MediaPlayerBase.UNKNOWN_TIME;
+ }
+
+ @Override
+ public long getBufferedPosition_impl() {
+ final MediaPlayerBase player = mPlayer;
+ if (player != null) {
+ return mPlayer.getBufferedPosition();
+ } else if (DEBUG) {
+ Log.d(TAG, "API calls after the close()", new IllegalStateException());
+ }
+ return MediaPlayerBase.UNKNOWN_TIME;
}
@Override
public void notifyError_impl(int errorCode, Bundle extras) {
- // TODO(jaewan): Implement
+ mSessionStub.notifyError(errorCode, extras);
}
///////////////////////////////////////////////////
@@ -706,7 +712,7 @@
private void notifyPlaylistChangedOnExecutor(MediaPlaylistAgent playlistAgent,
List<MediaItem2> list, MediaMetadata2 metadata) {
if (playlistAgent != mPlaylistAgent) {
- // Ignore calls from the old agent. Ignore.
+ // Ignore calls from the old agent.
return;
}
mCallback.onPlaylistChanged(mInstance, playlistAgent, list, metadata);
@@ -716,27 +722,39 @@
private void notifyPlaylistMetadataChangedOnExecutor(MediaPlaylistAgent playlistAgent,
MediaMetadata2 metadata) {
if (playlistAgent != mPlaylistAgent) {
- // Ignore calls from the old agent. Ignore.
+ // Ignore calls from the old agent.
return;
}
mCallback.onPlaylistMetadataChanged(mInstance, playlistAgent, metadata);
mSessionStub.notifyPlaylistMetadataChangedNotLocked(metadata);
}
+ private void notifyRepeatModeChangedOnExecutor(MediaPlaylistAgent playlistAgent,
+ int repeatMode) {
+ if (playlistAgent != mPlaylistAgent) {
+ // Ignore calls from the old agent.
+ return;
+ }
+ mCallback.onRepeatModeChanged(mInstance, playlistAgent, repeatMode);
+ mSessionStub.notifyRepeatModeChangedNotLocked(repeatMode);
+ }
+
+ private void notifyShuffleModeChangedOnExecutor(MediaPlaylistAgent playlistAgent,
+ int shuffleMode) {
+ if (playlistAgent != mPlaylistAgent) {
+ // Ignore calls from the old agent.
+ return;
+ }
+ mCallback.onShuffleModeChanged(mInstance, playlistAgent, shuffleMode);
+ mSessionStub.notifyShuffleModeChangedNotLocked(shuffleMode);
+ }
+
private void notifyPlayerUpdatedNotLocked(MediaPlayerBase oldPlayer) {
- ArrayMap<PlayerEventCallback, Executor> callbacks = new ArrayMap<>();
- MediaPlayerBase player;
- synchronized (mLock) {
- player = mPlayer;
- callbacks.putAll(mCallbacks);
- }
- // Notify to callbacks added directly to this session
- for (int i = 0; i < callbacks.size(); i++) {
- final PlayerEventCallback callback = callbacks.keyAt(i);
- final Executor executor = callbacks.valueAt(i);
- // TODO: Uncomment or remove
- //executor.execute(() -> callback.onPlaybackStateChanged(state));
- }
+ final MediaPlayerBase player = mPlayer;
+ // TODO(jaewan): (Can be post-P) Find better way for player.getPlayerState() //
+ // In theory, Session.getXXX() may not be the same as Player.getXXX()
+ // and we should notify information of the session.getXXX() instead of
+ // player.getXXX()
// Notify to controllers as well.
final int state = player.getPlayerState();
if (state != oldPlayer.getPlayerState()) {
@@ -760,21 +778,6 @@
}
}
- private void notifyErrorNotLocked(String mediaId, int what, int extra) {
- ArrayMap<PlayerEventCallback, Executor> callbacks = new ArrayMap<>();
- synchronized (mLock) {
- callbacks.putAll(mCallbacks);
- }
- // Notify to callbacks added directly to this session
- for (int i = 0; i < callbacks.size(); i++) {
- final PlayerEventCallback callback = callbacks.keyAt(i);
- final Executor executor = callbacks.valueAt(i);
- // TODO: Uncomment or remove
- //executor.execute(() -> callback.onError(mediaId, what, extra));
- }
- // TODO(jaewan): Notify to controllers as well.
- }
-
Context getContext() {
return mContext;
}
@@ -827,28 +830,32 @@
@Override
public void onCurrentDataSourceChanged(MediaPlayerBase mpb, DataSourceDesc dsd) {
MediaSession2Impl session = getSession();
- if (session == null) {
+ if (session == null || dsd == null) {
return;
}
session.getCallbackExecutor().execute(() -> {
- // TODO (jaewan): Convert dsd to MediaItem (b/74506462)
+ MediaItem2 item = getMediaItem(session, dsd);
+ if (item == null) {
+ return;
+ }
+ session.getCallback().onCurrentMediaItemChanged(session.getInstance(), mpb, item);
// TODO (jaewan): Notify controllers through appropriate callback. (b/74505936)
- session.getCallback().onCurrentMediaItemChanged(
- session.getInstance(), mpb, null /* MediaItem */);
});
}
@Override
public void onMediaPrepared(MediaPlayerBase mpb, DataSourceDesc dsd) {
MediaSession2Impl session = getSession();
- if (session == null) {
+ if (session == null || dsd == null) {
return;
}
session.getCallbackExecutor().execute(() -> {
- // TODO (jaewan): Convert dsd to MediaItem (b/74506462)
+ MediaItem2 item = getMediaItem(session, dsd);
+ if (item == null) {
+ return;
+ }
+ session.getCallback().onMediaPrepared(session.getInstance(), mpb, item);
// TODO (jaewan): Notify controllers through appropriate callback. (b/74505936)
- session.getCallback().onMediaPrepared(
- session.getInstance(), mpb, null /* MediaItem */);
});
}
@@ -867,14 +874,17 @@
@Override
public void onBufferingStateChanged(MediaPlayerBase mpb, DataSourceDesc dsd, int state) {
MediaSession2Impl session = getSession();
- if (session == null) {
+ if (session == null || dsd == null) {
return;
}
session.getCallbackExecutor().execute(() -> {
- // TODO (jaewan): Convert dsd to MediaItem (b/74506462)
- // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936)
+ MediaItem2 item = getMediaItem(session, dsd);
+ if (item == null) {
+ return;
+ }
session.getCallback().onBufferingStateChanged(
- session.getInstance(), mpb, null /* MediaItem */, state);
+ session.getInstance(), mpb, item, state);
+ // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936)
});
}
@@ -885,6 +895,24 @@
}
return session;
}
+
+ private MediaItem2 getMediaItem(MediaSession2Impl session, DataSourceDesc dsd) {
+ MediaPlaylistAgent agent = session.getPlaylistAgent();
+ if (agent == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Session is closed", new IllegalStateException());
+ }
+ return null;
+ }
+ MediaItem2 item = agent.getMediaItem(dsd);
+ if (item == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Could not find matching item for dsd=" + dsd,
+ new NoSuchElementException());
+ }
+ }
+ return item;
+ }
}
private static class MyPlaylistEventCallback extends PlaylistEventCallback {
@@ -915,15 +943,21 @@
}
@Override
- public void onShuffleModeChanged(MediaPlaylistAgent playlistAgent, int shuffleMode) {
- super.onShuffleModeChanged(playlistAgent, shuffleMode);
- // TODO(jaewan): Handle this (b/74118768)
+ public void onRepeatModeChanged(MediaPlaylistAgent playlistAgent, int repeatMode) {
+ final MediaSession2Impl session = mSession.get();
+ if (session == null) {
+ return;
+ }
+ session.notifyRepeatModeChangedOnExecutor(playlistAgent, repeatMode);
}
@Override
- public void onRepeatModeChanged(MediaPlaylistAgent playlistAgent, int repeatMode) {
- super.onRepeatModeChanged(playlistAgent, repeatMode);
- // TODO(jaewan): Handle this (b/74118768)
+ public void onShuffleModeChanged(MediaPlaylistAgent playlistAgent, int shuffleMode) {
+ final MediaSession2Impl session = mSession.get();
+ if (session == null) {
+ return;
+ }
+ session.notifyShuffleModeChangedOnExecutor(playlistAgent, shuffleMode);
}
}
@@ -1268,76 +1302,6 @@
}
}
- public static class PlaylistParamsImpl implements PlaylistParamsProvider {
- /**
- * Keys used for converting a PlaylistParams object to a bundle object and vice versa.
- */
- private static final String KEY_REPEAT_MODE =
- "android.media.session2.playlistparams2.repeat_mode";
- private static final String KEY_SHUFFLE_MODE =
- "android.media.session2.playlistparams2.shuffle_mode";
- private static final String KEY_MEDIA_METADATA2_BUNDLE =
- "android.media.session2.playlistparams2.metadata2_bundle";
-
- private Context mContext;
- private PlaylistParams mInstance;
- private @RepeatMode int mRepeatMode;
- private @ShuffleMode int mShuffleMode;
- private MediaMetadata2 mPlaylistMetadata;
-
- public PlaylistParamsImpl(Context context, PlaylistParams instance,
- @RepeatMode int repeatMode, @ShuffleMode int shuffleMode,
- MediaMetadata2 playlistMetadata) {
- // TODO(jaewan): Sanity check
- mContext = context;
- mInstance = instance;
- mRepeatMode = repeatMode;
- mShuffleMode = shuffleMode;
- mPlaylistMetadata = playlistMetadata;
- }
-
- public @RepeatMode int getRepeatMode_impl() {
- return mRepeatMode;
- }
-
- public @ShuffleMode int getShuffleMode_impl() {
- return mShuffleMode;
- }
-
- public MediaMetadata2 getPlaylistMetadata_impl() {
- return mPlaylistMetadata;
- }
-
- @Override
- public Bundle toBundle_impl() {
- Bundle bundle = new Bundle();
- bundle.putInt(KEY_REPEAT_MODE, mRepeatMode);
- bundle.putInt(KEY_SHUFFLE_MODE, mShuffleMode);
- if (mPlaylistMetadata != null) {
- bundle.putBundle(KEY_MEDIA_METADATA2_BUNDLE, mPlaylistMetadata.toBundle());
- }
- return bundle;
- }
-
- public static PlaylistParams fromBundle(Context context, Bundle bundle) {
- if (bundle == null) {
- return null;
- }
- if (!bundle.containsKey(KEY_REPEAT_MODE) || !bundle.containsKey(KEY_SHUFFLE_MODE)) {
- return null;
- }
-
- Bundle metadataBundle = bundle.getBundle(KEY_MEDIA_METADATA2_BUNDLE);
- MediaMetadata2 metadata = metadataBundle == null
- ? null : MediaMetadata2.fromBundle(context, metadataBundle);
-
- return new PlaylistParams(context,
- bundle.getInt(KEY_REPEAT_MODE),
- bundle.getInt(KEY_SHUFFLE_MODE),
- metadata);
- }
- }
-
public static class CommandButtonImpl implements CommandButtonProvider {
private static final String KEY_COMMAND
= "android.media.media_session2.command_button.command";
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 4ec0da9..caf834a 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -22,13 +22,11 @@
import android.media.MediaItem2;
import android.media.MediaLibraryService2.LibraryRoot;
import android.media.MediaMetadata2;
-import android.media.MediaPlayerBase;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.PlaylistParams;
import android.media.Rating2;
import android.media.VolumeProvider2;
import android.net.Uri;
@@ -391,13 +389,13 @@
// use thread poll for incoming calls.
final int playerState = session.getInstance().getPlayerState();
final long positionEventTimeMs = System.currentTimeMillis();
- final long positionMs = session.getInstance().getCurrentPosition();
+ final long positionMs = session.getInstance().getPosition();
final float playbackSpeed = session.getInstance().getPlaybackSpeed();
final long bufferedPositionMs = session.getInstance().getBufferedPosition();
final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl)
session.getPlaybackInfo().getProvider()).toBundle();
- final PlaylistParams params = session.getInstance().getPlaylistParams();
- final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
+ final int repeatMode = session.getInstance().getRepeatMode();
+ final int shuffleMode = session.getInstance().getShuffleMode();
final PendingIntent sessionActivity = session.getSessionActivity();
final List<MediaItem2> playlist =
allowedCommands.hasCommand(MediaSession2.COMMAND_CODE_PLAYLIST_GET_LIST)
@@ -427,8 +425,8 @@
try {
caller.onConnected(MediaSession2Stub.this, allowedCommands.toBundle(),
playerState, positionEventTimeMs, positionMs, playbackSpeed,
- bufferedPositionMs, playbackInfoBundle, paramsBundle, playlistBundle,
- sessionActivity);
+ bufferedPositionMs, playbackInfoBundle, repeatMode, shuffleMode,
+ playlistBundle, sessionActivity);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle here.
@@ -507,14 +505,6 @@
case MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO:
session.getInstance().seekTo(args.getLong(ARGUMENT_KEY_POSITION));
break;
- // TODO(jaewan): Remove (b/74116823)
- /*
- case MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS:
- session.getInstance().setPlaylistParams(
- PlaylistParams.fromBundle(session.getContext(),
- args.getBundle(ARGUMENT_KEY_PLAYLIST_PARAMS)));
- break;
- */
default:
// TODO(jaewan): Resend unknown (new) commands through the custom command.
}
@@ -741,6 +731,21 @@
});
}
+ @Override
+ public void setRepeatMode(IMediaController2 caller, int repeatMode) {
+ onCommand(caller, MediaSession2.COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE,
+ (session, controller) -> {
+ session.getInstance().setRepeatMode(repeatMode);
+ });
+ }
+
+ @Override
+ public void setShuffleMode(IMediaController2 caller, int shuffleMode) {
+ onCommand(caller, MediaSession2.COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE,
+ (session, controller) -> {
+ session.getInstance().setShuffleMode(shuffleMode);
+ });
+ }
//////////////////////////////////////////////////////////////////////////////////////////////
// AIDL methods for LibrarySession overrides
@@ -985,6 +990,18 @@
});
}
+ public void notifyRepeatModeChangedNotLocked(int repeatMode) {
+ notifyAll((unused, iController) -> {
+ iController.onRepeatModeChanged(repeatMode);
+ });
+ }
+
+ public void notifyShuffleModeChangedNotLocked(int shuffleMode) {
+ notifyAll((unused, iController) -> {
+ iController.onShuffleModeChanged(shuffleMode);
+ });
+ }
+
public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) {
final Bundle playbackInfoBundle =
((MediaController2Impl.PlaybackInfoImpl) playbackInfo.getProvider()).toBundle();
@@ -1027,6 +1044,12 @@
});
}
+ public void notifyError(int errorCode, Bundle extras) {
+ notifyAll((unused, iController) -> {
+ iController.onError(errorCode, extras);
+ });
+ }
+
//////////////////////////////////////////////////////////////////////////////////////////////
// APIs for MediaLibrarySessionImpl
//////////////////////////////////////////////////////////////////////////////////////////////
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/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 7f225de..05a54d5 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -18,8 +18,7 @@
import android.app.Notification;
import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.Resources.Theme;
+import android.content.pm.ApplicationInfo;
import android.media.MediaBrowser2;
import android.media.MediaBrowser2.BrowserCallback;
import android.media.MediaController2;
@@ -35,7 +34,6 @@
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.MediaSessionService2.MediaNotification;
@@ -52,7 +50,6 @@
import android.media.update.MediaSession2Provider;
import android.media.update.MediaSession2Provider.BuilderBaseProvider;
import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
-import android.media.update.MediaSession2Provider.PlaylistParamsProvider;
import android.media.update.MediaSessionService2Provider;
import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
import android.media.update.SessionToken2Provider;
@@ -76,7 +73,6 @@
import com.android.media.MediaMetadata2Impl;
import com.android.media.MediaPlaylistAgentImpl;
import com.android.media.MediaSession2Impl;
-import com.android.media.MediaSession2Impl.PlaylistParamsImpl;
import com.android.media.MediaSessionService2Impl;
import com.android.media.Rating2Impl;
import com.android.media.SessionToken2Impl;
@@ -86,10 +82,11 @@
import java.util.concurrent.Executor;
-public class ApiFactory implements StaticProvider {
- public static Object initialize(Resources libResources, Theme libTheme)
- throws ReflectiveOperationException {
- ApiHelper.initialize(libResources, libTheme);
+public final class ApiFactory implements StaticProvider {
+ private ApiFactory() { }
+
+ public static ApiFactory initialize(ApplicationInfo updatableInfo) {
+ ApiHelper.initialize(updatableInfo);
return new ApiFactory();
}
@@ -141,19 +138,6 @@
}
@Override
- public PlaylistParamsProvider createMediaSession2PlaylistParams(Context context,
- PlaylistParams playlistParams, int repeatMode, int shuffleMode,
- MediaMetadata2 playlistMetadata) {
- return new PlaylistParamsImpl(context, playlistParams, repeatMode, shuffleMode,
- playlistMetadata);
- }
-
- @Override
- public PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle) {
- return PlaylistParamsImpl.fromBundle(context, bundle);
- }
-
- @Override
public BuilderProvider createMediaSession2CommandButtonBuilder(Context context,
MediaSession2.CommandButton.Builder instance) {
return new MediaSession2Impl.CommandButtonImpl.BuilderImpl(context, instance);
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
index 7018844..ad8bb48 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
@@ -19,9 +19,12 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.XmlResourceParser;
+import android.support.annotation.GuardedBy;
import android.support.v4.widget.Space;
import android.support.v7.widget.ButtonBarLayout;
import android.util.AttributeSet;
@@ -35,45 +38,47 @@
import com.android.support.mediarouter.app.MediaRouteVolumeSlider;
import com.android.support.mediarouter.app.OverlayListView;
-public class ApiHelper {
- private static ApiHelper sInstance;
- private final Resources mLibResources;
- private final Theme mLibTheme;
+public final class ApiHelper {
+ private static ApplicationInfo sUpdatableInfo;
- public static ApiHelper getInstance() {
- return sInstance;
- }
+ @GuardedBy("this")
+ private static Theme sLibTheme;
- static void initialize(Resources libResources, Theme libTheme) {
- if (sInstance == null) {
- sInstance = new ApiHelper(libResources, libTheme);
+ private ApiHelper() { }
+
+ static void initialize(ApplicationInfo updatableInfo) {
+ if (sUpdatableInfo != null) {
+ throw new IllegalStateException("initialize should only be called once");
}
+
+ sUpdatableInfo = updatableInfo;
}
- private ApiHelper(Resources libResources, Theme libTheme) {
- mLibResources = libResources;
- mLibTheme = libTheme;
+ public static Resources getLibResources(Context context) {
+ return getLibTheme(context).getResources();
}
- public static Resources getLibResources() {
- return sInstance.mLibResources;
+ public static Theme getLibTheme(Context context) {
+ if (sLibTheme != null) return sLibTheme;
+
+ return getLibThemeSynchronized(context);
}
- public static Theme getLibTheme() {
- return sInstance.mLibTheme;
- }
-
- public static Theme getLibTheme(int themeId) {
- Theme theme = sInstance.mLibResources.newTheme();
+ public static Theme getLibTheme(Context context, int themeId) {
+ Theme theme = getLibResources(context).newTheme();
theme.applyStyle(themeId, true);
return theme;
}
public static LayoutInflater getLayoutInflater(Context context) {
- return getLayoutInflater(context, getLibTheme());
+ return getLayoutInflater(context, null);
}
public static LayoutInflater getLayoutInflater(Context context, Theme theme) {
+ if (theme == null) {
+ theme = getLibTheme(context);
+ }
+
// TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
LayoutInflater layoutInflater = LayoutInflater.from(context).cloneInContext(
new ContextThemeWrapper(context, theme));
@@ -106,7 +111,7 @@
}
public static View inflateLibLayout(Context context, int libResId) {
- return inflateLibLayout(context, getLibTheme(), libResId, null, false);
+ return inflateLibLayout(context, getLibTheme(context), libResId, null, false);
}
public static View inflateLibLayout(Context context, Theme theme, int libResId) {
@@ -115,8 +120,23 @@
public static View inflateLibLayout(Context context, Theme theme, int libResId,
@Nullable ViewGroup root, boolean attachToRoot) {
- try (XmlResourceParser parser = getLibResources().getLayout(libResId)) {
+ try (XmlResourceParser parser = getLibResources(context).getLayout(libResId)) {
return getLayoutInflater(context, theme).inflate(parser, root, attachToRoot);
}
}
+
+ private static synchronized Theme getLibThemeSynchronized(Context context) {
+ if (sLibTheme != null) return sLibTheme;
+
+ if (sUpdatableInfo == null) {
+ throw new IllegalStateException("initialize hasn't been called yet");
+ }
+
+ try {
+ return sLibTheme = context.getPackageManager()
+ .getResourcesForApplication(sUpdatableInfo).newTheme();
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
index fa94a81..fde8a63 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
@@ -130,7 +130,7 @@
mRouter = MediaRouter.getInstance(context);
mCallback = new MediaRouterCallback();
- Resources.Theme theme = ApiHelper.getLibResources().newTheme();
+ Resources.Theme theme = ApiHelper.getLibResources(context).newTheme();
theme.applyStyle(MediaRouterThemeHelper.getRouterThemeId(context), true);
TypedArray a = theme.obtainStyledAttributes(attrs,
R.styleable.MediaRouteButton, defStyleAttr, 0);
@@ -304,7 +304,8 @@
*/
void setCheatSheetEnabled(boolean enable) {
TooltipCompat.setTooltipText(this, enable
- ? ApiHelper.getLibResources().getString(R.string.mr_button_content_description)
+ ? ApiHelper.getLibResources(getContext())
+ .getString(R.string.mr_button_content_description)
: null);
}
@@ -547,7 +548,7 @@
} else {
resId = R.string.mr_cast_button_disconnected;
}
- setContentDescription(ApiHelper.getLibResources().getString(resId));
+ setContentDescription(ApiHelper.getLibResources(getContext()).getString(resId));
}
private final class MediaRouterCallback extends MediaRouter.Callback {
@@ -604,7 +605,7 @@
@Override
protected Drawable doInBackground(Void... params) {
- return ApiHelper.getLibResources().getDrawable(mResId);
+ return ApiHelper.getLibResources(getContext()).getDrawable(mResId);
}
@Override
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
index 6e70eaf..cac64d9 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
@@ -99,8 +99,8 @@
public MediaRouteChooserDialog(Context context, int theme) {
// TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
- super(new ContextThemeWrapper(context,
- ApiHelper.getLibTheme(MediaRouterThemeHelper.getRouterThemeId(context))), theme);
+ super(new ContextThemeWrapper(context, ApiHelper.getLibTheme(context,
+ MediaRouterThemeHelper.getRouterThemeId(context))), theme);
context = getContext();
mRouter = MediaRouter.getInstance(context);
@@ -187,8 +187,8 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(ApiHelper.inflateLibLayout(getContext(),
- ApiHelper.getLibTheme(MediaRouterThemeHelper.getRouterThemeId(getContext())),
+ setContentView(ApiHelper.inflateLibLayout(getContext(), ApiHelper.getLibTheme(getContext(),
+ MediaRouterThemeHelper.getRouterThemeId(getContext())),
R.layout.mr_chooser_dialog));
mRoutes = new ArrayList<>();
@@ -206,7 +206,7 @@
* Sets the width of the dialog. Also called when configuration changes.
*/
void updateLayout() {
- getWindow().setLayout(MediaRouteDialogHelper.getDialogWidth(),
+ getWindow().setLayout(MediaRouteDialogHelper.getDialogWidth(getContext()),
ViewGroup.LayoutParams.WRAP_CONTENT);
}
@@ -263,7 +263,7 @@
public RouteAdapter(Context context, List<MediaRouter.RouteInfo> routes) {
super(context, 0, routes);
- TypedArray styledAttributes = ApiHelper.getLibTheme(
+ TypedArray styledAttributes = ApiHelper.getLibTheme(context,
MediaRouterThemeHelper.getRouterThemeId(context)).obtainStyledAttributes(
new int[] {
R.attr.mediaRouteDefaultIconDrawable,
@@ -294,7 +294,7 @@
View view = convertView;
if (view == null) {
view = ApiHelper.inflateLibLayout(getContext(),
- ApiHelper.getLibTheme(
+ ApiHelper.getLibTheme(getContext(),
MediaRouterThemeHelper.getRouterThemeId(getContext())),
R.layout.mr_chooser_list_item, parent, false);
}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
index 269a6e9..060cfca 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
@@ -206,8 +206,8 @@
public MediaRouteControllerDialog(Context context, int theme) {
// TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
- super(new ContextThemeWrapper(context,
- ApiHelper.getLibTheme(MediaRouterThemeHelper.getRouterThemeId(context))), theme);
+ super(new ContextThemeWrapper(context, ApiHelper.getLibTheme(context,
+ MediaRouterThemeHelper.getRouterThemeId(context))), theme);
mContext = getContext();
mControllerCallback = new MediaControllerCallback();
@@ -215,7 +215,7 @@
mCallback = new MediaRouterCallback();
mRoute = mRouter.getSelectedRoute();
setMediaSession(mRouter.getMediaSessionToken());
- mVolumeGroupListPaddingTop = ApiHelper.getLibResources().getDimensionPixelSize(
+ mVolumeGroupListPaddingTop = ApiHelper.getLibResources(context).getDimensionPixelSize(
R.dimen.mr_controller_volume_group_list_padding_top);
mAccessibilityManager =
(AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
@@ -334,7 +334,7 @@
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
setContentView(ApiHelper.inflateLibLayout(mContext,
- ApiHelper.getLibTheme(MediaRouterThemeHelper.getRouterThemeId(mContext)),
+ ApiHelper.getLibTheme(mContext, MediaRouterThemeHelper.getRouterThemeId(mContext)),
R.layout.mr_controller_material_dialog_b));
// Remove the neutral button.
@@ -359,13 +359,13 @@
int color = MediaRouterThemeHelper.getButtonTextColor(mContext);
mDisconnectButton = findViewById(BUTTON_DISCONNECT_RES_ID);
mDisconnectButton.setText(
- ApiHelper.getLibResources().getString(R.string.mr_controller_disconnect));
+ ApiHelper.getLibResources(mContext).getString(R.string.mr_controller_disconnect));
mDisconnectButton.setTextColor(color);
mDisconnectButton.setOnClickListener(listener);
mStopCastingButton = findViewById(BUTTON_STOP_RES_ID);
mStopCastingButton.setText(
- ApiHelper.getLibResources().getString(R.string.mr_controller_stop_casting));
+ ApiHelper.getLibResources(mContext).getString(R.string.mr_controller_stop_casting));
mStopCastingButton.setTextColor(color);
mStopCastingButton.setOnClickListener(listener);
@@ -440,11 +440,11 @@
}
});
loadInterpolator();
- mGroupListAnimationDurationMs = ApiHelper.getLibResources().getInteger(
+ mGroupListAnimationDurationMs = ApiHelper.getLibResources(mContext).getInteger(
R.integer.mr_controller_volume_group_list_animation_duration_ms);
- mGroupListFadeInDurationMs = ApiHelper.getLibResources().getInteger(
+ mGroupListFadeInDurationMs = ApiHelper.getLibResources(mContext).getInteger(
R.integer.mr_controller_volume_group_list_fade_in_duration_ms);
- mGroupListFadeOutDurationMs = ApiHelper.getLibResources().getInteger(
+ mGroupListFadeOutDurationMs = ApiHelper.getLibResources(mContext).getInteger(
R.integer.mr_controller_volume_group_list_fade_out_duration_ms);
mCustomControlView = onCreateMediaControlView(savedInstanceState);
@@ -460,13 +460,13 @@
* Sets the width of the dialog. Also called when configuration changes.
*/
void updateLayout() {
- int width = MediaRouteDialogHelper.getDialogWidth();
+ int width = MediaRouteDialogHelper.getDialogWidth(mContext);
getWindow().setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT);
View decorView = getWindow().getDecorView();
mDialogContentWidth = width - decorView.getPaddingLeft() - decorView.getPaddingRight();
- Resources res = ApiHelper.getLibResources();
+ Resources res = ApiHelper.getLibResources(mContext);
mVolumeGroupListItemIconSize = res.getDimensionPixelSize(
R.dimen.mr_controller_volume_group_list_item_icon_size);
mVolumeGroupListItemHeight = res.getDimensionPixelSize(
@@ -991,16 +991,16 @@
if (mRoute.getPresentationDisplayId()
!= MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) {
// The user is currently casting screen.
- mTitleView.setText(ApiHelper.getLibResources().getString(
+ mTitleView.setText(ApiHelper.getLibResources(mContext).getString(
R.string.mr_controller_casting_screen));
showTitle = true;
} else if (mState == null || mState.getState() == PlaybackStateCompat.STATE_NONE) {
// Show "No media selected" as we don't yet know the playback state.
- mTitleView.setText(ApiHelper.getLibResources().getString(
+ mTitleView.setText(ApiHelper.getLibResources(mContext).getString(
R.string.mr_controller_no_media_selected));
showTitle = true;
} else if (!hasTitle && !hasSubtitle) {
- mTitleView.setText(ApiHelper.getLibResources().getString(
+ mTitleView.setText(ApiHelper.getLibResources(mContext).getString(
R.string.mr_controller_no_info_available));
showTitle = true;
} else {
@@ -1223,7 +1223,8 @@
AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
event.setPackageName(mContext.getPackageName());
event.setClassName(getClass().getName());
- event.getText().add(ApiHelper.getLibResources().getString(actionDescResId));
+ event.getText().add(
+ ApiHelper.getLibResources(mContext).getString(actionDescResId));
mAccessibilityManager.sendAccessibilityEvent(event);
}
}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java
index 62c050b..9aabf7b 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java
@@ -41,12 +41,13 @@
* The framework should set the dialog width properly, but somehow it doesn't work, hence
* duplicating a similar logic here to determine the appropriate dialog width.
*/
- public static int getDialogWidth() {
- DisplayMetrics metrics = ApiHelper.getLibResources().getDisplayMetrics();
+ public static int getDialogWidth(Context context) {
+ DisplayMetrics metrics = ApiHelper.getLibResources(context).getDisplayMetrics();
boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
TypedValue value = new TypedValue();
- ApiHelper.getLibResources().getValue(isPortrait ? R.dimen.mr_dialog_fixed_width_minor
+ ApiHelper.getLibResources(context).getValue(isPortrait
+ ? R.dimen.mr_dialog_fixed_width_minor
: R.dimen.mr_dialog_fixed_width_major, value, true);
if (value.type == TypedValue.TYPE_DIMENSION) {
return (int) value.getDimension(metrics);
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
index defeedb..6a0a95a 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
@@ -51,9 +51,9 @@
public MediaRouteExpandCollapseButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mExpandAnimationDrawable = (AnimationDrawable)
- ApiHelper.getLibResources().getDrawable(R.drawable.mr_group_expand);
+ ApiHelper.getLibResources(context).getDrawable(R.drawable.mr_group_expand);
mCollapseAnimationDrawable = (AnimationDrawable)
- ApiHelper.getLibResources().getDrawable(R.drawable.mr_group_collapse);
+ ApiHelper.getLibResources(context).getDrawable(R.drawable.mr_group_collapse);
ColorFilter filter = new PorterDuffColorFilter(
MediaRouterThemeHelper.getControllerColor(context, defStyleAttr),
@@ -62,9 +62,9 @@
mCollapseAnimationDrawable.setColorFilter(filter);
mExpandGroupDescription =
- ApiHelper.getLibResources().getString(R.string.mr_controller_expand_group);
+ ApiHelper.getLibResources(context).getString(R.string.mr_controller_expand_group);
mCollapseGroupDescription =
- ApiHelper.getLibResources().getString(R.string.mr_controller_collapse_group);
+ ApiHelper.getLibResources(context).getString(R.string.mr_controller_collapse_group);
setImageDrawable(mExpandAnimationDrawable.getFrame(0));
setContentDescription(mExpandGroupDescription);
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
index 33d92b4..a38491f 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
@@ -267,7 +267,7 @@
mCallbackObj = createCallbackObj();
mVolumeCallbackObj = createVolumeCallbackObj();
- Resources r = ApiHelper.getLibResources();
+ Resources r = ApiHelper.getLibResources(context);
mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
mRouterObj, r.getString(R.string.mr_user_route_category_name), false);
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index 16707c6..9ae75b0 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -101,6 +101,12 @@
private static final int SETTINGS_MODE_VIDEO_QUALITY = 4;
private static final int SETTINGS_MODE_MAIN = 5;
private static final int PLAYBACK_SPEED_1x_INDEX = 3;
+
+ private static final int SIZE_TYPE_EMBEDDED = 0;
+ private static final int SIZE_TYPE_FULL = 1;
+ // TODO: add support for Minimal size type.
+ private static final int SIZE_TYPE_MINIMAL = 2;
+
private static final int MAX_PROGRESS = 1000;
private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000;
private static final int REWIND_TIME_MS = 10000;
@@ -114,16 +120,10 @@
private MediaController.TransportControls mControls;
private PlaybackState mPlaybackState;
private MediaMetadata mMetadata;
- private ProgressBar mProgress;
- private TextView mEndTime, mCurrentTime;
- private TextView mTitleView;
- private TextView mAdSkipView, mAdRemainingView;
- private View mAdExternalLink;
- private View mTitleBar;
- private View mRoot;
private int mDuration;
private int mPrevState;
- private int mPrevLeftBarWidth;
+ private int mPrevWidth;
+ private int mOriginalLeftBarWidth;
private int mVideoTrackCount;
private int mAudioTrackCount;
private int mSubtitleTrackCount;
@@ -132,8 +132,13 @@
private int mSelectedAudioTrackIndex;
private int mSelectedVideoQualityIndex;
private int mSelectedSpeedIndex;
- private int mSettingsItemHeight;
+ private int mEmbeddedSettingsItemWidth;
+ private int mFullSettingsItemWidth;
+ private int mEmbeddedSettingsItemHeight;
+ private int mFullSettingsItemHeight;
private int mSettingsWindowMargin;
+ private int mSizeType;
+ private int mOrientation;
private long mPlaybackActions;
private boolean mDragging;
private boolean mIsFullScreen;
@@ -143,30 +148,56 @@
private boolean mSeekAvailable;
private boolean mIsAdvertisement;
private boolean mIsMute;
+
+ // Relating to Title Bar View
+ private View mRoot;
+ private View mTitleBar;
+ private TextView mTitleView;
+ private View mAdExternalLink;
+ private ImageButton mBackButton;
+ private MediaRouteButton mRouteButton;
+ private MediaRouteSelector mRouteSelector;
+
+ // Relating to Center View
+ private ViewGroup mCenterView;
+ private View mTransportControls;
private ImageButton mPlayPauseButton;
private ImageButton mFfwdButton;
private ImageButton mRewButton;
private ImageButton mNextButton;
private ImageButton mPrevButton;
- private ImageButton mBackButton;
+ // Relating to Progress Bar View
+ private ProgressBar mProgress;
+
+ // Relating to Bottom Bar Left View
+ private ViewGroup mBottomBarLeftView;
+ private ViewGroup mTimeView;
+ private TextView mEndTime;
+ private TextView mCurrentTime;
+ private TextView mAdSkipView;
+ private StringBuilder mFormatBuilder;
+ private Formatter mFormatter;
+
+ // Relating to Bottom Bar Right View
+ private ViewGroup mBottomBarRightView;
private ViewGroup mBasicControls;
+ private ViewGroup mExtraControls;
+ private ViewGroup mCustomButtons;
private ImageButton mSubtitleButton;
private ImageButton mFullScreenButton;
private ImageButton mOverflowButtonRight;
-
- private ViewGroup mExtraControls;
- private ViewGroup mCustomButtons;
private ImageButton mOverflowButtonLeft;
private ImageButton mMuteButton;
private ImageButton mVideoQualityButton;
private ImageButton mSettingsButton;
+ private TextView mAdRemainingView;
+ // Relating to Settings List View
private ListView mSettingsListView;
private PopupWindow mSettingsWindow;
private SettingsAdapter mSettingsAdapter;
private SubSettingsAdapter mSubSettingsAdapter;
-
private List<String> mSettingsMainTextsList;
private List<String> mSettingsSubTextsList;
private List<Integer> mSettingsIconIdsList;
@@ -180,12 +211,6 @@
private CharSequence mPauseDescription;
private CharSequence mReplayDescription;
- private StringBuilder mFormatBuilder;
- private Formatter mFormatter;
-
- private MediaRouteButton mRouteButton;
- private MediaRouteSelector mRouteSelector;
-
public MediaControlView2Impl(MediaControlView2 instance,
ViewGroupProvider superProvider, ViewGroupProvider privateProvider) {
super(instance, superProvider, privateProvider);
@@ -194,7 +219,7 @@
@Override
public void initialize(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- mResources = ApiHelper.getLibResources();
+ mResources = ApiHelper.getLibResources(mInstance.getContext());
// Inflate MediaControlView2 from XML
mRoot = makeControllerView();
mInstance.addView(mRoot);
@@ -310,6 +335,35 @@
}
@Override
+ public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
+
+ if (mPrevWidth != mInstance.getMeasuredWidth()) {
+ int currWidth = mInstance.getMeasuredWidth();
+
+ int iconSize = mResources.getDimensionPixelSize(R.dimen.mcv2_full_icon_size);
+ int marginSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_margin);
+ int bottomBarRightWidthMax = iconSize * 4 + marginSize * 8;
+
+ int fullWidth = mTransportControls.getWidth() + mTimeView.getWidth()
+ + bottomBarRightWidthMax;
+ // These views may not have been initialized yet.
+ if (mTransportControls.getWidth() == 0 || mTimeView.getWidth() == 0) {
+ return;
+ }
+ if (mSizeType == SIZE_TYPE_EMBEDDED && fullWidth <= currWidth) {
+ updateLayoutForSizeChange(SIZE_TYPE_FULL);
+ } else if (mSizeType == SIZE_TYPE_FULL && fullWidth > currWidth) {
+ updateLayoutForSizeChange(SIZE_TYPE_EMBEDDED);
+ }
+ // Dismiss SettingsWindow if it is showing.
+ mSettingsWindow.dismiss();
+ mPrevWidth = currWidth;
+ }
+ updateTitleBarLayout();
+ }
+
+ @Override
public void setEnabled_impl(boolean enabled) {
super.setEnabled_impl(enabled);
@@ -431,35 +485,47 @@
mReplayDescription =
mResources.getText(R.string.lockscreen_replay_button_content_description);
- mRouteButton = v.findViewById(R.id.cast);
-
- mPlayPauseButton = v.findViewById(R.id.pause);
- if (mPlayPauseButton != null) {
- mPlayPauseButton.requestFocus();
- mPlayPauseButton.setOnClickListener(mPlayPauseListener);
- }
- mFfwdButton = v.findViewById(R.id.ffwd);
- if (mFfwdButton != null) {
- mFfwdButton.setOnClickListener(mFfwdListener);
- }
- mRewButton = v.findViewById(R.id.rew);
- if (mRewButton != null) {
- mRewButton.setOnClickListener(mRewListener);
- }
- mNextButton = v.findViewById(R.id.next);
- if (mNextButton != null) {
- mNextButton.setOnClickListener(mNextListener);
- }
- mPrevButton = v.findViewById(R.id.prev);
- if (mPrevButton != null) {
- mPrevButton.setOnClickListener(mPrevListener);
- }
+ // Relating to Title Bar View
+ mTitleBar = v.findViewById(R.id.title_bar);
+ mTitleView = v.findViewById(R.id.title_text);
+ mAdExternalLink = v.findViewById(R.id.ad_external_link);
mBackButton = v.findViewById(R.id.back);
if (mBackButton != null) {
mBackButton.setOnClickListener(mBackListener);
}
+ mRouteButton = v.findViewById(R.id.cast);
+ // Relating to Center View
+ mCenterView = v.findViewById(R.id.center_view);
+ mTransportControls = inflateTransportControls(R.layout.embedded_transport_controls);
+ mCenterView.addView(mTransportControls);
+
+ // Relating to Progress Bar View
+ mProgress = v.findViewById(R.id.mediacontroller_progress);
+ if (mProgress != null) {
+ if (mProgress instanceof SeekBar) {
+ SeekBar seeker = (SeekBar) mProgress;
+ seeker.setOnSeekBarChangeListener(mSeekListener);
+ seeker.setProgressDrawable(mResources.getDrawable(R.drawable.custom_progress));
+ seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb));
+ }
+ mProgress.setMax(MAX_PROGRESS);
+ }
+
+ // Relating to Bottom Bar Left View
+ mBottomBarLeftView = v.findViewById(R.id.bottom_bar_left);
+ mTimeView = v.findViewById(R.id.time);
+ mEndTime = v.findViewById(R.id.time_end);
+ mCurrentTime = v.findViewById(R.id.time_current);
+ mAdSkipView = v.findViewById(R.id.ad_skip_time);
+ mFormatBuilder = new StringBuilder();
+ mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
+
+ // Relating to Bottom Bar Right View
+ mBottomBarRightView = v.findViewById(R.id.bottom_bar_right);
mBasicControls = v.findViewById(R.id.basic_controls);
+ mExtraControls = v.findViewById(R.id.extra_controls);
+ mCustomButtons = v.findViewById(R.id.custom_buttons);
mSubtitleButton = v.findViewById(R.id.subtitle);
if (mSubtitleButton != null) {
mSubtitleButton.setOnClickListener(mSubtitleListener);
@@ -473,10 +539,6 @@
if (mOverflowButtonRight != null) {
mOverflowButtonRight.setOnClickListener(mOverflowRightListener);
}
-
- // TODO: should these buttons be shown as default?
- mExtraControls = v.findViewById(R.id.extra_controls);
- mCustomButtons = v.findViewById(R.id.custom_buttons);
mOverflowButtonLeft = v.findViewById(R.id.overflow_left);
if (mOverflowButtonLeft != null) {
mOverflowButtonLeft.setOnClickListener(mOverflowLeftListener);
@@ -493,33 +555,9 @@
if (mVideoQualityButton != null) {
mVideoQualityButton.setOnClickListener(mVideoQualityListener);
}
-
- mProgress = v.findViewById(R.id.mediacontroller_progress);
- if (mProgress != null) {
- if (mProgress instanceof SeekBar) {
- SeekBar seeker = (SeekBar) mProgress;
- seeker.setOnSeekBarChangeListener(mSeekListener);
- seeker.setProgressDrawable(mResources.getDrawable(R.drawable.custom_progress));
- seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb));
- }
- mProgress.setMax(MAX_PROGRESS);
- }
-
- mTitleBar = v.findViewById(R.id.title_bar);
- if (mTitleBar != null) {
- mTitleBar.addOnLayoutChangeListener(mTitleBarLayoutChangeListener);
- }
- mTitleView = v.findViewById(R.id.title_text);
-
- mEndTime = v.findViewById(R.id.time);
- mCurrentTime = v.findViewById(R.id.time_current);
- mFormatBuilder = new StringBuilder();
- mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
-
- mAdSkipView = v.findViewById(R.id.ad_skip_time);
mAdRemainingView = v.findViewById(R.id.ad_remaining);
- mAdExternalLink = v.findViewById(R.id.ad_external_link);
+ // Relating to Settings List View
initializeSettingsLists();
mSettingsListView = (ListView) ApiHelper.inflateLibLayout(mInstance.getContext(),
R.layout.settings_list);
@@ -530,12 +568,16 @@
mSettingsListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
mSettingsListView.setOnItemClickListener(mSettingsItemClickListener);
- mSettingsItemHeight = mResources.getDimensionPixelSize(
- R.dimen.MediaControlView2_settings_height);
+ mEmbeddedSettingsItemWidth = mResources.getDimensionPixelSize(
+ R.dimen.mcv2_embedded_settings_width);
+ mFullSettingsItemWidth = mResources.getDimensionPixelSize(R.dimen.mcv2_full_settings_width);
+ mEmbeddedSettingsItemHeight = mResources.getDimensionPixelSize(
+ R.dimen.mcv2_embedded_settings_height);
+ mFullSettingsItemHeight = mResources.getDimensionPixelSize(
+ R.dimen.mcv2_full_settings_height);
mSettingsWindowMargin = (-1) * mResources.getDimensionPixelSize(
- R.dimen.MediaControlView2_settings_offset);
- int width = mResources.getDimensionPixelSize(R.dimen.MediaControlView2_settings_width);
- mSettingsWindow = new PopupWindow(mSettingsListView, width,
+ R.dimen.mcv2_settings_offset);
+ mSettingsWindow = new PopupWindow(mSettingsListView, mEmbeddedSettingsItemWidth,
ViewGroup.LayoutParams.WRAP_CONTENT, true);
}
@@ -954,36 +996,6 @@
}
};
- // The title bar is made up of two separate LinearLayouts. If the sum of the two bars are
- // greater than the length of the title bar, reduce the size of the left bar (which makes the
- // TextView that contains the title of the media file shrink).
- private final View.OnLayoutChangeListener mTitleBarLayoutChangeListener
- = new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- if (mRoot != null) {
- int titleBarWidth = mRoot.findViewById(R.id.title_bar).getWidth();
-
- View leftBar = mRoot.findViewById(R.id.title_bar_left);
- View rightBar = mRoot.findViewById(R.id.title_bar_right);
- int leftBarWidth = leftBar.getWidth();
- int rightBarWidth = rightBar.getWidth();
-
- RelativeLayout.LayoutParams params =
- (RelativeLayout.LayoutParams) leftBar.getLayoutParams();
- if (leftBarWidth + rightBarWidth > titleBarWidth) {
- params.width = titleBarWidth - rightBarWidth;
- mPrevLeftBarWidth = leftBarWidth;
- } else if (leftBarWidth + rightBarWidth < titleBarWidth && mPrevLeftBarWidth != 0) {
- params.width = mPrevLeftBarWidth;
- mPrevLeftBarWidth = 0;
- }
- leftBar.setLayoutParams(params);
- }
- }
- };
-
private void updateDuration() {
if (mMetadata != null) {
if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
@@ -1002,13 +1014,37 @@
}
}
+ // The title bar is made up of two separate LinearLayouts. If the sum of the two bars are
+ // greater than the length of the title bar, reduce the size of the left bar (which makes the
+ // TextView that contains the title of the media file shrink).
+ private void updateTitleBarLayout() {
+ if (mTitleBar != null) {
+ int titleBarWidth = mTitleBar.getWidth();
+
+ View leftBar = mTitleBar.findViewById(R.id.title_bar_left);
+ View rightBar = mTitleBar.findViewById(R.id.title_bar_right);
+ int leftBarWidth = leftBar.getWidth();
+ int rightBarWidth = rightBar.getWidth();
+
+ RelativeLayout.LayoutParams params =
+ (RelativeLayout.LayoutParams) leftBar.getLayoutParams();
+ if (leftBarWidth + rightBarWidth > titleBarWidth) {
+ params.width = titleBarWidth - rightBarWidth;
+ mOriginalLeftBarWidth = leftBarWidth;
+ } else if (leftBarWidth + rightBarWidth < titleBarWidth && mOriginalLeftBarWidth != 0) {
+ params.width = mOriginalLeftBarWidth;
+ mOriginalLeftBarWidth = 0;
+ }
+ leftBar.setLayoutParams(params);
+ }
+ }
+
private void updateLayout() {
if (mIsAdvertisement) {
mRewButton.setVisibility(View.GONE);
mFfwdButton.setVisibility(View.GONE);
mPrevButton.setVisibility(View.GONE);
- mCurrentTime.setVisibility(View.GONE);
- mEndTime.setVisibility(View.GONE);
+ mTimeView.setVisibility(View.GONE);
mAdSkipView.setVisibility(View.VISIBLE);
mAdRemainingView.setVisibility(View.VISIBLE);
@@ -1021,8 +1057,7 @@
mRewButton.setVisibility(View.VISIBLE);
mFfwdButton.setVisibility(View.VISIBLE);
mPrevButton.setVisibility(View.VISIBLE);
- mCurrentTime.setVisibility(View.VISIBLE);
- mEndTime.setVisibility(View.VISIBLE);
+ mTimeView.setVisibility(View.VISIBLE);
mAdSkipView.setVisibility(View.GONE);
mAdRemainingView.setVisibility(View.GONE);
@@ -1035,6 +1070,79 @@
}
}
+ private void updateLayoutForSizeChange(int sizeType) {
+ mSizeType = sizeType;
+ RelativeLayout.LayoutParams params =
+ (RelativeLayout.LayoutParams) mTimeView.getLayoutParams();
+ switch (mSizeType) {
+ case SIZE_TYPE_EMBEDDED:
+ mBottomBarLeftView.removeView(mTransportControls);
+ mBottomBarLeftView.setVisibility(View.GONE);
+ mTransportControls = inflateTransportControls(R.layout.embedded_transport_controls);
+ mCenterView.addView(mTransportControls, 0);
+
+ if (params.getRule(RelativeLayout.LEFT_OF) != 0) {
+ params.removeRule(RelativeLayout.LEFT_OF);
+ params.addRule(RelativeLayout.RIGHT_OF, R.id.bottom_bar_left);
+ }
+ break;
+ case SIZE_TYPE_FULL:
+ mCenterView.removeView(mTransportControls);
+ mTransportControls = inflateTransportControls(R.layout.full_transport_controls);
+ mBottomBarLeftView.addView(mTransportControls, 0);
+ mBottomBarLeftView.setVisibility(View.VISIBLE);
+
+ if (params.getRule(RelativeLayout.RIGHT_OF) != 0) {
+ params.removeRule(RelativeLayout.RIGHT_OF);
+ params.addRule(RelativeLayout.LEFT_OF, R.id.bottom_bar_right);
+ }
+ break;
+ case SIZE_TYPE_MINIMAL:
+ // TODO: implement
+ break;
+ }
+ mTimeView.setLayoutParams(params);
+
+ if (isPlaying()) {
+ mPlayPauseButton.setImageDrawable(
+ mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
+ mPlayPauseButton.setContentDescription(mPauseDescription);
+ } else {
+ mPlayPauseButton.setImageDrawable(
+ mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
+ mPlayPauseButton.setContentDescription(mPlayDescription);
+ }
+ }
+
+ private View inflateTransportControls(int layoutId) {
+ View v = ApiHelper.inflateLibLayout(mInstance.getContext(), layoutId);
+ mPlayPauseButton = v.findViewById(R.id.pause);
+ if (mPlayPauseButton != null) {
+ mPlayPauseButton.requestFocus();
+ mPlayPauseButton.setOnClickListener(mPlayPauseListener);
+ }
+ mFfwdButton = v.findViewById(R.id.ffwd);
+ if (mFfwdButton != null) {
+ mFfwdButton.setOnClickListener(mFfwdListener);
+ }
+ mRewButton = v.findViewById(R.id.rew);
+ if (mRewButton != null) {
+ mRewButton.setOnClickListener(mRewListener);
+ }
+ // TODO: Add support for Next and Previous buttons
+ mNextButton = v.findViewById(R.id.next);
+ if (mNextButton != null) {
+ mNextButton.setOnClickListener(mNextListener);
+ mNextButton.setVisibility(View.GONE);
+ }
+ mPrevButton = v.findViewById(R.id.prev);
+ if (mPrevButton != null) {
+ mPrevButton.setOnClickListener(mPrevListener);
+ mPrevButton.setVisibility(View.GONE);
+ }
+ return v;
+ }
+
private void initializeSettingsLists() {
mSettingsMainTextsList = new ArrayList<String>();
mSettingsMainTextsList.add(
@@ -1079,8 +1187,18 @@
}
private void displaySettingsWindow(BaseAdapter adapter) {
+ // Set Adapter
mSettingsListView.setAdapter(adapter);
- int totalHeight = adapter.getCount() * mSettingsItemHeight;
+
+ // Set width of window
+ int itemWidth = (mSizeType == SIZE_TYPE_EMBEDDED)
+ ? mEmbeddedSettingsItemWidth : mFullSettingsItemWidth;
+ mSettingsWindow.setWidth(itemWidth);
+
+ // Calculate height of window and show
+ int itemHeight = (mSizeType == SIZE_TYPE_EMBEDDED)
+ ? mEmbeddedSettingsItemHeight : mFullSettingsItemHeight;
+ int totalHeight = adapter.getCount() * itemHeight;
mSettingsWindow.dismiss();
mSettingsWindow.showAsDropDown(mInstance, mSettingsWindowMargin,
mSettingsWindowMargin - totalHeight, Gravity.BOTTOM | Gravity.RIGHT);
@@ -1273,8 +1391,14 @@
@Override
public View getView(int position, View convertView, ViewGroup container) {
- View row = ApiHelper.inflateLibLayout(mInstance.getContext(),
- R.layout.settings_list_item);
+ View row;
+ if (mSizeType == SIZE_TYPE_FULL) {
+ row = ApiHelper.inflateLibLayout(mInstance.getContext(),
+ R.layout.full_settings_list_item);
+ } else {
+ row = ApiHelper.inflateLibLayout(mInstance.getContext(),
+ R.layout.embedded_settings_list_item);
+ }
TextView mainTextView = (TextView) row.findViewById(R.id.main_text);
TextView subTextView = (TextView) row.findViewById(R.id.sub_text);
ImageView iconView = (ImageView) row.findViewById(R.id.icon);
@@ -1347,8 +1471,14 @@
@Override
public View getView(int position, View convertView, ViewGroup container) {
- View row = ApiHelper.inflateLibLayout(mInstance.getContext(),
- R.layout.sub_settings_list_item);
+ View row;
+ if (mSizeType == SIZE_TYPE_FULL) {
+ row = ApiHelper.inflateLibLayout(mInstance.getContext(),
+ R.layout.full_sub_settings_list_item);
+ } else {
+ row = ApiHelper.inflateLibLayout(mInstance.getContext(),
+ R.layout.embedded_sub_settings_list_item);
+ }
TextView textView = (TextView) row.findViewById(R.id.text);
ImageView checkView = (ImageView) row.findViewById(R.id.check);
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();
}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index ad63899..fb9b0ba 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1550,6 +1550,9 @@
switch(eventId) {
case ICameraService::EVENT_USER_SWITCHED: {
+ // Try to register for UID policy updates, in case we're recovering
+ // from a system server crash
+ mUidPolicy->registerSelf();
doUserSwitch(/*newUserIds*/ args);
break;
}
@@ -2365,17 +2368,31 @@
// ----------------------------------------------------------------------------
void CameraService::UidPolicy::registerSelf() {
+ Mutex::Autolock _l(mUidLock);
+
ActivityManager am;
+ if (mRegistered) return;
am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
| ActivityManager::UID_OBSERVER_IDLE
| ActivityManager::UID_OBSERVER_ACTIVE,
ActivityManager::PROCESS_STATE_UNKNOWN,
String16("cameraserver"));
+ status_t res = am.linkToDeath(this);
+ if (res == OK) {
+ mRegistered = true;
+ ALOGV("UidPolicy: Registered with ActivityManager");
+ }
}
void CameraService::UidPolicy::unregisterSelf() {
+ Mutex::Autolock _l(mUidLock);
+
ActivityManager am;
am.unregisterUidObserver(this);
+ am.unlinkToDeath(this);
+ mRegistered = false;
+ mActiveUids.clear();
+ ALOGV("UidPolicy: Unregistered with ActivityManager");
}
void CameraService::UidPolicy::onUidGone(uid_t uid, bool disabled) {
@@ -2404,17 +2421,14 @@
}
bool CameraService::UidPolicy::isUidActive(uid_t uid) {
- // Non-app UIDs are considered always active
- if (uid < FIRST_APPLICATION_UID) {
- return true;
- }
Mutex::Autolock _l(mUidLock);
return isUidActiveLocked(uid);
}
bool CameraService::UidPolicy::isUidActiveLocked(uid_t uid) {
// Non-app UIDs are considered always active
- if (uid < FIRST_APPLICATION_UID) {
+ // If activity manager is unreachable, assume everything is active
+ if (uid < FIRST_APPLICATION_UID || !mRegistered) {
return true;
}
auto it = mOverrideUids.find(uid);
@@ -2432,6 +2446,13 @@
updateOverrideUid(uid, false, false);
}
+void CameraService::UidPolicy::binderDied(const wp<IBinder>& /*who*/) {
+ Mutex::Autolock _l(mUidLock);
+ ALOGV("UidPolicy: ActivityManager has died");
+ mRegistered = false;
+ mActiveUids.clear();
+}
+
void CameraService::UidPolicy::updateOverrideUid(uid_t uid, bool active, bool insert) {
bool wasActive = false;
bool isActive = false;
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 3812925..86a2b81 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -518,10 +518,10 @@
// Observer for UID lifecycle enforcing that UIDs in idle
// state cannot use the camera to protect user privacy.
- class UidPolicy : public BnUidObserver {
+ class UidPolicy : public BnUidObserver, public virtual IBinder::DeathRecipient {
public:
explicit UidPolicy(sp<CameraService> service)
- : mService(service) {}
+ : mRegistered(false), mService(service) {}
void registerSelf();
void unregisterSelf();
@@ -535,11 +535,14 @@
void addOverrideUid(uid_t uid, bool active);
void removeOverrideUid(uid_t uid);
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder> &who);
private:
bool isUidActiveLocked(uid_t uid);
void updateOverrideUid(uid_t uid, bool active, bool insert);
Mutex mUidLock;
+ bool mRegistered;
wp<CameraService> mService;
std::unordered_set<uid_t> mActiveUids;
std::unordered_map<uid_t, bool> mOverrideUids;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index b37f004..077e05e 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -39,9 +39,6 @@
const std::string kLegacyProviderName("legacy/0");
const std::string kExternalProviderName("external/0");
-// Slash-separated list of provider types to consider for use via the old camera API
-const std::string kStandardProviderTypes("internal/legacy/external");
-
} // anonymous namespace
CameraProviderManager::HardwareServiceInteractionProxy
@@ -101,10 +98,8 @@
std::lock_guard<std::mutex> lock(mInterfaceMutex);
std::vector<std::string> deviceIds;
for (auto& provider : mProviders) {
- if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
- for (auto& id : provider->mUniqueAPI1CompatibleCameraIds) {
- deviceIds.push_back(id);
- }
+ for (auto& id : provider->mUniqueAPI1CompatibleCameraIds) {
+ deviceIds.push_back(id);
}
}
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index e3bb5dc..f4d5a18 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -423,12 +423,20 @@
__FUNCTION__, gbp.get(), strerror(-res), res);
return res;
}
+ if ((slot < 0) || (slot > BufferQueue::NUM_BUFFER_SLOTS)) {
+ SP_LOGE("%s: Slot received %d either bigger than expected maximum %d or negative!",
+ __FUNCTION__, slot, BufferQueue::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ }
//During buffer attach 'mMutex' is not held which makes the removal of
//"gbp" possible. Check whether this is the case and continue.
if (mOutputSlots[gbp] == nullptr) {
continue;
}
auto& outputSlots = *mOutputSlots[gbp];
+ if (static_cast<size_t> (slot + 1) > outputSlots.size()) {
+ outputSlots.resize(slot + 1);
+ }
if (outputSlots[slot] != nullptr) {
// If the buffer is attached to a slot which already contains a buffer,
// the previous buffer will be removed from the output queue. Decrement