Merge "FLAC: resize raw decoder's buffer according to flac max buffer size"
diff --git a/cmds/screenrecord/Android.bp b/cmds/screenrecord/Android.bp
new file mode 100644
index 0000000..86476cd
--- /dev/null
+++ b/cmds/screenrecord/Android.bp
@@ -0,0 +1,55 @@
+// Copyright 2013 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.
+
+cc_binary {
+ name: "screenrecord",
+
+ srcs: [
+ "screenrecord.cpp",
+ "EglWindow.cpp",
+ "FrameOutput.cpp",
+ "TextRenderer.cpp",
+ "Overlay.cpp",
+ "Program.cpp",
+ ],
+
+ shared_libs: [
+ "libstagefright",
+ "libmedia",
+ "libmedia_omx",
+ "libutils",
+ "libbinder",
+ "libstagefright_foundation",
+ "libjpeg",
+ "libui",
+ "libgui",
+ "libcutils",
+ "liblog",
+ "libEGL",
+ "libGLESv2",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright",
+ "frameworks/av/media/libstagefright/include",
+ "frameworks/native/include/media/openmax",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wno-multichar",
+ //"-UNDEBUG",
+ ]
+}
diff --git a/cmds/screenrecord/Android.mk b/cmds/screenrecord/Android.mk
deleted file mode 100644
index 5e83ed6..0000000
--- a/cmds/screenrecord/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2013 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_SRC_FILES := \
- screenrecord.cpp \
- EglWindow.cpp \
- FrameOutput.cpp \
- TextRenderer.cpp \
- Overlay.cpp \
- Program.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libstagefright libmedia libmedia_omx libutils libbinder libstagefright_foundation \
- libjpeg libui libgui libcutils liblog libEGL libGLESv2
-
-LOCAL_C_INCLUDES := \
- frameworks/av/media/libstagefright \
- frameworks/av/media/libstagefright/include \
- frameworks/native/include/media/openmax \
- external/jpeg
-
-LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CFLAGS += -Wno-multichar
-#LOCAL_CFLAGS += -UNDEBUG
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE:= screenrecord
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 34a9a40..bf36be0 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -46,6 +46,7 @@
#include <media/stagefright/JPEGSource.h>
#include <media/stagefright/InterfaceUtils.h>
#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
@@ -628,7 +629,7 @@
fprintf(stderr, " -l(ist) components\n");
fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n");
fprintf(stderr, " -b bug to reproduce\n");
- fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n");
+ fprintf(stderr, " -i(nfo) dump codec info (profiles and color formats supported, details)\n");
fprintf(stderr, " -t(humbnail) extract video thumbnail or album art\n");
fprintf(stderr, " -s(oftware) prefer software codec\n");
fprintf(stderr, " -r(hardware) force to use hardware codec\n");
@@ -646,55 +647,131 @@
fprintf(stderr, " -v be more verbose\n");
}
-static void dumpCodecProfiles(bool queryDecoders) {
- const char *kMimeTypes[] = {
- MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4,
- MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC,
- MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB,
- MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW,
- MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS,
- MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9,
- MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_VIDEO_HEVC,
- MEDIA_MIMETYPE_AUDIO_EAC3, MEDIA_MIMETYPE_AUDIO_AC4,
- MEDIA_MIMETYPE_VIDEO_AV1
- };
-
- const char *codecType = queryDecoders? "decoder" : "encoder";
- printf("%s profiles:\n", codecType);
+static void dumpCodecDetails(bool queryDecoders) {
+ const char *codecType = queryDecoders? "Decoder" : "Encoder";
+ printf("\n%s infos by media types:\n"
+ "=============================\n", codecType);
sp<IMediaCodecList> list = MediaCodecList::getInstance();
size_t numCodecs = list->countCodecs();
- for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++k) {
- printf("type '%s':\n", kMimeTypes[k]);
+ // gather all media types supported by codec class, and link to codecs that support them
+ KeyedVector<AString, Vector<sp<MediaCodecInfo>>> allMediaTypes;
+ for (size_t codec_ix = 0; codec_ix < numCodecs; ++codec_ix) {
+ sp<MediaCodecInfo> info = list->getCodecInfo(codec_ix);
+ if (info->isEncoder() == !queryDecoders) {
+ Vector<AString> supportedMediaTypes;
+ info->getSupportedMediaTypes(&supportedMediaTypes);
+ if (!supportedMediaTypes.size()) {
+ printf("warning: %s does not support any media types\n",
+ info->getCodecName());
+ } else {
+ for (const AString &mediaType : supportedMediaTypes) {
+ if (allMediaTypes.indexOfKey(mediaType) < 0) {
+ allMediaTypes.add(mediaType, Vector<sp<MediaCodecInfo>>());
+ }
+ allMediaTypes.editValueFor(mediaType).add(info);
+ }
+ }
+ }
+ }
- for (size_t index = 0; index < numCodecs; ++index) {
- sp<MediaCodecInfo> info = list->getCodecInfo(index);
- if (info == NULL || info->isEncoder() != !queryDecoders) {
- continue;
- }
- sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(kMimeTypes[k]);
+ KeyedVector<AString, bool> visitedCodecs;
+ for (size_t type_ix = 0; type_ix < allMediaTypes.size(); ++type_ix) {
+ const AString &mediaType = allMediaTypes.keyAt(type_ix);
+ printf("\nMedia type '%s':\n", mediaType.c_str());
+
+ for (const sp<MediaCodecInfo> &info : allMediaTypes.valueAt(type_ix)) {
+ sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(mediaType.c_str());
if (caps == NULL) {
+ printf("warning: %s does not have capabilities for type %s\n",
+ info->getCodecName(), mediaType.c_str());
continue;
}
- printf(" %s '%s' supports ",
+ printf(" %s \"%s\" supports\n",
codecType, info->getCodecName());
- Vector<MediaCodecInfo::ProfileLevel> profileLevels;
- caps->getSupportedProfileLevels(&profileLevels);
- if (profileLevels.size() == 0) {
- printf("NOTHING.\n");
- continue;
+ auto printList = [](const char *type, const Vector<AString> &values){
+ printf(" %s: [", type);
+ for (size_t j = 0; j < values.size(); ++j) {
+ printf("\n %s%s", values[j].c_str(),
+ j == values.size() - 1 ? " " : ",");
+ }
+ printf("]\n");
+ };
+
+ if (visitedCodecs.indexOfKey(info->getCodecName()) < 0) {
+ visitedCodecs.add(info->getCodecName(), true);
+ {
+ Vector<AString> aliases;
+ info->getAliases(&aliases);
+ // quote alias
+ for (AString &alias : aliases) {
+ alias.insert("\"", 1, 0);
+ alias.append('"');
+ }
+ printList("aliases", aliases);
+ }
+ {
+ uint32_t attrs = info->getAttributes();
+ Vector<AString> list;
+ list.add(AStringPrintf("encoder: %d", !!(attrs & MediaCodecInfo::kFlagIsEncoder)));
+ list.add(AStringPrintf("vendor: %d", !!(attrs & MediaCodecInfo::kFlagIsVendor)));
+ list.add(AStringPrintf("software-only: %d", !!(attrs & MediaCodecInfo::kFlagIsSoftwareOnly)));
+ list.add(AStringPrintf("hw-accelerated: %d", !!(attrs & MediaCodecInfo::kFlagIsHardwareAccelerated)));
+ printList(AStringPrintf("attributes: %#x", attrs).c_str(), list);
+ }
+
+ printf(" owner: \"%s\"\n", info->getOwnerName());
+ printf(" rank: %u\n", info->getRank());
+ } else {
+ printf(" aliases, attributes, owner, rank: see above\n");
}
- for (size_t j = 0; j < profileLevels.size(); ++j) {
- const MediaCodecInfo::ProfileLevel &profileLevel = profileLevels[j];
+ {
+ Vector<AString> list;
+ Vector<MediaCodecInfo::ProfileLevel> profileLevels;
+ caps->getSupportedProfileLevels(&profileLevels);
+ for (const MediaCodecInfo::ProfileLevel &pl : profileLevels) {
+ const char *niceProfile =
+ mediaType.equalsIgnoreCase(MIMETYPE_AUDIO_AAC) ? asString_AACObject(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2) ? asString_MPEG2Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263) ? asString_H263Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4) ? asString_MPEG4Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC) ? asString_AVCProfile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8) ? asString_VP8Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC) ? asString_HEVCProfile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9) ? asString_VP9Profile(pl.mProfile) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1) ? asString_AV1Profile(pl.mProfile) :"??";
+ const char *niceLevel =
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2) ? asString_MPEG2Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263) ? asString_H263Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4) ? asString_MPEG4Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC) ? asString_AVCLevel(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8) ? asString_VP8Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC) ? asString_HEVCTierLevel(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9) ? asString_VP9Level(pl.mLevel) :
+ mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1) ? asString_AV1Level(pl.mLevel) :
+ "??";
- printf("%s%u/%u", j > 0 ? ", " : "",
- profileLevel.mProfile, profileLevel.mLevel);
+ list.add(AStringPrintf("% 5u/% 5u (%s/%s)",
+ pl.mProfile, pl.mLevel, niceProfile, niceLevel));
+ }
+ printList("profile/levels", list);
}
- printf("\n");
+ {
+ Vector<AString> list;
+ Vector<uint32_t> colors;
+ caps->getSupportedColorFormats(&colors);
+ for (uint32_t color : colors) {
+ list.add(AStringPrintf("%#x (%s)", color,
+ asString_ColorFormat((int32_t)color)));
+ }
+ printList("colors", list);
+ }
+
+ printf(" details: %s\n", caps->getDetails()->debugString(6).c_str());
}
}
}
@@ -704,7 +781,7 @@
bool audioOnly = false;
bool listComponents = false;
- bool dumpProfiles = false;
+ bool dumpCodecInfo = false;
bool extractThumbnail = false;
bool seekTest = false;
bool useSurfaceAlloc = false;
@@ -724,7 +801,7 @@
sp<android::ALooper> looper;
int res;
- while ((res = getopt(argc, argv, "vhaqn:lm:b:ptsrow:kN:xSTd:D:")) >= 0) {
+ while ((res = getopt(argc, argv, "vhaqn:lm:b:itsrow:kN:xSTd:D:")) >= 0) {
switch (res) {
case 'a':
{
@@ -794,9 +871,9 @@
break;
}
- case 'p':
+ case 'i':
{
- dumpProfiles = true;
+ dumpCodecInfo = true;
break;
}
@@ -937,9 +1014,9 @@
return 0;
}
- if (dumpProfiles) {
- dumpCodecProfiles(true /* queryDecoders */);
- dumpCodecProfiles(false /* queryDecoders */);
+ if (dumpCodecInfo) {
+ dumpCodecDetails(true /* queryDecoders */);
+ dumpCodecDetails(false /* queryDecoders */);
}
if (listComponents) {
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 84713c1..5888af0 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -168,7 +168,7 @@
case OfflineLicenseState::USABLE:
return DrmPlugin::kOfflineLicenseStateUsable;
case OfflineLicenseState::INACTIVE:
- return DrmPlugin::kOfflineLicenseStateInactive;
+ return DrmPlugin::kOfflineLicenseStateReleased;
default:
return DrmPlugin::kOfflineLicenseStateUnknown;
}
@@ -1556,22 +1556,22 @@
void DrmHal::reportFrameworkMetrics() const
{
- MediaAnalyticsItem item("mediadrm");
- item.generateSessionID();
- item.setPkgName(mMetrics.GetAppPackageName().c_str());
+ std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("mediadrm"));
+ item->generateSessionID();
+ item->setPkgName(mMetrics.GetAppPackageName().c_str());
String8 vendor;
String8 description;
status_t result = getPropertyStringInternal(String8("vendor"), vendor);
if (result != OK) {
ALOGE("Failed to get vendor from drm plugin: %d", result);
} else {
- item.setCString("vendor", vendor.c_str());
+ item->setCString("vendor", vendor.c_str());
}
result = getPropertyStringInternal(String8("description"), description);
if (result != OK) {
ALOGE("Failed to get description from drm plugin: %d", result);
} else {
- item.setCString("description", description.c_str());
+ item->setCString("description", description.c_str());
}
std::string serializedMetrics;
@@ -1582,9 +1582,9 @@
std::string b64EncodedMetrics = toBase64StringNoPad(serializedMetrics.data(),
serializedMetrics.size());
if (!b64EncodedMetrics.empty()) {
- item.setCString("serialized_metrics", b64EncodedMetrics.c_str());
+ item->setCString("serialized_metrics", b64EncodedMetrics.c_str());
}
- if (!item.selfrecord()) {
+ if (!item->selfrecord()) {
ALOGE("Failed to self record framework metrics");
}
}
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 5cb48bf..8cd6f96 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -34,17 +34,17 @@
status_t reportVendorMetrics(const std::string& metrics,
const String8& name,
const String8& appPackageName) {
- MediaAnalyticsItem analyticsItem(name.c_str());
- analyticsItem.generateSessionID();
+ std::unique_ptr<MediaAnalyticsItem> analyticsItem(MediaAnalyticsItem::create(name.c_str()));
+ analyticsItem->generateSessionID();
std::string app_package_name(appPackageName.c_str(), appPackageName.size());
- analyticsItem.setPkgName(app_package_name);
+ analyticsItem->setPkgName(app_package_name);
if (metrics.size() > 0) {
- analyticsItem.setCString(kSerializedMetricsField, metrics.c_str());
+ analyticsItem->setCString(kSerializedMetricsField, metrics.c_str());
}
- if (!analyticsItem.selfrecord()) {
- ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem.getSessionID());
+ if (!analyticsItem->selfrecord()) {
+ ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem->getSessionID());
}
return OK;
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
index 1558e8b..27bd631 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
@@ -65,7 +65,20 @@
*plugin = new ClearKeyCasPlugin(appData, callback);
return OK;
}
-///////////////////////////////////////////////////////////////////////////////
+
+status_t ClearKeyCasFactory::createPlugin(
+ int32_t CA_system_id,
+ void *appData,
+ CasPluginCallbackExt callback,
+ CasPlugin **plugin) {
+ if (!isSystemIdSupported(CA_system_id)) {
+ return BAD_VALUE;
+ }
+
+ *plugin = new ClearKeyCasPlugin(appData, callback);
+ return OK;
+}
+////////////////////////////////////////////////////////////////////////////////
bool ClearKeyDescramblerFactory::isSystemIdSupported(
int32_t CA_system_id) const {
return CA_system_id == sClearKeySystemId;
@@ -88,6 +101,12 @@
ALOGV("CTOR");
}
+ClearKeyCasPlugin::ClearKeyCasPlugin(
+ void *appData, CasPluginCallbackExt callback)
+ : mCallbackExt(callback), mAppData(appData) {
+ ALOGV("CTOR");
+}
+
ClearKeyCasPlugin::~ClearKeyCasPlugin() {
ALOGV("DTOR");
ClearKeySessionLibrary::get()->destroyPlugin(this);
@@ -167,11 +186,30 @@
// Echo the received event to the callback.
// Clear key plugin doesn't use any event, echo'ing for testing only.
if (mCallback != NULL) {
- mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(), eventData.size());
+ mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(),
+ eventData.size());
+ } else if (mCallbackExt != NULL) {
+ mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(),
+ eventData.size(), NULL);
}
return OK;
}
+status_t ClearKeyCasPlugin::sendSessionEvent(
+ const CasSessionId &sessionId, int32_t event,
+ int arg, const CasData &eventData) {
+ ALOGV("sendSessionEvent: sessionId=%s, event=%d, arg=%d",
+ sessionIdToString(sessionId).string(), event, arg);
+ // Echo the received event to the callback.
+ // Clear key plugin doesn't use any event, echo'ing for testing only.
+ if (mCallbackExt != NULL) {
+ mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(),
+ eventData.size(), &sessionId);
+ }
+
+ return OK;
+}
+
status_t ClearKeyCasPlugin::provision(const String8 &str) {
ALOGV("provision: provisionString=%s", str.string());
Mutex::Autolock lock(mKeyFetcherLock);
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
index 389e172..f48d5b1 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
@@ -47,6 +47,11 @@
void *appData,
CasPluginCallback callback,
CasPlugin **plugin) override;
+ virtual status_t createPlugin(
+ int32_t CA_system_id,
+ void *appData,
+ CasPluginCallbackExt callback,
+ CasPlugin **plugin) override;
};
class ClearKeyDescramblerFactory : public DescramblerFactory {
@@ -63,6 +68,7 @@
class ClearKeyCasPlugin : public CasPlugin {
public:
ClearKeyCasPlugin(void *appData, CasPluginCallback callback);
+ ClearKeyCasPlugin(void *appData, CasPluginCallbackExt callback);
virtual ~ClearKeyCasPlugin();
virtual status_t setPrivateData(
@@ -85,6 +91,10 @@
virtual status_t sendEvent(
int32_t event, int32_t arg, const CasData &eventData) override;
+ virtual status_t sendSessionEvent(
+ const CasSessionId &sessionId,
+ int32_t event, int32_t arg, const CasData &eventData) override;
+
virtual status_t provision(const String8 &str) override;
virtual status_t refreshEntitlements(
@@ -94,6 +104,7 @@
Mutex mKeyFetcherLock;
std::unique_ptr<KeyFetcher> mKeyFetcher;
CasPluginCallback mCallback;
+ CasPluginCallbackExt mCallbackExt;
void* mAppData;
};
diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.cpp b/drm/mediacas/plugins/mock/MockCasPlugin.cpp
index 8404a83..2964791 100644
--- a/drm/mediacas/plugins/mock/MockCasPlugin.cpp
+++ b/drm/mediacas/plugins/mock/MockCasPlugin.cpp
@@ -60,6 +60,19 @@
return OK;
}
+status_t MockCasFactory::createPlugin(
+ int32_t CA_system_id,
+ void* /*appData*/,
+ CasPluginCallbackExt /*callback*/,
+ CasPlugin **plugin) {
+ if (!isSystemIdSupported(CA_system_id)) {
+ return BAD_VALUE;
+ }
+
+ *plugin = new MockCasPlugin();
+ return OK;
+}
+
///////////////////////////////////////////////////////////////////////////////
bool MockDescramblerFactory::isSystemIdSupported(int32_t CA_system_id) const {
@@ -170,6 +183,16 @@
return OK;
}
+status_t MockCasPlugin::sendSessionEvent(
+ const CasSessionId &sessionId, int32_t event,
+ int /*arg*/, const CasData& /*eventData*/) {
+ ALOGV("sendSessionEvent: sessionId=%s, event=%d",
+ arrayToString(sessionId).string(), event);
+ Mutex::Autolock lock(mLock);
+
+ return OK;
+}
+
status_t MockCasPlugin::provision(const String8 &str) {
ALOGV("provision: provisionString=%s", str.string());
Mutex::Autolock lock(mLock);
diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.h b/drm/mediacas/plugins/mock/MockCasPlugin.h
index 8106990..74b540c 100644
--- a/drm/mediacas/plugins/mock/MockCasPlugin.h
+++ b/drm/mediacas/plugins/mock/MockCasPlugin.h
@@ -42,6 +42,11 @@
void *appData,
CasPluginCallback callback,
CasPlugin **plugin) override;
+ virtual status_t createPlugin(
+ int32_t CA_system_id,
+ void *appData,
+ CasPluginCallbackExt callback,
+ CasPlugin **plugin) override;
};
class MockDescramblerFactory : public DescramblerFactory {
@@ -80,7 +85,11 @@
virtual status_t sendEvent(
int32_t event, int32_t arg, const CasData &eventData) override;
- virtual status_t provision(const String8 &str) override;
+ virtual status_t sendSessionEvent(
+ const CasSessionId &sessionId,
+ int32_t event, int32_t arg, const CasData &eventData) override;
+
+ virtual status_t provision(const String8 &str) override;
virtual status_t refreshEntitlements(
int32_t refreshType, const CasData &refreshData) override;
diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk
index f05c84b..10d8b13 100644
--- a/media/audioserver/Android.mk
+++ b/media/audioserver/Android.mk
@@ -19,7 +19,8 @@
libnbaio \
libnblog \
libsoundtriggerservice \
- libutils
+ libutils \
+ libvibrator
# TODO oboeservice is the old folder name for aaudioservice. It will be changed.
LOCAL_C_INCLUDES := \
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 23939b5..0357115 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -180,6 +180,7 @@
kParamIndexPictureTypeMask,
kParamIndexPictureType,
+ kParamIndexHdr10PlusMetadata,
/* ------------------------------------ video components ------------------------------------ */
@@ -194,7 +195,6 @@
kParamIndexLayerIndex,
kParamIndexLayerCount,
kParamIndexIntraRefresh,
- kParamIndexHdr10PlusMetadata,
/* ------------------------------------ image components ------------------------------------ */
diff --git a/media/codec2/hidl/1.0/vts/OWNERS b/media/codec2/hidl/1.0/vts/OWNERS
new file mode 100644
index 0000000..6733e0c
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/OWNERS
@@ -0,0 +1,9 @@
+# Media team
+lajos@google.com
+pawin@google.com
+taklee@google.com
+wonsik@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/media/codec2/hidl/1.0/vts/audio/Android.bp b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/audio/Android.bp
rename to media/codec2/hidl/1.0/vts/functional/audio/Android.bp
diff --git a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/audio/media_c2_audio_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/audio/media_c2_audio_hidl_test_common.h
similarity index 100%
rename from media/codec2/hidl/1.0/vts/audio/media_c2_audio_hidl_test_common.h
rename to media/codec2/hidl/1.0/vts/functional/audio/media_c2_audio_hidl_test_common.h
diff --git a/media/codec2/hidl/1.0/vts/common/Android.bp b/media/codec2/hidl/1.0/vts/functional/common/Android.bp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/common/Android.bp
rename to media/codec2/hidl/1.0/vts/functional/common/Android.bp
diff --git a/media/codec2/hidl/1.0/vts/common/README.md b/media/codec2/hidl/1.0/vts/functional/common/README.md
similarity index 100%
rename from media/codec2/hidl/1.0/vts/common/README.md
rename to media/codec2/hidl/1.0/vts/functional/common/README.md
diff --git a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.cpp
rename to media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
diff --git a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
similarity index 100%
rename from media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h
rename to media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
diff --git a/media/codec2/hidl/1.0/vts/component/Android.bp b/media/codec2/hidl/1.0/vts/functional/component/Android.bp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/component/Android.bp
rename to media/codec2/hidl/1.0/vts/functional/component/Android.bp
diff --git a/media/codec2/hidl/1.0/vts/component/VtsHidlC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/component/VtsHidlC2V1_0TargetComponentTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/master/Android.bp b/media/codec2/hidl/1.0/vts/functional/master/Android.bp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/master/Android.bp
rename to media/codec2/hidl/1.0/vts/functional/master/Android.bp
diff --git a/media/codec2/hidl/1.0/vts/master/VtsHidlC2V1_0TargetMasterTest.cpp b/media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/master/VtsHidlC2V1_0TargetMasterTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_352x288_420p_30fps_32frames.yuv b/media/codec2/hidl/1.0/vts/functional/res/bbb_352x288_420p_30fps_32frames.yuv
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_352x288_420p_30fps_32frames.yuv
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_352x288_420p_30fps_32frames.yuv
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz.aac b/media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz.aac
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz.aac
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz.aac
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz.amrwb b/media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz.amrwb
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz.amrwb
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz.amrwb
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.av1 b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144.av1
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.av1
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144.av1
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.av1 b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360.av1
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.av1
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360.av1
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_avc_176x144_300kbps_60fps.h264 b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps.h264
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_avc_176x144_300kbps_60fps.h264
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps.h264
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_avc_176x144_300kbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_avc_176x144_300kbps_60fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_avc_640x360_768kbps_30fps.h264 b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps.h264
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_avc_640x360_768kbps_30fps.h264
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps.h264
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_avc_640x360_768kbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_avc_640x360_768kbps_30fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_flac_stereo_680kbps_48000hz.flac b/media/codec2/hidl/1.0/vts/functional/res/bbb_flac_stereo_680kbps_48000hz.flac
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_flac_stereo_680kbps_48000hz.flac
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_flac_stereo_680kbps_48000hz.flac
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_flac_stereo_680kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_flac_stereo_680kbps_48000hz.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_flac_stereo_680kbps_48000hz.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_flac_stereo_680kbps_48000hz.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_g711alaw_1ch_8khz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_g711alaw_1ch_8khz.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_g711alaw_1ch_8khz.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_g711alaw_1ch_8khz.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_g711alaw_1ch_8khz.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_g711alaw_1ch_8khz.raw
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_g711alaw_1ch_8khz.raw
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_g711alaw_1ch_8khz.raw
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_g711mulaw_1ch_8khz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_g711mulaw_1ch_8khz.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_g711mulaw_1ch_8khz.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_g711mulaw_1ch_8khz.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_g711mulaw_1ch_8khz.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_g711mulaw_1ch_8khz.raw
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_g711mulaw_1ch_8khz.raw
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_g711mulaw_1ch_8khz.raw
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_gsm_1ch_8khz_13kbps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_gsm_1ch_8khz_13kbps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_gsm_1ch_8khz_13kbps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_gsm_1ch_8khz_13kbps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_gsm_1ch_8khz_13kbps.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_gsm_1ch_8khz_13kbps.raw
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_gsm_1ch_8khz_13kbps.raw
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_gsm_1ch_8khz_13kbps.raw
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_h263_352x288_300kbps_12fps.h263 b/media/codec2/hidl/1.0/vts/functional/res/bbb_h263_352x288_300kbps_12fps.h263
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_h263_352x288_300kbps_12fps.h263
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_h263_352x288_300kbps_12fps.h263
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_h263_352x288_300kbps_12fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_h263_352x288_300kbps_12fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_h263_352x288_300kbps_12fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_h263_352x288_300kbps_12fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_hevc_176x144_176kbps_60fps.hevc b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps.hevc
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_hevc_176x144_176kbps_60fps.hevc
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps.hevc
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_hevc_176x144_176kbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_hevc_176x144_176kbps_60fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_hevc_640x360_1600kbps_30fps.hevc b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps.hevc
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_hevc_640x360_1600kbps_30fps.hevc
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps.hevc
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_hevc_640x360_1600kbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_hevc_640x360_1600kbps_30fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz.mp3 b/media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz.mp3
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz.mp3
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz.mp3
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg2_176x144_105kbps_25fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_176x144_105kbps_25fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg2_176x144_105kbps_25fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_176x144_105kbps_25fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg2_176x144_105kbps_25fps.m2v b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_176x144_105kbps_25fps.m2v
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg2_176x144_105kbps_25fps.m2v
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_176x144_105kbps_25fps.m2v
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg2_352x288_1mbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_352x288_1mbps_60fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg2_352x288_1mbps_60fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_352x288_1mbps_60fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg2_352x288_1mbps_60fps.m2v b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_352x288_1mbps_60fps.m2v
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg2_352x288_1mbps_60fps.m2v
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_352x288_1mbps_60fps.m2v
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg4_352x288_512kbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg4_352x288_512kbps_30fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg4_352x288_512kbps_30fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg4_352x288_512kbps_30fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg4_352x288_512kbps_30fps.m4v b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg4_352x288_512kbps_30fps.m4v
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg4_352x288_512kbps_30fps.m4v
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg4_352x288_512kbps_30fps.m4v
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_opus_stereo_128kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_opus_stereo_128kbps_48000hz.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_opus_stereo_128kbps_48000hz.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_opus_stereo_128kbps_48000hz.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_opus_stereo_128kbps_48000hz.opus b/media/codec2/hidl/1.0/vts/functional/res/bbb_opus_stereo_128kbps_48000hz.opus
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_opus_stereo_128kbps_48000hz.opus
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_opus_stereo_128kbps_48000hz.opus
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_16khz_s16le.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_16khz_s16le.raw
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_16khz_s16le.raw
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_16khz_s16le.raw
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s16le.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s16le.raw
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s16le.raw
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s16le.raw
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s32le.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s32le.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s32le.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le.raw
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s32le.raw
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le.raw
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_2ch_48khz_s16le.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_2ch_48khz_s16le.raw
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_raw_2ch_48khz_s16le.raw
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_2ch_48khz_s16le.raw
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vorbis_stereo_128kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vorbis_stereo_128kbps_48000hz.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vorbis_stereo_128kbps_48000hz.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vorbis_stereo_128kbps_48000hz.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vorbis_stereo_128kbps_48000hz.vorbis b/media/codec2/hidl/1.0/vts/functional/res/bbb_vorbis_stereo_128kbps_48000hz.vorbis
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vorbis_stereo_128kbps_48000hz.vorbis
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vorbis_stereo_128kbps_48000hz.vorbis
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp8_176x144_240kbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_176x144_240kbps_60fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vp8_176x144_240kbps_60fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_176x144_240kbps_60fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp8_176x144_240kbps_60fps.vp8 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_176x144_240kbps_60fps.vp8
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vp8_176x144_240kbps_60fps.vp8
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_176x144_240kbps_60fps.vp8
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp8_640x360_2mbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vp8_640x360_2mbps_30fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp8_640x360_2mbps_30fps.vp8 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps.vp8
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vp8_640x360_2mbps_30fps.vp8
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps.vp8
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp9_176x144_285kbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_176x144_285kbps_60fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vp9_176x144_285kbps_60fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_176x144_285kbps_60fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp9_176x144_285kbps_60fps.vp9 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_176x144_285kbps_60fps.vp9
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vp9_176x144_285kbps_60fps.vp9
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_176x144_285kbps_60fps.vp9
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp9_640x360_1600kbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vp9_640x360_1600kbps_30fps.info
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps.info
diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp9_640x360_1600kbps_30fps.vp9 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps.vp9
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/bbb_vp9_640x360_1600kbps_30fps.vp9
rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps.vp9
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz.amrnb b/media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz.amrnb
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz.amrnb
rename to media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz.amrnb
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz.info b/media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz.info
rename to media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz.info
diff --git a/media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info
similarity index 100%
rename from media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info
rename to media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info
diff --git a/media/codec2/hidl/1.0/vts/video/Android.bp b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/video/Android.bp
rename to media/codec2/hidl/1.0/vts/functional/video/Android.bp
diff --git a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/video/media_c2_video_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
similarity index 100%
rename from media/codec2/hidl/1.0/vts/video/media_c2_video_hidl_test_common.h
rename to media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 8b21bd5..9616e47 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1519,6 +1519,11 @@
mKey = C2Config::ORDINAL;
}
+void CCodecBufferChannel::ReorderStash::flush() {
+ mPending.clear();
+ mStash.clear();
+}
+
void CCodecBufferChannel::ReorderStash::setDepth(uint32_t depth) {
mPending.splice(mPending.end(), mStash);
mDepth = depth;
@@ -2526,6 +2531,7 @@
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
(*buffers)->flush(flushedWork);
}
+ mReorderStash.lock()->flush();
}
void CCodecBufferChannel::onWorkDone(
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index fd806b7..ebc1491 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -359,6 +359,7 @@
ReorderStash();
void clear();
+ void flush();
void setDepth(uint32_t depth);
void setKey(C2Config::ordinal_key_t key);
bool pop(Entry *entry);
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index f550089..4a65fc9 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -326,16 +326,13 @@
if (mAudioRecord.get() == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- // Get current position so we can detect when the track is recording.
- status_t err = mAudioRecord->getPosition(&mPositionWhenStarting);
- if (err != OK) {
- return AAudioConvert_androidToAAudioResult(err);
- }
- // Enable callback before starting AudioTrack to avoid shutting
+ // Enable callback before starting AudioRecord to avoid shutting
// down because of a race condition.
mCallbackEnabled.store(true);
- err = mAudioRecord->start();
+ mFramesWritten.reset32(); // service writes frames
+ mTimestampPosition.reset32();
+ status_t err = mAudioRecord->start(); // resets position to zero
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
} else {
@@ -349,12 +346,10 @@
return AAUDIO_ERROR_INVALID_STATE;
}
setState(AAUDIO_STREAM_STATE_STOPPING);
- incrementFramesWritten(getFramesRead() - getFramesWritten()); // TODO review
- mTimestampPosition.set(getFramesRead());
+ mFramesWritten.catchUpTo(getFramesRead());
+ mTimestampPosition.catchUpTo(getFramesRead());
mAudioRecord->stop();
mCallbackEnabled.store(false);
- mFramesWritten.reset32(); // service writes frames, service position reset on flush
- mTimestampPosition.reset32();
// Pass false to prevent errorCallback from being called after disconnect
// when app has already requested a stop().
return checkForDisconnectRequest(false);
@@ -368,10 +363,12 @@
switch (getState()) {
// TODO add better state visibility to AudioRecord
case AAUDIO_STREAM_STATE_STARTING:
+ // When starting, the position will begin at zero and then go positive.
+ // The position can wrap but by that time the state will not be STARTING.
err = mAudioRecord->getPosition(&position);
if (err != OK) {
result = AAudioConvert_androidToAAudioResult(err);
- } else if (position != mPositionWhenStarting) {
+ } else if (position > 0) {
setState(AAUDIO_STREAM_STATE_STARTED);
}
break;
@@ -504,12 +501,12 @@
switch (getState()) {
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STARTED:
- case AAUDIO_STREAM_STATE_STOPPING:
result = mAudioRecord->getPosition(&position);
if (result == OK) {
mFramesWritten.update32(position);
}
break;
+ case AAUDIO_STREAM_STATE_STOPPING:
default:
break;
}
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index c995e99..ff95aed 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -323,8 +323,8 @@
}
setState(AAUDIO_STREAM_STATE_STOPPING);
- incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
- mTimestampPosition.set(getFramesWritten());
+ mFramesRead.catchUpTo(getFramesWritten());
+ mTimestampPosition.catchUpTo(getFramesWritten());
mFramesRead.reset32(); // service reads frames, service position reset on stop
mTimestampPosition.reset32();
mAudioTrack->stop();
diff --git a/media/libaaudio/src/utility/MonotonicCounter.h b/media/libaaudio/src/utility/MonotonicCounter.h
index 5833eab..63add4e 100644
--- a/media/libaaudio/src/utility/MonotonicCounter.h
+++ b/media/libaaudio/src/utility/MonotonicCounter.h
@@ -41,10 +41,12 @@
}
/**
- * set the current value of the counter
+ * advance the current value to match the counter
*/
- void set(int64_t counter) {
- mCounter64 = counter;
+ void catchUpTo(int64_t counter) {
+ if ((counter - mCounter64) > 0) {
+ mCounter64 = counter;
+ }
}
/**
diff --git a/media/libaaudio/tests/test_clock_model.cpp b/media/libaaudio/tests/test_clock_model.cpp
index 3c09025..7f7abbd 100644
--- a/media/libaaudio/tests/test_clock_model.cpp
+++ b/media/libaaudio/tests/test_clock_model.cpp
@@ -43,13 +43,47 @@
}
void TearDown() {
-
}
~ClockModelTestFixture() {
// cleanup any pending stuff, but no exceptions allowed
}
+ // Test processing of timestamps when the hardware may be slightly off from
+ // the expected sample rate.
+ void checkDriftingClock(double hardwareFramesPerSecond, int numLoops) {
+ const int64_t startTimeNanos = 500000000; // arbitrary
+ model.start(startTimeNanos);
+
+ const int64_t startPositionFrames = HW_FRAMES_PER_BURST; // hardware
+ // arbitrary time for first burst
+ const int64_t markerTime = startTimeNanos + NANOS_PER_MILLISECOND
+ + (200 * NANOS_PER_MICROSECOND);
+
+ // Should set initial marker.
+ model.processTimestamp(startPositionFrames, markerTime);
+ ASSERT_EQ(startPositionFrames, model.convertTimeToPosition(markerTime));
+
+ double elapsedTimeSeconds = startTimeNanos / (double) NANOS_PER_SECOND;
+ for (int i = 0; i < numLoops; i++) {
+ // Calculate random delay over several bursts.
+ const double timeDelaySeconds = 10.0 * drand48() * NANOS_PER_BURST / NANOS_PER_SECOND;
+ elapsedTimeSeconds += timeDelaySeconds;
+ const int64_t elapsedTimeNanos = (int64_t)(elapsedTimeSeconds * NANOS_PER_SECOND);
+ const int64_t currentTimeNanos = startTimeNanos + elapsedTimeNanos;
+ // Simulate DSP running at the specified rate.
+ const int64_t currentTimeFrames = startPositionFrames +
+ (int64_t)(hardwareFramesPerSecond * elapsedTimeSeconds);
+ const int64_t numBursts = currentTimeFrames / HW_FRAMES_PER_BURST;
+ const int64_t alignedPosition = startPositionFrames + (numBursts * HW_FRAMES_PER_BURST);
+
+ // Apply drifting timestamp.
+ model.processTimestamp(alignedPosition, currentTimeNanos);
+
+ ASSERT_EQ(alignedPosition, model.convertTimeToPosition(currentTimeNanos));
+ }
+ }
+
IsochronousClockModel model;
};
@@ -95,7 +129,6 @@
}
// timestamps moves the window if outside the bounds
-// TODO test nudging the window
TEST_F(ClockModelTestFixture, clock_timestamp) {
const int64_t startTime = 100000000;
model.start(startTime);
@@ -113,3 +146,21 @@
// convertPositionToTime rounds up
EXPECT_EQ(markerTime + NANOS_PER_BURST, model.convertPositionToTime(position + 17));
}
+
+#define NUM_LOOPS_DRIFT 10000
+
+// test nudging the window by using a drifting HW clock
+TEST_F(ClockModelTestFixture, clock_no_drift) {
+ checkDriftingClock(SAMPLE_RATE, NUM_LOOPS_DRIFT);
+}
+
+// These slow drift rates caused errors when I disabled the code that handles
+// drifting in the clock model. So I think the test is valid.
+// It is unlikely that real hardware would be off by more than this amount.
+TEST_F(ClockModelTestFixture, clock_slow_drift) {
+ checkDriftingClock(0.998 * SAMPLE_RATE, NUM_LOOPS_DRIFT);
+}
+
+TEST_F(ClockModelTestFixture, clock_fast_drift) {
+ checkDriftingClock(1.002 * SAMPLE_RATE, NUM_LOOPS_DRIFT);
+}
\ No newline at end of file
diff --git a/media/libaaudio/tests/test_return_stop.cpp b/media/libaaudio/tests/test_return_stop.cpp
index 9a9e00c..1252dd3 100644
--- a/media/libaaudio/tests/test_return_stop.cpp
+++ b/media/libaaudio/tests/test_return_stop.cpp
@@ -140,7 +140,7 @@
printf("%s() - error = %d\n", __func__, error);
}
-void usage() {
+static void s_usage() {
printf("test_return_stop [-i] [-x] [-n] [-c]\n");
printf(" -i direction INPUT, otherwise OUTPUT\n");
printf(" -x sharing mode EXCLUSIVE, otherwise SHARED\n");
@@ -148,6 +148,28 @@
printf(" -c always return CONTINUE from callback, not STOP\n");
}
+/**
+ * @return 0 is OK, -1 for error
+ */
+static int s_checkEnginePositions(AudioEngine *engine) {
+ const int64_t framesRead = AAudioStream_getFramesRead(engine->stream);
+ const int64_t framesWritten = AAudioStream_getFramesWritten(engine->stream);
+ const int32_t delta = (int32_t)(framesWritten - framesRead);
+ printf("playing framesRead = %7d, framesWritten = %7d"
+ ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
+ (int32_t) framesRead,
+ (int32_t) framesWritten,
+ delta,
+ engine->framesCalled.load(),
+ engine->callbackCount.load()
+ );
+ if (delta > AAudioStream_getBufferCapacityInFrames(engine->stream)) {
+ printf("ERROR - delta > capacity\n");
+ return -1;
+ }
+ return 0;
+}
+
int main(int argc, char **argv) {
(void) argc;
(void) argv;
@@ -188,12 +210,12 @@
sharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
break;
default:
- usage();
+ s_usage();
exit(EXIT_FAILURE);
break;
}
} else {
- usage();
+ s_usage();
exit(EXIT_FAILURE);
break;
}
@@ -201,12 +223,20 @@
result = s_OpenAudioStream(&engine, direction, sharingMode, perfMode);
if (result != AAUDIO_OK) {
- printf("s_OpenAudioStream returned %s",
+ printf("s_OpenAudioStream returned %s\n",
AAudio_convertResultToText(result));
errorCount++;
}
int32_t framesPerBurst = AAudioStream_getFramesPerBurst(engine.stream);
+ // Use double buffered stream.
+ const int32_t bufferSize = AAudioStream_setBufferSizeInFrames(engine.stream, 2 * framesPerBurst);
+ if (bufferSize < 0) {
+ printf("AAudioStream_setBufferSizeInFrames returned %s\n",
+ AAudio_convertResultToText(bufferSize));
+ errorCount++;
+ }
+
// Check to see what kind of stream we actually got.
int32_t deviceId = AAudioStream_getDeviceId(engine.stream);
aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream);
@@ -235,21 +265,14 @@
if (result == AAUDIO_OK) {
const int watchLoops = LOOP_DURATION_MSEC / SLEEP_DURATION_MSEC;
for (int i = watchLoops; i > 0; i--) {
- printf("playing silence #%02d, framesRead = %7d, framesWritten = %7d,"
- " framesCalled = %6d, callbackCount = %4d\n",
- i,
- (int32_t) AAudioStream_getFramesRead(engine.stream),
- (int32_t) AAudioStream_getFramesWritten(engine.stream),
- engine.framesCalled.load(),
- engine.callbackCount.load()
- );
+ errorCount += s_checkEnginePositions(&engine) ? 1 : 0;
usleep(SLEEP_DURATION_MSEC * 1000);
}
}
if (engine.stopAtFrame != INT32_MAX) {
callbackResult = (engine.callbackCountAfterStop == 0) ? EXIT_SUCCESS
- : EXIT_FAILURE;
+ : EXIT_FAILURE;
if (callbackResult) {
printf("ERROR - Callback count after STOP = %d\n",
engine.callbackCountAfterStop.load());
@@ -268,9 +291,7 @@
errorCount++;
}
usleep(SLEEP_DURATION_MSEC * 1000);
- printf("getFramesRead() = %d, getFramesWritten() = %d\n",
- (int32_t) AAudioStream_getFramesRead(engine.stream),
- (int32_t) AAudioStream_getFramesWritten(engine.stream));
+ errorCount += s_checkEnginePositions(&engine) ? 1 : 0;
}
s_CloseAudioStream(&engine);
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 4c762ed..896198b 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -1281,6 +1281,20 @@
return aps->getMasterMono(mono);
}
+status_t AudioSystem::setMasterBalance(float balance)
+{
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+ return af->setMasterBalance(balance);
+}
+
+status_t AudioSystem::getMasterBalance(float *balance)
+{
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+ return af->getMasterBalance(balance);
+}
+
float AudioSystem::getStreamVolumeDB(audio_stream_type_t stream, int index, audio_devices_t device)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index e9a0e22..c2ee2ee 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -293,6 +293,11 @@
mPreviousSchedulingGroup(SP_DEFAULT),
mPausedPosition(0)
{
+ mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
+ mAttributes.usage = AUDIO_USAGE_UNKNOWN;
+ mAttributes.flags = 0x0;
+ strcpy(mAttributes.tags, "");
+
(void)set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType,
@@ -324,6 +329,11 @@
mPausedPosition(0),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
+ mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
+ mAttributes.usage = AUDIO_USAGE_UNKNOWN;
+ mAttributes.flags = 0x0;
+ strcpy(mAttributes.tags, "");
+
(void)set(streamType, sampleRate, format, channelMask,
0 /*frameCount*/, flags, cbf, user, notificationFrames,
sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo,
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 00678c2..825cd4e 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -87,6 +87,8 @@
SYSTEM_READY,
FRAME_COUNT_HAL,
GET_MICROPHONES,
+ SET_MASTER_BALANCE,
+ GET_MASTER_BALANCE,
};
#define MAX_ITEMS_PER_LIST 1024
@@ -242,6 +244,34 @@
return reply.readInt32();
}
+ status_t setMasterBalance(float balance) override
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeFloat(balance);
+ status_t status = remote()->transact(SET_MASTER_BALANCE, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ return reply.readInt32();
+ }
+
+ status_t getMasterBalance(float *balance) const override
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ status_t status = remote()->transact(GET_MASTER_BALANCE, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = (status_t)reply.readInt32();
+ if (status != NO_ERROR) {
+ return status;
+ }
+ *balance = reply.readFloat();
+ return NO_ERROR;
+ }
+
virtual status_t setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
@@ -1050,6 +1080,21 @@
reply->writeInt32( masterMute() );
return NO_ERROR;
} break;
+ case SET_MASTER_BALANCE: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ reply->writeInt32( setMasterBalance(data.readFloat()) );
+ return NO_ERROR;
+ } break;
+ case GET_MASTER_BALANCE: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ float f;
+ const status_t status = getMasterBalance(&f);
+ reply->writeInt32((int32_t)status);
+ if (status == NO_ERROR) {
+ (void)reply->writeFloat(f);
+ }
+ return NO_ERROR;
+ } break;
case SET_STREAM_VOLUME: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
int stream = data.readInt32();
diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h
index 3ae7104..fbbbd11 100644
--- a/media/libaudioclient/include/media/AudioMixer.h
+++ b/media/libaudioclient/include/media/AudioMixer.h
@@ -80,6 +80,7 @@
MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output
// for haptic
HAPTIC_ENABLED = 0x4007, // Set haptic data from this track should be played or not.
+ HAPTIC_INTENSITY = 0x4008, // Set the intensity to play haptic data.
// for target RESAMPLE
SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name;
// parameter 'value' is the new sample rate in Hz.
@@ -102,6 +103,31 @@
// parameter 'value' is a pointer to the new playback rate.
};
+ enum { // Haptic intensity, should keep consistent with VibratorService
+ HAPTIC_SCALE_VERY_LOW = -2,
+ HAPTIC_SCALE_LOW = -1,
+ HAPTIC_SCALE_NONE = 0,
+ HAPTIC_SCALE_HIGH = 1,
+ HAPTIC_SCALE_VERY_HIGH = 2,
+ };
+ typedef int32_t haptic_intensity_t;
+ static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2 / 3;
+ static constexpr float HAPTIC_SCALE_LOW_RATIO = 3 / 4;
+ static const CONSTEXPR float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
+
+ static inline bool isValidHapticIntensity(haptic_intensity_t hapticIntensity) {
+ switch (hapticIntensity) {
+ case HAPTIC_SCALE_VERY_LOW:
+ case HAPTIC_SCALE_LOW:
+ case HAPTIC_SCALE_NONE:
+ case HAPTIC_SCALE_HIGH:
+ case HAPTIC_SCALE_VERY_HIGH:
+ return true;
+ default:
+ return false;
+ }
+ }
+
AudioMixer(size_t frameCount, uint32_t sampleRate)
: mSampleRate(sampleRate)
, mFrameCount(frameCount) {
@@ -147,6 +173,7 @@
}
}
(this->*mHook)();
+ processHapticData();
}
size_t getUnreleasedFrames(int name) const;
@@ -364,6 +391,7 @@
// Haptic
bool mHapticPlaybackEnabled;
+ haptic_intensity_t mHapticIntensity;
audio_channel_mask_t mHapticChannelMask;
uint32_t mHapticChannelCount;
audio_channel_mask_t mMixerHapticChannelMask;
@@ -374,6 +402,37 @@
uint32_t mAdjustNonDestructiveOutChannelCount;
bool mKeepContractedChannels;
+ float getHapticScaleGamma() const {
+ // Need to keep consistent with the value in VibratorService.
+ switch (mHapticIntensity) {
+ case HAPTIC_SCALE_VERY_LOW:
+ return 2.0f;
+ case HAPTIC_SCALE_LOW:
+ return 1.5f;
+ case HAPTIC_SCALE_HIGH:
+ return 0.5f;
+ case HAPTIC_SCALE_VERY_HIGH:
+ return 0.25f;
+ default:
+ return 1.0f;
+ }
+ }
+
+ float getHapticMaxAmplitudeRatio() const {
+ // Need to keep consistent with the value in VibratorService.
+ switch (mHapticIntensity) {
+ case HAPTIC_SCALE_VERY_LOW:
+ return HAPTIC_SCALE_VERY_LOW_RATIO;
+ case HAPTIC_SCALE_LOW:
+ return HAPTIC_SCALE_LOW_RATIO;
+ case HAPTIC_SCALE_NONE:
+ case HAPTIC_SCALE_HIGH:
+ case HAPTIC_SCALE_VERY_HIGH:
+ default:
+ return 1.0f;
+ }
+ }
+
private:
// hooks
void track__genericResample(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
@@ -410,6 +469,8 @@
template <int MIXTYPE, typename TO, typename TI, typename TA>
void process__noResampleOneTrack();
+ void processHapticData();
+
static process_hook_t getProcessHook(int processType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index ebee124..1f71844 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -719,7 +719,7 @@
private:
class MediaMetrics {
public:
- MediaMetrics() : mAnalyticsItem(new MediaAnalyticsItem("audiorecord")),
+ MediaMetrics() : mAnalyticsItem(MediaAnalyticsItem::create("audiorecord")),
mCreatedNs(systemTime(SYSTEM_TIME_REALTIME)),
mStartedNs(0), mDurationNs(0), mCount(0),
mLastError(NO_ERROR) {
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index a208602..1fb7add 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -339,6 +339,9 @@
static status_t setMasterMono(bool mono);
static status_t getMasterMono(bool *mono);
+ static status_t setMasterBalance(float balance);
+ static status_t getMasterBalance(float *balance);
+
static float getStreamVolumeDB(
audio_stream_type_t stream, int index, audio_devices_t device);
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 7fdf7cc..cbb750f 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1227,7 +1227,7 @@
private:
class MediaMetrics {
public:
- MediaMetrics() : mAnalyticsItem(new MediaAnalyticsItem("audiotrack")) {
+ MediaMetrics() : mAnalyticsItem(MediaAnalyticsItem::create("audiotrack")) {
}
~MediaMetrics() {
// mAnalyticsItem alloc failure will be flagged in the constructor
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index a34b207..ef0ed0c 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -359,6 +359,9 @@
virtual float masterVolume() const = 0;
virtual bool masterMute() const = 0;
+ virtual status_t setMasterBalance(float balance) = 0;
+ virtual status_t getMasterBalance(float *balance) const = 0;
+
/* set/get stream type state. This will probably be used by
* the preference panel, mostly.
*/
diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp
index 2e35be6..e396cf3 100644
--- a/media/libaudiohal/impl/StreamHalHidl.cpp
+++ b/media/libaudiohal/impl/StreamHalHidl.cpp
@@ -106,7 +106,7 @@
status_t status = parametersFromHal(kvPairs, &hidlParams);
if (status != OK) return status;
return processReturn("setParameters",
- utils::setParameters(mStream, hidlParams, {} /* options */));
+ utils::setParameters(mStream, {} /* context */, hidlParams));
}
status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) {
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index 86711de..86777d6 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -167,6 +167,7 @@
t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// haptic
t->mHapticPlaybackEnabled = false;
+ t->mHapticIntensity = HAPTIC_SCALE_NONE;
t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
t->mMixerHapticChannelCount = 0;
t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
@@ -717,6 +718,12 @@
track->prepareForAdjustChannels();
}
} break;
+ case HAPTIC_INTENSITY: {
+ const haptic_intensity_t hapticIntensity = static_cast<haptic_intensity_t>(valueInt);
+ if (track->mHapticIntensity != hapticIntensity) {
+ track->mHapticIntensity = hapticIntensity;
+ }
+ } break;
default:
LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
}
@@ -1846,6 +1853,40 @@
}
}
+void AudioMixer::processHapticData()
+{
+ // Need to keep consistent with VibrationEffect.scale(int, float, int)
+ for (const auto &pair : mGroups) {
+ // process by group of tracks with same output main buffer.
+ const auto &group = pair.second;
+ for (const int name : group) {
+ const std::shared_ptr<Track> &t = mTracks[name];
+ if (t->mHapticPlaybackEnabled) {
+ size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount;
+ float gamma = t->getHapticScaleGamma();
+ float maxAmplitudeRatio = t->getHapticMaxAmplitudeRatio();
+ uint8_t* buffer = (uint8_t*)pair.first + mFrameCount * audio_bytes_per_frame(
+ t->mMixerChannelCount, t->mMixerFormat);
+ switch (t->mMixerFormat) {
+ // Mixer format should be AUDIO_FORMAT_PCM_FLOAT.
+ case AUDIO_FORMAT_PCM_FLOAT: {
+ float* fout = (float*) buffer;
+ for (size_t i = 0; i < sampleCount; i++) {
+ float mul = fout[i] >= 0 ? 1.0 : -1.0;
+ fout[i] = powf(fabsf(fout[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
+ * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * mul;
+ }
+ } break;
+ default:
+ LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat);
+ break;
+ }
+ break;
+ }
+ }
+ }
+}
+
/* This track hook is called to do resampling then mixing,
* pulling from the track's upstream AudioBufferProvider.
*
diff --git a/media/libmedia/CharacterEncodingDetector.cpp b/media/libmedia/CharacterEncodingDetector.cpp
index 990d260..5c6b981 100644
--- a/media/libmedia/CharacterEncodingDetector.cpp
+++ b/media/libmedia/CharacterEncodingDetector.cpp
@@ -28,6 +28,8 @@
#include <unicode/ucsdet.h>
#include <unicode/ustring.h>
+#include <cutils/properties.h>
+
namespace android {
CharacterEncodingDetector::CharacterEncodingDetector() {
@@ -38,6 +40,26 @@
ALOGE("could not create UConverter for UTF-8");
mUtf8Conv = NULL;
}
+
+ // Read system locale setting from system property and map to ICU encoding names.
+ mLocaleEnc = NULL;
+ char locale_value[PROPERTY_VALUE_MAX] = "";
+ if (property_get("persist.sys.locale", locale_value, NULL) > 0) {
+ const size_t len = strnlen(locale_value, sizeof(locale_value));
+
+ if (len == 3 && !strncmp(locale_value, "und", 3)) {
+ // Undetermined
+ } else if (!strncmp(locale_value, "th", 2)) { // Thai
+ mLocaleEnc = "windows-874-2000";
+ }
+ if (mLocaleEnc != NULL) {
+ ALOGV("System locale encoding = %s", mLocaleEnc);
+ } else {
+ ALOGV("Didn't recognize system locale setting, defaulting to en_US");
+ }
+ } else {
+ ALOGV("Couldn't read system locale setting, assuming en_US");
+ }
}
CharacterEncodingDetector::~CharacterEncodingDetector() {
@@ -157,7 +179,11 @@
}
}
- if (bestCombinedMatch != NULL) {
+ if (mLocaleEnc != NULL && !goodmatch && highest < 50) {
+ combinedenc = mLocaleEnc;
+ ALOGV("confidence is low but we have recognized predefined encoding, "
+ "so try this (%s) instead", mLocaleEnc);
+ } else if (bestCombinedMatch != NULL) {
combinedenc = ucsdet_getName(bestCombinedMatch, &status);
} else {
combinedenc = "ISO-8859-1";
diff --git a/media/libmedia/include/media/CharacterEncodingDetector.h b/media/libmedia/include/media/CharacterEncodingDetector.h
index deaa377..62564b1 100644
--- a/media/libmedia/include/media/CharacterEncodingDetector.h
+++ b/media/libmedia/include/media/CharacterEncodingDetector.h
@@ -54,6 +54,7 @@
StringArray mValues;
UConverter* mUtf8Conv;
+ const char* mLocaleEnc;
};
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index 2f9e7c2..4a36f6a 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -82,16 +82,19 @@
PROTO_LAST = PROTO_V1,
};
+ private:
+ // use the ::create() method instead
+ MediaAnalyticsItem();
+ MediaAnalyticsItem(Key);
+ MediaAnalyticsItem(const MediaAnalyticsItem&);
+ MediaAnalyticsItem &operator=(const MediaAnalyticsItem&);
public:
- // so clients do not need to know size details
static MediaAnalyticsItem* create(Key key);
static MediaAnalyticsItem* create();
// access functions for the class
- MediaAnalyticsItem();
- MediaAnalyticsItem(Key);
~MediaAnalyticsItem();
// SessionID ties multiple submissions for same key together
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index f2a3038..37b13f0 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -202,7 +202,7 @@
}
mAnalyticsDirty = false;
if (reinitialize) {
- mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder);
+ mAnalyticsItem = MediaAnalyticsItem::create(kKeyRecorder);
}
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index a820445..1b396c0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -97,7 +97,7 @@
mMediaClock->init();
// set up an analytics record
- mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
+ mAnalyticsItem = MediaAnalyticsItem::create(kKeyPlayer);
mLooper->start(
false, /* runOnCallingThread */
@@ -635,7 +635,7 @@
// re-init in case we prepare() and start() again.
delete mAnalyticsItem ;
- mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
+ mAnalyticsItem = MediaAnalyticsItem::create("nuplayer");
if (mAnalyticsItem) {
mAnalyticsItem->setUid(mClientUid);
}
diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp
index f632e40..b050b83 100644
--- a/media/libnblog/ReportPerformance.cpp
+++ b/media/libnblog/ReportPerformance.cpp
@@ -168,7 +168,7 @@
return false;
}
- std::unique_ptr<MediaAnalyticsItem> item(new MediaAnalyticsItem("audiothread"));
+ std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("audiothread"));
const Histogram &workHist = data.workHist;
if (workHist.totalCount() > 0) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index c7da7c7..9c58e05 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -550,7 +550,7 @@
void MediaCodec::initAnalyticsItem() {
if (mAnalyticsItem == NULL) {
- mAnalyticsItem = new MediaAnalyticsItem(kCodecKeyName);
+ mAnalyticsItem = MediaAnalyticsItem::create(kCodecKeyName);
}
mLatencyHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor);
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index 9d2c42b..b0ce688 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -49,7 +49,7 @@
mAnalyticsItem = nullptr;
if (MEDIA_LOG) {
- mAnalyticsItem = new MediaAnalyticsItem(kKeyExtractor);
+ mAnalyticsItem = MediaAnalyticsItem::create(kKeyExtractor);
// track the container format (mpeg, aac, wvm, etc)
size_t ntracks = extractor->countTracks();
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 16179d3..4392799 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -706,6 +706,12 @@
++lineNo;
}
+ // playlist has no item, would cause exception
+ if (mItems.size() == 0) {
+ ALOGE("playlist has no item");
+ return ERROR_MALFORMED;
+ }
+
// error checking of all fields that's required to appear once
// (currently only checking "target-duration"), and
// initialization of playlist properties (eg. mTargetDurationUs)
diff --git a/media/mediaserver/Android.bp b/media/mediaserver/Android.bp
new file mode 100644
index 0000000..16c7be9
--- /dev/null
+++ b/media/mediaserver/Android.bp
@@ -0,0 +1,46 @@
+
+cc_library_static {
+ name: "libregistermsext",
+ srcs: ["register.cpp"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
+
+cc_binary {
+ name: "mediaserver",
+
+ srcs: ["main_mediaserver.cpp"],
+
+ shared_libs: [
+ "libresourcemanagerservice",
+ "liblog",
+ "libmediaplayerservice",
+ "libutils",
+ "libbinder",
+ "libandroidicu",
+ "android.hardware.media.omx@1.0",
+ ],
+
+ static_libs: [
+ "libicuandroid_utils",
+ "libregistermsext",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libmediaplayerservice",
+ "frameworks/av/services/mediaresourcemanager",
+ ],
+
+ compile_multilib: "32",
+
+ init_rc: ["mediaserver.rc"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+}
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
deleted file mode 100644
index 1fbb85e..0000000
--- a/media/mediaserver/Android.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-ifneq ($(BOARD_USE_CUSTOM_MEDIASERVEREXTENSIONS),true)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := register.cpp
-LOCAL_MODULE := libregistermsext
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Werror -Wall
-include $(BUILD_STATIC_LIBRARY)
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- main_mediaserver.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libresourcemanagerservice \
- liblog \
- libmediaplayerservice \
- libutils \
- libbinder \
- libandroidicu \
- android.hardware.media.omx@1.0 \
-
-LOCAL_STATIC_LIBRARIES := \
- libicuandroid_utils \
- libregistermsext
-
-LOCAL_C_INCLUDES := \
- frameworks/av/media/libmediaplayerservice \
- frameworks/av/services/mediaresourcemanager \
-
-LOCAL_MODULE:= mediaserver
-LOCAL_32_BIT_ONLY := true
-
-LOCAL_INIT_RC := mediaserver.rc
-
-LOCAL_CFLAGS := -Werror -Wall
-
-include $(BUILD_EXECUTABLE)
diff --git a/packages/MediaComponents/apex/Android.bp b/packages/MediaComponents/apex/Android.bp
deleted file mode 100644
index d89eb77..0000000
--- a/packages/MediaComponents/apex/Android.bp
+++ /dev/null
@@ -1,41 +0,0 @@
-filegroup {
- name: "media_aidl",
- srcs: [
- "java/android/media/**/*.aidl",
- "java/android/service/**/*.aidl",
- ],
- exclude_srcs: [
- // Exclude these aidls to avoid errors such as
- // "Refusing to generate code with unstructured parcelables."
- "java/android/media/MediaDescription.aidl",
- "java/android/media/MediaMetadata.aidl",
- // TODO(insun): check why MediaParceledListSlice.aidl should be added here
- "java/android/media/MediaParceledListSlice.aidl",
- "java/android/media/Rating.aidl",
- "java/android/media/browse/MediaBrowser.aidl",
- "java/android/media/session/MediaSession.aidl",
- "java/android/media/session/ParcelableVolumeInfo.aidl",
- "java/android/media/session/PlaybackState.aidl",
- ],
-}
-
-java_library {
- name: "media",
- installable: true,
- sdk_version: "system_current",
- srcs: [
- "java/android/media/**/*.java",
- "java/android/service/**/*.java",
- ":media_aidl",
- ":framework-media-annotation-srcs",
- ],
- aidl: {
- local_include_dirs: ["java"],
- include_dirs: [
- "frameworks/base/core/java",
- // for android.graphics.Bitmap
- // from IMediaBrowserServiceCallback
- "frameworks/base/graphics/java",
- ],
- },
-}
diff --git a/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl b/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl
deleted file mode 100644
index e4a4a42..0000000
--- a/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.media.session.ISessionController;
-
-/**
- * AIDL for the MediaSessionService to report interesting events on remote playback
- * to a volume control dialog. See also IVolumeController for the AudioService half.
- * TODO add in better support for multiple remote sessions.
- * @hide
- */
-oneway interface IRemoteVolumeController {
- void remoteVolumeChanged(ISessionController session, int flags);
- // sets the default session to use with the slider, replaces remoteSliderVisibility
- // on IVolumeController
- void updateRemoteController(ISessionController session);
-}
diff --git a/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl b/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl
deleted file mode 100644
index c83a19e..0000000
--- a/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 android.media;
-
-import android.os.Bundle;
-
-/**
- * Listens for changes to the list of session tokens.
- * @hide
- */
-oneway interface ISessionTokensListener {
- void onSessionTokensChanged(in List<Bundle> tokens);
-}
diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl b/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl
deleted file mode 100644
index 6f934f7..0000000
--- a/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media;
-
-parcelable MediaDescription;
diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.java b/packages/MediaComponents/apex/java/android/media/MediaDescription.java
deleted file mode 100644
index 31079e5..0000000
--- a/packages/MediaComponents/apex/java/android/media/MediaDescription.java
+++ /dev/null
@@ -1,383 +0,0 @@
-package android.media;
-
-import android.annotation.Nullable;
-import android.graphics.Bitmap;
-import android.media.browse.MediaBrowser;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-/**
- * A simple set of metadata for a media item suitable for display. This can be
- * created using the Builder or retrieved from existing metadata using
- * {@link MediaMetadata#getDescription()}.
- */
-public class MediaDescription implements Parcelable {
- /**
- * A unique persistent id for the content or null.
- */
- private final String mMediaId;
- /**
- * A primary title suitable for display or null.
- */
- private final CharSequence mTitle;
- /**
- * A subtitle suitable for display or null.
- */
- private final CharSequence mSubtitle;
- /**
- * A description suitable for display or null.
- */
- private final CharSequence mDescription;
- /**
- * A bitmap icon suitable for display or null.
- */
- private final Bitmap mIcon;
- /**
- * A Uri for an icon suitable for display or null.
- */
- private final Uri mIconUri;
- /**
- * Extras for opaque use by apps/system.
- */
- private final Bundle mExtras;
- /**
- * A Uri to identify this content.
- */
- private final Uri mMediaUri;
-
- /**
- * Used as a long extra field to indicate the bluetooth folder type of the media item as
- * specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5. This is valid only for
- * {@link MediaBrowser.MediaItem} with {@link MediaBrowser.MediaItem#FLAG_BROWSABLE}. The value
- * should be one of the following:
- * <ul>
- * <li>{@link #BT_FOLDER_TYPE_MIXED}</li>
- * <li>{@link #BT_FOLDER_TYPE_TITLES}</li>
- * <li>{@link #BT_FOLDER_TYPE_ALBUMS}</li>
- * <li>{@link #BT_FOLDER_TYPE_ARTISTS}</li>
- * <li>{@link #BT_FOLDER_TYPE_GENRES}</li>
- * <li>{@link #BT_FOLDER_TYPE_PLAYLISTS}</li>
- * <li>{@link #BT_FOLDER_TYPE_YEARS}</li>
- * </ul>
- *
- * @see #getExtras()
- */
- public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
-
- /**
- * The type of folder that is unknown or contains media elements of mixed types as specified in
- * the section 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_MIXED = 0;
-
- /**
- * The type of folder that contains media elements only as specified in the section 6.10.2.2 of
- * the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_TITLES = 1;
-
- /**
- * The type of folder that contains folders categorized by album as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_ALBUMS = 2;
-
- /**
- * The type of folder that contains folders categorized by artist as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_ARTISTS = 3;
-
- /**
- * The type of folder that contains folders categorized by genre as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_GENRES = 4;
-
- /**
- * The type of folder that contains folders categorized by playlist as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_PLAYLISTS = 5;
-
- /**
- * The type of folder that contains folders categorized by year as specified in the section
- * 6.10.2.2 of the Bluetooth AVRCP 1.5.
- */
- public static final long BT_FOLDER_TYPE_YEARS = 6;
-
- private MediaDescription(String mediaId, CharSequence title, CharSequence subtitle,
- CharSequence description, Bitmap icon, Uri iconUri, Bundle extras, Uri mediaUri) {
- mMediaId = mediaId;
- mTitle = title;
- mSubtitle = subtitle;
- mDescription = description;
- mIcon = icon;
- mIconUri = iconUri;
- mExtras = extras;
- mMediaUri = mediaUri;
- }
-
- private MediaDescription(Parcel in) {
- mMediaId = in.readString();
- mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mIcon = in.readParcelable(null);
- mIconUri = in.readParcelable(null);
- mExtras = in.readBundle();
- mMediaUri = in.readParcelable(null);
- }
-
- /**
- * Returns the media id or null. See
- * {@link MediaMetadata#METADATA_KEY_MEDIA_ID}.
- */
- public @Nullable String getMediaId() {
- return mMediaId;
- }
-
- /**
- * Returns a title suitable for display or null.
- *
- * @return A title or null.
- */
- public @Nullable CharSequence getTitle() {
- return mTitle;
- }
-
- /**
- * Returns a subtitle suitable for display or null.
- *
- * @return A subtitle or null.
- */
- public @Nullable CharSequence getSubtitle() {
- return mSubtitle;
- }
-
- /**
- * Returns a description suitable for display or null.
- *
- * @return A description or null.
- */
- public @Nullable CharSequence getDescription() {
- return mDescription;
- }
-
- /**
- * Returns a bitmap icon suitable for display or null.
- *
- * @return An icon or null.
- */
- public @Nullable Bitmap getIconBitmap() {
- return mIcon;
- }
-
- /**
- * Returns a Uri for an icon suitable for display or null.
- *
- * @return An icon uri or null.
- */
- public @Nullable Uri getIconUri() {
- return mIconUri;
- }
-
- /**
- * Returns any extras that were added to the description.
- *
- * @return A bundle of extras or null.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Returns a Uri representing this content or null.
- *
- * @return A media Uri or null.
- */
- public @Nullable Uri getMediaUri() {
- return mMediaUri;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mMediaId);
- TextUtils.writeToParcel(mTitle, dest, 0);
- TextUtils.writeToParcel(mSubtitle, dest, 0);
- TextUtils.writeToParcel(mDescription, dest, 0);
- dest.writeParcelable(mIcon, flags);
- dest.writeParcelable(mIconUri, flags);
- dest.writeBundle(mExtras);
- dest.writeParcelable(mMediaUri, flags);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) {
- return false;
- }
-
- if (!(o instanceof MediaDescription)){
- return false;
- }
-
- final MediaDescription d = (MediaDescription) o;
-
- if (!String.valueOf(mTitle).equals(String.valueOf(d.mTitle))) {
- return false;
- }
-
- if (!String.valueOf(mSubtitle).equals(String.valueOf(d.mSubtitle))) {
- return false;
- }
-
- if (!String.valueOf(mDescription).equals(String.valueOf(d.mDescription))) {
- return false;
- }
-
- return true;
- }
-
- @Override
- public String toString() {
- return mTitle + ", " + mSubtitle + ", " + mDescription;
- }
-
- public static final Parcelable.Creator<MediaDescription> CREATOR =
- new Parcelable.Creator<MediaDescription>() {
- @Override
- public MediaDescription createFromParcel(Parcel in) {
- return new MediaDescription(in);
- }
-
- @Override
- public MediaDescription[] newArray(int size) {
- return new MediaDescription[size];
- }
- };
-
- /**
- * Builder for {@link MediaDescription} objects.
- */
- public static class Builder {
- private String mMediaId;
- private CharSequence mTitle;
- private CharSequence mSubtitle;
- private CharSequence mDescription;
- private Bitmap mIcon;
- private Uri mIconUri;
- private Bundle mExtras;
- private Uri mMediaUri;
-
- /**
- * Creates an initially empty builder.
- */
- public Builder() {
- }
-
- /**
- * Sets the media id.
- *
- * @param mediaId The unique id for the item or null.
- * @return this
- */
- public Builder setMediaId(@Nullable String mediaId) {
- mMediaId = mediaId;
- return this;
- }
-
- /**
- * Sets the title.
- *
- * @param title A title suitable for display to the user or null.
- * @return this
- */
- public Builder setTitle(@Nullable CharSequence title) {
- mTitle = title;
- return this;
- }
-
- /**
- * Sets the subtitle.
- *
- * @param subtitle A subtitle suitable for display to the user or null.
- * @return this
- */
- public Builder setSubtitle(@Nullable CharSequence subtitle) {
- mSubtitle = subtitle;
- return this;
- }
-
- /**
- * Sets the description.
- *
- * @param description A description suitable for display to the user or
- * null.
- * @return this
- */
- public Builder setDescription(@Nullable CharSequence description) {
- mDescription = description;
- return this;
- }
-
- /**
- * Sets the icon.
- *
- * @param icon A {@link Bitmap} icon suitable for display to the user or
- * null.
- * @return this
- */
- public Builder setIconBitmap(@Nullable Bitmap icon) {
- mIcon = icon;
- return this;
- }
-
- /**
- * Sets the icon uri.
- *
- * @param iconUri A {@link Uri} for an icon suitable for display to the
- * user or null.
- * @return this
- */
- public Builder setIconUri(@Nullable Uri iconUri) {
- mIconUri = iconUri;
- return this;
- }
-
- /**
- * Sets a bundle of extras.
- *
- * @param extras The extras to include with this description or null.
- * @return this
- */
- public Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Sets the media uri.
- *
- * @param mediaUri The content's {@link Uri} for the item or null.
- * @return this
- */
- public Builder setMediaUri(@Nullable Uri mediaUri) {
- mMediaUri = mediaUri;
- return this;
- }
-
- public MediaDescription build() {
- return new MediaDescription(mMediaId, mTitle, mSubtitle, mDescription, mIcon, mIconUri,
- mExtras, mMediaUri);
- }
- }
-}
diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl b/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl
deleted file mode 100644
index 66ee483..0000000
--- a/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media;
-
-parcelable MediaMetadata;
diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java
deleted file mode 100644
index adfd20b..0000000
--- a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java
+++ /dev/null
@@ -1,941 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media;
-
-import android.annotation.NonNull;
-import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
-import android.content.ContentResolver;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.browse.MediaBrowser;
-import android.media.session.MediaController;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Set;
-import java.util.Objects;
-
-/**
- * Contains metadata about an item, such as the title, artist, etc.
- */
-public final class MediaMetadata implements Parcelable {
- private static final String TAG = "MediaMetadata";
-
- /**
- * @hide
- */
- @StringDef(prefix = { "METADATA_KEY_" }, value = {
- METADATA_KEY_TITLE,
- METADATA_KEY_ARTIST,
- METADATA_KEY_ALBUM,
- METADATA_KEY_AUTHOR,
- METADATA_KEY_WRITER,
- METADATA_KEY_COMPOSER,
- METADATA_KEY_COMPILATION,
- METADATA_KEY_DATE,
- METADATA_KEY_GENRE,
- METADATA_KEY_ALBUM_ARTIST,
- METADATA_KEY_ART_URI,
- METADATA_KEY_ALBUM_ART_URI,
- METADATA_KEY_DISPLAY_TITLE,
- METADATA_KEY_DISPLAY_SUBTITLE,
- METADATA_KEY_DISPLAY_DESCRIPTION,
- METADATA_KEY_DISPLAY_ICON_URI,
- METADATA_KEY_MEDIA_ID,
- METADATA_KEY_MEDIA_URI,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TextKey {}
-
- /**
- * @hide
- */
- @StringDef(prefix = { "METADATA_KEY_" }, value = {
- METADATA_KEY_DURATION,
- METADATA_KEY_YEAR,
- METADATA_KEY_TRACK_NUMBER,
- METADATA_KEY_NUM_TRACKS,
- METADATA_KEY_DISC_NUMBER,
- METADATA_KEY_BT_FOLDER_TYPE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface LongKey {}
-
- /**
- * @hide
- */
- @StringDef(prefix = { "METADATA_KEY_" }, value = {
- METADATA_KEY_ART,
- METADATA_KEY_ALBUM_ART,
- METADATA_KEY_DISPLAY_ICON,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface BitmapKey {}
-
- /**
- * @hide
- */
- @StringDef(prefix = { "METADATA_KEY_" }, value = {
- METADATA_KEY_USER_RATING,
- METADATA_KEY_RATING,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface RatingKey {}
-
- /**
- * The title of the media.
- */
- public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
-
- /**
- * The artist of the media.
- */
- public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
-
- /**
- * The duration of the media in ms. A negative duration indicates that the
- * duration is unknown (or infinite).
- */
- public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
-
- /**
- * The album title for the media.
- */
- public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
-
- /**
- * The author of the media.
- */
- public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
-
- /**
- * The writer of the media.
- */
- public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
-
- /**
- * The composer of the media.
- */
- public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
-
- /**
- * The compilation status of the media.
- */
- public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
-
- /**
- * The date the media was created or published. The format is unspecified
- * but RFC 3339 is recommended.
- */
- public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
-
- /**
- * The year the media was created or published as a long.
- */
- public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
-
- /**
- * The genre of the media.
- */
- public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
-
- /**
- * The track number for the media.
- */
- public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
-
- /**
- * The number of tracks in the media's original source.
- */
- public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
-
- /**
- * The disc number for the media's original source.
- */
- public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
-
- /**
- * The artist for the album of the media's original source.
- */
- public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
-
- /**
- * The artwork for the media as a {@link Bitmap}.
- * <p>
- * The artwork should be relatively small and may be scaled down by the
- * system if it is too large. For higher resolution artwork
- * {@link #METADATA_KEY_ART_URI} should be used instead.
- */
- public static final String METADATA_KEY_ART = "android.media.metadata.ART";
-
- /**
- * The artwork for the media as a Uri formatted String. The artwork can be
- * loaded using a combination of {@link ContentResolver#openInputStream} and
- * {@link BitmapFactory#decodeStream}.
- * <p>
- * For the best results, Uris should use the content:// style and support
- * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
- * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
- */
- public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
-
- /**
- * The artwork for the album of the media's original source as a
- * {@link Bitmap}.
- * <p>
- * The artwork should be relatively small and may be scaled down by the
- * system if it is too large. For higher resolution artwork
- * {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
- */
- public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
-
- /**
- * The artwork for the album of the media's original source as a Uri
- * formatted String. The artwork can be loaded using a combination of
- * {@link ContentResolver#openInputStream} and
- * {@link BitmapFactory#decodeStream}.
- * <p>
- * For the best results, Uris should use the content:// style and support
- * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
- * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
- */
- public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
-
- /**
- * The user's rating for the media.
- *
- * @see Rating
- */
- public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
-
- /**
- * The overall rating for the media.
- *
- * @see Rating
- */
- public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
-
- /**
- * A title that is suitable for display to the user. This will generally be
- * the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
- * When displaying media described by this metadata this should be preferred
- * if present.
- */
- public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
-
- /**
- * A subtitle that is suitable for display to the user. When displaying a
- * second line for media described by this metadata this should be preferred
- * to other fields if present.
- */
- public static final String METADATA_KEY_DISPLAY_SUBTITLE
- = "android.media.metadata.DISPLAY_SUBTITLE";
-
- /**
- * A description that is suitable for display to the user. When displaying
- * more information for media described by this metadata this should be
- * preferred to other fields if present.
- */
- public static final String METADATA_KEY_DISPLAY_DESCRIPTION
- = "android.media.metadata.DISPLAY_DESCRIPTION";
-
- /**
- * An icon or thumbnail that is suitable for display to the user. When
- * displaying an icon for media described by this metadata this should be
- * preferred to other fields if present. This must be a {@link Bitmap}.
- * <p>
- * The icon should be relatively small and may be scaled down by the system
- * if it is too large. For higher resolution artwork
- * {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
- */
- public static final String METADATA_KEY_DISPLAY_ICON
- = "android.media.metadata.DISPLAY_ICON";
-
- /**
- * A Uri formatted String for an icon or thumbnail that is suitable for
- * display to the user. When displaying more information for media described
- * by this metadata the display description should be preferred to other
- * fields when present. The icon can be loaded using a combination of
- * {@link ContentResolver#openInputStream} and
- * {@link BitmapFactory#decodeStream}.
- * <p>
- * For the best results, Uris should use the content:// style and support
- * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
- * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
- */
- public static final String METADATA_KEY_DISPLAY_ICON_URI
- = "android.media.metadata.DISPLAY_ICON_URI";
-
- /**
- * A String key for identifying the content. This value is specific to the
- * service providing the content. If used, this should be a persistent
- * unique key for the underlying content. It may be used with
- * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
- * to initiate playback when provided by a {@link MediaBrowser} connected to
- * the same app.
- */
- public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
-
- /**
- * A Uri formatted String representing the content. This value is specific to the
- * service providing the content. It may be used with
- * {@link MediaController.TransportControls#playFromUri(Uri, Bundle)}
- * to initiate playback when provided by a {@link MediaBrowser} connected to
- * the same app.
- */
- public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
-
- /**
- * The bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
- * AVRCP 1.5. It should be one of the following:
- * <ul>
- * <li>{@link MediaDescription#BT_FOLDER_TYPE_MIXED}</li>
- * <li>{@link MediaDescription#BT_FOLDER_TYPE_TITLES}</li>
- * <li>{@link MediaDescription#BT_FOLDER_TYPE_ALBUMS}</li>
- * <li>{@link MediaDescription#BT_FOLDER_TYPE_ARTISTS}</li>
- * <li>{@link MediaDescription#BT_FOLDER_TYPE_GENRES}</li>
- * <li>{@link MediaDescription#BT_FOLDER_TYPE_PLAYLISTS}</li>
- * <li>{@link MediaDescription#BT_FOLDER_TYPE_YEARS}</li>
- * </ul>
- */
- public static final String METADATA_KEY_BT_FOLDER_TYPE
- = "android.media.metadata.BT_FOLDER_TYPE";
-
- private static final @TextKey String[] PREFERRED_DESCRIPTION_ORDER = {
- METADATA_KEY_TITLE,
- METADATA_KEY_ARTIST,
- METADATA_KEY_ALBUM,
- METADATA_KEY_ALBUM_ARTIST,
- METADATA_KEY_WRITER,
- METADATA_KEY_AUTHOR,
- METADATA_KEY_COMPOSER
- };
-
- private static final @BitmapKey String[] PREFERRED_BITMAP_ORDER = {
- METADATA_KEY_DISPLAY_ICON,
- METADATA_KEY_ART,
- METADATA_KEY_ALBUM_ART
- };
-
- private static final @TextKey String[] PREFERRED_URI_ORDER = {
- METADATA_KEY_DISPLAY_ICON_URI,
- METADATA_KEY_ART_URI,
- METADATA_KEY_ALBUM_ART_URI
- };
-
- private static final int METADATA_TYPE_INVALID = -1;
- private static final int METADATA_TYPE_LONG = 0;
- private static final int METADATA_TYPE_TEXT = 1;
- private static final int METADATA_TYPE_BITMAP = 2;
- private static final int METADATA_TYPE_RATING = 3;
- private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
-
- static {
- METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
- METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
- METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
- METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
- METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
- METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
- METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
- METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
- METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
- METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
- METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
- METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG);
- METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
- METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
- }
-
- private static final SparseArray<String> EDITOR_KEY_MAPPING;
-
- static {
- EDITOR_KEY_MAPPING = new SparseArray<String>();
- EDITOR_KEY_MAPPING.put(MediaMetadataEditor.BITMAP_KEY_ARTWORK, METADATA_KEY_ART);
- EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_OTHERS, METADATA_KEY_RATING);
- EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_USER, METADATA_KEY_USER_RATING);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_KEY_ALBUM);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
- METADATA_KEY_ALBUM_ARTIST);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_KEY_ARTIST);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_KEY_AUTHOR);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
- METADATA_KEY_TRACK_NUMBER);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_KEY_COMPOSER);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPILATION,
- METADATA_KEY_COMPILATION);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_KEY_DATE);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
- METADATA_KEY_DISC_NUMBER);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_KEY_DURATION);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_KEY_GENRE);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS,
- METADATA_KEY_NUM_TRACKS);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_KEY_TITLE);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_KEY_WRITER);
- EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_KEY_YEAR);
- }
-
- private final Bundle mBundle;
- private MediaDescription mDescription;
-
- private MediaMetadata(Bundle bundle) {
- mBundle = new Bundle(bundle);
- }
-
- private MediaMetadata(Parcel in) {
- mBundle = in.readBundle();
- }
-
- /**
- * Returns true if the given key is contained in the metadata
- *
- * @param key a String key
- * @return true if the key exists in this metadata, false otherwise
- */
- public boolean containsKey(String key) {
- return mBundle.containsKey(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if no mapping of
- * the desired type exists for the given key or a null value is explicitly
- * associated with the key.
- *
- * @param key The key the value is stored under
- * @return a CharSequence value, or null
- */
- public CharSequence getText(@TextKey String key) {
- return mBundle.getCharSequence(key);
- }
-
- /**
- * Returns the text value associated with the given key as a String, or null
- * if no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key. This is equivalent to
- * calling {@link #getText getText().toString()} if the value is not null.
- *
- * @param key The key the value is stored under
- * @return a String value, or null
- */
- public String getString(@TextKey String key) {
- CharSequence text = getText(key);
- if (text != null) {
- return text.toString();
- }
- return null;
- }
-
- /**
- * Returns the value associated with the given key, or 0L if no long exists
- * for the given key.
- *
- * @param key The key the value is stored under
- * @return a long value
- */
- public long getLong(@LongKey String key) {
- return mBundle.getLong(key, 0);
- }
-
- /**
- * Returns a {@link Rating} for the given key or null if no rating exists
- * for the given key.
- *
- * @param key The key the value is stored under
- * @return A {@link Rating} or null
- */
- public Rating getRating(@RatingKey String key) {
- Rating rating = null;
- try {
- rating = mBundle.getParcelable(key);
- } catch (Exception e) {
- // ignore, value was not a bitmap
- Log.w(TAG, "Failed to retrieve a key as Rating.", e);
- }
- return rating;
- }
-
- /**
- * Returns a {@link Bitmap} for the given key or null if no bitmap exists
- * for the given key.
- *
- * @param key The key the value is stored under
- * @return A {@link Bitmap} or null
- */
- public Bitmap getBitmap(@BitmapKey String key) {
- Bitmap bmp = null;
- try {
- bmp = mBundle.getParcelable(key);
- } catch (Exception e) {
- // ignore, value was not a bitmap
- Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
- }
- return bmp;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeBundle(mBundle);
- }
-
- /**
- * Returns the number of fields in this metadata.
- *
- * @return The number of fields in the metadata.
- */
- public int size() {
- return mBundle.size();
- }
-
- /**
- * Returns a Set containing the Strings used as keys in this metadata.
- *
- * @return a Set of String keys
- */
- public Set<String> keySet() {
- return mBundle.keySet();
- }
-
- /**
- * Returns a simple description of this metadata for display purposes.
- *
- * @return A simple description of this metadata.
- */
- public @NonNull MediaDescription getDescription() {
- if (mDescription != null) {
- return mDescription;
- }
-
- String mediaId = getString(METADATA_KEY_MEDIA_ID);
-
- CharSequence[] text = new CharSequence[3];
- Bitmap icon = null;
- Uri iconUri = null;
-
- // First handle the case where display data is set already
- CharSequence displayText = getText(METADATA_KEY_DISPLAY_TITLE);
- if (!TextUtils.isEmpty(displayText)) {
- // If they have a display title use only display data, otherwise use
- // our best bets
- text[0] = displayText;
- text[1] = getText(METADATA_KEY_DISPLAY_SUBTITLE);
- text[2] = getText(METADATA_KEY_DISPLAY_DESCRIPTION);
- } else {
- // Use whatever fields we can
- int textIndex = 0;
- int keyIndex = 0;
- while (textIndex < text.length && keyIndex < PREFERRED_DESCRIPTION_ORDER.length) {
- CharSequence next = getText(PREFERRED_DESCRIPTION_ORDER[keyIndex++]);
- if (!TextUtils.isEmpty(next)) {
- // Fill in the next empty bit of text
- text[textIndex++] = next;
- }
- }
- }
-
- // Get the best art bitmap we can find
- for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
- Bitmap next = getBitmap(PREFERRED_BITMAP_ORDER[i]);
- if (next != null) {
- icon = next;
- break;
- }
- }
-
- // Get the best Uri we can find
- for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) {
- String next = getString(PREFERRED_URI_ORDER[i]);
- if (!TextUtils.isEmpty(next)) {
- iconUri = Uri.parse(next);
- break;
- }
- }
-
- Uri mediaUri = null;
- String mediaUriStr = getString(METADATA_KEY_MEDIA_URI);
- if (!TextUtils.isEmpty(mediaUriStr)) {
- mediaUri = Uri.parse(mediaUriStr);
- }
-
- MediaDescription.Builder bob = new MediaDescription.Builder();
- bob.setMediaId(mediaId);
- bob.setTitle(text[0]);
- bob.setSubtitle(text[1]);
- bob.setDescription(text[2]);
- bob.setIconBitmap(icon);
- bob.setIconUri(iconUri);
- bob.setMediaUri(mediaUri);
- if (mBundle.containsKey(METADATA_KEY_BT_FOLDER_TYPE)) {
- Bundle bundle = new Bundle();
- bundle.putLong(MediaDescription.EXTRA_BT_FOLDER_TYPE,
- getLong(METADATA_KEY_BT_FOLDER_TYPE));
- bob.setExtras(bundle);
- }
- mDescription = bob.build();
-
- return mDescription;
- }
-
- /**
- * Helper for getting the String key used by {@link MediaMetadata} from the
- * integer key that {@link MediaMetadataEditor} uses.
- *
- * @param editorKey The key used by the editor
- * @return The key used by this class or null if no mapping exists
- * @hide
- */
- @UnsupportedAppUsage
- public static String getKeyFromMetadataEditorKey(int editorKey) {
- return EDITOR_KEY_MAPPING.get(editorKey, null);
- }
-
- public static final Parcelable.Creator<MediaMetadata> CREATOR =
- new Parcelable.Creator<MediaMetadata>() {
- @Override
- public MediaMetadata createFromParcel(Parcel in) {
- return new MediaMetadata(in);
- }
-
- @Override
- public MediaMetadata[] newArray(int size) {
- return new MediaMetadata[size];
- }
- };
-
- /**
- * Compares the contents of this object to another MediaMetadata object. It
- * does not compare Bitmaps and Ratings as the media player can choose to
- * forgo these fields depending on how you retrieve the MediaMetadata.
- *
- * @param o The Metadata object to compare this object against
- * @return Whether or not the two objects have matching fields (excluding
- * Bitmaps and Ratings)
- */
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
-
- if (!(o instanceof MediaMetadata)) {
- return false;
- }
-
- final MediaMetadata m = (MediaMetadata) o;
-
- for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) {
- String key = METADATA_KEYS_TYPE.keyAt(i);
- switch (METADATA_KEYS_TYPE.valueAt(i)) {
- case METADATA_TYPE_TEXT:
- if (!Objects.equals(getString(key), m.getString(key))) {
- return false;
- }
- break;
- case METADATA_TYPE_LONG:
- if (getLong(key) != m.getLong(key)) {
- return false;
- }
- break;
- default:
- // Ignore ratings and bitmaps when comparing
- break;
- }
- }
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int hashCode = 17;
-
- for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) {
- String key = METADATA_KEYS_TYPE.keyAt(i);
- switch (METADATA_KEYS_TYPE.valueAt(i)) {
- case METADATA_TYPE_TEXT:
- hashCode = 31 * hashCode + Objects.hash(getString(key));
- break;
- case METADATA_TYPE_LONG:
- hashCode = 31 * hashCode + Long.hashCode(getLong(key));
- break;
- default:
- // Ignore ratings and bitmaps when comparing
- break;
- }
- }
-
- return hashCode;
- }
-
- /**
- * Use to build MediaMetadata objects. The system defined metadata keys must
- * use the appropriate data type.
- */
- public static final class Builder {
- private final Bundle mBundle;
-
- /**
- * Create an empty Builder. Any field that should be included in the
- * {@link MediaMetadata} must be added.
- */
- public Builder() {
- mBundle = new Bundle();
- }
-
- /**
- * Create a Builder using a {@link MediaMetadata} instance to set the
- * initial values. All fields in the source metadata will be included in
- * the new metadata. Fields can be overwritten by adding the same key.
- *
- * @param source
- */
- public Builder(MediaMetadata source) {
- mBundle = new Bundle(source.mBundle);
- }
-
- /**
- * Create a Builder using a {@link MediaMetadata} instance to set
- * initial values, but replace bitmaps with a scaled down copy if they
- * are larger than maxBitmapSize.
- *
- * @param source The original metadata to copy.
- * @param maxBitmapSize The maximum height/width for bitmaps contained
- * in the metadata.
- * @hide
- */
- public Builder(MediaMetadata source, int maxBitmapSize) {
- this(source);
- for (String key : mBundle.keySet()) {
- Object value = mBundle.get(key);
- if (value != null && value instanceof Bitmap) {
- Bitmap bmp = (Bitmap) value;
- if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
- putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
- }
- }
- }
- }
-
- /**
- * Put a CharSequence value into the metadata. Custom keys may be used,
- * but if the METADATA_KEYs defined in this class are used they may only
- * be one of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_TITLE}</li>
- * <li>{@link #METADATA_KEY_ARTIST}</li>
- * <li>{@link #METADATA_KEY_ALBUM}</li>
- * <li>{@link #METADATA_KEY_AUTHOR}</li>
- * <li>{@link #METADATA_KEY_WRITER}</li>
- * <li>{@link #METADATA_KEY_COMPOSER}</li>
- * <li>{@link #METADATA_KEY_DATE}</li>
- * <li>{@link #METADATA_KEY_GENRE}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
- * <li>{@link #METADATA_KEY_ART_URI}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
- * </ul>
- *
- * @param key The key for referencing this value
- * @param value The CharSequence value to store
- * @return The Builder to allow chaining
- */
- public Builder putText(@TextKey String key, CharSequence value) {
- if (METADATA_KEYS_TYPE.containsKey(key)) {
- if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
- throw new IllegalArgumentException("The " + key
- + " key cannot be used to put a CharSequence");
- }
- }
- mBundle.putCharSequence(key, value);
- return this;
- }
-
- /**
- * Put a String value into the metadata. Custom keys may be used, but if
- * the METADATA_KEYs defined in this class are used they may only be one
- * of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_TITLE}</li>
- * <li>{@link #METADATA_KEY_ARTIST}</li>
- * <li>{@link #METADATA_KEY_ALBUM}</li>
- * <li>{@link #METADATA_KEY_AUTHOR}</li>
- * <li>{@link #METADATA_KEY_WRITER}</li>
- * <li>{@link #METADATA_KEY_COMPOSER}</li>
- * <li>{@link #METADATA_KEY_DATE}</li>
- * <li>{@link #METADATA_KEY_GENRE}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
- * <li>{@link #METADATA_KEY_ART_URI}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
- * </ul>
- * <p>
- * Uris for artwork should use the content:// style and support
- * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork
- * through {@link ContentResolver#openTypedAssetFileDescriptor(Uri,
- * String, Bundle)}.
- *
- * @param key The key for referencing this value
- * @param value The String value to store
- * @return The Builder to allow chaining
- */
- public Builder putString(@TextKey String key, String value) {
- if (METADATA_KEYS_TYPE.containsKey(key)) {
- if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
- throw new IllegalArgumentException("The " + key
- + " key cannot be used to put a String");
- }
- }
- mBundle.putCharSequence(key, value);
- return this;
- }
-
- /**
- * Put a long value into the metadata. Custom keys may be used, but if
- * the METADATA_KEYs defined in this class are used they may only be one
- * of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_DURATION}</li>
- * <li>{@link #METADATA_KEY_TRACK_NUMBER}</li>
- * <li>{@link #METADATA_KEY_NUM_TRACKS}</li>
- * <li>{@link #METADATA_KEY_DISC_NUMBER}</li>
- * <li>{@link #METADATA_KEY_YEAR}</li>
- * </ul>
- *
- * @param key The key for referencing this value
- * @param value The long value to store
- * @return The Builder to allow chaining
- */
- public Builder putLong(@LongKey String key, long value) {
- if (METADATA_KEYS_TYPE.containsKey(key)) {
- if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
- throw new IllegalArgumentException("The " + key
- + " key cannot be used to put a long");
- }
- }
- mBundle.putLong(key, value);
- return this;
- }
-
- /**
- * Put a {@link Rating} into the metadata. Custom keys may be used, but
- * if the METADATA_KEYs defined in this class are used they may only be
- * one of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_RATING}</li>
- * <li>{@link #METADATA_KEY_USER_RATING}</li>
- * </ul>
- *
- * @param key The key for referencing this value
- * @param value The Rating value to store
- * @return The Builder to allow chaining
- */
- public Builder putRating(@RatingKey String key, Rating value) {
- if (METADATA_KEYS_TYPE.containsKey(key)) {
- if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
- throw new IllegalArgumentException("The " + key
- + " key cannot be used to put a Rating");
- }
- }
- mBundle.putParcelable(key, value);
- return this;
- }
-
- /**
- * Put a {@link Bitmap} into the metadata. Custom keys may be used, but
- * if the METADATA_KEYs defined in this class are used they may only be
- * one of the following:
- * <ul>
- * <li>{@link #METADATA_KEY_ART}</li>
- * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
- * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
- * </ul>
- * <p>
- * Large bitmaps may be scaled down by the system when
- * {@link android.media.session.MediaSession#setMetadata} is called.
- * To pass full resolution images {@link Uri Uris} should be used with
- * {@link #putString}.
- *
- * @param key The key for referencing this value
- * @param value The Bitmap to store
- * @return The Builder to allow chaining
- */
- public Builder putBitmap(@BitmapKey String key, Bitmap value) {
- if (METADATA_KEYS_TYPE.containsKey(key)) {
- if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
- throw new IllegalArgumentException("The " + key
- + " key cannot be used to put a Bitmap");
- }
- }
- mBundle.putParcelable(key, value);
- return this;
- }
-
- /**
- * Creates a {@link MediaMetadata} instance with the specified fields.
- *
- * @return The new MediaMetadata instance
- */
- public MediaMetadata build() {
- return new MediaMetadata(mBundle);
- }
-
- private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
- float maxSizeF = maxSize;
- float widthScale = maxSizeF / bmp.getWidth();
- float heightScale = maxSizeF / bmp.getHeight();
- float scale = Math.min(widthScale, heightScale);
- int height = (int) (bmp.getHeight() * scale);
- int width = (int) (bmp.getWidth() * scale);
- return Bitmap.createScaledBitmap(bmp, width, height, true);
- }
- }
-}
diff --git a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl b/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl
deleted file mode 100644
index 228ea9c..0000000
--- a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/* Copyright (C) 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 android.media;
-
-/** @hide */
-parcelable MediaParceledListSlice;
\ No newline at end of file
diff --git a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java b/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java
deleted file mode 100644
index ec3fdb7..0000000
--- a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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 android.media;
-
-import android.annotation.UnsupportedAppUsage;
-import android.os.Binder;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Transfer a large list of objects across an IPC. Splits into multiple transactions if needed.
- * Note: Only use classes declared final in order to avoid subclasses overriding reading/writing
- * parcel logic.
- *
- * TODO: Add test for sending large data
- * @hide
- */
-public class MediaParceledListSlice<T extends Parcelable> implements Parcelable {
- private static final String TAG = "MediaParceledListSlice";
- private static final boolean DEBUG = false;
-
- private static final int MAX_IPC_SIZE = 64 * 1024; // IBinder.MAX_IPC_SIZE
-
- final List<T> mList;
-
- public MediaParceledListSlice(List<T> list) {
- if (list == null) {
- throw new IllegalArgumentException("list shouldn't be null");
- }
- mList = list;
- }
-
- MediaParceledListSlice(Parcel p) {
- final int itemCount = p.readInt();
- mList = new ArrayList<>(itemCount);
- if (DEBUG) {
- Log.d(TAG, "Retrieving " + itemCount + " items");
- }
- if (itemCount <= 0) {
- return;
- }
-
- int i = 0;
- while (i < itemCount) {
- if (p.readInt() == 0) {
- break;
- }
-
- final T parcelable = p.readParcelable(null);
- mList.add(parcelable);
-
- if (DEBUG) {
- Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size() - 1));
- }
- i++;
- }
- if (i >= itemCount) {
- return;
- }
- final IBinder retriever = p.readStrongBinder();
- while (i < itemCount) {
- if (DEBUG) {
- Log.d(TAG, "Reading more @" + i + " of " + itemCount + ": retriever=" + retriever);
- }
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInt(i);
- try {
- retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Failure retrieving array; only received " + i + " of " + itemCount, e);
- return;
- }
- while (i < itemCount && reply.readInt() != 0) {
- final T parcelable = reply.readParcelable(null);
- mList.add(parcelable);
-
- if (DEBUG) {
- Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size() - 1));
- }
- i++;
- }
- reply.recycle();
- data.recycle();
- }
- }
-
- public List<T> getList() {
- return mList;
- }
-
- /**
- * Write this to another Parcel. Note that this discards the internal Parcel
- * and should not be used anymore. This is so we can pass this to a Binder
- * where we won't have a chance to call recycle on this.
- */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- final int itemCount = mList.size();
- dest.writeInt(itemCount);
- if (DEBUG) {
- Log.d(TAG, "Writing " + itemCount + " items");
- }
- if (itemCount > 0) {
- int i = 0;
- while (i < itemCount && dest.dataSize() < MAX_IPC_SIZE) {
- dest.writeInt(1);
-
- final T parcelable = mList.get(i);
- dest.writeParcelable(parcelable, flags);
-
- if (DEBUG) {
- Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
- }
- i++;
- }
- if (i < itemCount) {
- dest.writeInt(0);
- Binder retriever = new Binder() {
- @Override
- protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- if (code != FIRST_CALL_TRANSACTION) {
- return super.onTransact(code, data, reply, flags);
- }
- int i = data.readInt();
- if (DEBUG) {
- Log.d(TAG, "Writing more @" + i + " of " + itemCount);
- }
- while (i < itemCount && reply.dataSize() < MAX_IPC_SIZE) {
- reply.writeInt(1);
-
- final T parcelable = mList.get(i);
- reply.writeParcelable(parcelable, flags);
-
- if (DEBUG) {
- Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
- }
- i++;
- }
- if (i < itemCount) {
- if (DEBUG) {
- Log.d(TAG, "Breaking @" + i + " of " + itemCount);
- }
- reply.writeInt(0);
- }
- return true;
- }
- };
- if (DEBUG) {
- Log.d(TAG, "Breaking @" + i + " of " + itemCount + ": retriever=" + retriever);
- }
- dest.writeStrongBinder(retriever);
- }
- }
- }
-
- @Override
- public int describeContents() {
- int contents = 0;
- final List<T> list = getList();
- for (int i = 0; i < list.size(); i++) {
- contents |= list.get(i).describeContents();
- }
- return contents;
- }
-
- public static final Parcelable.Creator<MediaParceledListSlice> CREATOR =
- new Parcelable.Creator<MediaParceledListSlice>() {
- @Override
- public MediaParceledListSlice createFromParcel(Parcel in) {
- return new MediaParceledListSlice(in);
- }
-
- @Override
- public MediaParceledListSlice[] newArray(int size) {
- return new MediaParceledListSlice[size];
- }
- };
-}
diff --git a/packages/MediaComponents/apex/java/android/media/Rating.aidl b/packages/MediaComponents/apex/java/android/media/Rating.aidl
deleted file mode 100644
index 1dc336a..0000000
--- a/packages/MediaComponents/apex/java/android/media/Rating.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-parcelable Rating;
diff --git a/packages/MediaComponents/apex/java/android/media/Rating.java b/packages/MediaComponents/apex/java/android/media/Rating.java
deleted file mode 100644
index 04d5364..0000000
--- a/packages/MediaComponents/apex/java/android/media/Rating.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.IntDef;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A class to encapsulate rating information used as content metadata.
- * A rating is defined by its rating style (see {@link #RATING_HEART},
- * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
- * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may
- * be defined as "unrated"), both of which are defined when the rating instance is constructed
- * through one of the factory methods.
- */
-public final class Rating implements Parcelable {
- private final static String TAG = "Rating";
-
- /**
- * @hide
- */
- @IntDef({RATING_NONE, RATING_HEART, RATING_THUMB_UP_DOWN, RATING_3_STARS, RATING_4_STARS,
- RATING_5_STARS, RATING_PERCENTAGE})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Style {}
-
- /**
- * @hide
- */
- @IntDef({RATING_3_STARS, RATING_4_STARS, RATING_5_STARS})
- @Retention(RetentionPolicy.SOURCE)
- public @interface StarStyle {}
-
- /**
- * Indicates a rating style is not supported. A Rating will never have this
- * type, but can be used by other classes to indicate they do not support
- * Rating.
- */
- public final static int RATING_NONE = 0;
-
- /**
- * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
- * indicate the content referred to is a favorite (or not).
- */
- public final static int RATING_HEART = 1;
-
- /**
- * A rating style for "thumb up" vs "thumb down".
- */
- public final static int RATING_THUMB_UP_DOWN = 2;
-
- /**
- * A rating style with 0 to 3 stars.
- */
- public final static int RATING_3_STARS = 3;
-
- /**
- * A rating style with 0 to 4 stars.
- */
- public final static int RATING_4_STARS = 4;
-
- /**
- * A rating style with 0 to 5 stars.
- */
- public final static int RATING_5_STARS = 5;
-
- /**
- * A rating style expressed as a percentage.
- */
- public final static int RATING_PERCENTAGE = 6;
-
- private final static float RATING_NOT_RATED = -1.0f;
-
- private final int mRatingStyle;
-
- private final float mRatingValue;
-
- private Rating(@Style int ratingStyle, float rating) {
- mRatingStyle = ratingStyle;
- mRatingValue = rating;
- }
-
- @Override
- public String toString() {
- return "Rating:style=" + mRatingStyle + " rating="
- + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue));
- }
-
- @Override
- public int describeContents() {
- return mRatingStyle;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mRatingStyle);
- dest.writeFloat(mRatingValue);
- }
-
- public static final Parcelable.Creator<Rating> CREATOR
- = new Parcelable.Creator<Rating>() {
- /**
- * Rebuilds a Rating previously stored with writeToParcel().
- * @param p Parcel object to read the Rating from
- * @return a new Rating created from the data in the parcel
- */
- @Override
- public Rating createFromParcel(Parcel p) {
- return new Rating(p.readInt(), p.readFloat());
- }
-
- @Override
- public Rating[] newArray(int size) {
- return new Rating[size];
- }
- };
-
- /**
- * Return a Rating instance with no rating.
- * Create and return a new Rating instance with no rating known for the given
- * rating style.
- * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
- * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
- * or {@link #RATING_PERCENTAGE}.
- * @return null if an invalid rating style is passed, a new Rating instance otherwise.
- */
- public static Rating newUnratedRating(@Style int ratingStyle) {
- switch(ratingStyle) {
- case RATING_HEART:
- case RATING_THUMB_UP_DOWN:
- case RATING_3_STARS:
- case RATING_4_STARS:
- case RATING_5_STARS:
- case RATING_PERCENTAGE:
- return new Rating(ratingStyle, RATING_NOT_RATED);
- default:
- return null;
- }
- }
-
- /**
- * Return a Rating instance with a heart-based rating.
- * Create and return a new Rating instance with a rating style of {@link #RATING_HEART},
- * and a heart-based rating.
- * @param hasHeart true for a "heart selected" rating, false for "heart unselected".
- * @return a new Rating instance.
- */
- public static Rating newHeartRating(boolean hasHeart) {
- return new Rating(RATING_HEART, hasHeart ? 1.0f : 0.0f);
- }
-
- /**
- * Return a Rating instance with a thumb-based rating.
- * Create and return a new Rating instance with a {@link #RATING_THUMB_UP_DOWN}
- * rating style, and a "thumb up" or "thumb down" rating.
- * @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
- * @return a new Rating instance.
- */
- public static Rating newThumbRating(boolean thumbIsUp) {
- return new Rating(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f);
- }
-
- /**
- * Return a Rating instance with a star-based rating.
- * Create and return a new Rating instance with one of the star-base rating styles
- * and the given integer or fractional number of stars. Non integer values can for instance
- * be used to represent an average rating value, which might not be an integer number of stars.
- * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
- * {@link #RATING_5_STARS}.
- * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to
- * the rating style.
- * @return null if the rating style is invalid, or the rating is out of range,
- * a new Rating instance otherwise.
- */
- public static Rating newStarRating(@StarStyle int starRatingStyle, float starRating) {
- float maxRating = -1.0f;
- switch(starRatingStyle) {
- case RATING_3_STARS:
- maxRating = 3.0f;
- break;
- case RATING_4_STARS:
- maxRating = 4.0f;
- break;
- case RATING_5_STARS:
- maxRating = 5.0f;
- break;
- default:
- Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating");
- return null;
- }
- if ((starRating < 0.0f) || (starRating > maxRating)) {
- Log.e(TAG, "Trying to set out of range star-based rating");
- return null;
- }
- return new Rating(starRatingStyle, starRating);
- }
-
- /**
- * Return a Rating instance with a percentage-based rating.
- * Create and return a new Rating instance with a {@link #RATING_PERCENTAGE}
- * rating style, and a rating of the given percentage.
- * @param percent the value of the rating
- * @return null if the rating is out of range, a new Rating instance otherwise.
- */
- public static Rating newPercentageRating(float percent) {
- if ((percent < 0.0f) || (percent > 100.0f)) {
- Log.e(TAG, "Invalid percentage-based rating value");
- return null;
- } else {
- return new Rating(RATING_PERCENTAGE, percent);
- }
- }
-
- /**
- * Return whether there is a rating value available.
- * @return true if the instance was not created with {@link #newUnratedRating(int)}.
- */
- public boolean isRated() {
- return mRatingValue >= 0.0f;
- }
-
- /**
- * Return the rating style.
- * @return one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
- * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
- * or {@link #RATING_PERCENTAGE}.
- */
- @Style
- public int getRatingStyle() {
- return mRatingStyle;
- }
-
- /**
- * Return whether the rating is "heart selected".
- * @return true if the rating is "heart selected", false if the rating is "heart unselected",
- * if the rating style is not {@link #RATING_HEART} or if it is unrated.
- */
- public boolean hasHeart() {
- if (mRatingStyle != RATING_HEART) {
- return false;
- } else {
- return (mRatingValue == 1.0f);
- }
- }
-
- /**
- * Return whether the rating is "thumb up".
- * @return true if the rating is "thumb up", false if the rating is "thumb down",
- * if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated.
- */
- public boolean isThumbUp() {
- if (mRatingStyle != RATING_THUMB_UP_DOWN) {
- return false;
- } else {
- return (mRatingValue == 1.0f);
- }
- }
-
- /**
- * Return the star-based rating value.
- * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
- * not star-based, or if it is unrated.
- */
- public float getStarRating() {
- switch (mRatingStyle) {
- case RATING_3_STARS:
- case RATING_4_STARS:
- case RATING_5_STARS:
- if (isRated()) {
- return mRatingValue;
- }
- default:
- return -1.0f;
- }
- }
-
- /**
- * Return the percentage-based rating value.
- * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
- * not percentage-based, or if it is unrated.
- */
- public float getPercentRating() {
- if ((mRatingStyle != RATING_PERCENTAGE) || !isRated()) {
- return -1.0f;
- } else {
- return mRatingValue;
- }
- }
-}
diff --git a/packages/MediaComponents/apex/java/android/media/VolumeProvider.java b/packages/MediaComponents/apex/java/android/media/VolumeProvider.java
deleted file mode 100644
index 1c017c5..0000000
--- a/packages/MediaComponents/apex/java/android/media/VolumeProvider.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media;
-
-import android.annotation.IntDef;
-import android.media.session.MediaSession;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Handles requests to adjust or set the volume on a session. This is also used
- * to push volume updates back to the session. The provider must call
- * {@link #setCurrentVolume(int)} each time the volume being provided changes.
- * <p>
- * You can set a volume provider on a session by calling
- * {@link MediaSession#setPlaybackToRemote}.
- */
-public abstract class VolumeProvider {
-
- /**
- * @hide
- */
- @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE})
- @Retention(RetentionPolicy.SOURCE)
- public @interface ControlType {}
-
- /**
- * The volume is fixed and can not be modified. Requests to change volume
- * should be ignored.
- */
- public static final int VOLUME_CONTROL_FIXED = 0;
-
- /**
- * The volume control uses relative adjustment via
- * {@link #onAdjustVolume(int)}. Attempts to set the volume to a specific
- * value should be ignored.
- */
- public static final int VOLUME_CONTROL_RELATIVE = 1;
-
- /**
- * The volume control uses an absolute value. It may be adjusted using
- * {@link #onAdjustVolume(int)} or set directly using
- * {@link #onSetVolumeTo(int)}.
- */
- public static final int VOLUME_CONTROL_ABSOLUTE = 2;
-
- private final int mControlType;
- private final int mMaxVolume;
- private int mCurrentVolume;
- private Callback mCallback;
-
- /**
- * Create a new volume provider for handling volume events. You must specify
- * the type of volume control, the maximum volume that can be used, and the
- * current volume on the output.
- *
- * @param volumeControl The method for controlling volume that is used by
- * this provider.
- * @param maxVolume The maximum allowed volume.
- * @param currentVolume The current volume on the output.
- */
- public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume) {
- mControlType = volumeControl;
- mMaxVolume = maxVolume;
- mCurrentVolume = currentVolume;
- }
-
- /**
- * Get the volume control type that this volume provider uses.
- *
- * @return The volume control type for this volume provider
- */
- @ControlType
- public final int getVolumeControl() {
- return mControlType;
- }
-
- /**
- * Get the maximum volume this provider allows.
- *
- * @return The max allowed volume.
- */
- public final int getMaxVolume() {
- return mMaxVolume;
- }
-
- /**
- * Gets the current volume. This will be the last value set by
- * {@link #setCurrentVolume(int)}.
- *
- * @return The current volume.
- */
- public final int getCurrentVolume() {
- return mCurrentVolume;
- }
-
- /**
- * Notify the system that the current volume has been changed. This must be
- * called every time the volume changes to ensure it is displayed properly.
- *
- * @param currentVolume The current volume on the output.
- */
- public final void setCurrentVolume(int currentVolume) {
- mCurrentVolume = currentVolume;
- if (mCallback != null) {
- mCallback.onVolumeChanged(this);
- }
- }
-
- /**
- * Override to handle requests to set the volume of the current output.
- * After the volume has been modified {@link #setCurrentVolume} must be
- * called to notify the system.
- *
- * @param volume The volume to set the output to.
- */
- public void onSetVolumeTo(int volume) {
- }
-
- /**
- * Override to handle requests to adjust the volume of the current output.
- * Direction will be one of {@link AudioManager#ADJUST_LOWER},
- * {@link AudioManager#ADJUST_RAISE}, {@link AudioManager#ADJUST_SAME}.
- * After the volume has been modified {@link #setCurrentVolume} must be
- * called to notify the system.
- *
- * @param direction The direction to change the volume in.
- */
- public void onAdjustVolume(int direction) {
- }
-
- /**
- * Sets a callback to receive volume changes.
- * @hide
- */
- public void setCallback(Callback callback) {
- mCallback = callback;
- }
-
- /**
- * Listens for changes to the volume.
- * @hide
- */
- public static abstract class Callback {
- public abstract void onVolumeChanged(VolumeProvider volumeProvider);
- }
-}
diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl
deleted file mode 100644
index 782e094..0000000
--- a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media.browse;
-
-parcelable MediaBrowser.MediaItem;
\ No newline at end of file
diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java
deleted file mode 100644
index b1b14c6..0000000
--- a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java
+++ /dev/null
@@ -1,1171 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.browse;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.media.MediaDescription;
-import android.media.MediaParceledListSlice;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.service.media.IMediaBrowserService;
-import android.service.media.IMediaBrowserServiceCallbacks;
-import android.service.media.MediaBrowserService;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map.Entry;
-
-/**
- * Browses media content offered by a link MediaBrowserService.
- * <p>
- * This object is not thread-safe. All calls should happen on the thread on which the browser
- * was constructed.
- * </p>
- * <h3>Standard Extra Data</h3>
- *
- * <p>These are the current standard fields that can be used as extra data via
- * {@link #subscribe(String, Bundle, SubscriptionCallback)},
- * {@link #unsubscribe(String, SubscriptionCallback)}, and
- * {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}.
- *
- * <ul>
- * <li> {@link #EXTRA_PAGE}
- * <li> {@link #EXTRA_PAGE_SIZE}
- * </ul>
- */
-public final class MediaBrowser {
- private static final String TAG = "MediaBrowser";
- private static final boolean DBG = false;
-
- /**
- * Used as an int extra field to denote the page number to subscribe.
- * The value of {@code EXTRA_PAGE} should be greater than or equal to 0.
- *
- * @see #EXTRA_PAGE_SIZE
- */
- public static final String EXTRA_PAGE = "android.media.browse.extra.PAGE";
-
- /**
- * Used as an int extra field to denote the number of media items in a page.
- * The value of {@code EXTRA_PAGE_SIZE} should be greater than or equal to 1.
- *
- * @see #EXTRA_PAGE
- */
- public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
-
- private static final int CONNECT_STATE_DISCONNECTING = 0;
- private static final int CONNECT_STATE_DISCONNECTED = 1;
- private static final int CONNECT_STATE_CONNECTING = 2;
- private static final int CONNECT_STATE_CONNECTED = 3;
- private static final int CONNECT_STATE_SUSPENDED = 4;
-
- private final Context mContext;
- private final ComponentName mServiceComponent;
- private final ConnectionCallback mCallback;
- private final Bundle mRootHints;
- private final Handler mHandler = new Handler();
- private final ArrayMap<String, Subscription> mSubscriptions = new ArrayMap<>();
-
- private volatile int mState = CONNECT_STATE_DISCONNECTED;
- private volatile String mRootId;
- private volatile MediaSession.Token mMediaSessionToken;
- private volatile Bundle mExtras;
-
- private MediaServiceConnection mServiceConnection;
- private IMediaBrowserService mServiceBinder;
- private IMediaBrowserServiceCallbacks mServiceCallbacks;
-
- /**
- * Creates a media browser for the specified media browser service.
- *
- * @param context The context.
- * @param serviceComponent The component name of the media browser service.
- * @param callback The connection callback.
- * @param rootHints An optional bundle of service-specific arguments to send
- * to the media browser service when connecting and retrieving the root id
- * for browsing, or null if none. The contents of this bundle may affect
- * the information returned when browsing.
- * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_RECENT
- * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
- * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
- */
- public MediaBrowser(Context context, ComponentName serviceComponent,
- ConnectionCallback callback, Bundle rootHints) {
- if (context == null) {
- throw new IllegalArgumentException("context must not be null");
- }
- if (serviceComponent == null) {
- throw new IllegalArgumentException("service component must not be null");
- }
- if (callback == null) {
- throw new IllegalArgumentException("connection callback must not be null");
- }
- mContext = context;
- mServiceComponent = serviceComponent;
- mCallback = callback;
- mRootHints = rootHints == null ? null : new Bundle(rootHints);
- }
-
- /**
- * Connects to the media browser service.
- * <p>
- * The connection callback specified in the constructor will be invoked
- * when the connection completes or fails.
- * </p>
- */
- public void connect() {
- if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) {
- throw new IllegalStateException("connect() called while neither disconnecting nor "
- + "disconnected (state=" + getStateLabel(mState) + ")");
- }
-
- mState = CONNECT_STATE_CONNECTING;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mState == CONNECT_STATE_DISCONNECTING) {
- return;
- }
- mState = CONNECT_STATE_CONNECTING;
- // TODO: remove this extra check.
- if (DBG) {
- if (mServiceConnection != null) {
- throw new RuntimeException("mServiceConnection should be null. Instead it"
- + " is " + mServiceConnection);
- }
- }
- if (mServiceBinder != null) {
- throw new RuntimeException("mServiceBinder should be null. Instead it is "
- + mServiceBinder);
- }
- if (mServiceCallbacks != null) {
- throw new RuntimeException("mServiceCallbacks should be null. Instead it is "
- + mServiceCallbacks);
- }
-
- final Intent intent = new Intent(MediaBrowserService.SERVICE_INTERFACE);
- intent.setComponent(mServiceComponent);
-
- mServiceConnection = new MediaServiceConnection();
-
- boolean bound = false;
- try {
- bound = mContext.bindService(intent, mServiceConnection,
- Context.BIND_AUTO_CREATE);
- } catch (Exception ex) {
- Log.e(TAG, "Failed binding to service " + mServiceComponent);
- }
-
- if (!bound) {
- // Tell them that it didn't work.
- forceCloseConnection();
- mCallback.onConnectionFailed();
- }
-
- if (DBG) {
- Log.d(TAG, "connect...");
- dump();
- }
- }
- });
- }
-
- /**
- * Disconnects from the media browser service.
- * After this, no more callbacks will be received.
- */
- public void disconnect() {
- // It's ok to call this any state, because allowing this lets apps not have
- // to check isConnected() unnecessarily. They won't appreciate the extra
- // assertions for this. We do everything we can here to go back to a sane state.
- mState = CONNECT_STATE_DISCONNECTING;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // connect() could be called before this. Then we will disconnect and reconnect.
- if (mServiceCallbacks != null) {
- try {
- mServiceBinder.disconnect(mServiceCallbacks);
- } catch (RemoteException ex) {
- // We are disconnecting anyway. Log, just for posterity but it's not
- // a big problem.
- Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
- }
- }
- int state = mState;
- forceCloseConnection();
- // If the state was not CONNECT_STATE_DISCONNECTING, keep the state so that
- // the operation came after disconnect() can be handled properly.
- if (state != CONNECT_STATE_DISCONNECTING) {
- mState = state;
- }
- if (DBG) {
- Log.d(TAG, "disconnect...");
- dump();
- }
- }
- });
- }
-
- /**
- * Null out the variables and unbind from the service. This doesn't include
- * calling disconnect on the service, because we only try to do that in the
- * clean shutdown cases.
- * <p>
- * Everywhere that calls this EXCEPT for disconnect() should follow it with
- * a call to mCallback.onConnectionFailed(). Disconnect doesn't do that callback
- * for a clean shutdown, but everywhere else is a dirty shutdown and should
- * notify the app.
- * <p>
- * Also, mState should be updated properly. Mostly it should be CONNECT_STATE_DIACONNECTED
- * except for disconnect().
- */
- private void forceCloseConnection() {
- if (mServiceConnection != null) {
- try {
- mContext.unbindService(mServiceConnection);
- } catch (IllegalArgumentException e) {
- if (DBG) {
- Log.d(TAG, "unbindService failed", e);
- }
- }
- }
- mState = CONNECT_STATE_DISCONNECTED;
- mServiceConnection = null;
- mServiceBinder = null;
- mServiceCallbacks = null;
- mRootId = null;
- mMediaSessionToken = null;
- }
-
- /**
- * Returns whether the browser is connected to the service.
- */
- public boolean isConnected() {
- return mState == CONNECT_STATE_CONNECTED;
- }
-
- /**
- * Gets the service component that the media browser is connected to.
- */
- public @NonNull ComponentName getServiceComponent() {
- if (!isConnected()) {
- throw new IllegalStateException("getServiceComponent() called while not connected" +
- " (state=" + mState + ")");
- }
- return mServiceComponent;
- }
-
- /**
- * Gets the root id.
- * <p>
- * Note that the root id may become invalid or change when the
- * browser is disconnected.
- * </p>
- *
- * @throws IllegalStateException if not connected.
- */
- public @NonNull String getRoot() {
- if (!isConnected()) {
- throw new IllegalStateException("getRoot() called while not connected (state="
- + getStateLabel(mState) + ")");
- }
- return mRootId;
- }
-
- /**
- * Gets any extras for the media service.
- *
- * @throws IllegalStateException if not connected.
- */
- public @Nullable Bundle getExtras() {
- if (!isConnected()) {
- throw new IllegalStateException("getExtras() called while not connected (state="
- + getStateLabel(mState) + ")");
- }
- return mExtras;
- }
-
- /**
- * Gets the media session token associated with the media browser.
- * <p>
- * Note that the session token may become invalid or change when the
- * browser is disconnected.
- * </p>
- *
- * @return The session token for the browser, never null.
- *
- * @throws IllegalStateException if not connected.
- */
- public @NonNull MediaSession.Token getSessionToken() {
- if (!isConnected()) {
- throw new IllegalStateException("getSessionToken() called while not connected (state="
- + mState + ")");
- }
- return mMediaSessionToken;
- }
-
- /**
- * Queries for information about the media items that are contained within
- * the specified id and subscribes to receive updates when they change.
- * <p>
- * The list of subscriptions is maintained even when not connected and is
- * restored after the reconnection. It is ok to subscribe while not connected
- * but the results will not be returned until the connection completes.
- * </p>
- * <p>
- * If the id is already subscribed with a different callback then the new
- * callback will replace the previous one and the child data will be
- * reloaded.
- * </p>
- *
- * @param parentId The id of the parent media item whose list of children
- * will be subscribed.
- * @param callback The callback to receive the list of children.
- */
- public void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
- subscribeInternal(parentId, null, callback);
- }
-
- /**
- * Queries with service-specific arguments for information about the media items
- * that are contained within the specified id and subscribes to receive updates
- * when they change.
- * <p>
- * The list of subscriptions is maintained even when not connected and is
- * restored after the reconnection. It is ok to subscribe while not connected
- * but the results will not be returned until the connection completes.
- * </p>
- * <p>
- * If the id is already subscribed with a different callback then the new
- * callback will replace the previous one and the child data will be
- * reloaded.
- * </p>
- *
- * @param parentId The id of the parent media item whose list of children
- * will be subscribed.
- * @param options The bundle of service-specific arguments to send to the media
- * browser service. The contents of this bundle may affect the
- * information returned when browsing.
- * @param callback The callback to receive the list of children.
- */
- public void subscribe(@NonNull String parentId, @NonNull Bundle options,
- @NonNull SubscriptionCallback callback) {
- if (options == null) {
- throw new IllegalArgumentException("options cannot be null");
- }
- subscribeInternal(parentId, new Bundle(options), callback);
- }
-
- /**
- * Unsubscribes for changes to the children of the specified media id.
- * <p>
- * The query callback will no longer be invoked for results associated with
- * this id once this method returns.
- * </p>
- *
- * @param parentId The id of the parent media item whose list of children
- * will be unsubscribed.
- */
- public void unsubscribe(@NonNull String parentId) {
- unsubscribeInternal(parentId, null);
- }
-
- /**
- * Unsubscribes for changes to the children of the specified media id through a callback.
- * <p>
- * The query callback will no longer be invoked for results associated with
- * this id once this method returns.
- * </p>
- *
- * @param parentId The id of the parent media item whose list of children
- * will be unsubscribed.
- * @param callback A callback sent to the media browser service to subscribe.
- */
- public void unsubscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- unsubscribeInternal(parentId, callback);
- }
-
- /**
- * Retrieves a specific {@link MediaItem} from the connected service. Not
- * all services may support this, so falling back to subscribing to the
- * parent's id should be used when unavailable.
- *
- * @param mediaId The id of the item to retrieve.
- * @param cb The callback to receive the result on.
- */
- public void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb) {
- if (TextUtils.isEmpty(mediaId)) {
- throw new IllegalArgumentException("mediaId cannot be empty.");
- }
- if (cb == null) {
- throw new IllegalArgumentException("cb cannot be null.");
- }
- if (mState != CONNECT_STATE_CONNECTED) {
- Log.i(TAG, "Not connected, unable to retrieve the MediaItem.");
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- cb.onError(mediaId);
- }
- });
- return;
- }
- ResultReceiver receiver = new ResultReceiver(mHandler) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (!isConnected()) {
- return;
- }
- if (resultCode != 0 || resultData == null
- || !resultData.containsKey(MediaBrowserService.KEY_MEDIA_ITEM)) {
- cb.onError(mediaId);
- return;
- }
- Parcelable item = resultData.getParcelable(MediaBrowserService.KEY_MEDIA_ITEM);
- if (item != null && !(item instanceof MediaItem)) {
- cb.onError(mediaId);
- return;
- }
- cb.onItemLoaded((MediaItem)item);
- }
- };
- try {
- mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks);
- } catch (RemoteException e) {
- Log.i(TAG, "Remote error getting media item.");
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- cb.onError(mediaId);
- }
- });
- }
- }
-
- private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) {
- // Check arguments.
- if (TextUtils.isEmpty(parentId)) {
- throw new IllegalArgumentException("parentId cannot be empty.");
- }
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- // Update or create the subscription.
- Subscription sub = mSubscriptions.get(parentId);
- if (sub == null) {
- sub = new Subscription();
- mSubscriptions.put(parentId, sub);
- }
- sub.putCallback(mContext, options, callback);
-
- // If we are connected, tell the service that we are watching. If we aren't connected,
- // the service will be told when we connect.
- if (isConnected()) {
- try {
- if (options == null) {
- mServiceBinder.addSubscriptionDeprecated(parentId, mServiceCallbacks);
- }
- mServiceBinder.addSubscription(parentId, callback.mToken, options,
- mServiceCallbacks);
- } catch (RemoteException ex) {
- // Process is crashing. We will disconnect, and upon reconnect we will
- // automatically reregister. So nothing to do here.
- Log.d(TAG, "addSubscription failed with RemoteException parentId=" + parentId);
- }
- }
- }
-
- private void unsubscribeInternal(String parentId, SubscriptionCallback callback) {
- // Check arguments.
- if (TextUtils.isEmpty(parentId)) {
- throw new IllegalArgumentException("parentId cannot be empty.");
- }
-
- Subscription sub = mSubscriptions.get(parentId);
- if (sub == null) {
- return;
- }
- // Tell the service if necessary.
- try {
- if (callback == null) {
- if (isConnected()) {
- mServiceBinder.removeSubscriptionDeprecated(parentId, mServiceCallbacks);
- mServiceBinder.removeSubscription(parentId, null, mServiceCallbacks);
- }
- } else {
- final List<SubscriptionCallback> callbacks = sub.getCallbacks();
- final List<Bundle> optionsList = sub.getOptionsList();
- for (int i = callbacks.size() - 1; i >= 0; --i) {
- if (callbacks.get(i) == callback) {
- if (isConnected()) {
- mServiceBinder.removeSubscription(
- parentId, callback.mToken, mServiceCallbacks);
- }
- callbacks.remove(i);
- optionsList.remove(i);
- }
- }
- }
- } catch (RemoteException ex) {
- // Process is crashing. We will disconnect, and upon reconnect we will
- // automatically reregister. So nothing to do here.
- Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
- }
-
- if (sub.isEmpty() || callback == null) {
- mSubscriptions.remove(parentId);
- }
- }
-
- /**
- * For debugging.
- */
- private static String getStateLabel(int state) {
- switch (state) {
- case CONNECT_STATE_DISCONNECTING:
- return "CONNECT_STATE_DISCONNECTING";
- case CONNECT_STATE_DISCONNECTED:
- return "CONNECT_STATE_DISCONNECTED";
- case CONNECT_STATE_CONNECTING:
- return "CONNECT_STATE_CONNECTING";
- case CONNECT_STATE_CONNECTED:
- return "CONNECT_STATE_CONNECTED";
- case CONNECT_STATE_SUSPENDED:
- return "CONNECT_STATE_SUSPENDED";
- default:
- return "UNKNOWN/" + state;
- }
- }
-
- private final void onServiceConnected(final IMediaBrowserServiceCallbacks callback,
- final String root, final MediaSession.Token session, final Bundle extra) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // Check to make sure there hasn't been a disconnect or a different
- // ServiceConnection.
- if (!isCurrent(callback, "onConnect")) {
- return;
- }
- // Don't allow them to call us twice.
- if (mState != CONNECT_STATE_CONNECTING) {
- Log.w(TAG, "onConnect from service while mState="
- + getStateLabel(mState) + "... ignoring");
- return;
- }
- mRootId = root;
- mMediaSessionToken = session;
- mExtras = extra;
- mState = CONNECT_STATE_CONNECTED;
-
- if (DBG) {
- Log.d(TAG, "ServiceCallbacks.onConnect...");
- dump();
- }
- mCallback.onConnected();
-
- // we may receive some subscriptions before we are connected, so re-subscribe
- // everything now
- for (Entry<String, Subscription> subscriptionEntry : mSubscriptions.entrySet()) {
- String id = subscriptionEntry.getKey();
- Subscription sub = subscriptionEntry.getValue();
- List<SubscriptionCallback> callbackList = sub.getCallbacks();
- List<Bundle> optionsList = sub.getOptionsList();
- for (int i = 0; i < callbackList.size(); ++i) {
- try {
- mServiceBinder.addSubscription(id, callbackList.get(i).mToken,
- optionsList.get(i), mServiceCallbacks);
- } catch (RemoteException ex) {
- // Process is crashing. We will disconnect, and upon reconnect we will
- // automatically reregister. So nothing to do here.
- Log.d(TAG, "addSubscription failed with RemoteException parentId="
- + id);
- }
- }
- }
- }
- });
- }
-
- private final void onConnectionFailed(final IMediaBrowserServiceCallbacks callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Log.e(TAG, "onConnectFailed for " + mServiceComponent);
-
- // Check to make sure there hasn't been a disconnect or a different
- // ServiceConnection.
- if (!isCurrent(callback, "onConnectFailed")) {
- return;
- }
- // Don't allow them to call us twice.
- if (mState != CONNECT_STATE_CONNECTING) {
- Log.w(TAG, "onConnect from service while mState="
- + getStateLabel(mState) + "... ignoring");
- return;
- }
-
- // Clean up
- forceCloseConnection();
-
- // Tell the app.
- mCallback.onConnectionFailed();
- }
- });
- }
-
- private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback,
- final String parentId, final MediaParceledListSlice list, final Bundle options) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // Check that there hasn't been a disconnect or a different
- // ServiceConnection.
- if (!isCurrent(callback, "onLoadChildren")) {
- return;
- }
-
- if (DBG) {
- Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId);
- }
-
- // Check that the subscription is still subscribed.
- final Subscription subscription = mSubscriptions.get(parentId);
- if (subscription != null) {
- // Tell the app.
- SubscriptionCallback subscriptionCallback =
- subscription.getCallback(mContext, options);
- if (subscriptionCallback != null) {
- List<MediaItem> data = list == null ? null : list.getList();
- if (options == null) {
- if (data == null) {
- subscriptionCallback.onError(parentId);
- } else {
- subscriptionCallback.onChildrenLoaded(parentId, data);
- }
- } else {
- if (data == null) {
- subscriptionCallback.onError(parentId, options);
- } else {
- subscriptionCallback.onChildrenLoaded(parentId, data, options);
- }
- }
- return;
- }
- }
- if (DBG) {
- Log.d(TAG, "onLoadChildren for id that isn't subscribed id=" + parentId);
- }
- }
- });
- }
-
- /**
- * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
- */
- private boolean isCurrent(IMediaBrowserServiceCallbacks callback, String funcName) {
- if (mServiceCallbacks != callback || mState == CONNECT_STATE_DISCONNECTING
- || mState == CONNECT_STATE_DISCONNECTED) {
- if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) {
- Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection="
- + mServiceCallbacks + " this=" + this);
- }
- return false;
- }
- return true;
- }
-
- private ServiceCallbacks getNewServiceCallbacks() {
- return new ServiceCallbacks(this);
- }
-
- /**
- * Log internal state.
- * @hide
- */
- void dump() {
- Log.d(TAG, "MediaBrowser...");
- Log.d(TAG, " mServiceComponent=" + mServiceComponent);
- Log.d(TAG, " mCallback=" + mCallback);
- Log.d(TAG, " mRootHints=" + mRootHints);
- Log.d(TAG, " mState=" + getStateLabel(mState));
- Log.d(TAG, " mServiceConnection=" + mServiceConnection);
- Log.d(TAG, " mServiceBinder=" + mServiceBinder);
- Log.d(TAG, " mServiceCallbacks=" + mServiceCallbacks);
- Log.d(TAG, " mRootId=" + mRootId);
- Log.d(TAG, " mMediaSessionToken=" + mMediaSessionToken);
- }
-
- /**
- * A class with information on a single media item for use in browsing/searching media.
- * MediaItems are application dependent so we cannot guarantee that they contain the
- * right values.
- */
- public static class MediaItem implements Parcelable {
- private final int mFlags;
- private final MediaDescription mDescription;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
- public @interface Flags { }
-
- /**
- * Flag: Indicates that the item has children of its own.
- */
- public static final int FLAG_BROWSABLE = 1 << 0;
-
- /**
- * Flag: Indicates that the item is playable.
- * <p>
- * The id of this item may be passed to
- * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
- * to start playing it.
- * </p>
- */
- public static final int FLAG_PLAYABLE = 1 << 1;
-
- /**
- * Create a new MediaItem for use in browsing media.
- * @param description The description of the media, which must include a
- * media id.
- * @param flags The flags for this item.
- */
- public MediaItem(@NonNull MediaDescription description, @Flags int flags) {
- if (description == null) {
- throw new IllegalArgumentException("description cannot be null");
- }
- if (TextUtils.isEmpty(description.getMediaId())) {
- throw new IllegalArgumentException("description must have a non-empty media id");
- }
- mFlags = flags;
- mDescription = description;
- }
-
- /**
- * Private constructor.
- */
- private MediaItem(Parcel in) {
- mFlags = in.readInt();
- mDescription = MediaDescription.CREATOR.createFromParcel(in);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mFlags);
- mDescription.writeToParcel(out, flags);
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("MediaItem{");
- sb.append("mFlags=").append(mFlags);
- sb.append(", mDescription=").append(mDescription);
- sb.append('}');
- return sb.toString();
- }
-
- public static final Parcelable.Creator<MediaItem> CREATOR =
- new Parcelable.Creator<MediaItem>() {
- @Override
- public MediaItem createFromParcel(Parcel in) {
- return new MediaItem(in);
- }
-
- @Override
- public MediaItem[] newArray(int size) {
- return new MediaItem[size];
- }
- };
-
- /**
- * Gets the flags of the item.
- */
- public @Flags int getFlags() {
- return mFlags;
- }
-
- /**
- * Returns whether this item is browsable.
- * @see #FLAG_BROWSABLE
- */
- public boolean isBrowsable() {
- return (mFlags & FLAG_BROWSABLE) != 0;
- }
-
- /**
- * Returns whether this item is playable.
- * @see #FLAG_PLAYABLE
- */
- public boolean isPlayable() {
- return (mFlags & FLAG_PLAYABLE) != 0;
- }
-
- /**
- * Returns the description of the media.
- */
- public @NonNull MediaDescription getDescription() {
- return mDescription;
- }
-
- /**
- * Returns the media id in the {@link MediaDescription} for this item.
- * @see android.media.MediaMetadata#METADATA_KEY_MEDIA_ID
- */
- public @Nullable String getMediaId() {
- return mDescription.getMediaId();
- }
- }
-
- /**
- * Callbacks for connection related events.
- */
- public static class ConnectionCallback {
- /**
- * Invoked after {@link MediaBrowser#connect()} when the request has successfully completed.
- */
- public void onConnected() {
- }
-
- /**
- * Invoked when the client is disconnected from the media browser.
- */
- public void onConnectionSuspended() {
- }
-
- /**
- * Invoked when the connection to the media browser failed.
- */
- public void onConnectionFailed() {
- }
- }
-
- /**
- * Callbacks for subscription related events.
- */
- public static abstract class SubscriptionCallback {
- Binder mToken;
-
- public SubscriptionCallback() {
- mToken = new Binder();
- }
-
- /**
- * Called when the list of children is loaded or updated.
- *
- * @param parentId The media id of the parent media item.
- * @param children The children which were loaded.
- */
- public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children) {
- }
-
- /**
- * Called when the list of children is loaded or updated.
- *
- * @param parentId The media id of the parent media item.
- * @param children The children which were loaded.
- * @param options The bundle of service-specific arguments sent to the media
- * browser service. The contents of this bundle may affect the
- * information returned when browsing.
- */
- public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children,
- @NonNull Bundle options) {
- }
-
- /**
- * Called when the id doesn't exist or other errors in subscribing.
- * <p>
- * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe}
- * called, because some errors may heal themselves.
- * </p>
- *
- * @param parentId The media id of the parent media item whose children could
- * not be loaded.
- */
- public void onError(@NonNull String parentId) {
- }
-
- /**
- * Called when the id doesn't exist or other errors in subscribing.
- * <p>
- * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe}
- * called, because some errors may heal themselves.
- * </p>
- *
- * @param parentId The media id of the parent media item whose children could
- * not be loaded.
- * @param options The bundle of service-specific arguments sent to the media
- * browser service.
- */
- public void onError(@NonNull String parentId, @NonNull Bundle options) {
- }
- }
-
- /**
- * Callback for receiving the result of {@link #getItem}.
- */
- public static abstract class ItemCallback {
- /**
- * Called when the item has been returned by the connected service.
- *
- * @param item The item that was returned or null if it doesn't exist.
- */
- public void onItemLoaded(MediaItem item) {
- }
-
- /**
- * Called there was an error retrieving it or the connected service doesn't support
- * {@link #getItem}.
- *
- * @param mediaId The media id of the media item which could not be loaded.
- */
- public void onError(@NonNull String mediaId) {
- }
- }
-
- /**
- * ServiceConnection to the other app.
- */
- private class MediaServiceConnection implements ServiceConnection {
- @Override
- public void onServiceConnected(final ComponentName name, final IBinder binder) {
- postOrRun(new Runnable() {
- @Override
- public void run() {
- if (DBG) {
- Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name
- + " binder=" + binder);
- dump();
- }
-
- // Make sure we are still the current connection, and that they haven't called
- // disconnect().
- if (!isCurrent("onServiceConnected")) {
- return;
- }
-
- // Save their binder
- mServiceBinder = IMediaBrowserService.Stub.asInterface(binder);
-
- // We make a new mServiceCallbacks each time we connect so that we can drop
- // responses from previous connections.
- mServiceCallbacks = getNewServiceCallbacks();
- mState = CONNECT_STATE_CONNECTING;
-
- // Call connect, which is async. When we get a response from that we will
- // say that we're connected.
- try {
- if (DBG) {
- Log.d(TAG, "ServiceCallbacks.onConnect...");
- dump();
- }
- mServiceBinder.connect(mContext.getPackageName(), mRootHints,
- mServiceCallbacks);
- } catch (RemoteException ex) {
- // Connect failed, which isn't good. But the auto-reconnect on the service
- // will take over and we will come back. We will also get the
- // onServiceDisconnected, which has all the cleanup code. So let that do
- // it.
- Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
- if (DBG) {
- Log.d(TAG, "ServiceCallbacks.onConnect...");
- dump();
- }
- }
- }
- });
- }
-
- @Override
- public void onServiceDisconnected(final ComponentName name) {
- postOrRun(new Runnable() {
- @Override
- public void run() {
- if (DBG) {
- Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name
- + " this=" + this + " mServiceConnection=" + mServiceConnection);
- dump();
- }
-
- // Make sure we are still the current connection, and that they haven't called
- // disconnect().
- if (!isCurrent("onServiceDisconnected")) {
- return;
- }
-
- // Clear out what we set in onServiceConnected
- mServiceBinder = null;
- mServiceCallbacks = null;
-
- // And tell the app that it's suspended.
- mState = CONNECT_STATE_SUSPENDED;
- mCallback.onConnectionSuspended();
- }
- });
- }
-
- private void postOrRun(Runnable r) {
- if (Thread.currentThread() == mHandler.getLooper().getThread()) {
- r.run();
- } else {
- mHandler.post(r);
- }
- }
-
- /**
- * Return true if this is the current ServiceConnection. Also logs if it's not.
- */
- private boolean isCurrent(String funcName) {
- if (mServiceConnection != this || mState == CONNECT_STATE_DISCONNECTING
- || mState == CONNECT_STATE_DISCONNECTED) {
- if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) {
- // Check mState, because otherwise this log is noisy.
- Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection="
- + mServiceConnection + " this=" + this);
- }
- return false;
- }
- return true;
- }
- }
-
- /**
- * Callbacks from the service.
- */
- private static class ServiceCallbacks extends IMediaBrowserServiceCallbacks.Stub {
- private WeakReference<MediaBrowser> mMediaBrowser;
-
- public ServiceCallbacks(MediaBrowser mediaBrowser) {
- mMediaBrowser = new WeakReference<MediaBrowser>(mediaBrowser);
- }
-
- /**
- * The other side has acknowledged our connection. The parameters to this function
- * are the initial data as requested.
- */
- @Override
- public void onConnect(String root, MediaSession.Token session,
- final Bundle extras) {
- MediaBrowser mediaBrowser = mMediaBrowser.get();
- if (mediaBrowser != null) {
- mediaBrowser.onServiceConnected(this, root, session, extras);
- }
- }
-
- /**
- * The other side does not like us. Tell the app via onConnectionFailed.
- */
- @Override
- public void onConnectFailed() {
- MediaBrowser mediaBrowser = mMediaBrowser.get();
- if (mediaBrowser != null) {
- mediaBrowser.onConnectionFailed(this);
- }
- }
-
- @Override
- public void onLoadChildren(String parentId, MediaParceledListSlice list) {
- onLoadChildrenWithOptions(parentId, list, null);
- }
-
- @Override
- public void onLoadChildrenWithOptions(String parentId, MediaParceledListSlice list,
- final Bundle options) {
- MediaBrowser mediaBrowser = mMediaBrowser.get();
- if (mediaBrowser != null) {
- mediaBrowser.onLoadChildren(this, parentId, list, options);
- }
- }
- }
-
- private static class Subscription {
- private final List<SubscriptionCallback> mCallbacks;
- private final List<Bundle> mOptionsList;
-
- public Subscription() {
- mCallbacks = new ArrayList<>();
- mOptionsList = new ArrayList<>();
- }
-
- public boolean isEmpty() {
- return mCallbacks.isEmpty();
- }
-
- public List<Bundle> getOptionsList() {
- return mOptionsList;
- }
-
- public List<SubscriptionCallback> getCallbacks() {
- return mCallbacks;
- }
-
- public SubscriptionCallback getCallback(Context context, Bundle options) {
- if (options != null) {
- options.setClassLoader(context.getClassLoader());
- }
- for (int i = 0; i < mOptionsList.size(); ++i) {
- if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
- return mCallbacks.get(i);
- }
- }
- return null;
- }
-
- public void putCallback(Context context, Bundle options, SubscriptionCallback callback) {
- if (options != null) {
- options.setClassLoader(context.getClassLoader());
- }
- for (int i = 0; i < mOptionsList.size(); ++i) {
- if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
- mCallbacks.set(i, callback);
- return;
- }
- }
- mCallbacks.add(callback);
- mOptionsList.add(options);
- }
- }
-}
diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java
deleted file mode 100644
index 2943e60..0000000
--- a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.browse;
-
-import android.os.Bundle;
-
-/**
- * @hide
- */
-public class MediaBrowserUtils {
- public static boolean areSameOptions(Bundle options1, Bundle options2) {
- if (options1 == options2) {
- return true;
- } else if (options1 == null) {
- return options2.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1
- && options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1;
- } else if (options2 == null) {
- return options1.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1
- && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1;
- } else {
- return options1.getInt(MediaBrowser.EXTRA_PAGE, -1)
- == options2.getInt(MediaBrowser.EXTRA_PAGE, -1)
- && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1)
- == options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
- }
- }
-
- public static boolean hasDuplicatedItems(Bundle options1, Bundle options2) {
- int page1 = options1 == null ? -1 : options1.getInt(MediaBrowser.EXTRA_PAGE, -1);
- int page2 = options2 == null ? -1 : options2.getInt(MediaBrowser.EXTRA_PAGE, -1);
- int pageSize1 = options1 == null ? -1 : options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
- int pageSize2 = options2 == null ? -1 : options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
-
- int startIndex1, startIndex2, endIndex1, endIndex2;
- if (page1 == -1 || pageSize1 == -1) {
- startIndex1 = 0;
- endIndex1 = Integer.MAX_VALUE;
- } else {
- startIndex1 = pageSize1 * page1;
- endIndex1 = startIndex1 + pageSize1 - 1;
- }
-
- if (page2 == -1 || pageSize2 == -1) {
- startIndex2 = 0;
- endIndex2 = Integer.MAX_VALUE;
- } else {
- startIndex2 = pageSize2 * page2;
- endIndex2 = startIndex2 + pageSize2 - 1;
- }
-
- if (startIndex1 <= startIndex2 && startIndex2 <= endIndex1) {
- return true;
- } else if (startIndex1 <= endIndex2 && endIndex2 <= endIndex1) {
- return true;
- }
- return false;
- }
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl
deleted file mode 100644
index 4b9e4bd..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.media.session.MediaSession;
-
-/**
- * Listens for changes to the list of active sessions.
- * @hide
- */
-oneway interface IActiveSessionsListener {
- void onActiveSessionsChanged(in List<MediaSession.Token> sessions);
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl
deleted file mode 100644
index 322bffa..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.media.session.MediaSession;
-import android.view.KeyEvent;
-
-/**
- * @hide
- */
-oneway interface ICallback {
- void onMediaKeyEventDispatchedToMediaSession(in KeyEvent event,
- in MediaSession.Token sessionToken);
- void onMediaKeyEventDispatchedToMediaButtonReceiver(in KeyEvent event,
- in ComponentName mediaButtonReceiver);
-
- void onAddressedPlayerChangedToMediaSession(in MediaSession.Token sessionToken);
- void onAddressedPlayerChangedToMediaButtonReceiver(in ComponentName mediaButtonReceiver);
-}
-
diff --git a/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl
deleted file mode 100644
index aa98ea3..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.os.ResultReceiver;
-import android.view.KeyEvent;
-
-/**
- * Listener to handle media key.
- * @hide
- */
-oneway interface IOnMediaKeyListener {
- void onMediaKey(in KeyEvent event, in ResultReceiver result);
-}
-
diff --git a/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl
deleted file mode 100644
index 07b8347..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.view.KeyEvent;
-
-/**
- * Listener to handle volume key long-press.
- * @hide
- */
-oneway interface IOnVolumeKeyLongPressListener {
- void onVolumeKeyLongPress(in KeyEvent event);
-}
-
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl
deleted file mode 100644
index 14b1c64..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.app.PendingIntent;
-//import android.media.AudioAttributes;
-import android.media.MediaMetadata;
-import android.media.MediaParceledListSlice;
-import android.media.session.ISessionController;
-import android.media.session.PlaybackState;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-/**
- * Interface to a MediaSession in the system.
- * @hide
- */
-interface ISession {
- void sendEvent(String event, in Bundle data);
- ISessionController getController();
- void setFlags(int flags);
- void setActive(boolean active);
- void setMediaButtonReceiver(in PendingIntent mbr);
- void setLaunchPendingIntent(in PendingIntent pi);
- void destroy();
-
- // These commands are for the TransportPerformer
- void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription);
- void setPlaybackState(in PlaybackState state);
- void setQueue(in MediaParceledListSlice queue);
- void setQueueTitle(CharSequence title);
- void setExtras(in Bundle extras);
- void setRatingType(int type);
-
- // These commands relate to volume handling
- //TODO(b/119751592): Decide if AudioAttributes should be updated.
- //void setPlaybackToLocal(in AudioAttributes attributes);
- void setPlaybackToRemote(int control, int max);
- void setCurrentVolume(int currentVolume);
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl
deleted file mode 100644
index 626338d..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl
+++ /dev/null
@@ -1,71 +0,0 @@
-/* Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.content.Intent;
-import android.media.Rating;
-import android.media.session.ISessionControllerCallback;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-/**
- * @hide
- */
-oneway interface ISessionCallback {
- void onCommand(String packageName, int pid, int uid, ISessionControllerCallback caller,
- String command, in Bundle args, in ResultReceiver cb);
- void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
- int sequenceNumber, in ResultReceiver cb);
- void onMediaButtonFromController(String packageName, int pid, int uid,
- ISessionControllerCallback caller, in Intent mediaButtonIntent);
-
- // These callbacks are for the TransportPerformer
- void onPrepare(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onPrepareFromMediaId(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String mediaId, in Bundle extras);
- void onPrepareFromSearch(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String query, in Bundle extras);
- void onPrepareFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
- in Uri uri, in Bundle extras);
- void onPlay(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onPlayFromMediaId(String packageName, int pid, int uid, ISessionControllerCallback caller,
- String mediaId, in Bundle extras);
- void onPlayFromSearch(String packageName, int pid, int uid, ISessionControllerCallback caller,
- String query, in Bundle extras);
- void onPlayFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
- in Uri uri, in Bundle extras);
- void onSkipToTrack(String packageName, int pid, int uid, ISessionControllerCallback caller,
- long id);
- void onPause(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onStop(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onNext(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onPrevious(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onFastForward(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onRewind(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onSeekTo(String packageName, int pid, int uid, ISessionControllerCallback caller,
- long pos);
- void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
- in Rating rating);
- void onCustomAction(String packageName, int pid, int uid, ISessionControllerCallback caller,
- String action, in Bundle args);
-
- // These callbacks are for volume handling
- void onAdjustVolume(String packageName, int pid, int uid, ISessionControllerCallback caller,
- int direction);
- void onSetVolumeTo(String packageName, int pid, int uid,
- ISessionControllerCallback caller, int value);
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl
deleted file mode 100644
index 433b12f..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.media.MediaMetadata;
-import android.media.MediaParceledListSlice;
-import android.media.Rating;
-import android.media.session.ISessionControllerCallback;
-import android.media.session.MediaSession;
-import android.media.session.ParcelableVolumeInfo;
-import android.media.session.PlaybackState;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.view.KeyEvent;
-
-import java.util.List;
-
-/**
- * Interface to MediaSessionRecord in the system.
- * @hide
- */
-interface ISessionController {
- void sendCommand(String packageName, ISessionControllerCallback caller,
- String command, in Bundle args, in ResultReceiver cb);
- boolean sendMediaButton(String packageName, ISessionControllerCallback caller,
- boolean asSystemService, in KeyEvent mediaButton);
- void registerCallbackListener(String packageName, ISessionControllerCallback cb);
- void unregisterCallbackListener(ISessionControllerCallback cb);
- boolean isTransportControlEnabled();
- String getPackageName();
- String getTag();
- PendingIntent getLaunchPendingIntent();
- long getFlags();
- ParcelableVolumeInfo getVolumeAttributes();
- void adjustVolume(String packageName, String opPackageName, ISessionControllerCallback caller,
- boolean asSystemService, int direction, int flags);
- void setVolumeTo(String packageName, String opPackageName, ISessionControllerCallback caller,
- int value, int flags);
-
- // These commands are for the TransportControls
- void prepare(String packageName, ISessionControllerCallback caller);
- void prepareFromMediaId(String packageName, ISessionControllerCallback caller,
- String mediaId, in Bundle extras);
- void prepareFromSearch(String packageName, ISessionControllerCallback caller,
- String string, in Bundle extras);
- void prepareFromUri(String packageName, ISessionControllerCallback caller,
- in Uri uri, in Bundle extras);
- void play(String packageName, ISessionControllerCallback caller);
- void playFromMediaId(String packageName, ISessionControllerCallback caller,
- String mediaId, in Bundle extras);
- void playFromSearch(String packageName, ISessionControllerCallback caller,
- String string, in Bundle extras);
- void playFromUri(String packageName, ISessionControllerCallback caller,
- in Uri uri, in Bundle extras);
- void skipToQueueItem(String packageName, ISessionControllerCallback caller, long id);
- void pause(String packageName, ISessionControllerCallback caller);
- void stop(String packageName, ISessionControllerCallback caller);
- void next(String packageName, ISessionControllerCallback caller);
- void previous(String packageName, ISessionControllerCallback caller);
- void fastForward(String packageName, ISessionControllerCallback caller);
- void rewind(String packageName, ISessionControllerCallback caller);
- void seekTo(String packageName, ISessionControllerCallback caller, long pos);
- void rate(String packageName, ISessionControllerCallback caller, in Rating rating);
- void sendCustomAction(String packageName, ISessionControllerCallback caller,
- String action, in Bundle args);
- MediaMetadata getMetadata();
- PlaybackState getPlaybackState();
- MediaParceledListSlice getQueue();
- CharSequence getQueueTitle();
- Bundle getExtras();
- int getRatingType();
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl
deleted file mode 100644
index f5cc4f6..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.media.MediaMetadata;
-import android.media.MediaParceledListSlice;
-import android.media.session.ParcelableVolumeInfo;
-import android.media.session.PlaybackState;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-
-/**
- * @hide
- */
-oneway interface ISessionControllerCallback {
- void onEvent(String event, in Bundle extras);
- void onSessionDestroyed();
-
- // These callbacks are for the TransportController
- void onPlaybackStateChanged(in PlaybackState state);
- void onMetadataChanged(in MediaMetadata metadata);
- void onQueueChanged(in MediaParceledListSlice queue);
- void onQueueTitleChanged(CharSequence title);
- void onExtrasChanged(in Bundle extras);
- void onVolumeInfoChanged(in ParcelableVolumeInfo info);
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl
deleted file mode 100644
index d6c226f..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.content.ComponentName;
-import android.media.IRemoteVolumeController;
-import android.media.session.IActiveSessionsListener;
-import android.media.session.ICallback;
-import android.media.session.IOnMediaKeyListener;
-import android.media.session.IOnVolumeKeyLongPressListener;
-import android.media.session.ISession;
-import android.media.session.ISessionCallback;
-import android.os.Bundle;
-import android.view.KeyEvent;
-
-/**
- * Interface to the MediaSessionManagerService
- * @hide
- */
-interface ISessionManager {
- ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
- List<IBinder> getSessions(in ComponentName compName, int userId);
- void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
- boolean needWakeLock);
- void dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService,
- in KeyEvent keyEvent, int stream, boolean musicOnly);
- void dispatchAdjustVolume(String packageName, String opPackageName, int suggestedStream,
- int delta, int flags);
- void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
- int userId);
- void removeSessionsListener(in IActiveSessionsListener listener);
-
- // This is for the system volume UI only
- void setRemoteVolumeController(in IRemoteVolumeController rvc);
-
- // For PhoneWindowManager to precheck media keys
- boolean isGlobalPriorityActive();
-
- void setCallback(in ICallback callback);
- void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
- void setOnMediaKeyListener(in IOnMediaKeyListener listener);
-
- // MediaSession2
- boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java
deleted file mode 100644
index 65682a8..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java
+++ /dev/null
@@ -1,1190 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.media.MediaMetadata;
-import android.media.MediaParceledListSlice;
-import android.media.Rating;
-import android.media.VolumeProvider;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.KeyEvent;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Allows an app to interact with an ongoing media session. Media buttons and
- * other commands can be sent to the session. A callback may be registered to
- * receive updates from the session, such as metadata and play state changes.
- * <p>
- * A MediaController can be created through {@link MediaSessionManager} if you
- * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or are an
- * enabled notification listener or by getting a {@link MediaSession.Token}
- * directly from the session owner.
- * <p>
- * MediaController objects are thread-safe.
- */
-public final class MediaController {
- private static final String TAG = "MediaController";
-
- private static final int MSG_EVENT = 1;
- private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
- private static final int MSG_UPDATE_METADATA = 3;
- private static final int MSG_UPDATE_VOLUME = 4;
- private static final int MSG_UPDATE_QUEUE = 5;
- private static final int MSG_UPDATE_QUEUE_TITLE = 6;
- private static final int MSG_UPDATE_EXTRAS = 7;
- private static final int MSG_DESTROYED = 8;
-
- private final ISessionController mSessionBinder;
-
- private final MediaSession.Token mToken;
- private final Context mContext;
- private final CallbackStub mCbStub = new CallbackStub(this);
- private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
- private final Object mLock = new Object();
-
- private boolean mCbRegistered = false;
- private String mPackageName;
- private String mTag;
-
- private final TransportControls mTransportControls;
-
- /**
- * Call for creating a MediaController directly from a binder. Should only
- * be used by framework code.
- *
- * @hide
- */
- public MediaController(Context context, ISessionController sessionBinder) {
- if (sessionBinder == null) {
- throw new IllegalArgumentException("Session token cannot be null");
- }
- if (context == null) {
- throw new IllegalArgumentException("Context cannot be null");
- }
- mSessionBinder = sessionBinder;
- mTransportControls = new TransportControls();
- mToken = new MediaSession.Token(sessionBinder);
- mContext = context;
- }
-
- /**
- * Create a new MediaController from a session's token.
- *
- * @param context The caller's context.
- * @param token The token for the session.
- */
- public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) {
- this(context, token.getBinder());
- }
-
- /**
- * Get a {@link TransportControls} instance to send transport actions to
- * the associated session.
- *
- * @return A transport controls instance.
- */
- public @NonNull TransportControls getTransportControls() {
- return mTransportControls;
- }
-
- /**
- * Send the specified media button event to the session. Only media keys can
- * be sent by this method, other keys will be ignored.
- *
- * @param keyEvent The media button event to dispatch.
- * @return true if the event was sent to the session, false otherwise.
- */
- public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
- return dispatchMediaButtonEventInternal(false, keyEvent);
- }
-
- /**
- * Dispatches the media button event as system service to the session.
- * <p>
- * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
- * foreground activity didn't consume the key from the hardware devices.
- *
- * @param keyEvent media key event
- * @return {@code true} if the event was sent to the session, {@code false} otherwise
- * @hide
- */
- public boolean dispatchMediaButtonEventAsSystemService(@NonNull KeyEvent keyEvent) {
- return dispatchMediaButtonEventInternal(true, keyEvent);
- }
-
- private boolean dispatchMediaButtonEventInternal(boolean asSystemService,
- @NonNull KeyEvent keyEvent) {
- if (keyEvent == null) {
- throw new IllegalArgumentException("KeyEvent may not be null");
- }
- if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
- return false;
- }
- try {
- return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub,
- asSystemService, keyEvent);
- } catch (RemoteException e) {
- // System is dead. =(
- }
- return false;
- }
-
- /**
- * Dispatches the volume button event as system service to the session.
- * <p>
- * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
- * foreground activity didn't consume the key from the hardware devices.
- *
- * @param keyEvent volume key event
- * @hide
- */
- public void dispatchVolumeButtonEventAsSystemService(@NonNull KeyEvent keyEvent) {
- switch (keyEvent.getAction()) {
- case KeyEvent.ACTION_DOWN: {
- int direction = 0;
- switch (keyEvent.getKeyCode()) {
- case KeyEvent.KEYCODE_VOLUME_UP:
- direction = AudioManager.ADJUST_RAISE;
- break;
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- direction = AudioManager.ADJUST_LOWER;
- break;
- case KeyEvent.KEYCODE_VOLUME_MUTE:
- direction = AudioManager.ADJUST_TOGGLE_MUTE;
- break;
- }
- try {
- mSessionBinder.adjustVolume(mContext.getPackageName(),
- mContext.getOpPackageName(), mCbStub, true, direction,
- AudioManager.FLAG_SHOW_UI);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling adjustVolumeBy", e);
- }
- }
-
- case KeyEvent.ACTION_UP: {
- final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
- | AudioManager.FLAG_FROM_KEY;
- try {
- mSessionBinder.adjustVolume(mContext.getPackageName(),
- mContext.getOpPackageName(), mCbStub, true, 0, flags);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling adjustVolumeBy", e);
- }
- }
- }
- }
-
- /**
- * Get the current playback state for this session.
- *
- * @return The current PlaybackState or null
- */
- public @Nullable PlaybackState getPlaybackState() {
- try {
- return mSessionBinder.getPlaybackState();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getPlaybackState.", e);
- return null;
- }
- }
-
- /**
- * Get the current metadata for this session.
- *
- * @return The current MediaMetadata or null.
- */
- public @Nullable MediaMetadata getMetadata() {
- try {
- return mSessionBinder.getMetadata();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getMetadata.", e);
- return null;
- }
- }
-
- /**
- * Get the current play queue for this session if one is set. If you only
- * care about the current item {@link #getMetadata()} should be used.
- *
- * @return The current play queue or null.
- */
- public @Nullable List<MediaSession.QueueItem> getQueue() {
- try {
- MediaParceledListSlice queue = mSessionBinder.getQueue();
- if (queue != null) {
- return queue.getList();
- }
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getQueue.", e);
- }
- return null;
- }
-
- /**
- * Get the queue title for this session.
- */
- public @Nullable CharSequence getQueueTitle() {
- try {
- return mSessionBinder.getQueueTitle();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getQueueTitle", e);
- }
- return null;
- }
-
- /**
- * Get the extras for this session.
- */
- public @Nullable Bundle getExtras() {
- try {
- return mSessionBinder.getExtras();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getExtras", e);
- }
- return null;
- }
-
- /**
- * Get the rating type supported by the session. One of:
- * <ul>
- * <li>{@link Rating#RATING_NONE}</li>
- * <li>{@link Rating#RATING_HEART}</li>
- * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
- * <li>{@link Rating#RATING_3_STARS}</li>
- * <li>{@link Rating#RATING_4_STARS}</li>
- * <li>{@link Rating#RATING_5_STARS}</li>
- * <li>{@link Rating#RATING_PERCENTAGE}</li>
- * </ul>
- *
- * @return The supported rating type
- */
- public int getRatingType() {
- try {
- return mSessionBinder.getRatingType();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getRatingType.", e);
- return Rating.RATING_NONE;
- }
- }
-
- /**
- * Get the flags for this session. Flags are defined in {@link MediaSession}.
- *
- * @return The current set of flags for the session.
- */
- public @MediaSession.SessionFlags long getFlags() {
- try {
- return mSessionBinder.getFlags();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getFlags.", e);
- }
- return 0;
- }
-
- /**
- * Get the current playback info for this session.
- *
- * @return The current playback info or null.
- */
- public @Nullable PlaybackInfo getPlaybackInfo() {
- try {
- ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
- return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType,
- result.maxVolume, result.currentVolume);
-
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getAudioInfo.", e);
- }
- return null;
- }
-
- /**
- * Get an intent for launching UI associated with this session if one
- * exists.
- *
- * @return A {@link PendingIntent} to launch UI or null.
- */
- public @Nullable PendingIntent getSessionActivity() {
- try {
- return mSessionBinder.getLaunchPendingIntent();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getPendingIntent.", e);
- }
- return null;
- }
-
- /**
- * Get the token for the session this is connected to.
- *
- * @return The token for the connected session.
- */
- public @NonNull MediaSession.Token getSessionToken() {
- return mToken;
- }
-
- /**
- * Set the volume of the output this session is playing on. The command will
- * be ignored if it does not support
- * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
- * {@link AudioManager} may be used to affect the handling.
- *
- * @see #getPlaybackInfo()
- * @param value The value to set it to, between 0 and the reported max.
- * @param flags Flags from {@link AudioManager} to include with the volume
- * request.
- */
- public void setVolumeTo(int value, int flags) {
- try {
- mSessionBinder.setVolumeTo(mContext.getPackageName(), mContext.getOpPackageName(),
- mCbStub, value, flags);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling setVolumeTo.", e);
- }
- }
-
- /**
- * Adjust the volume of the output this session is playing on. The direction
- * must be one of {@link AudioManager#ADJUST_LOWER},
- * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
- * The command will be ignored if the session does not support
- * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
- * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
- * {@link AudioManager} may be used to affect the handling.
- *
- * @see #getPlaybackInfo()
- * @param direction The direction to adjust the volume in.
- * @param flags Any flags to pass with the command.
- */
- public void adjustVolume(int direction, int flags) {
- try {
- mSessionBinder.adjustVolume(mContext.getPackageName(), mContext.getOpPackageName(),
- mCbStub, false, direction, flags);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
- }
- }
-
- /**
- * Registers a callback to receive updates from the Session. Updates will be
- * posted on the caller's thread.
- *
- * @param callback The callback object, must not be null.
- */
- public void registerCallback(@NonNull Callback callback) {
- registerCallback(callback, null);
- }
-
- /**
- * Registers a callback to receive updates from the session. Updates will be
- * posted on the specified handler's thread.
- *
- * @param callback The callback object, must not be null.
- * @param handler The handler to post updates on. If null the callers thread
- * will be used.
- */
- public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) {
- if (callback == null) {
- throw new IllegalArgumentException("callback must not be null");
- }
- if (handler == null) {
- handler = new Handler();
- }
- synchronized (mLock) {
- addCallbackLocked(callback, handler);
- }
- }
-
- /**
- * Unregisters the specified callback. If an update has already been posted
- * you may still receive it after calling this method.
- *
- * @param callback The callback to remove.
- */
- public void unregisterCallback(@NonNull Callback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback must not be null");
- }
- synchronized (mLock) {
- removeCallbackLocked(callback);
- }
- }
-
- /**
- * Sends a generic command to the session. It is up to the session creator
- * to decide what commands and parameters they will support. As such,
- * commands should only be sent to sessions that the controller owns.
- *
- * @param command The command to send
- * @param args Any parameters to include with the command
- * @param cb The callback to receive the result on
- */
- public void sendCommand(@NonNull String command, @Nullable Bundle args,
- @Nullable ResultReceiver cb) {
- if (TextUtils.isEmpty(command)) {
- throw new IllegalArgumentException("command cannot be null or empty");
- }
- try {
- mSessionBinder.sendCommand(mContext.getPackageName(), mCbStub, command, args, cb);
- } catch (RemoteException e) {
- Log.d(TAG, "Dead object in sendCommand.", e);
- }
- }
-
- /**
- * Get the session owner's package name.
- *
- * @return The package name of of the session owner.
- */
- public String getPackageName() {
- if (mPackageName == null) {
- try {
- mPackageName = mSessionBinder.getPackageName();
- } catch (RemoteException e) {
- Log.d(TAG, "Dead object in getPackageName.", e);
- }
- }
- return mPackageName;
- }
-
- /**
- * Get the session's tag for debugging purposes.
- *
- * @return The session's tag.
- * @hide
- */
- public String getTag() {
- if (mTag == null) {
- try {
- mTag = mSessionBinder.getTag();
- } catch (RemoteException e) {
- Log.d(TAG, "Dead object in getTag.", e);
- }
- }
- return mTag;
- }
-
- /*
- * @hide
- */
- ISessionController getSessionBinder() {
- return mSessionBinder;
- }
-
- /**
- * @hide
- */
- @UnsupportedAppUsage
- public boolean controlsSameSession(MediaController other) {
- if (other == null) return false;
- return mSessionBinder.asBinder() == other.getSessionBinder().asBinder();
- }
-
- private void addCallbackLocked(Callback cb, Handler handler) {
- if (getHandlerForCallbackLocked(cb) != null) {
- Log.w(TAG, "Callback is already added, ignoring");
- return;
- }
- MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
- mCallbacks.add(holder);
- holder.mRegistered = true;
-
- if (!mCbRegistered) {
- try {
- mSessionBinder.registerCallbackListener(mContext.getPackageName(), mCbStub);
- mCbRegistered = true;
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerCallback", e);
- }
- }
- }
-
- private boolean removeCallbackLocked(Callback cb) {
- boolean success = false;
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- MessageHandler handler = mCallbacks.get(i);
- if (cb == handler.mCallback) {
- mCallbacks.remove(i);
- success = true;
- handler.mRegistered = false;
- }
- }
- if (mCbRegistered && mCallbacks.size() == 0) {
- try {
- mSessionBinder.unregisterCallbackListener(mCbStub);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in removeCallbackLocked");
- }
- mCbRegistered = false;
- }
- return success;
- }
-
- private MessageHandler getHandlerForCallbackLocked(Callback cb) {
- if (cb == null) {
- throw new IllegalArgumentException("Callback cannot be null");
- }
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- MessageHandler handler = mCallbacks.get(i);
- if (cb == handler.mCallback) {
- return handler;
- }
- }
- return null;
- }
-
- private final void postMessage(int what, Object obj, Bundle data) {
- synchronized (mLock) {
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(what, obj, data);
- }
- }
- }
-
- /**
- * Callback for receiving updates from the session. A Callback can be
- * registered using {@link #registerCallback}.
- */
- public static abstract class Callback {
- /**
- * Override to handle the session being destroyed. The session is no
- * longer valid after this call and calls to it will be ignored.
- */
- public void onSessionDestroyed() {
- }
-
- /**
- * Override to handle custom events sent by the session owner without a
- * specified interface. Controllers should only handle these for
- * sessions they own.
- *
- * @param event The event from the session.
- * @param extras Optional parameters for the event, may be null.
- */
- public void onSessionEvent(@NonNull String event, @Nullable Bundle extras) {
- }
-
- /**
- * Override to handle changes in playback state.
- *
- * @param state The new playback state of the session
- */
- public void onPlaybackStateChanged(@Nullable PlaybackState state) {
- }
-
- /**
- * Override to handle changes to the current metadata.
- *
- * @param metadata The current metadata for the session or null if none.
- * @see MediaMetadata
- */
- public void onMetadataChanged(@Nullable MediaMetadata metadata) {
- }
-
- /**
- * Override to handle changes to items in the queue.
- *
- * @param queue A list of items in the current play queue. It should
- * include the currently playing item as well as previous and
- * upcoming items if applicable.
- * @see MediaSession.QueueItem
- */
- public void onQueueChanged(@Nullable List<MediaSession.QueueItem> queue) {
- }
-
- /**
- * Override to handle changes to the queue title.
- *
- * @param title The title that should be displayed along with the play queue such as
- * "Now Playing". May be null if there is no such title.
- */
- public void onQueueTitleChanged(@Nullable CharSequence title) {
- }
-
- /**
- * Override to handle changes to the {@link MediaSession} extras.
- *
- * @param extras The extras that can include other information associated with the
- * {@link MediaSession}.
- */
- public void onExtrasChanged(@Nullable Bundle extras) {
- }
-
- /**
- * Override to handle changes to the audio info.
- *
- * @param info The current audio info for this session.
- */
- public void onAudioInfoChanged(PlaybackInfo info) {
- }
- }
-
- /**
- * Interface for controlling media playback on a session. This allows an app
- * to send media transport commands to the session.
- */
- public final class TransportControls {
- private static final String TAG = "TransportController";
-
- private TransportControls() {
- }
-
- /**
- * Request that the player prepare its playback. In other words, other sessions can continue
- * to play during the preparation of this session. This method can be used to speed up the
- * start of the playback. Once the preparation is done, the session will change its playback
- * state to {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to
- * start playback.
- */
- public void prepare() {
- try {
- mSessionBinder.prepare(mContext.getPackageName(), mCbStub);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare.", e);
- }
- }
-
- /**
- * Request that the player prepare playback for a specific media id. In other words, other
- * sessions can continue to play during the preparation of this session. This method can be
- * used to speed up the start of the playback. Once the preparation is done, the session
- * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
- * {@link #play} can be called to start playback. If the preparation is not needed,
- * {@link #playFromMediaId} can be directly called without this method.
- *
- * @param mediaId The id of the requested media.
- * @param extras Optional extras that can include extra information about the media item
- * to be prepared.
- */
- public void prepareFromMediaId(String mediaId, Bundle extras) {
- if (TextUtils.isEmpty(mediaId)) {
- throw new IllegalArgumentException(
- "You must specify a non-empty String for prepareFromMediaId.");
- }
- try {
- mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mCbStub, mediaId,
- extras);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
- }
- }
-
- /**
- * Request that the player prepare playback for a specific search query. An empty or null
- * query should be treated as a request to prepare any music. In other words, other sessions
- * can continue to play during the preparation of this session. This method can be used to
- * speed up the start of the playback. Once the preparation is done, the session will
- * change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
- * {@link #play} can be called to start playback. If the preparation is not needed,
- * {@link #playFromSearch} can be directly called without this method.
- *
- * @param query The search query.
- * @param extras Optional extras that can include extra information
- * about the query.
- */
- public void prepareFromSearch(String query, Bundle extras) {
- if (query == null) {
- // This is to remain compatible with
- // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
- query = "";
- }
- try {
- mSessionBinder.prepareFromSearch(mContext.getPackageName(), mCbStub, query,
- extras);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
- }
- }
-
- /**
- * Request that the player prepare playback for a specific {@link Uri}. In other words,
- * other sessions can continue to play during the preparation of this session. This method
- * can be used to speed up the start of the playback. Once the preparation is done, the
- * session will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
- * {@link #play} can be called to start playback. If the preparation is not needed,
- * {@link #playFromUri} can be directly called without this method.
- *
- * @param uri The URI of the requested media.
- * @param extras Optional extras that can include extra information about the media item
- * to be prepared.
- */
- public void prepareFromUri(Uri uri, Bundle extras) {
- if (uri == null || Uri.EMPTY.equals(uri)) {
- throw new IllegalArgumentException(
- "You must specify a non-empty Uri for prepareFromUri.");
- }
- try {
- mSessionBinder.prepareFromUri(mContext.getPackageName(), mCbStub, uri, extras);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
- }
- }
-
- /**
- * Request that the player start its playback at its current position.
- */
- public void play() {
- try {
- mSessionBinder.play(mContext.getPackageName(), mCbStub);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play.", e);
- }
- }
-
- /**
- * Request that the player start playback for a specific media id.
- *
- * @param mediaId The id of the requested media.
- * @param extras Optional extras that can include extra information about the media item
- * to be played.
- */
- public void playFromMediaId(String mediaId, Bundle extras) {
- if (TextUtils.isEmpty(mediaId)) {
- throw new IllegalArgumentException(
- "You must specify a non-empty String for playFromMediaId.");
- }
- try {
- mSessionBinder.playFromMediaId(mContext.getPackageName(), mCbStub, mediaId,
- extras);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
- }
- }
-
- /**
- * Request that the player start playback for a specific search query.
- * An empty or null query should be treated as a request to play any
- * music.
- *
- * @param query The search query.
- * @param extras Optional extras that can include extra information
- * about the query.
- */
- public void playFromSearch(String query, Bundle extras) {
- if (query == null) {
- // This is to remain compatible with
- // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
- query = "";
- }
- try {
- mSessionBinder.playFromSearch(mContext.getPackageName(), mCbStub, query, extras);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + query + ").", e);
- }
- }
-
- /**
- * Request that the player start playback for a specific {@link Uri}.
- *
- * @param uri The URI of the requested media.
- * @param extras Optional extras that can include extra information about the media item
- * to be played.
- */
- public void playFromUri(Uri uri, Bundle extras) {
- if (uri == null || Uri.EMPTY.equals(uri)) {
- throw new IllegalArgumentException(
- "You must specify a non-empty Uri for playFromUri.");
- }
- try {
- mSessionBinder.playFromUri(mContext.getPackageName(), mCbStub, uri, extras);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + uri + ").", e);
- }
- }
-
- /**
- * Play an item with a specific id in the play queue. If you specify an
- * id that is not in the play queue, the behavior is undefined.
- */
- public void skipToQueueItem(long id) {
- try {
- mSessionBinder.skipToQueueItem(mContext.getPackageName(), mCbStub, id);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
- }
- }
-
- /**
- * Request that the player pause its playback and stay at its current
- * position.
- */
- public void pause() {
- try {
- mSessionBinder.pause(mContext.getPackageName(), mCbStub);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling pause.", e);
- }
- }
-
- /**
- * Request that the player stop its playback; it may clear its state in
- * whatever way is appropriate.
- */
- public void stop() {
- try {
- mSessionBinder.stop(mContext.getPackageName(), mCbStub);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling stop.", e);
- }
- }
-
- /**
- * Move to a new location in the media stream.
- *
- * @param pos Position to move to, in milliseconds.
- */
- public void seekTo(long pos) {
- try {
- mSessionBinder.seekTo(mContext.getPackageName(), mCbStub, pos);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling seekTo.", e);
- }
- }
-
- /**
- * Start fast forwarding. If playback is already fast forwarding this
- * may increase the rate.
- */
- public void fastForward() {
- try {
- mSessionBinder.fastForward(mContext.getPackageName(), mCbStub);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling fastForward.", e);
- }
- }
-
- /**
- * Skip to the next item.
- */
- public void skipToNext() {
- try {
- mSessionBinder.next(mContext.getPackageName(), mCbStub);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling next.", e);
- }
- }
-
- /**
- * Start rewinding. If playback is already rewinding this may increase
- * the rate.
- */
- public void rewind() {
- try {
- mSessionBinder.rewind(mContext.getPackageName(), mCbStub);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling rewind.", e);
- }
- }
-
- /**
- * Skip to the previous item.
- */
- public void skipToPrevious() {
- try {
- mSessionBinder.previous(mContext.getPackageName(), mCbStub);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling previous.", e);
- }
- }
-
- /**
- * Rate the current content. This will cause the rating to be set for
- * the current user. The Rating type must match the type returned by
- * {@link #getRatingType()}.
- *
- * @param rating The rating to set for the current content
- */
- public void setRating(Rating rating) {
- try {
- mSessionBinder.rate(mContext.getPackageName(), mCbStub, rating);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling rate.", e);
- }
- }
-
- /**
- * Send a custom action back for the {@link MediaSession} to perform.
- *
- * @param customAction The action to perform.
- * @param args Optional arguments to supply to the {@link MediaSession} for this
- * custom action.
- */
- public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
- @Nullable Bundle args) {
- if (customAction == null) {
- throw new IllegalArgumentException("CustomAction cannot be null.");
- }
- sendCustomAction(customAction.getAction(), args);
- }
-
- /**
- * Send the id and args from a custom action back for the {@link MediaSession} to perform.
- *
- * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args)
- * @param action The action identifier of the {@link PlaybackState.CustomAction} as
- * specified by the {@link MediaSession}.
- * @param args Optional arguments to supply to the {@link MediaSession} for this
- * custom action.
- */
- public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
- if (TextUtils.isEmpty(action)) {
- throw new IllegalArgumentException("CustomAction cannot be null.");
- }
- try {
- mSessionBinder.sendCustomAction(mContext.getPackageName(), mCbStub, action, args);
- } catch (RemoteException e) {
- Log.d(TAG, "Dead object in sendCustomAction.", e);
- }
- }
- }
-
- /**
- * Holds information about the current playback and how audio is handled for
- * this session.
- */
- public static final class PlaybackInfo {
- /**
- * The session uses remote playback.
- */
- public static final int PLAYBACK_TYPE_REMOTE = 2;
- /**
- * The session uses local playback.
- */
- public static final int PLAYBACK_TYPE_LOCAL = 1;
-
- private final int mVolumeType;
- private final int mVolumeControl;
- private final int mMaxVolume;
- private final int mCurrentVolume;
- private final AudioAttributes mAudioAttrs;
-
- /**
- * @hide
- */
- public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
- mVolumeType = type;
- mAudioAttrs = attrs;
- mVolumeControl = control;
- mMaxVolume = max;
- mCurrentVolume = current;
- }
-
- /**
- * Get the type of playback which affects volume handling. One of:
- * <ul>
- * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
- * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
- * </ul>
- *
- * @return The type of playback this session is using.
- */
- public int getPlaybackType() {
- return mVolumeType;
- }
-
- /**
- * Get the audio attributes for this session. The attributes will affect
- * volume handling for the session. When the volume type is
- * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
- * remote volume handler.
- *
- * @return The attributes for this session.
- */
- public AudioAttributes getAudioAttributes() {
- return mAudioAttrs;
- }
-
- /**
- * Get the type of volume control that can be used. One of:
- * <ul>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
- * </ul>
- *
- * @return The type of volume control that may be used with this
- * session.
- */
- public int getVolumeControl() {
- return mVolumeControl;
- }
-
- /**
- * Get the maximum volume that may be set for this session.
- *
- * @return The maximum allowed volume where this session is playing.
- */
- public int getMaxVolume() {
- return mMaxVolume;
- }
-
- /**
- * Get the current volume for this session.
- *
- * @return The current volume where this session is playing.
- */
- public int getCurrentVolume() {
- return mCurrentVolume;
- }
- }
-
- private final static class CallbackStub extends ISessionControllerCallback.Stub {
- private final WeakReference<MediaController> mController;
-
- public CallbackStub(MediaController controller) {
- mController = new WeakReference<MediaController>(controller);
- }
-
- @Override
- public void onSessionDestroyed() {
- MediaController controller = mController.get();
- if (controller != null) {
- controller.postMessage(MSG_DESTROYED, null, null);
- }
- }
-
- @Override
- public void onEvent(String event, Bundle extras) {
- MediaController controller = mController.get();
- if (controller != null) {
- controller.postMessage(MSG_EVENT, event, extras);
- }
- }
-
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- MediaController controller = mController.get();
- if (controller != null) {
- controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
- }
- }
-
- @Override
- public void onMetadataChanged(MediaMetadata metadata) {
- MediaController controller = mController.get();
- if (controller != null) {
- controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
- }
- }
-
- @Override
- public void onQueueChanged(MediaParceledListSlice parceledQueue) {
- List<MediaSession.QueueItem> queue = parceledQueue == null ? null : parceledQueue
- .getList();
- MediaController controller = mController.get();
- if (controller != null) {
- controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
- }
- }
-
- @Override
- public void onQueueTitleChanged(CharSequence title) {
- MediaController controller = mController.get();
- if (controller != null) {
- controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null);
- }
- }
-
- @Override
- public void onExtrasChanged(Bundle extras) {
- MediaController controller = mController.get();
- if (controller != null) {
- controller.postMessage(MSG_UPDATE_EXTRAS, extras, null);
- }
- }
-
- @Override
- public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
- MediaController controller = mController.get();
- if (controller != null) {
- PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs,
- pvi.controlType, pvi.maxVolume, pvi.currentVolume);
- controller.postMessage(MSG_UPDATE_VOLUME, info, null);
- }
- }
-
- }
-
- private final static class MessageHandler extends Handler {
- private final MediaController.Callback mCallback;
- private boolean mRegistered = false;
-
- public MessageHandler(Looper looper, MediaController.Callback cb) {
- super(looper);
- mCallback = cb;
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (!mRegistered) {
- return;
- }
- switch (msg.what) {
- case MSG_EVENT:
- mCallback.onSessionEvent((String) msg.obj, msg.getData());
- break;
- case MSG_UPDATE_PLAYBACK_STATE:
- mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
- break;
- case MSG_UPDATE_METADATA:
- mCallback.onMetadataChanged((MediaMetadata) msg.obj);
- break;
- case MSG_UPDATE_QUEUE:
- mCallback.onQueueChanged((List<MediaSession.QueueItem>) msg.obj);
- break;
- case MSG_UPDATE_QUEUE_TITLE:
- mCallback.onQueueTitleChanged((CharSequence) msg.obj);
- break;
- case MSG_UPDATE_EXTRAS:
- mCallback.onExtrasChanged((Bundle) msg.obj);
- break;
- case MSG_UPDATE_VOLUME:
- mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj);
- break;
- case MSG_DESTROYED:
- mCallback.onSessionDestroyed();
- break;
- }
- }
-
- public void post(int what, Object obj, Bundle data) {
- Message msg = obtainMessage(what, obj);
- msg.setAsynchronous(true);
- msg.setData(data);
- msg.sendToTarget();
- }
- }
-
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl b/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl
deleted file mode 100644
index f657cef..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media.session;
-
-parcelable MediaSession.Token;
-parcelable MediaSession.QueueItem;
\ No newline at end of file
diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java
deleted file mode 100644
index 3cbeff9..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java
+++ /dev/null
@@ -1,1569 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.media.AudioAttributes;
-import android.media.MediaDescription;
-import android.media.MediaMetadata;
-import android.media.MediaParceledListSlice;
-import android.media.Rating;
-import android.media.VolumeProvider;
-import android.media.session.MediaSessionManager.RemoteUserInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.UserHandle;
-import android.service.media.MediaBrowserService;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-import android.view.KeyEvent;
-import android.view.ViewConfiguration;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Allows interaction with media controllers, volume keys, media buttons, and
- * transport controls.
- * <p>
- * A MediaSession should be created when an app wants to publish media playback
- * information or handle media keys. In general an app only needs one session
- * for all playback, though multiple sessions can be created to provide finer
- * grain controls of media.
- * <p>
- * Once a session is created the owner of the session may pass its
- * {@link #getSessionToken() session token} to other processes to allow them to
- * create a {@link MediaController} to interact with the session.
- * <p>
- * To receive commands, media keys, and other events a {@link Callback} must be
- * set with {@link #setCallback(Callback)} and {@link #setActive(boolean)
- * setActive(true)} must be called.
- * <p>
- * When an app is finished performing playback it must call {@link #release()}
- * to clean up the session and notify any controllers.
- * <p>
- * MediaSession objects are thread safe.
- */
-public final class MediaSession {
- private static final String TAG = "MediaSession";
-
- /**
- * Set this flag on the session to indicate that it can handle media button
- * events.
- * @deprecated This flag is no longer used. All media sessions are expected to handle media
- * button events now.
- */
- @Deprecated
- public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0;
-
- /**
- * Set this flag on the session to indicate that it handles transport
- * control commands through its {@link Callback}.
- * @deprecated This flag is no longer used. All media sessions are expected to handle transport
- * controls now.
- */
- @Deprecated
- public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
-
- /**
- * System only flag for a session that needs to have priority over all other
- * sessions. This flag ensures this session will receive media button events
- * regardless of the current ordering in the system.
- *
- * @hide
- */
- public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16;
-
- /**
- * @hide
- */
- public static final int INVALID_UID = -1;
-
- /**
- * @hide
- */
- public static final int INVALID_PID = -1;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {
- FLAG_HANDLES_MEDIA_BUTTONS,
- FLAG_HANDLES_TRANSPORT_CONTROLS,
- FLAG_EXCLUSIVE_GLOBAL_PRIORITY })
- public @interface SessionFlags { }
-
- private final Object mLock = new Object();
- private final int mMaxBitmapSize;
-
- private final MediaSession.Token mSessionToken;
- private final MediaController mController;
- private final ISession mBinder;
- private final CallbackStub mCbStub;
-
- // Do not change the name of mCallback. Support lib accesses this by using reflection.
- @UnsupportedAppUsage
- private CallbackMessageHandler mCallback;
- private VolumeProvider mVolumeProvider;
- private PlaybackState mPlaybackState;
-
- private boolean mActive = false;
-
- /**
- * Creates a new session. The session will automatically be registered with
- * the system but will not be published until {@link #setActive(boolean)
- * setActive(true)} is called. You must call {@link #release()} when
- * finished with the session.
- *
- * @param context The context to use to create the session.
- * @param tag A short name for debugging purposes.
- */
- public MediaSession(@NonNull Context context, @NonNull String tag) {
- this(context, tag, UserHandle.myUserId());
- }
-
- /**
- * Creates a new session as the specified user. To create a session as a
- * user other than your own you must hold the
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
- * permission.
- *
- * @param context The context to use to create the session.
- * @param tag A short name for debugging purposes.
- * @param userId The user id to create the session as.
- * @hide
- */
- public MediaSession(@NonNull Context context, @NonNull String tag, int userId) {
- if (context == null) {
- throw new IllegalArgumentException("context cannot be null.");
- }
- if (TextUtils.isEmpty(tag)) {
- throw new IllegalArgumentException("tag cannot be null or empty");
- }
- mMaxBitmapSize = context.getResources().getDimensionPixelSize(
- android.R.dimen.config_mediaMetadataBitmapMaxSize);
- mCbStub = new CallbackStub(this);
- MediaSessionManager manager = (MediaSessionManager) context
- .getSystemService(Context.MEDIA_SESSION_SERVICE);
- try {
- //TODO(b/119749862): Resolve hidden API usage. MediaSessioManager#createSession
- //mBinder = manager.createSession(mCbStub, tag, userId);
- mBinder = null; //TODO: remove this.
- mSessionToken = new Token(mBinder.getController());
- mController = new MediaController(context, mSessionToken);
- } catch (RemoteException e) {
- throw new RuntimeException("Remote error creating session.", e);
- }
- }
-
- /**
- * Set the callback to receive updates for the MediaSession. This includes
- * media button events and transport controls. The caller's thread will be
- * used to post updates.
- * <p>
- * Set the callback to null to stop receiving updates.
- *
- * @param callback The callback object
- */
- public void setCallback(@Nullable Callback callback) {
- setCallback(callback, null);
- }
-
- /**
- * Set the callback to receive updates for the MediaSession. This includes
- * media button events and transport controls.
- * <p>
- * Set the callback to null to stop receiving updates.
- *
- * @param callback The callback to receive updates on.
- * @param handler The handler that events should be posted on.
- */
- public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
- synchronized (mLock) {
- if (mCallback != null) {
- // We're updating the callback, clear the session from the old one.
- mCallback.mCallback.mSession = null;
- mCallback.removeCallbacksAndMessages(null);
- }
- if (callback == null) {
- mCallback = null;
- return;
- }
- if (handler == null) {
- handler = new Handler();
- }
- callback.mSession = this;
- CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
- callback);
- mCallback = msgHandler;
- }
- }
-
- /**
- * Set an intent for launching UI for this Session. This can be used as a
- * quick link to an ongoing media screen. The intent should be for an
- * activity that may be started using {@link Activity#startActivity(Intent)}.
- *
- * @param pi The intent to launch to show UI for this Session.
- */
- public void setSessionActivity(@Nullable PendingIntent pi) {
- try {
- mBinder.setLaunchPendingIntent(pi);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Failure in setLaunchPendingIntent.", e);
- }
- }
-
- /**
- * Set a pending intent for your media button receiver to allow restarting
- * playback after the session has been stopped. If your app is started in
- * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
- * the pending intent.
- *
- * @param mbr The {@link PendingIntent} to send the media button event to.
- */
- public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
- try {
- mBinder.setMediaButtonReceiver(mbr);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
- }
- }
-
- /**
- * Set any flags for the session.
- *
- * @param flags The flags to set for this session.
- */
- public void setFlags(@SessionFlags int flags) {
- try {
- mBinder.setFlags(flags);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Failure in setFlags.", e);
- }
- }
-
- /**
- * Set the attributes for this session's audio. This will affect the
- * system's volume handling for this session. If
- * {@link #setPlaybackToRemote} was previously called it will stop receiving
- * volume commands and the system will begin sending volume changes to the
- * appropriate stream.
- * <p>
- * By default sessions use attributes for media.
- *
- * @param attributes The {@link AudioAttributes} for this session's audio.
- */
- public void setPlaybackToLocal(AudioAttributes attributes) {
- if (attributes == null) {
- throw new IllegalArgumentException("Attributes cannot be null for local playback.");
- }
- //TODO(b/119751592): Decide if AudioAttributes should be updated.
- /*
- try {
- mBinder.setPlaybackToLocal(attributes);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
- }
- */
- }
-
- /**
- * Configure this session to use remote volume handling. This must be called
- * to receive volume button events, otherwise the system will adjust the
- * appropriate stream volume for this session. If
- * {@link #setPlaybackToLocal} was previously called the system will stop
- * handling volume changes for this session and pass them to the volume
- * provider instead.
- *
- * @param volumeProvider The provider that will handle volume changes. May
- * not be null.
- */
- public void setPlaybackToRemote(@NonNull VolumeProvider volumeProvider) {
- if (volumeProvider == null) {
- throw new IllegalArgumentException("volumeProvider may not be null!");
- }
- synchronized (mLock) {
- mVolumeProvider = volumeProvider;
- }
- volumeProvider.setCallback(new VolumeProvider.Callback() {
- @Override
- public void onVolumeChanged(VolumeProvider volumeProvider) {
- notifyRemoteVolumeChanged(volumeProvider);
- }
- });
-
- try {
- mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(),
- volumeProvider.getMaxVolume());
- mBinder.setCurrentVolume(volumeProvider.getCurrentVolume());
- } catch (RemoteException e) {
- Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
- }
- }
-
- /**
- * Set if this session is currently active and ready to receive commands. If
- * set to false your session's controller may not be discoverable. You must
- * set the session to active before it can start receiving media button
- * events or transport commands.
- *
- * @param active Whether this session is active or not.
- */
- public void setActive(boolean active) {
- if (mActive == active) {
- return;
- }
- try {
- mBinder.setActive(active);
- mActive = active;
- } catch (RemoteException e) {
- Log.wtf(TAG, "Failure in setActive.", e);
- }
- }
-
- /**
- * Get the current active state of this session.
- *
- * @return True if the session is active, false otherwise.
- */
- public boolean isActive() {
- return mActive;
- }
-
- /**
- * Send a proprietary event to all MediaControllers listening to this
- * Session. It's up to the Controller/Session owner to determine the meaning
- * of any events.
- *
- * @param event The name of the event to send
- * @param extras Any extras included with the event
- */
- public void sendSessionEvent(@NonNull String event, @Nullable Bundle extras) {
- if (TextUtils.isEmpty(event)) {
- throw new IllegalArgumentException("event cannot be null or empty");
- }
- try {
- mBinder.sendEvent(event, extras);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error sending event", e);
- }
- }
-
- /**
- * This must be called when an app has finished performing playback. If
- * playback is expected to start again shortly the session can be left open,
- * but it must be released if your activity or service is being destroyed.
- */
- public void release() {
- try {
- mBinder.destroy();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error releasing session: ", e);
- }
- }
-
- /**
- * Retrieve a token object that can be used by apps to create a
- * {@link MediaController} for interacting with this session. The owner of
- * the session is responsible for deciding how to distribute these tokens.
- *
- * @return A token that can be used to create a MediaController for this
- * session
- */
- public @NonNull Token getSessionToken() {
- return mSessionToken;
- }
-
- /**
- * Get a controller for this session. This is a convenience method to avoid
- * having to cache your own controller in process.
- *
- * @return A controller for this session.
- */
- public @NonNull MediaController getController() {
- return mController;
- }
-
- /**
- * Update the current playback state.
- *
- * @param state The current state of playback
- */
- public void setPlaybackState(@Nullable PlaybackState state) {
- mPlaybackState = state;
- try {
- mBinder.setPlaybackState(state);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Dead object in setPlaybackState.", e);
- }
- }
-
- /**
- * Update the current metadata. New metadata can be created using
- * {@link android.media.MediaMetadata.Builder}. This operation may take time proportional to
- * the size of the bitmap to replace large bitmaps with a scaled down copy.
- *
- * @param metadata The new metadata
- * @see android.media.MediaMetadata.Builder#putBitmap
- */
- public void setMetadata(@Nullable MediaMetadata metadata) {
- long duration = -1;
- int fields = 0;
- MediaDescription description = null;
- if (metadata != null) {
- metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
- if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
- duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
- }
- fields = metadata.size();
- description = metadata.getDescription();
- }
- String metadataDescription = "size=" + fields + ", description=" + description;
-
- try {
- mBinder.setMetadata(metadata, duration, metadataDescription);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Dead object in setPlaybackState.", e);
- }
- }
-
- /**
- * Update the list of items in the play queue. It is an ordered list and
- * should contain the current item, and previous or upcoming items if they
- * exist. Specify null if there is no current play queue.
- * <p>
- * The queue should be of reasonable size. If the play queue is unbounded
- * within your app, it is better to send a reasonable amount in a sliding
- * window instead.
- *
- * @param queue A list of items in the play queue.
- */
- public void setQueue(@Nullable List<QueueItem> queue) {
- try {
- mBinder.setQueue(queue == null ? null : new MediaParceledListSlice<QueueItem>(queue));
- } catch (RemoteException e) {
- Log.wtf("Dead object in setQueue.", e);
- }
- }
-
- /**
- * Set the title of the play queue. The UI should display this title along
- * with the play queue itself.
- * e.g. "Play Queue", "Now Playing", or an album name.
- *
- * @param title The title of the play queue.
- */
- public void setQueueTitle(@Nullable CharSequence title) {
- try {
- mBinder.setQueueTitle(title);
- } catch (RemoteException e) {
- Log.wtf("Dead object in setQueueTitle.", e);
- }
- }
-
- /**
- * Set the style of rating used by this session. Apps trying to set the
- * rating should use this style. Must be one of the following:
- * <ul>
- * <li>{@link Rating#RATING_NONE}</li>
- * <li>{@link Rating#RATING_3_STARS}</li>
- * <li>{@link Rating#RATING_4_STARS}</li>
- * <li>{@link Rating#RATING_5_STARS}</li>
- * <li>{@link Rating#RATING_HEART}</li>
- * <li>{@link Rating#RATING_PERCENTAGE}</li>
- * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
- * </ul>
- */
- public void setRatingType(@Rating.Style int type) {
- try {
- mBinder.setRatingType(type);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setRatingType.", e);
- }
- }
-
- /**
- * Set some extras that can be associated with the {@link MediaSession}. No assumptions should
- * be made as to how a {@link MediaController} will handle these extras.
- * Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
- *
- * @param extras The extras associated with the {@link MediaSession}.
- */
- public void setExtras(@Nullable Bundle extras) {
- try {
- mBinder.setExtras(extras);
- } catch (RemoteException e) {
- Log.wtf("Dead object in setExtras.", e);
- }
- }
-
- /**
- * Gets the controller information who sent the current request.
- * <p>
- * Note: This is only valid while in a request callback, such as {@link Callback#onPlay}.
- *
- * @throws IllegalStateException If this method is called outside of {@link Callback} methods.
- * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
- */
- public final @NonNull RemoteUserInfo getCurrentControllerInfo() {
- if (mCallback == null || mCallback.mCurrentControllerInfo == null) {
- throw new IllegalStateException(
- "This should be called inside of MediaSession.Callback methods");
- }
- return mCallback.mCurrentControllerInfo;
- }
-
- /**
- * Notify the system that the remote volume changed.
- *
- * @param provider The provider that is handling volume changes.
- * @hide
- */
- public void notifyRemoteVolumeChanged(VolumeProvider provider) {
- synchronized (mLock) {
- if (provider == null || provider != mVolumeProvider) {
- Log.w(TAG, "Received update from stale volume provider");
- return;
- }
- }
- try {
- mBinder.setCurrentVolume(provider.getCurrentVolume());
- } catch (RemoteException e) {
- Log.e(TAG, "Error in notifyVolumeChanged", e);
- }
- }
-
- /**
- * Returns the name of the package that sent the last media button, transport control, or
- * command from controllers and the system. This is only valid while in a request callback, such
- * as {@link Callback#onPlay}.
- *
- * @hide
- */
- @UnsupportedAppUsage
- public String getCallingPackage() {
- if (mCallback != null && mCallback.mCurrentControllerInfo != null) {
- return mCallback.mCurrentControllerInfo.getPackageName();
- }
- return null;
- }
-
- private void dispatchPrepare(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null);
- }
-
- private void dispatchPrepareFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras);
- }
-
- private void dispatchPrepareFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_SEARCH, query, extras);
- }
-
- private void dispatchPrepareFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_URI, uri, extras);
- }
-
- private void dispatchPlay(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_PLAY, null, null);
- }
-
- private void dispatchPlayFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras);
- }
-
- private void dispatchPlayFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras);
- }
-
- private void dispatchPlayFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
- }
-
- private void dispatchSkipToItem(RemoteUserInfo caller, long id) {
- postToCallback(caller, CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, null);
- }
-
- private void dispatchPause(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_PAUSE, null, null);
- }
-
- private void dispatchStop(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_STOP, null, null);
- }
-
- private void dispatchNext(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_NEXT, null, null);
- }
-
- private void dispatchPrevious(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREVIOUS, null, null);
- }
-
- private void dispatchFastForward(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_FAST_FORWARD, null, null);
- }
-
- private void dispatchRewind(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_REWIND, null, null);
- }
-
- private void dispatchSeekTo(RemoteUserInfo caller, long pos) {
- postToCallback(caller, CallbackMessageHandler.MSG_SEEK_TO, pos, null);
- }
-
- private void dispatchRate(RemoteUserInfo caller, Rating rating) {
- postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null);
- }
-
- private void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) {
- postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
- }
-
- private void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) {
- postToCallback(caller, CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, null);
- }
-
- private void dispatchMediaButtonDelayed(RemoteUserInfo info, Intent mediaButtonIntent,
- long delay) {
- postToCallbackDelayed(info, CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
- mediaButtonIntent, null, delay);
- }
-
- private void dispatchAdjustVolume(RemoteUserInfo caller, int direction) {
- postToCallback(caller, CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, null);
- }
-
- private void dispatchSetVolumeTo(RemoteUserInfo caller, int volume) {
- postToCallback(caller, CallbackMessageHandler.MSG_SET_VOLUME, volume, null);
- }
-
- private void dispatchCommand(RemoteUserInfo caller, String command, Bundle args,
- ResultReceiver resultCb) {
- Command cmd = new Command(command, args, resultCb);
- postToCallback(caller, CallbackMessageHandler.MSG_COMMAND, cmd, null);
- }
-
- private void postToCallback(RemoteUserInfo caller, int what, Object obj, Bundle data) {
- postToCallbackDelayed(caller, what, obj, data, 0);
- }
-
- private void postToCallbackDelayed(RemoteUserInfo caller, int what, Object obj, Bundle data,
- long delay) {
- synchronized (mLock) {
- if (mCallback != null) {
- mCallback.post(caller, what, obj, data, delay);
- }
- }
- }
-
- /**
- * Return true if this is considered an active playback state.
- *
- * @hide
- */
- public static boolean isActiveState(int state) {
- switch (state) {
- case PlaybackState.STATE_FAST_FORWARDING:
- case PlaybackState.STATE_REWINDING:
- case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
- case PlaybackState.STATE_SKIPPING_TO_NEXT:
- case PlaybackState.STATE_BUFFERING:
- case PlaybackState.STATE_CONNECTING:
- case PlaybackState.STATE_PLAYING:
- return true;
- }
- return false;
- }
-
- /**
- * Represents an ongoing session. This may be passed to apps by the session
- * owner to allow them to create a {@link MediaController} to communicate with
- * the session.
- */
- public static final class Token implements Parcelable {
-
- private ISessionController mBinder;
-
- /**
- * @hide
- */
- public Token(ISessionController binder) {
- mBinder = binder;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeStrongBinder(mBinder.asBinder());
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((mBinder == null) ? 0 : mBinder.asBinder().hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Token other = (Token) obj;
- if (mBinder == null) {
- if (other.mBinder != null)
- return false;
- } else if (!mBinder.asBinder().equals(other.mBinder.asBinder()))
- return false;
- return true;
- }
-
- ISessionController getBinder() {
- return mBinder;
- }
-
- public static final Parcelable.Creator<Token> CREATOR
- = new Parcelable.Creator<Token>() {
- @Override
- public Token createFromParcel(Parcel in) {
- return new Token(ISessionController.Stub.asInterface(in.readStrongBinder()));
- }
-
- @Override
- public Token[] newArray(int size) {
- return new Token[size];
- }
- };
- }
-
- /**
- * Receives media buttons, transport controls, and commands from controllers
- * and the system. A callback may be set using {@link #setCallback}.
- */
- public abstract static class Callback {
-
- private MediaSession mSession;
- private CallbackMessageHandler mHandler;
- private boolean mMediaPlayPauseKeyPending;
-
- public Callback() {
- }
-
- /**
- * Called when a controller has sent a command to this session.
- * The owner of the session may handle custom commands but is not
- * required to.
- *
- * @param command The command name.
- * @param args Optional parameters for the command, may be null.
- * @param cb A result receiver to which a result may be sent by the command, may be null.
- */
- public void onCommand(@NonNull String command, @Nullable Bundle args,
- @Nullable ResultReceiver cb) {
- }
-
- /**
- * Called when a media button is pressed and this session has the
- * highest priority or a controller sends a media button event to the
- * session. The default behavior will call the relevant method if the
- * action for it was set.
- * <p>
- * The intent will be of type {@link Intent#ACTION_MEDIA_BUTTON} with a
- * KeyEvent in {@link Intent#EXTRA_KEY_EVENT}
- *
- * @param mediaButtonIntent an intent containing the KeyEvent as an
- * extra
- * @return True if the event was handled, false otherwise.
- */
- public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
- if (mSession != null && mHandler != null
- && Intent.ACTION_MEDIA_BUTTON.equals(mediaButtonIntent.getAction())) {
- KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
- if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) {
- PlaybackState state = mSession.mPlaybackState;
- long validActions = state == null ? 0 : state.getActions();
- switch (ke.getKeyCode()) {
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- case KeyEvent.KEYCODE_HEADSETHOOK:
- if (ke.getRepeatCount() > 0) {
- // Consider long-press as a single tap.
- handleMediaPlayPauseKeySingleTapIfPending();
- } else if (mMediaPlayPauseKeyPending) {
- // Consider double tap as the next.
- mHandler.removeMessages(CallbackMessageHandler
- .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
- mMediaPlayPauseKeyPending = false;
- if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
- onSkipToNext();
- }
- } else {
- mMediaPlayPauseKeyPending = true;
- mSession.dispatchMediaButtonDelayed(
- mSession.getCurrentControllerInfo(),
- mediaButtonIntent, ViewConfiguration.getDoubleTapTimeout());
- }
- return true;
- default:
- // If another key is pressed within double tap timeout, consider the
- // pending play/pause as a single tap to handle media keys in order.
- handleMediaPlayPauseKeySingleTapIfPending();
- break;
- }
-
- switch (ke.getKeyCode()) {
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- if ((validActions & PlaybackState.ACTION_PLAY) != 0) {
- onPlay();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- if ((validActions & PlaybackState.ACTION_PAUSE) != 0) {
- onPause();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
- onSkipToNext();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
- onSkipToPrevious();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_STOP:
- if ((validActions & PlaybackState.ACTION_STOP) != 0) {
- onStop();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
- if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
- onFastForward();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_REWIND:
- if ((validActions & PlaybackState.ACTION_REWIND) != 0) {
- onRewind();
- return true;
- }
- break;
- }
- }
- }
- return false;
- }
-
- private void handleMediaPlayPauseKeySingleTapIfPending() {
- if (!mMediaPlayPauseKeyPending) {
- return;
- }
- mMediaPlayPauseKeyPending = false;
- mHandler.removeMessages(CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
- PlaybackState state = mSession.mPlaybackState;
- long validActions = state == null ? 0 : state.getActions();
- boolean isPlaying = state != null
- && state.getState() == PlaybackState.STATE_PLAYING;
- boolean canPlay = (validActions & (PlaybackState.ACTION_PLAY_PAUSE
- | PlaybackState.ACTION_PLAY)) != 0;
- boolean canPause = (validActions & (PlaybackState.ACTION_PLAY_PAUSE
- | PlaybackState.ACTION_PAUSE)) != 0;
- if (isPlaying && canPause) {
- onPause();
- } else if (!isPlaying && canPlay) {
- onPlay();
- }
- }
-
- /**
- * Override to handle requests to prepare playback. During the preparation, a session should
- * not hold audio focus in order to allow other sessions play seamlessly. The state of
- * playback should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is
- * done.
- */
- public void onPrepare() {
- }
-
- /**
- * Override to handle requests to prepare for playing a specific mediaId that was provided
- * by your app's {@link MediaBrowserService}. During the preparation, a session should not
- * hold audio focus in order to allow other sessions play seamlessly. The state of playback
- * should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done.
- * The playback of the prepared content should start in the implementation of
- * {@link #onPlay}. Override {@link #onPlayFromMediaId} to handle requests for starting
- * playback without preparation.
- */
- public void onPrepareFromMediaId(String mediaId, Bundle extras) {
- }
-
- /**
- * Override to handle requests to prepare playback from a search query. An empty query
- * indicates that the app may prepare any music. The implementation should attempt to make a
- * smart choice about what to play. During the preparation, a session should not hold audio
- * focus in order to allow other sessions play seamlessly. The state of playback should be
- * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback
- * of the prepared content should start in the implementation of {@link #onPlay}. Override
- * {@link #onPlayFromSearch} to handle requests for starting playback without preparation.
- */
- public void onPrepareFromSearch(String query, Bundle extras) {
- }
-
- /**
- * Override to handle requests to prepare a specific media item represented by a URI.
- * During the preparation, a session should not hold audio focus in order to allow
- * other sessions play seamlessly. The state of playback should be updated to
- * {@link PlaybackState#STATE_PAUSED} after the preparation is done.
- * The playback of the prepared content should start in the implementation of
- * {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests
- * for starting playback without preparation.
- */
- public void onPrepareFromUri(Uri uri, Bundle extras) {
- }
-
- /**
- * Override to handle requests to begin playback.
- */
- public void onPlay() {
- }
-
- /**
- * Override to handle requests to begin playback from a search query. An
- * empty query indicates that the app may play any music. The
- * implementation should attempt to make a smart choice about what to
- * play.
- */
- public void onPlayFromSearch(String query, Bundle extras) {
- }
-
- /**
- * Override to handle requests to play a specific mediaId that was
- * provided by your app's {@link MediaBrowserService}.
- */
- public void onPlayFromMediaId(String mediaId, Bundle extras) {
- }
-
- /**
- * Override to handle requests to play a specific media item represented by a URI.
- */
- public void onPlayFromUri(Uri uri, Bundle extras) {
- }
-
- /**
- * Override to handle requests to play an item with a given id from the
- * play queue.
- */
- public void onSkipToQueueItem(long id) {
- }
-
- /**
- * Override to handle requests to pause playback.
- */
- public void onPause() {
- }
-
- /**
- * Override to handle requests to skip to the next media item.
- */
- public void onSkipToNext() {
- }
-
- /**
- * Override to handle requests to skip to the previous media item.
- */
- public void onSkipToPrevious() {
- }
-
- /**
- * Override to handle requests to fast forward.
- */
- public void onFastForward() {
- }
-
- /**
- * Override to handle requests to rewind.
- */
- public void onRewind() {
- }
-
- /**
- * Override to handle requests to stop playback.
- */
- public void onStop() {
- }
-
- /**
- * Override to handle requests to seek to a specific position in ms.
- *
- * @param pos New position to move to, in milliseconds.
- */
- public void onSeekTo(long pos) {
- }
-
- /**
- * Override to handle the item being rated.
- *
- * @param rating
- */
- public void onSetRating(@NonNull Rating rating) {
- }
-
- /**
- * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
- * performed.
- *
- * @param action The action that was originally sent in the
- * {@link PlaybackState.CustomAction}.
- * @param extras Optional extras specified by the {@link MediaController}.
- */
- public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
- }
- }
-
- /**
- * @hide
- */
- public static class CallbackStub extends ISessionCallback.Stub {
- private WeakReference<MediaSession> mMediaSession;
-
- public CallbackStub(MediaSession session) {
- mMediaSession = new WeakReference<>(session);
- }
-
- private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
- return new RemoteUserInfo(packageName, pid, uid);
- }
-
- @Override
- public void onCommand(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchCommand(createRemoteUserInfo(packageName, pid, uid, caller),
- command, args, cb);
- }
- }
-
- @Override
- public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
- int sequenceNumber, ResultReceiver cb) {
- MediaSession session = mMediaSession.get();
- try {
- if (session != null) {
- session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, null),
- mediaButtonIntent);
- }
- } finally {
- if (cb != null) {
- cb.send(sequenceNumber, null);
- }
- }
- }
-
- @Override
- public void onMediaButtonFromController(String packageName, int pid, int uid,
- ISessionControllerCallback caller, Intent mediaButtonIntent) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, caller),
- mediaButtonIntent);
- }
- }
-
- @Override
- public void onPrepare(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onPrepareFromMediaId(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String mediaId,
- Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrepareFromMediaId(
- createRemoteUserInfo(packageName, pid, uid, caller), mediaId, extras);
- }
- }
-
- @Override
- public void onPrepareFromSearch(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String query,
- Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrepareFromSearch(
- createRemoteUserInfo(packageName, pid, uid, caller), query, extras);
- }
- }
-
- @Override
- public void onPrepareFromUri(String packageName, int pid, int uid,
- ISessionControllerCallback caller, Uri uri, Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrepareFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
- uri, extras);
- }
- }
-
- @Override
- public void onPlay(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPlay(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onPlayFromMediaId(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String mediaId,
- Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPlayFromMediaId(createRemoteUserInfo(packageName, pid, uid, caller),
- mediaId, extras);
- }
- }
-
- @Override
- public void onPlayFromSearch(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String query,
- Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPlayFromSearch(createRemoteUserInfo(packageName, pid, uid, caller),
- query, extras);
- }
- }
-
- @Override
- public void onPlayFromUri(String packageName, int pid, int uid,
- ISessionControllerCallback caller, Uri uri, Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPlayFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
- uri, extras);
- }
- }
-
- @Override
- public void onSkipToTrack(String packageName, int pid, int uid,
- ISessionControllerCallback caller, long id) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchSkipToItem(createRemoteUserInfo(packageName, pid, uid, caller), id);
- }
- }
-
- @Override
- public void onPause(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPause(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onStop(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchStop(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onNext(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchNext(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onPrevious(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onFastForward(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchFastForward(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onRewind(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchRewind(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onSeekTo(String packageName, int pid, int uid,
- ISessionControllerCallback caller, long pos) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchSeekTo(createRemoteUserInfo(packageName, pid, uid, caller), pos);
- }
- }
-
- @Override
- public void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
- Rating rating) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchRate(createRemoteUserInfo(packageName, pid, uid, caller), rating);
- }
- }
-
- @Override
- public void onCustomAction(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String action, Bundle args) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchCustomAction(createRemoteUserInfo(packageName, pid, uid, caller),
- action, args);
- }
- }
-
- @Override
- public void onAdjustVolume(String packageName, int pid, int uid,
- ISessionControllerCallback caller, int direction) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchAdjustVolume(createRemoteUserInfo(packageName, pid, uid, caller),
- direction);
- }
- }
-
- @Override
- public void onSetVolumeTo(String packageName, int pid, int uid,
- ISessionControllerCallback caller, int value) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchSetVolumeTo(createRemoteUserInfo(packageName, pid, uid, caller),
- value);
- }
- }
- }
-
- /**
- * A single item that is part of the play queue. It contains a description
- * of the item and its id in the queue.
- */
- public static final class QueueItem implements Parcelable {
- /**
- * This id is reserved. No items can be explicitly assigned this id.
- */
- public static final int UNKNOWN_ID = -1;
-
- private final MediaDescription mDescription;
- @UnsupportedAppUsage
- private final long mId;
-
- /**
- * Create a new {@link MediaSession.QueueItem}.
- *
- * @param description The {@link MediaDescription} for this item.
- * @param id An identifier for this item. It must be unique within the
- * play queue and cannot be {@link #UNKNOWN_ID}.
- */
- public QueueItem(MediaDescription description, long id) {
- if (description == null) {
- throw new IllegalArgumentException("Description cannot be null.");
- }
- if (id == UNKNOWN_ID) {
- throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID");
- }
- mDescription = description;
- mId = id;
- }
-
- private QueueItem(Parcel in) {
- mDescription = MediaDescription.CREATOR.createFromParcel(in);
- mId = in.readLong();
- }
-
- /**
- * Get the description for this item.
- */
- public MediaDescription getDescription() {
- return mDescription;
- }
-
- /**
- * Get the queue id for this item.
- */
- public long getQueueId() {
- return mId;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- mDescription.writeToParcel(dest, flags);
- dest.writeLong(mId);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final Creator<MediaSession.QueueItem> CREATOR =
- new Creator<MediaSession.QueueItem>() {
-
- @Override
- public MediaSession.QueueItem createFromParcel(Parcel p) {
- return new MediaSession.QueueItem(p);
- }
-
- @Override
- public MediaSession.QueueItem[] newArray(int size) {
- return new MediaSession.QueueItem[size];
- }
- };
-
- @Override
- public String toString() {
- return "MediaSession.QueueItem {" +
- "Description=" + mDescription +
- ", Id=" + mId + " }";
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) {
- return false;
- }
-
- if (!(o instanceof QueueItem)) {
- return false;
- }
-
- final QueueItem item = (QueueItem) o;
- if (mId != item.mId) {
- return false;
- }
-
- if (!Objects.equals(mDescription, item.mDescription)) {
- return false;
- }
-
- return true;
- }
- }
-
- private static final class Command {
- public final String command;
- public final Bundle extras;
- public final ResultReceiver stub;
-
- public Command(String command, Bundle extras, ResultReceiver stub) {
- this.command = command;
- this.extras = extras;
- this.stub = stub;
- }
- }
-
- private class CallbackMessageHandler extends Handler {
- private static final int MSG_COMMAND = 1;
- private static final int MSG_MEDIA_BUTTON = 2;
- private static final int MSG_PREPARE = 3;
- private static final int MSG_PREPARE_MEDIA_ID = 4;
- private static final int MSG_PREPARE_SEARCH = 5;
- private static final int MSG_PREPARE_URI = 6;
- private static final int MSG_PLAY = 7;
- private static final int MSG_PLAY_MEDIA_ID = 8;
- private static final int MSG_PLAY_SEARCH = 9;
- private static final int MSG_PLAY_URI = 10;
- private static final int MSG_SKIP_TO_ITEM = 11;
- private static final int MSG_PAUSE = 12;
- private static final int MSG_STOP = 13;
- private static final int MSG_NEXT = 14;
- private static final int MSG_PREVIOUS = 15;
- private static final int MSG_FAST_FORWARD = 16;
- private static final int MSG_REWIND = 17;
- private static final int MSG_SEEK_TO = 18;
- private static final int MSG_RATE = 19;
- private static final int MSG_CUSTOM_ACTION = 20;
- private static final int MSG_ADJUST_VOLUME = 21;
- private static final int MSG_SET_VOLUME = 22;
- private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23;
-
- private MediaSession.Callback mCallback;
- private RemoteUserInfo mCurrentControllerInfo;
-
- public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
- super(looper);
- mCallback = callback;
- mCallback.mHandler = this;
- }
-
- public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) {
- Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj);
- Message msg = obtainMessage(what, objWithCaller);
- msg.setAsynchronous(true);
- msg.setData(data);
- if (delayMs > 0) {
- sendMessageDelayed(msg, delayMs);
- } else {
- sendMessage(msg);
- }
- }
-
- @Override
- public void handleMessage(Message msg) {
- mCurrentControllerInfo = ((Pair<RemoteUserInfo, Object>) msg.obj).first;
-
- VolumeProvider vp;
- Object obj = ((Pair<RemoteUserInfo, Object>) msg.obj).second;
-
- switch (msg.what) {
- case MSG_COMMAND:
- Command cmd = (Command) obj;
- mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
- break;
- case MSG_MEDIA_BUTTON:
- mCallback.onMediaButtonEvent((Intent) obj);
- break;
- case MSG_PREPARE:
- mCallback.onPrepare();
- break;
- case MSG_PREPARE_MEDIA_ID:
- mCallback.onPrepareFromMediaId((String) obj, msg.getData());
- break;
- case MSG_PREPARE_SEARCH:
- mCallback.onPrepareFromSearch((String) obj, msg.getData());
- break;
- case MSG_PREPARE_URI:
- mCallback.onPrepareFromUri((Uri) obj, msg.getData());
- break;
- case MSG_PLAY:
- mCallback.onPlay();
- break;
- case MSG_PLAY_MEDIA_ID:
- mCallback.onPlayFromMediaId((String) obj, msg.getData());
- break;
- case MSG_PLAY_SEARCH:
- mCallback.onPlayFromSearch((String) obj, msg.getData());
- break;
- case MSG_PLAY_URI:
- mCallback.onPlayFromUri((Uri) obj, msg.getData());
- break;
- case MSG_SKIP_TO_ITEM:
- mCallback.onSkipToQueueItem((Long) obj);
- break;
- case MSG_PAUSE:
- mCallback.onPause();
- break;
- case MSG_STOP:
- mCallback.onStop();
- break;
- case MSG_NEXT:
- mCallback.onSkipToNext();
- break;
- case MSG_PREVIOUS:
- mCallback.onSkipToPrevious();
- break;
- case MSG_FAST_FORWARD:
- mCallback.onFastForward();
- break;
- case MSG_REWIND:
- mCallback.onRewind();
- break;
- case MSG_SEEK_TO:
- mCallback.onSeekTo((Long) obj);
- break;
- case MSG_RATE:
- mCallback.onSetRating((Rating) obj);
- break;
- case MSG_CUSTOM_ACTION:
- mCallback.onCustomAction((String) obj, msg.getData());
- break;
- case MSG_ADJUST_VOLUME:
- synchronized (mLock) {
- vp = mVolumeProvider;
- }
- if (vp != null) {
- vp.onAdjustVolume((int) obj);
- }
- break;
- case MSG_SET_VOLUME:
- synchronized (mLock) {
- vp = mVolumeProvider;
- }
- if (vp != null) {
- vp.onSetVolumeTo((int) obj);
- }
- break;
- case MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT:
- mCallback.handleMediaPlayPauseKeySingleTapIfPending();
- break;
- }
- mCurrentControllerInfo = null;
- }
- }
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl
deleted file mode 100644
index c4250f0..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media.session;
-
-parcelable ParcelableVolumeInfo;
diff --git a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java
deleted file mode 100644
index f59c975..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-package android.media.session;
-
-import android.media.AudioAttributes;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Convenience class for passing information about the audio configuration of a
- * session. The public implementation is {@link MediaController.PlaybackInfo}.
- *
- * @hide
- */
-public class ParcelableVolumeInfo implements Parcelable {
- public int volumeType;
- public AudioAttributes audioAttrs;
- public int controlType;
- public int maxVolume;
- public int currentVolume;
-
- public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType,
- int maxVolume,
- int currentVolume) {
- this.volumeType = volumeType;
- this.audioAttrs = audioAttrs;
- this.controlType = controlType;
- this.maxVolume = maxVolume;
- this.currentVolume = currentVolume;
- }
-
- public ParcelableVolumeInfo(Parcel from) {
- volumeType = from.readInt();
- controlType = from.readInt();
- maxVolume = from.readInt();
- currentVolume = from.readInt();
- audioAttrs = AudioAttributes.CREATOR.createFromParcel(from);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(volumeType);
- dest.writeInt(controlType);
- dest.writeInt(maxVolume);
- dest.writeInt(currentVolume);
- audioAttrs.writeToParcel(dest, flags);
- }
-
-
- public static final Parcelable.Creator<ParcelableVolumeInfo> CREATOR
- = new Parcelable.Creator<ParcelableVolumeInfo>() {
- @Override
- public ParcelableVolumeInfo createFromParcel(Parcel in) {
- return new ParcelableVolumeInfo(in);
- }
-
- @Override
- public ParcelableVolumeInfo[] newArray(int size) {
- return new ParcelableVolumeInfo[size];
- }
- };
-}
diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl
deleted file mode 100644
index 0876ebd..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media.session;
-
-parcelable PlaybackState;
diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java
deleted file mode 100644
index ed4f9af..0000000
--- a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java
+++ /dev/null
@@ -1,1081 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.session;
-
-import android.annotation.DrawableRes;
-import android.annotation.IntDef;
-import android.annotation.LongDef;
-import android.annotation.Nullable;
-import android.media.RemoteControlClient;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import java.util.ArrayList;
-import java.util.List;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Playback state for a {@link MediaSession}. This includes a state like
- * {@link PlaybackState#STATE_PLAYING}, the current playback position,
- * and the current control capabilities.
- */
-public final class PlaybackState implements Parcelable {
- private static final String TAG = "PlaybackState";
-
- /**
- * @hide
- */
- @LongDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND,
- ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
- ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
- ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
- ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Actions {}
-
- /**
- * Indicates this session supports the stop command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_STOP = 1 << 0;
-
- /**
- * Indicates this session supports the pause command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PAUSE = 1 << 1;
-
- /**
- * Indicates this session supports the play command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PLAY = 1 << 2;
-
- /**
- * Indicates this session supports the rewind command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_REWIND = 1 << 3;
-
- /**
- * Indicates this session supports the previous command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
-
- /**
- * Indicates this session supports the next command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
-
- /**
- * Indicates this session supports the fast forward command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_FAST_FORWARD = 1 << 6;
-
- /**
- * Indicates this session supports the set rating command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_SET_RATING = 1 << 7;
-
- /**
- * Indicates this session supports the seek to command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_SEEK_TO = 1 << 8;
-
- /**
- * Indicates this session supports the play/pause toggle command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PLAY_PAUSE = 1 << 9;
-
- /**
- * Indicates this session supports the play from media id command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10;
-
- /**
- * Indicates this session supports the play from search command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11;
-
- /**
- * Indicates this session supports the skip to queue item command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12;
-
- /**
- * Indicates this session supports the play from URI command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PLAY_FROM_URI = 1 << 13;
-
- /**
- * Indicates this session supports the prepare command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PREPARE = 1 << 14;
-
- /**
- * Indicates this session supports the prepare from media id command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PREPARE_FROM_MEDIA_ID = 1 << 15;
-
- /**
- * Indicates this session supports the prepare from search command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PREPARE_FROM_SEARCH = 1 << 16;
-
- /**
- * Indicates this session supports the prepare from URI command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_PREPARE_FROM_URI = 1 << 17;
-
- /**
- * @hide
- */
- @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING,
- STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING,
- STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM})
- @Retention(RetentionPolicy.SOURCE)
- public @interface State {}
-
- /**
- * This is the default playback state and indicates that no media has been
- * added yet, or the performer has been reset and has no content to play.
- *
- * @see Builder#setState(int, long, float)
- * @see Builder#setState(int, long, float, long)
- */
- public final static int STATE_NONE = 0;
-
- /**
- * State indicating this item is currently stopped.
- *
- * @see Builder#setState
- */
- public final static int STATE_STOPPED = 1;
-
- /**
- * State indicating this item is currently paused.
- *
- * @see Builder#setState
- */
- public final static int STATE_PAUSED = 2;
-
- /**
- * State indicating this item is currently playing.
- *
- * @see Builder#setState
- */
- public final static int STATE_PLAYING = 3;
-
- /**
- * State indicating this item is currently fast forwarding.
- *
- * @see Builder#setState
- */
- public final static int STATE_FAST_FORWARDING = 4;
-
- /**
- * State indicating this item is currently rewinding.
- *
- * @see Builder#setState
- */
- public final static int STATE_REWINDING = 5;
-
- /**
- * State indicating this item is currently buffering and will begin playing
- * when enough data has buffered.
- *
- * @see Builder#setState
- */
- public final static int STATE_BUFFERING = 6;
-
- /**
- * State indicating this item is currently in an error state. The error
- * message should also be set when entering this state.
- *
- * @see Builder#setState
- */
- public final static int STATE_ERROR = 7;
-
- /**
- * State indicating the class doing playback is currently connecting to a
- * new destination. Depending on the implementation you may return to the previous
- * state when the connection finishes or enter {@link #STATE_NONE}.
- * If the connection failed {@link #STATE_ERROR} should be used.
- *
- * @see Builder#setState
- */
- public final static int STATE_CONNECTING = 8;
-
- /**
- * State indicating the player is currently skipping to the previous item.
- *
- * @see Builder#setState
- */
- public final static int STATE_SKIPPING_TO_PREVIOUS = 9;
-
- /**
- * State indicating the player is currently skipping to the next item.
- *
- * @see Builder#setState
- */
- public final static int STATE_SKIPPING_TO_NEXT = 10;
-
- /**
- * State indicating the player is currently skipping to a specific item in
- * the queue.
- *
- * @see Builder#setState
- */
- public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
-
- /**
- * Use this value for the position to indicate the position is not known.
- */
- public final static long PLAYBACK_POSITION_UNKNOWN = -1;
-
- private final int mState;
- private final long mPosition;
- private final long mBufferedPosition;
- private final float mSpeed;
- private final long mActions;
- private List<PlaybackState.CustomAction> mCustomActions;
- private final CharSequence mErrorMessage;
- private final long mUpdateTime;
- private final long mActiveItemId;
- private final Bundle mExtras;
-
- private PlaybackState(int state, long position, long updateTime, float speed,
- long bufferedPosition, long transportControls,
- List<PlaybackState.CustomAction> customActions, long activeItemId,
- CharSequence error, Bundle extras) {
- mState = state;
- mPosition = position;
- mSpeed = speed;
- mUpdateTime = updateTime;
- mBufferedPosition = bufferedPosition;
- mActions = transportControls;
- mCustomActions = new ArrayList<>(customActions);
- mActiveItemId = activeItemId;
- mErrorMessage = error;
- mExtras = extras;
- }
-
- private PlaybackState(Parcel in) {
- mState = in.readInt();
- mPosition = in.readLong();
- mSpeed = in.readFloat();
- mUpdateTime = in.readLong();
- mBufferedPosition = in.readLong();
- mActions = in.readLong();
- mCustomActions = in.createTypedArrayList(CustomAction.CREATOR);
- mActiveItemId = in.readLong();
- mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mExtras = in.readBundle();
- }
-
- @Override
- public String toString() {
- StringBuilder bob = new StringBuilder("PlaybackState {");
- bob.append("state=").append(mState);
- bob.append(", position=").append(mPosition);
- bob.append(", buffered position=").append(mBufferedPosition);
- bob.append(", speed=").append(mSpeed);
- bob.append(", updated=").append(mUpdateTime);
- bob.append(", actions=").append(mActions);
- bob.append(", custom actions=").append(mCustomActions);
- bob.append(", active item id=").append(mActiveItemId);
- bob.append(", error=").append(mErrorMessage);
- bob.append("}");
- return bob.toString();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mState);
- dest.writeLong(mPosition);
- dest.writeFloat(mSpeed);
- dest.writeLong(mUpdateTime);
- dest.writeLong(mBufferedPosition);
- dest.writeLong(mActions);
- dest.writeTypedList(mCustomActions);
- dest.writeLong(mActiveItemId);
- TextUtils.writeToParcel(mErrorMessage, dest, 0);
- dest.writeBundle(mExtras);
- }
-
- /**
- * Get the current state of playback. One of the following:
- * <ul>
- * <li> {@link PlaybackState#STATE_NONE}</li>
- * <li> {@link PlaybackState#STATE_STOPPED}</li>
- * <li> {@link PlaybackState#STATE_PLAYING}</li>
- * <li> {@link PlaybackState#STATE_PAUSED}</li>
- * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
- * <li> {@link PlaybackState#STATE_REWINDING}</li>
- * <li> {@link PlaybackState#STATE_BUFFERING}</li>
- * <li> {@link PlaybackState#STATE_ERROR}</li>
- * <li> {@link PlaybackState#STATE_CONNECTING}</li>
- * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
- * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
- * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
- * </ul>
- */
- @State
- public int getState() {
- return mState;
- }
-
- /**
- * Get the current playback position in ms.
- */
- public long getPosition() {
- return mPosition;
- }
-
- /**
- * Get the current buffered position in ms. This is the farthest playback
- * point that can be reached from the current position using only buffered
- * content.
- */
- public long getBufferedPosition() {
- return mBufferedPosition;
- }
-
- /**
- * Get the current playback speed as a multiple of normal playback. This
- * should be negative when rewinding. A value of 1 means normal playback and
- * 0 means paused.
- *
- * @return The current speed of playback.
- */
- public float getPlaybackSpeed() {
- return mSpeed;
- }
-
- /**
- * Get the current actions available on this session. This should use a
- * bitmask of the available actions.
- * <ul>
- * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
- * <li> {@link PlaybackState#ACTION_REWIND}</li>
- * <li> {@link PlaybackState#ACTION_PLAY}</li>
- * <li> {@link PlaybackState#ACTION_PAUSE}</li>
- * <li> {@link PlaybackState#ACTION_STOP}</li>
- * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
- * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
- * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
- * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
- * <li> {@link PlaybackState#ACTION_PLAY_PAUSE}</li>
- * <li> {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}</li>
- * <li> {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}</li>
- * <li> {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}</li>
- * <li> {@link PlaybackState#ACTION_PLAY_FROM_URI}</li>
- * <li> {@link PlaybackState#ACTION_PREPARE}</li>
- * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
- * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
- * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
- * </ul>
- */
- @Actions
- public long getActions() {
- return mActions;
- }
-
- /**
- * Get the list of custom actions.
- */
- public List<PlaybackState.CustomAction> getCustomActions() {
- return mCustomActions;
- }
-
- /**
- * Get a user readable error message. This should be set when the state is
- * {@link PlaybackState#STATE_ERROR}.
- */
- public CharSequence getErrorMessage() {
- return mErrorMessage;
- }
-
- /**
- * Get the elapsed real time at which position was last updated. If the
- * position has never been set this will return 0;
- *
- * @return The last time the position was updated.
- */
- public long getLastPositionUpdateTime() {
- return mUpdateTime;
- }
-
- /**
- * Get the id of the currently active item in the queue. If there is no
- * queue or a queue is not supported by the session this will be
- * {@link MediaSession.QueueItem#UNKNOWN_ID}.
- *
- * @return The id of the currently active item in the queue or
- * {@link MediaSession.QueueItem#UNKNOWN_ID}.
- */
- public long getActiveQueueItemId() {
- return mActiveItemId;
- }
-
- /**
- * Get any custom extras that were set on this playback state.
- *
- * @return The extras for this state or null.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Get the {@link PlaybackState} state for the given
- * {@link RemoteControlClient} state.
- *
- * @param rccState The state used by {@link RemoteControlClient}.
- * @return The equivalent state used by {@link PlaybackState}.
- * @hide
- */
- public static int getStateFromRccState(int rccState) {
- switch (rccState) {
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- return STATE_BUFFERING;
- case RemoteControlClient.PLAYSTATE_ERROR:
- return STATE_ERROR;
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- return STATE_FAST_FORWARDING;
- //RemoteControlClient.PLAYSTATE_NONE is hidden
- case 0: //RemoteControlClient.PLAYSTATE_NONE:
- return STATE_NONE;
- case RemoteControlClient.PLAYSTATE_PAUSED:
- return STATE_PAUSED;
- case RemoteControlClient.PLAYSTATE_PLAYING:
- return STATE_PLAYING;
- case RemoteControlClient.PLAYSTATE_REWINDING:
- return STATE_REWINDING;
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- return STATE_SKIPPING_TO_PREVIOUS;
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- return STATE_SKIPPING_TO_NEXT;
- case RemoteControlClient.PLAYSTATE_STOPPED:
- return STATE_STOPPED;
- default:
- return -1;
- }
- }
-
- /**
- * Get the {@link RemoteControlClient} state for the given
- * {@link PlaybackState} state.
- *
- * @param state The state used by {@link PlaybackState}.
- * @return The equivalent state used by {@link RemoteControlClient}.
- * @hide
- */
- public static int getRccStateFromState(int state) {
- switch (state) {
- case STATE_BUFFERING:
- return RemoteControlClient.PLAYSTATE_BUFFERING;
- case STATE_ERROR:
- return RemoteControlClient.PLAYSTATE_ERROR;
- case STATE_FAST_FORWARDING:
- return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
- case STATE_NONE:
- //RemoteControlClient.PLAYSTATE_NONE is hidden
- return 0; //RemoteControlClient.PLAYSTATE_NONE;
- case STATE_PAUSED:
- return RemoteControlClient.PLAYSTATE_PAUSED;
- case STATE_PLAYING:
- return RemoteControlClient.PLAYSTATE_PLAYING;
- case STATE_REWINDING:
- return RemoteControlClient.PLAYSTATE_REWINDING;
- case STATE_SKIPPING_TO_PREVIOUS:
- return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
- case STATE_SKIPPING_TO_NEXT:
- return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
- case STATE_STOPPED:
- return RemoteControlClient.PLAYSTATE_STOPPED;
- default:
- return -1;
- }
- }
-
- /**
- * @hide
- */
- public static long getActionsFromRccControlFlags(int rccFlags) {
- long actions = 0;
- long flag = 1;
- while (flag <= rccFlags) {
- if ((flag & rccFlags) != 0) {
- actions |= getActionForRccFlag((int) flag);
- }
- flag = flag << 1;
- }
- return actions;
- }
-
- /**
- * @hide
- */
- public static int getRccControlFlagsFromActions(long actions) {
- int rccFlags = 0;
- long action = 1;
- while (action <= actions && action < Integer.MAX_VALUE) {
- if ((action & actions) != 0) {
- rccFlags |= getRccFlagForAction(action);
- }
- action = action << 1;
- }
- return rccFlags;
- }
-
- private static long getActionForRccFlag(int flag) {
- switch (flag) {
- case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
- return ACTION_SKIP_TO_PREVIOUS;
- case RemoteControlClient.FLAG_KEY_MEDIA_REWIND:
- return ACTION_REWIND;
- case RemoteControlClient.FLAG_KEY_MEDIA_PLAY:
- return ACTION_PLAY;
- case RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE:
- return ACTION_PLAY_PAUSE;
- case RemoteControlClient.FLAG_KEY_MEDIA_PAUSE:
- return ACTION_PAUSE;
- case RemoteControlClient.FLAG_KEY_MEDIA_STOP:
- return ACTION_STOP;
- case RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD:
- return ACTION_FAST_FORWARD;
- case RemoteControlClient.FLAG_KEY_MEDIA_NEXT:
- return ACTION_SKIP_TO_NEXT;
- case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE:
- return ACTION_SEEK_TO;
- case RemoteControlClient.FLAG_KEY_MEDIA_RATING:
- return ACTION_SET_RATING;
- }
- return 0;
- }
-
- private static int getRccFlagForAction(long action) {
- // We only care about the lower set of actions that can map to rcc
- // flags.
- int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
- switch (testAction) {
- case (int) ACTION_SKIP_TO_PREVIOUS:
- return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
- case (int) ACTION_REWIND:
- return RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
- case (int) ACTION_PLAY:
- return RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
- case (int) ACTION_PLAY_PAUSE:
- return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
- case (int) ACTION_PAUSE:
- return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
- case (int) ACTION_STOP:
- return RemoteControlClient.FLAG_KEY_MEDIA_STOP;
- case (int) ACTION_FAST_FORWARD:
- return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
- case (int) ACTION_SKIP_TO_NEXT:
- return RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
- case (int) ACTION_SEEK_TO:
- return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
- case (int) ACTION_SET_RATING:
- return RemoteControlClient.FLAG_KEY_MEDIA_RATING;
- }
- return 0;
- }
-
- public static final Parcelable.Creator<PlaybackState> CREATOR =
- new Parcelable.Creator<PlaybackState>() {
- @Override
- public PlaybackState createFromParcel(Parcel in) {
- return new PlaybackState(in);
- }
-
- @Override
- public PlaybackState[] newArray(int size) {
- return new PlaybackState[size];
- }
- };
-
- /**
- * {@link PlaybackState.CustomAction CustomActions} can be used to extend the capabilities of
- * the standard transport controls by exposing app specific actions to
- * {@link MediaController MediaControllers}.
- */
- public static final class CustomAction implements Parcelable {
- private final String mAction;
- private final CharSequence mName;
- private final int mIcon;
- private final Bundle mExtras;
-
- /**
- * Use {@link PlaybackState.CustomAction.Builder#build()}.
- */
- private CustomAction(String action, CharSequence name, int icon, Bundle extras) {
- mAction = action;
- mName = name;
- mIcon = icon;
- mExtras = extras;
- }
-
- private CustomAction(Parcel in) {
- mAction = in.readString();
- mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mIcon = in.readInt();
- mExtras = in.readBundle();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mAction);
- TextUtils.writeToParcel(mName, dest, flags);
- dest.writeInt(mIcon);
- dest.writeBundle(mExtras);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final Parcelable.Creator<PlaybackState.CustomAction> CREATOR
- = new Parcelable.Creator<PlaybackState.CustomAction>() {
-
- @Override
- public PlaybackState.CustomAction createFromParcel(Parcel p) {
- return new PlaybackState.CustomAction(p);
- }
-
- @Override
- public PlaybackState.CustomAction[] newArray(int size) {
- return new PlaybackState.CustomAction[size];
- }
- };
-
- /**
- * Returns the action of the {@link CustomAction}.
- *
- * @return The action of the {@link CustomAction}.
- */
- public String getAction() {
- return mAction;
- }
-
- /**
- * Returns the display name of this action. e.g. "Favorite"
- *
- * @return The display name of this {@link CustomAction}.
- */
- public CharSequence getName() {
- return mName;
- }
-
- /**
- * Returns the resource id of the icon in the {@link MediaSession MediaSession's} package.
- *
- * @return The resource id of the icon in the {@link MediaSession MediaSession's} package.
- */
- public int getIcon() {
- return mIcon;
- }
-
- /**
- * Returns extras which provide additional application-specific information about the
- * action, or null if none. These arguments are meant to be consumed by a
- * {@link MediaController} if it knows how to handle them.
- *
- * @return Optional arguments for the {@link CustomAction}.
- */
- public Bundle getExtras() {
- return mExtras;
- }
-
- @Override
- public String toString() {
- return "Action:" +
- "mName='" + mName +
- ", mIcon=" + mIcon +
- ", mExtras=" + mExtras;
- }
-
- /**
- * Builder for {@link CustomAction} objects.
- */
- public static final class Builder {
- private final String mAction;
- private final CharSequence mName;
- private final int mIcon;
- private Bundle mExtras;
-
- /**
- * Creates a {@link CustomAction} builder with the id, name, and icon set.
- *
- * @param action The action of the {@link CustomAction}.
- * @param name The display name of the {@link CustomAction}. This name will be displayed
- * along side the action if the UI supports it.
- * @param icon The icon resource id of the {@link CustomAction}. This resource id
- * must be in the same package as the {@link MediaSession}. It will be
- * displayed with the custom action if the UI supports it.
- */
- public Builder(String action, CharSequence name, @DrawableRes int icon) {
- if (TextUtils.isEmpty(action)) {
- throw new IllegalArgumentException(
- "You must specify an action to build a CustomAction.");
- }
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException(
- "You must specify a name to build a CustomAction.");
- }
- if (icon == 0) {
- throw new IllegalArgumentException(
- "You must specify an icon resource id to build a CustomAction.");
- }
- mAction = action;
- mName = name;
- mIcon = icon;
- }
-
- /**
- * Set optional extras for the {@link CustomAction}. These extras are meant to be
- * consumed by a {@link MediaController} if it knows how to handle them.
- * Keys should be fully qualified (e.g. "com.example.MY_ARG") to avoid collisions.
- *
- * @param extras Optional extras for the {@link CustomAction}.
- * @return this.
- */
- public Builder setExtras(Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Build and return the {@link CustomAction} instance with the specified values.
- *
- * @return A new {@link CustomAction} instance.
- */
- public CustomAction build() {
- return new CustomAction(mAction, mName, mIcon, mExtras);
- }
- }
- }
-
- /**
- * Builder for {@link PlaybackState} objects.
- */
- public static final class Builder {
- private final List<PlaybackState.CustomAction> mCustomActions = new ArrayList<>();
-
- private int mState;
- private long mPosition;
- private long mBufferedPosition;
- private float mSpeed;
- private long mActions;
- private CharSequence mErrorMessage;
- private long mUpdateTime;
- private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID;
- private Bundle mExtras;
-
- /**
- * Creates an initially empty state builder.
- */
- public Builder() {
- }
-
- /**
- * Creates a builder with the same initial values as those in the from
- * state.
- *
- * @param from The state to use for initializing the builder.
- */
- public Builder(PlaybackState from) {
- if (from == null) {
- return;
- }
- mState = from.mState;
- mPosition = from.mPosition;
- mBufferedPosition = from.mBufferedPosition;
- mSpeed = from.mSpeed;
- mActions = from.mActions;
- if (from.mCustomActions != null) {
- mCustomActions.addAll(from.mCustomActions);
- }
- mErrorMessage = from.mErrorMessage;
- mUpdateTime = from.mUpdateTime;
- mActiveItemId = from.mActiveItemId;
- mExtras = from.mExtras;
- }
-
- /**
- * Set the current state of playback.
- * <p>
- * The position must be in ms and indicates the current playback
- * position within the item. If the position is unknown use
- * {@link #PLAYBACK_POSITION_UNKNOWN}. When not using an unknown
- * position the time at which the position was updated must be provided.
- * It is okay to use {@link SystemClock#elapsedRealtime()} if the
- * current position was just retrieved.
- * <p>
- * The speed is a multiple of normal playback and should be 0 when
- * paused and negative when rewinding. Normal playback speed is 1.0.
- * <p>
- * The state must be one of the following:
- * <ul>
- * <li> {@link PlaybackState#STATE_NONE}</li>
- * <li> {@link PlaybackState#STATE_STOPPED}</li>
- * <li> {@link PlaybackState#STATE_PLAYING}</li>
- * <li> {@link PlaybackState#STATE_PAUSED}</li>
- * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
- * <li> {@link PlaybackState#STATE_REWINDING}</li>
- * <li> {@link PlaybackState#STATE_BUFFERING}</li>
- * <li> {@link PlaybackState#STATE_ERROR}</li>
- * <li> {@link PlaybackState#STATE_CONNECTING}</li>
- * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
- * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
- * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
- * </ul>
- *
- * @param state The current state of playback.
- * @param position The position in the current item in ms.
- * @param playbackSpeed The current speed of playback as a multiple of
- * normal playback.
- * @param updateTime The time in the {@link SystemClock#elapsedRealtime}
- * timebase that the position was updated at.
- * @return this
- */
- public Builder setState(@State int state, long position, float playbackSpeed,
- long updateTime) {
- mState = state;
- mPosition = position;
- mUpdateTime = updateTime;
- mSpeed = playbackSpeed;
- return this;
- }
-
- /**
- * Set the current state of playback.
- * <p>
- * The position must be in ms and indicates the current playback
- * position within the item. If the position is unknown use
- * {@link #PLAYBACK_POSITION_UNKNOWN}. The update time will be set to
- * the current {@link SystemClock#elapsedRealtime()}.
- * <p>
- * The speed is a multiple of normal playback and should be 0 when
- * paused and negative when rewinding. Normal playback speed is 1.0.
- * <p>
- * The state must be one of the following:
- * <ul>
- * <li> {@link PlaybackState#STATE_NONE}</li>
- * <li> {@link PlaybackState#STATE_STOPPED}</li>
- * <li> {@link PlaybackState#STATE_PLAYING}</li>
- * <li> {@link PlaybackState#STATE_PAUSED}</li>
- * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
- * <li> {@link PlaybackState#STATE_REWINDING}</li>
- * <li> {@link PlaybackState#STATE_BUFFERING}</li>
- * <li> {@link PlaybackState#STATE_ERROR}</li>
- * <li> {@link PlaybackState#STATE_CONNECTING}</li>
- * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
- * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
- * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
- * </ul>
- *
- * @param state The current state of playback.
- * @param position The position in the current item in ms.
- * @param playbackSpeed The current speed of playback as a multiple of
- * normal playback.
- * @return this
- */
- public Builder setState(@State int state, long position, float playbackSpeed) {
- return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime());
- }
-
- /**
- * Set the current actions available on this session. This should use a
- * bitmask of possible actions.
- * <ul>
- * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
- * <li> {@link PlaybackState#ACTION_REWIND}</li>
- * <li> {@link PlaybackState#ACTION_PLAY}</li>
- * <li> {@link PlaybackState#ACTION_PAUSE}</li>
- * <li> {@link PlaybackState#ACTION_STOP}</li>
- * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
- * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
- * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
- * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
- * <li> {@link PlaybackState#ACTION_PLAY_PAUSE}</li>
- * <li> {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}</li>
- * <li> {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}</li>
- * <li> {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}</li>
- * <li> {@link PlaybackState#ACTION_PLAY_FROM_URI}</li>
- * <li> {@link PlaybackState#ACTION_PREPARE}</li>
- * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
- * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
- * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
- * </ul>
- *
- * @param actions The set of actions allowed.
- * @return this
- */
- public Builder setActions(@Actions long actions) {
- mActions = actions;
- return this;
- }
-
- /**
- * Add a custom action to the playback state. Actions can be used to
- * expose additional functionality to {@link MediaController
- * MediaControllers} beyond what is offered by the standard transport
- * controls.
- * <p>
- * e.g. start a radio station based on the current item or skip ahead by
- * 30 seconds.
- *
- * @param action An identifier for this action. It can be sent back to
- * the {@link MediaSession} through
- * {@link MediaController.TransportControls#sendCustomAction(String, Bundle)}.
- * @param name The display name for the action. If text is shown with
- * the action or used for accessibility, this is what should
- * be used.
- * @param icon The resource action of the icon that should be displayed
- * for the action. The resource should be in the package of
- * the {@link MediaSession}.
- * @return this
- */
- public Builder addCustomAction(String action, String name, int icon) {
- return addCustomAction(new PlaybackState.CustomAction(action, name, icon, null));
- }
-
- /**
- * Add a custom action to the playback state. Actions can be used to expose additional
- * functionality to {@link MediaController MediaControllers} beyond what is offered by the
- * standard transport controls.
- * <p>
- * An example of an action would be to start a radio station based on the current item
- * or to skip ahead by 30 seconds.
- *
- * @param customAction The custom action to add to the {@link PlaybackState}.
- * @return this
- */
- public Builder addCustomAction(PlaybackState.CustomAction customAction) {
- if (customAction == null) {
- throw new IllegalArgumentException(
- "You may not add a null CustomAction to PlaybackState.");
- }
- mCustomActions.add(customAction);
- return this;
- }
-
- /**
- * Set the current buffered position in ms. This is the farthest
- * playback point that can be reached from the current position using
- * only buffered content.
- *
- * @param bufferedPosition The position in ms that playback is buffered
- * to.
- * @return this
- */
- public Builder setBufferedPosition(long bufferedPosition) {
- mBufferedPosition = bufferedPosition;
- return this;
- }
-
- /**
- * Set the active item in the play queue by specifying its id. The
- * default value is {@link MediaSession.QueueItem#UNKNOWN_ID}
- *
- * @param id The id of the active item.
- * @return this
- */
- public Builder setActiveQueueItemId(long id) {
- mActiveItemId = id;
- return this;
- }
-
- /**
- * Set a user readable error message. This should be set when the state
- * is {@link PlaybackState#STATE_ERROR}.
- *
- * @param error The error message for display to the user.
- * @return this
- */
- public Builder setErrorMessage(CharSequence error) {
- mErrorMessage = error;
- return this;
- }
-
- /**
- * Set any custom extras to be included with the playback state.
- *
- * @param extras The extras to include.
- * @return this
- */
- public Builder setExtras(Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Build and return the {@link PlaybackState} instance with these
- * values.
- *
- * @return A new state instance.
- */
- public PlaybackState build() {
- return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferedPosition,
- mActions, mCustomActions, mActiveItemId, mErrorMessage, mExtras);
- }
- }
-}
diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl
deleted file mode 100644
index 84f41f6..0000000
--- a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 Google Inc. All Rights Reserved.
-
-package android.service.media;
-
-import android.content.res.Configuration;
-import android.service.media.IMediaBrowserServiceCallbacks;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-/**
- * Media API allows clients to browse through hierarchy of a user’s media collection,
- * playback a specific media entry and interact with the now playing queue.
- * @hide
- */
-oneway interface IMediaBrowserService {
- void connect(String pkg, in Bundle rootHints, IMediaBrowserServiceCallbacks callbacks);
- void disconnect(IMediaBrowserServiceCallbacks callbacks);
-
- void addSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
- void removeSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
-
- void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
- void addSubscription(String uri, in IBinder token, in Bundle options,
- IMediaBrowserServiceCallbacks callbacks);
- void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
-}
diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
deleted file mode 100644
index 8dc480d..0000000
--- a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2014 Google Inc. All Rights Reserved.
-
-package android.service.media;
-
-import android.graphics.Bitmap;
-import android.media.MediaParceledListSlice;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-
-/**
- * Media API allows clients to browse through hierarchy of a user’s media collection,
- * playback a specific media entry and interact with the now playing queue.
- * @hide
- */
-oneway interface IMediaBrowserServiceCallbacks {
- /**
- * Invoked when the connected has been established.
- * @param root The root media id for browsing.
- * @param session The {@link MediaSession.Token media session token} that can be used to control
- * the playback of the media app.
- * @param extra Extras returned by the media service.
- */
- void onConnect(String root, in MediaSession.Token session, in Bundle extras);
- void onConnectFailed();
- void onLoadChildren(String mediaId, in MediaParceledListSlice list);
- void onLoadChildrenWithOptions(String mediaId, in MediaParceledListSlice list,
- in Bundle options);
-}
diff --git a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java
deleted file mode 100644
index 76c99b9..0000000
--- a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java
+++ /dev/null
@@ -1,856 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
-import android.app.Service;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.media.MediaParceledListSlice;
-import android.media.browse.MediaBrowser;
-import android.media.browse.MediaBrowserUtils;
-import android.media.session.MediaSession;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.media.session.MediaSessionManager;
-import android.media.session.MediaSessionManager.RemoteUserInfo;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.service.media.IMediaBrowserService;
-import android.service.media.IMediaBrowserServiceCallbacks;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Pair;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Base class for media browser services.
- * <p>
- * Media browser services enable applications to browse media content provided by an application
- * and ask the application to start playing it. They may also be used to control content that
- * is already playing by way of a {@link MediaSession}.
- * </p>
- *
- * To extend this class, you must declare the service in your manifest file with
- * an intent filter with the {@link #SERVICE_INTERFACE} action.
- *
- * For example:
- * </p><pre>
- * <service android:name=".MyMediaBrowserService"
- * android:label="@string/service_name" >
- * <intent-filter>
- * <action android:name="android.media.browse.MediaBrowserService" />
- * </intent-filter>
- * </service>
- * </pre>
- *
- */
-public abstract class MediaBrowserService extends Service {
- private static final String TAG = "MediaBrowserService";
- private static final boolean DBG = false;
-
- /**
- * The {@link Intent} that must be declared as handled by the service.
- */
- @SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
-
- /**
- * A key for passing the MediaItem to the ResultReceiver in getItem.
- * @hide
- */
- @UnsupportedAppUsage
- public static final String KEY_MEDIA_ITEM = "media_item";
-
- private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0;
- private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1;
-
- private static final int RESULT_ERROR = -1;
- private static final int RESULT_OK = 0;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
- RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED })
- private @interface ResultFlags { }
-
- private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
- private ConnectionRecord mCurConnection;
- private final Handler mHandler = new Handler();
- private ServiceBinder mBinder;
- MediaSession.Token mSession;
-
- /**
- * All the info about a connection.
- */
- private class ConnectionRecord implements IBinder.DeathRecipient {
- String pkg;
- int uid;
- int pid;
- Bundle rootHints;
- IMediaBrowserServiceCallbacks callbacks;
- BrowserRoot root;
- HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
-
- @Override
- public void binderDied() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mConnections.remove(callbacks.asBinder());
- }
- });
- }
- }
-
- /**
- * Completion handler for asynchronous callback methods in {@link MediaBrowserService}.
- * <p>
- * Each of the methods that takes one of these to send the result must call
- * {@link #sendResult} to respond to the caller with the given results. If those
- * functions return without calling {@link #sendResult}, they must instead call
- * {@link #detach} before returning, and then may call {@link #sendResult} when
- * they are done. If more than one of those methods is called, an exception will
- * be thrown.
- *
- * @see #onLoadChildren
- * @see #onLoadItem
- */
- public class Result<T> {
- private Object mDebug;
- private boolean mDetachCalled;
- private boolean mSendResultCalled;
- @UnsupportedAppUsage
- private int mFlags;
-
- Result(Object debug) {
- mDebug = debug;
- }
-
- /**
- * Send the result back to the caller.
- */
- public void sendResult(T result) {
- if (mSendResultCalled) {
- throw new IllegalStateException("sendResult() called twice for: " + mDebug);
- }
- mSendResultCalled = true;
- onResultSent(result, mFlags);
- }
-
- /**
- * Detach this message from the current thread and allow the {@link #sendResult}
- * call to happen later.
- */
- public void detach() {
- if (mDetachCalled) {
- throw new IllegalStateException("detach() called when detach() had already"
- + " been called for: " + mDebug);
- }
- if (mSendResultCalled) {
- throw new IllegalStateException("detach() called when sendResult() had already"
- + " been called for: " + mDebug);
- }
- mDetachCalled = true;
- }
-
- boolean isDone() {
- return mDetachCalled || mSendResultCalled;
- }
-
- void setFlags(@ResultFlags int flags) {
- mFlags = flags;
- }
-
- /**
- * Called when the result is sent, after assertions about not being called twice
- * have happened.
- */
- void onResultSent(T result, @ResultFlags int flags) {
- }
- }
-
- private class ServiceBinder extends IMediaBrowserService.Stub {
- @Override
- public void connect(final String pkg, final Bundle rootHints,
- final IMediaBrowserServiceCallbacks callbacks) {
-
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- if (!isValidPackage(pkg, uid)) {
- throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
- + " package=" + pkg);
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
-
- // Clear out the old subscriptions. We are getting new ones.
- mConnections.remove(b);
-
- final ConnectionRecord connection = new ConnectionRecord();
- connection.pkg = pkg;
- connection.pid = pid;
- connection.uid = uid;
- connection.rootHints = rootHints;
- connection.callbacks = callbacks;
-
- mCurConnection = connection;
- connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
- mCurConnection = null;
-
- // If they didn't return something, don't allow this client.
- if (connection.root == null) {
- Log.i(TAG, "No root for client " + pkg + " from service "
- + getClass().getName());
- try {
- callbacks.onConnectFailed();
- } catch (RemoteException ex) {
- Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
- + "pkg=" + pkg);
- }
- } else {
- try {
- mConnections.put(b, connection);
- b.linkToDeath(connection, 0);
- if (mSession != null) {
- callbacks.onConnect(connection.root.getRootId(),
- mSession, connection.root.getExtras());
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "Calling onConnect() failed. Dropping client. "
- + "pkg=" + pkg);
- mConnections.remove(b);
- }
- }
- }
- });
- }
-
- @Override
- public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
-
- // Clear out the old subscriptions. We are getting new ones.
- final ConnectionRecord old = mConnections.remove(b);
- if (old != null) {
- // TODO
- old.callbacks.asBinder().unlinkToDeath(old, 0);
- }
- }
- });
- }
-
- @Override
- public void addSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) {
- // do-nothing
- }
-
- @Override
- public void addSubscription(final String id, final IBinder token, final Bundle options,
- final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
-
- // Get the record for the connection
- final ConnectionRecord connection = mConnections.get(b);
- if (connection == null) {
- Log.w(TAG, "addSubscription for callback that isn't registered id="
- + id);
- return;
- }
-
- MediaBrowserService.this.addSubscription(id, connection, token, options);
- }
- });
- }
-
- @Override
- public void removeSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) {
- // do-nothing
- }
-
- @Override
- public void removeSubscription(final String id, final IBinder token,
- final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
-
- ConnectionRecord connection = mConnections.get(b);
- if (connection == null) {
- Log.w(TAG, "removeSubscription for callback that isn't registered id="
- + id);
- return;
- }
- if (!MediaBrowserService.this.removeSubscription(id, connection, token)) {
- Log.w(TAG, "removeSubscription called for " + id
- + " which is not subscribed");
- }
- }
- });
- }
-
- @Override
- public void getMediaItem(final String mediaId, final ResultReceiver receiver,
- final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
- ConnectionRecord connection = mConnections.get(b);
- if (connection == null) {
- Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
- return;
- }
- performLoadItem(mediaId, connection, receiver);
- }
- });
- }
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mBinder = new ServiceBinder();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mBinder;
- }
- return null;
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- }
-
- /**
- * Called to get the root information for browsing by a particular client.
- * <p>
- * The implementation should verify that the client package has permission
- * to access browse media information before returning the root id; it
- * should return null if the client is not allowed to access this
- * information.
- * </p>
- *
- * @param clientPackageName The package name of the application which is
- * requesting access to browse media.
- * @param clientUid The uid of the application which is requesting access to
- * browse media.
- * @param rootHints An optional bundle of service-specific arguments to send
- * to the media browser service when connecting and retrieving the
- * root id for browsing, or null if none. The contents of this
- * bundle may affect the information returned when browsing.
- * @return The {@link BrowserRoot} for accessing this app's content or null.
- * @see BrowserRoot#EXTRA_RECENT
- * @see BrowserRoot#EXTRA_OFFLINE
- * @see BrowserRoot#EXTRA_SUGGESTED
- */
- public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
- int clientUid, @Nullable Bundle rootHints);
-
- /**
- * Called to get information about the children of a media item.
- * <p>
- * Implementations must call {@link Result#sendResult result.sendResult}
- * with the list of children. If loading the children will be an expensive
- * operation that should be performed on another thread,
- * {@link Result#detach result.detach} may be called before returning from
- * this function, and then {@link Result#sendResult result.sendResult}
- * called when the loading is complete.
- * </p><p>
- * In case the media item does not have any children, call {@link Result#sendResult}
- * with an empty list. When the given {@code parentId} is invalid, implementations must
- * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
- * {@link MediaBrowser.SubscriptionCallback#onError}.
- * </p>
- *
- * @param parentId The id of the parent media item whose children are to be
- * queried.
- * @param result The Result to send the list of children to.
- */
- public abstract void onLoadChildren(@NonNull String parentId,
- @NonNull Result<List<MediaBrowser.MediaItem>> result);
-
- /**
- * Called to get information about the children of a media item.
- * <p>
- * Implementations must call {@link Result#sendResult result.sendResult}
- * with the list of children. If loading the children will be an expensive
- * operation that should be performed on another thread,
- * {@link Result#detach result.detach} may be called before returning from
- * this function, and then {@link Result#sendResult result.sendResult}
- * called when the loading is complete.
- * </p><p>
- * In case the media item does not have any children, call {@link Result#sendResult}
- * with an empty list. When the given {@code parentId} is invalid, implementations must
- * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
- * {@link MediaBrowser.SubscriptionCallback#onError}.
- * </p>
- *
- * @param parentId The id of the parent media item whose children are to be
- * queried.
- * @param result The Result to send the list of children to.
- * @param options The bundle of service-specific arguments sent from the media
- * browser. The information returned through the result should be
- * affected by the contents of this bundle.
- */
- public void onLoadChildren(@NonNull String parentId,
- @NonNull Result<List<MediaBrowser.MediaItem>> result, @NonNull Bundle options) {
- // To support backward compatibility, when the implementation of MediaBrowserService doesn't
- // override onLoadChildren() with options, onLoadChildren() without options will be used
- // instead, and the options will be applied in the implementation of result.onResultSent().
- result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED);
- onLoadChildren(parentId, result);
- }
-
- /**
- * Called to get information about a specific media item.
- * <p>
- * Implementations must call {@link Result#sendResult result.sendResult}. If
- * loading the item will be an expensive operation {@link Result#detach
- * result.detach} may be called before returning from this function, and
- * then {@link Result#sendResult result.sendResult} called when the item has
- * been loaded.
- * </p><p>
- * When the given {@code itemId} is invalid, implementations must call
- * {@link Result#sendResult result.sendResult} with {@code null}.
- * </p><p>
- * The default implementation will invoke {@link MediaBrowser.ItemCallback#onError}.
- * </p>
- *
- * @param itemId The id for the specific
- * {@link android.media.browse.MediaBrowser.MediaItem}.
- * @param result The Result to send the item to.
- */
- public void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) {
- result.setFlags(RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED);
- result.sendResult(null);
- }
-
- /**
- * Call to set the media session.
- * <p>
- * This should be called as soon as possible during the service's startup.
- * It may only be called once.
- *
- * @param token The token for the service's {@link MediaSession}.
- */
- public void setSessionToken(final MediaSession.Token token) {
- if (token == null) {
- throw new IllegalArgumentException("Session token may not be null.");
- }
- if (mSession != null) {
- throw new IllegalStateException("The session token has already been set.");
- }
- mSession = token;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Iterator<ConnectionRecord> iter = mConnections.values().iterator();
- while (iter.hasNext()){
- ConnectionRecord connection = iter.next();
- try {
- connection.callbacks.onConnect(connection.root.getRootId(), token,
- connection.root.getExtras());
- } catch (RemoteException e) {
- Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
- iter.remove();
- }
- }
- }
- });
- }
-
- /**
- * Gets the session token, or null if it has not yet been created
- * or if it has been destroyed.
- */
- public @Nullable MediaSession.Token getSessionToken() {
- return mSession;
- }
-
- /**
- * Gets the root hints sent from the currently connected {@link MediaBrowser}.
- * The root hints are service-specific arguments included in an optional bundle sent to the
- * media browser service when connecting and retrieving the root id for browsing, or null if
- * none. The contents of this bundle may affect the information returned when browsing.
- *
- * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
- * {@link #onLoadChildren} or {@link #onLoadItem}.
- * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
- * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
- * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
- */
- public final Bundle getBrowserRootHints() {
- if (mCurConnection == null) {
- throw new IllegalStateException("This should be called inside of onGetRoot or"
- + " onLoadChildren or onLoadItem methods");
- }
- return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
- }
-
- /**
- * Gets the browser information who sent the current request.
- *
- * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
- * {@link #onLoadChildren} or {@link #onLoadItem}.
- * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
- */
- public final RemoteUserInfo getCurrentBrowserInfo() {
- if (mCurConnection == null) {
- throw new IllegalStateException("This should be called inside of onGetRoot or"
- + " onLoadChildren or onLoadItem methods");
- }
- return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid);
- }
-
- /**
- * Notifies all connected media browsers that the children of
- * the specified parent id have changed in some way.
- * This will cause browsers to fetch subscribed content again.
- *
- * @param parentId The id of the parent media item whose
- * children changed.
- */
- public void notifyChildrenChanged(@NonNull String parentId) {
- notifyChildrenChangedInternal(parentId, null);
- }
-
- /**
- * Notifies all connected media browsers that the children of
- * the specified parent id have changed in some way.
- * This will cause browsers to fetch subscribed content again.
- *
- * @param parentId The id of the parent media item whose
- * children changed.
- * @param options The bundle of service-specific arguments to send
- * to the media browser. The contents of this bundle may
- * contain the information about the change.
- */
- public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
- if (options == null) {
- throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged");
- }
- notifyChildrenChangedInternal(parentId, options);
- }
-
- private void notifyChildrenChangedInternal(final String parentId, final Bundle options) {
- if (parentId == null) {
- throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (IBinder binder : mConnections.keySet()) {
- ConnectionRecord connection = mConnections.get(binder);
- List<Pair<IBinder, Bundle>> callbackList =
- connection.subscriptions.get(parentId);
- if (callbackList != null) {
- for (Pair<IBinder, Bundle> callback : callbackList) {
- if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) {
- performLoadChildren(parentId, connection, callback.second);
- }
- }
- }
- }
- }
- });
- }
-
- /**
- * Return whether the given package is one of the ones that is owned by the uid.
- */
- private boolean isValidPackage(String pkg, int uid) {
- if (pkg == null) {
- return false;
- }
- final PackageManager pm = getPackageManager();
- final String[] packages = pm.getPackagesForUid(uid);
- final int N = packages.length;
- for (int i=0; i<N; i++) {
- if (packages[i].equals(pkg)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Save the subscription and if it is a new subscription send the results.
- */
- private void addSubscription(String id, ConnectionRecord connection, IBinder token,
- Bundle options) {
- // Save the subscription
- List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
- if (callbackList == null) {
- callbackList = new ArrayList<>();
- }
- for (Pair<IBinder, Bundle> callback : callbackList) {
- if (token == callback.first
- && MediaBrowserUtils.areSameOptions(options, callback.second)) {
- return;
- }
- }
- callbackList.add(new Pair<>(token, options));
- connection.subscriptions.put(id, callbackList);
- // send the results
- performLoadChildren(id, connection, options);
- }
-
- /**
- * Remove the subscription.
- */
- private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) {
- if (token == null) {
- return connection.subscriptions.remove(id) != null;
- }
- boolean removed = false;
- List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
- if (callbackList != null) {
- Iterator<Pair<IBinder, Bundle>> iter = callbackList.iterator();
- while (iter.hasNext()){
- if (token == iter.next().first) {
- removed = true;
- iter.remove();
- }
- }
- if (callbackList.size() == 0) {
- connection.subscriptions.remove(id);
- }
- }
- return removed;
- }
-
- /**
- * Call onLoadChildren and then send the results back to the connection.
- * <p>
- * Callers must make sure that this connection is still connected.
- */
- private void performLoadChildren(final String parentId, final ConnectionRecord connection,
- final Bundle options) {
- final Result<List<MediaBrowser.MediaItem>> result
- = new Result<List<MediaBrowser.MediaItem>>(parentId) {
- @Override
- void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) {
- if (mConnections.get(connection.callbacks.asBinder()) != connection) {
- if (DBG) {
- Log.d(TAG, "Not sending onLoadChildren result for connection that has"
- + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
- }
- return;
- }
-
- List<MediaBrowser.MediaItem> filteredList =
- (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
- ? applyOptions(list, options) : list;
- final MediaParceledListSlice<MediaBrowser.MediaItem> pls =
- filteredList == null ? null : new MediaParceledListSlice<>(filteredList);
- try {
- connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options);
- } catch (RemoteException ex) {
- // The other side is in the process of crashing.
- Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
- + " package=" + connection.pkg);
- }
- }
- };
-
- mCurConnection = connection;
- if (options == null) {
- onLoadChildren(parentId, result);
- } else {
- onLoadChildren(parentId, result, options);
- }
- mCurConnection = null;
-
- if (!result.isDone()) {
- throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
- + " before returning for package=" + connection.pkg + " id=" + parentId);
- }
- }
-
- private List<MediaBrowser.MediaItem> applyOptions(List<MediaBrowser.MediaItem> list,
- final Bundle options) {
- if (list == null) {
- return null;
- }
- int page = options.getInt(MediaBrowser.EXTRA_PAGE, -1);
- int pageSize = options.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
- if (page == -1 && pageSize == -1) {
- return list;
- }
- int fromIndex = pageSize * page;
- int toIndex = fromIndex + pageSize;
- if (page < 0 || pageSize < 1 || fromIndex >= list.size()) {
- return Collections.EMPTY_LIST;
- }
- if (toIndex > list.size()) {
- toIndex = list.size();
- }
- return list.subList(fromIndex, toIndex);
- }
-
- private void performLoadItem(String itemId, final ConnectionRecord connection,
- final ResultReceiver receiver) {
- final Result<MediaBrowser.MediaItem> result =
- new Result<MediaBrowser.MediaItem>(itemId) {
- @Override
- void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
- if (mConnections.get(connection.callbacks.asBinder()) != connection) {
- if (DBG) {
- Log.d(TAG, "Not sending onLoadItem result for connection that has"
- + " been disconnected. pkg=" + connection.pkg + " id=" + itemId);
- }
- return;
- }
- if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
- receiver.send(RESULT_ERROR, null);
- return;
- }
- Bundle bundle = new Bundle();
- bundle.putParcelable(KEY_MEDIA_ITEM, item);
- receiver.send(RESULT_OK, bundle);
- }
- };
-
- mCurConnection = connection;
- onLoadItem(itemId, result);
- mCurConnection = null;
-
- if (!result.isDone()) {
- throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
- + " before returning for id=" + itemId);
- }
- }
-
- /**
- * Contains information that the browser service needs to send to the client
- * when first connected.
- */
- public static final class BrowserRoot {
- /**
- * The lookup key for a boolean that indicates whether the browser service should return a
- * browser root for recently played media items.
- *
- * <p>When creating a media browser for a given media browser service, this key can be
- * supplied as a root hint for retrieving media items that are recently played.
- * If the media browser service can provide such media items, the implementation must return
- * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
- *
- * <p>The root hint may contain multiple keys.
- *
- * @see #EXTRA_OFFLINE
- * @see #EXTRA_SUGGESTED
- */
- public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
-
- /**
- * The lookup key for a boolean that indicates whether the browser service should return a
- * browser root for offline media items.
- *
- * <p>When creating a media browser for a given media browser service, this key can be
- * supplied as a root hint for retrieving media items that are can be played without an
- * internet connection.
- * If the media browser service can provide such media items, the implementation must return
- * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
- *
- * <p>The root hint may contain multiple keys.
- *
- * @see #EXTRA_RECENT
- * @see #EXTRA_SUGGESTED
- */
- public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
-
- /**
- * The lookup key for a boolean that indicates whether the browser service should return a
- * browser root for suggested media items.
- *
- * <p>When creating a media browser for a given media browser service, this key can be
- * supplied as a root hint for retrieving the media items suggested by the media browser
- * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
- * is considered ordered by relevance, first being the top suggestion.
- * If the media browser service can provide such media items, the implementation must return
- * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
- *
- * <p>The root hint may contain multiple keys.
- *
- * @see #EXTRA_RECENT
- * @see #EXTRA_OFFLINE
- */
- public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
-
- final private String mRootId;
- final private Bundle mExtras;
-
- /**
- * Constructs a browser root.
- * @param rootId The root id for browsing.
- * @param extras Any extras about the browser service.
- */
- public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
- if (rootId == null) {
- throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
- "Use null for BrowserRoot instead.");
- }
- mRootId = rootId;
- mExtras = extras;
- }
-
- /**
- * Gets the root id for browsing.
- */
- public String getRootId() {
- return mRootId;
- }
-
- /**
- * Gets any extras about the browser service.
- */
- public Bundle getExtras() {
- return mExtras;
- }
- }
-}
diff --git a/packages/OWNERS b/packages/OWNERS
deleted file mode 100644
index 3b9fd2b..0000000
--- a/packages/OWNERS
+++ /dev/null
@@ -1,9 +0,0 @@
-akersten@google.com
-dwkang@google.com
-hdmoon@google.com
-insun@google.com
-jaewan@google.com
-jinpark@google.com
-marcone@google.com
-sungsoo@google.com
-wjia@google.com
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index c0aa477..91b7587 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -38,7 +38,8 @@
libpowermanager \
libmediautils \
libmemunreachable \
- libmedia_helper
+ libmedia_helper \
+ libvibrator
LOCAL_STATIC_LIBRARIES := \
libcpustats \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 0d6ef46..bc99099 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -27,6 +27,7 @@
#include <sys/time.h>
#include <sys/resource.h>
+#include <android/os/IExternalVibratorService.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
@@ -122,6 +123,21 @@
}
}
+// Keep a strong reference to external vibrator service
+static sp<os::IExternalVibratorService> sExternalVibratorService;
+
+static sp<os::IExternalVibratorService> getExternalVibratorService() {
+ if (sExternalVibratorService == 0) {
+ sp <IBinder> binder = defaultServiceManager()->getService(
+ String16("external_vibrator_service"));
+ if (binder != 0) {
+ sExternalVibratorService =
+ interface_cast<os::IExternalVibratorService>(binder);
+ }
+ }
+ return sExternalVibratorService;
+}
+
// ----------------------------------------------------------------------------
std::string formatToString(audio_format_t format) {
@@ -318,6 +334,27 @@
return ret;
}
+/* static */
+int AudioFlinger::onExternalVibrationStart(const sp<os::ExternalVibration>& externalVibration) {
+ sp<os::IExternalVibratorService> evs = getExternalVibratorService();
+ if (evs != 0) {
+ int32_t ret;
+ binder::Status status = evs->onExternalVibrationStart(*externalVibration, &ret);
+ if (status.isOk()) {
+ return ret;
+ }
+ }
+ return AudioMixer::HAPTIC_SCALE_NONE;
+}
+
+/* static */
+void AudioFlinger::onExternalVibrationStop(const sp<os::ExternalVibration>& externalVibration) {
+ sp<os::IExternalVibratorService> evs = getExternalVibratorService();
+ if (evs != 0) {
+ evs->onExternalVibrationStop(*externalVibration);
+ }
+}
+
static const char * const audio_interfaces[] = {
AUDIO_HARDWARE_MODULE_ID_PRIMARY,
AUDIO_HARDWARE_MODULE_ID_A2DP,
@@ -897,6 +934,40 @@
return NO_ERROR;
}
+status_t AudioFlinger::setMasterBalance(float balance)
+{
+ status_t ret = initCheck();
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ // check range
+ if (isnan(balance) || fabs(balance) > 1.f) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mLock);
+
+ // short cut.
+ if (mMasterBalance == balance) return NO_ERROR;
+
+ mMasterBalance = balance;
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i)->isDuplicating()) {
+ continue;
+ }
+ mPlaybackThreads.valueAt(i)->setMasterBalance(balance);
+ }
+
+ return NO_ERROR;
+}
+
status_t AudioFlinger::setMode(audio_mode_t mode)
{
status_t ret = initCheck();
@@ -1036,6 +1107,13 @@
return masterVolume_l();
}
+status_t AudioFlinger::getMasterBalance(float *balance) const
+{
+ Mutex::Autolock _l(mLock);
+ *balance = getMasterBalance_l();
+ return NO_ERROR; // if called through binder, may return a transactional error
+}
+
bool AudioFlinger::masterMute() const
{
Mutex::Autolock _l(mLock);
@@ -1047,6 +1125,11 @@
return mMasterVolume;
}
+float AudioFlinger::getMasterBalance_l() const
+{
+ return mMasterBalance;
+}
+
bool AudioFlinger::masterMute_l() const
{
return mMasterMute;
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c1169d2..d8c0da5 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -30,6 +30,7 @@
#include <sys/types.h>
#include <limits.h>
+#include <android/os/BnExternalVibrationController.h>
#include <android-base/macros.h>
#include <cutils/atomic.h>
@@ -84,6 +85,8 @@
#include <private/media/AudioEffectShared.h>
#include <private/media/AudioTrackShared.h>
+#include <vibrator/ExternalVibration.h>
+
#include "android/media/BnAudioRecord.h"
namespace android {
@@ -137,6 +140,10 @@
virtual float masterVolume() const;
virtual bool masterMute() const;
+ // Balance value must be within -1.f (left only) to 1.f (right only) inclusive.
+ status_t setMasterBalance(float balance) override;
+ status_t getMasterBalance(float *balance) const override;
+
virtual status_t setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output);
virtual status_t setStreamMute(audio_stream_type_t stream, bool muted);
@@ -284,6 +291,9 @@
const sp<MmapStreamCallback>& callback,
sp<MmapStreamInterface>& interface,
audio_port_handle_t *handle);
+
+ static int onExternalVibrationStart(const sp<os::ExternalVibration>& externalVibration);
+ static void onExternalVibrationStop(const sp<os::ExternalVibration>& externalVibration);
private:
// FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
static const size_t kLogMemorySize = 400 * 1024;
@@ -776,6 +786,7 @@
// member variables below are protected by mLock
float mMasterVolume;
bool mMasterMute;
+ float mMasterBalance = 0.f;
// end of variables protected by mLock
DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> > mRecordThreads;
@@ -793,6 +804,7 @@
Vector<AudioSessionRef*> mAudioSessionRefs;
float masterVolume_l() const;
+ float getMasterBalance_l() const;
bool masterMute_l() const;
audio_module_handle_t loadHwModule_l(const char *name);
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index f328577..e78c98b 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -60,7 +60,6 @@
mSinkChannelCount(FCC_2),
mMixerBuffer(NULL),
mMixerBufferSize(0),
- mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT),
mMixerBufferState(UNDEFINED),
mFormat(Format_Invalid),
mSampleRate(0),
@@ -161,6 +160,7 @@
mOutputSink = current->mOutputSink;
mOutputSinkGen = current->mOutputSinkGen;
mSinkChannelMask = current->mSinkChannelMask;
+ mBalance.setChannelMask(mSinkChannelMask);
if (mOutputSink == NULL) {
mFormat = Format_Invalid;
mSampleRate = 0;
@@ -191,10 +191,6 @@
free(mSinkBuffer);
mSinkBuffer = NULL;
if (frameCount > 0 && mSampleRate > 0) {
- // The mixer produces either 16 bit PCM or float output, select
- // float output if the HAL supports higher than 16 bit precision.
- mMixerBufferFormat = mFormat.mFormat == AUDIO_FORMAT_PCM_16_BIT ?
- AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT;
// FIXME new may block for unbounded time at internal mutex of the heap
// implementation; it would be better to have normal mixer allocate for us
// to avoid blocking here and to prevent possible priority inversion
@@ -295,6 +291,8 @@
(void *)(uintptr_t)mSinkChannelMask);
mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
(void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
+ mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
+ (void *)(uintptr_t)fastTrack->mHapticIntensity);
mMixer->enable(name);
}
mGenerations[i] = fastTrack->mGeneration;
@@ -333,6 +331,8 @@
(void *)(uintptr_t)mSinkChannelMask);
mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
(void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
+ mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
+ (void *)(uintptr_t)fastTrack->mHapticIntensity);
// already enabled
}
mGenerations[i] = fastTrack->mGeneration;
@@ -471,6 +471,12 @@
mono_blend(mMixerBuffer, mMixerBufferFormat, Format_channelCount(mFormat), frameCount,
true /*limit*/);
}
+
+ // Balance must take effect after mono conversion.
+ // mBalance detects zero balance within the class for speed (not needed here).
+ mBalance.setBalance(mMasterBalance.load());
+ mBalance.process((float *)mMixerBuffer, frameCount);
+
// prepare the buffer used to write to sink
void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer;
if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 1d332e0..c31d476 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -18,6 +18,7 @@
#define ANDROID_AUDIO_FAST_MIXER_H
#include <atomic>
+#include <audio_utils/Balance.h>
#include "FastThread.h"
#include "StateQueue.h"
#include "FastMixerState.h"
@@ -41,6 +42,8 @@
FastMixerStateQueue* sq();
virtual void setMasterMono(bool mono) { mMasterMono.store(mono); /* memory_order_seq_cst */ }
+ virtual void setMasterBalance(float balance) { mMasterBalance.store(balance); }
+ virtual float getMasterBalance() const { return mMasterBalance.load(); }
virtual void setBoottimeOffset(int64_t boottimeOffset) {
mBoottimeOffset.store(boottimeOffset); /* memory_order_seq_cst */
}
@@ -74,7 +77,7 @@
audio_channel_mask_t mSinkChannelMask;
void* mMixerBuffer; // mixer output buffer.
size_t mMixerBufferSize;
- audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT).
+ static constexpr audio_format_t mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT;
uint32_t mAudioChannelCount; // audio channel count, excludes haptic channels.
@@ -89,8 +92,11 @@
ExtendedTimestamp mTimestamp;
int64_t mNativeFramesWrittenButNotPresented;
+ audio_utils::Balance mBalance;
+
// accessed without lock between multiple threads.
std::atomic_bool mMasterMono;
+ std::atomic<float> mMasterBalance{};
std::atomic_int_fast64_t mBoottimeOffset;
const audio_io_handle_t mThreadIoHandle; // parent thread id for debugging purposes
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 9d2a733..c27f2b7 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -19,6 +19,7 @@
#include <audio_utils/minifloat.h>
#include <system/audio.h>
+#include <media/AudioMixer.h>
#include <media/ExtendedAudioBufferProvider.h>
#include <media/nbaio/NBAIO.h>
#include <media/nblog/NBLog.h>
@@ -48,6 +49,8 @@
audio_format_t mFormat; // track format
int mGeneration; // increment when any field is assigned
bool mHapticPlaybackEnabled = false; // haptic playback is enabled or not
+ AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; // intensity of
+ // haptic data
};
// Represents a single state of the fast mixer
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index d9f570d..bad3ca8 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -119,6 +119,15 @@
void setHapticPlaybackEnabled(bool hapticPlaybackEnabled) {
mHapticPlaybackEnabled = hapticPlaybackEnabled;
}
+ /** Return at what intensity to play haptics, used in mixer. */
+ AudioMixer::haptic_intensity_t getHapticIntensity() const { return mHapticIntensity; }
+ /** Set intensity of haptic playback, should be set after querying vibrator service. */
+ void setHapticIntensity(AudioMixer::haptic_intensity_t hapticIntensity) {
+ if (AudioMixer::isValidHapticIntensity(hapticIntensity)) {
+ mHapticIntensity = hapticIntensity;
+ }
+ }
+ sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
protected:
// for numerous
@@ -197,6 +206,18 @@
sp<media::VolumeHandler> mVolumeHandler; // handles multiple VolumeShaper configs and operations
bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not
+ // intensity to play haptic data
+ AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE;
+ class AudioVibrationController : public os::BnExternalVibrationController {
+ public:
+ explicit AudioVibrationController(Track* track) : mTrack(track) {}
+ binder::Status mute(/*out*/ bool *ret) override;
+ binder::Status unmute(/*out*/ bool *ret) override;
+ private:
+ Track* const mTrack;
+ };
+ sp<AudioVibrationController> mAudioVibrationController;
+ sp<os::ExternalVibration> mExternalVibration;
private:
// The following fields are only for fast tracks, and should be in a subclass
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 9f838a3..5a70864 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -38,6 +38,7 @@
#include <private/media/AudioTrackShared.h>
#include <private/android_filesystem_config.h>
+#include <audio_utils/Balance.h>
#include <audio_utils/channels.h>
#include <audio_utils/mono_blend.h>
#include <audio_utils/primitives.h>
@@ -2271,6 +2272,11 @@
}
}
+void AudioFlinger::PlaybackThread::setMasterBalance(float balance)
+{
+ mMasterBalance.store(balance);
+}
+
void AudioFlinger::PlaybackThread::setMasterMute(bool muted)
{
if (isDuplicating()) {
@@ -2358,15 +2364,23 @@
track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
}
- // Disable all haptic playback for all other active tracks when haptic playback is supported
- // and the track contains haptic channels. Enable haptic playback for current track.
- // TODO: Request actual haptic playback status from vibrator service
if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
&& mHapticChannelMask != AUDIO_CHANNEL_NONE) {
- for (auto &t : mActiveTracks) {
- t->setHapticPlaybackEnabled(false);
+ // Unlock due to VibratorService will lock for this call and will
+ // call Tracks.mute/unmute which also require thread's lock.
+ mLock.unlock();
+ const int intensity = AudioFlinger::onExternalVibrationStart(
+ track->getExternalVibration());
+ mLock.lock();
+ // Haptic playback should be enabled by vibrator service.
+ if (track->getHapticPlaybackEnabled()) {
+ // Disable haptic playback of all active track to ensure only
+ // one track playing haptic if current track should play haptic.
+ for (const auto &t : mActiveTracks) {
+ t->setHapticPlaybackEnabled(false);
+ }
}
- track->setHapticPlaybackEnabled(true);
+ track->setHapticIntensity(intensity);
}
track->mResetDone = false;
@@ -2523,6 +2537,7 @@
mChannelMask);
}
mChannelCount = audio_channel_count_from_out_mask(mChannelMask);
+ mBalance.setChannelMask(mChannelMask);
// Get actual HAL format.
status_t result = mOutput->stream->getFormat(&mHALFormat);
@@ -2642,7 +2657,7 @@
free(mMixerBuffer);
mMixerBuffer = NULL;
if (mMixerBufferEnabled) {
- mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // also valid: AUDIO_FORMAT_PCM_16_BIT.
+ mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // no longer valid: AUDIO_FORMAT_PCM_16_BIT.
mMixerBufferSize = mNormalFrameCount * mChannelCount
* audio_bytes_per_sample(mMixerBufferFormat);
(void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize);
@@ -3531,6 +3546,14 @@
true /*limit*/);
}
+ if (!hasFastMixer()) {
+ // Balance must take effect after mono conversion.
+ // We do it here if there is no FastMixer.
+ // mBalance detects zero balance within the class for speed (not needed here).
+ mBalance.setBalance(mMasterBalance.load());
+ mBalance.process((float *)mMixerBuffer, mNormalFrameCount);
+ }
+
memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
mNormalFrameCount * (mChannelCount + mHapticChannelCount));
@@ -3585,6 +3608,14 @@
true /*limit*/);
}
+ if (!hasFastMixer()) {
+ // Balance must take effect after mono conversion.
+ // We do it here if there is no FastMixer.
+ // mBalance detects zero balance within the class for speed (not needed here).
+ mBalance.setBalance(mMasterBalance.load());
+ mBalance.process((float *)mEffectBuffer, mNormalFrameCount);
+ }
+
memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat,
mNormalFrameCount * (mChannelCount + mHapticChannelCount));
// The sample data is partially interleaved when haptic channels exist,
@@ -3760,7 +3791,6 @@
// removeTracks_l() must be called with ThreadBase::mLock held
void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove)
{
- bool enabledHapticTracksRemoved = false;
for (const auto& track : tracksToRemove) {
mActiveTracks.remove(track);
ALOGV("%s(%d): removing track on session %d", __func__, track->id(), track->sessionId());
@@ -3782,17 +3812,13 @@
// remove from our tracks vector
removeTrack_l(track);
}
- enabledHapticTracksRemoved |= track->getHapticPlaybackEnabled();
- }
- // If the thread supports haptic playback and the track playing haptic data was removed,
- // enable haptic playback on the first active track that contains haptic channels.
- // TODO: Query vibrator service to know which track should enable haptic playback.
- if (enabledHapticTracksRemoved && mHapticChannelMask != AUDIO_CHANNEL_NONE) {
- for (auto &t : mActiveTracks) {
- if (t->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) {
- t->setHapticPlaybackEnabled(true);
- break;
- }
+ if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
+ && mHapticChannelCount > 0) {
+ mLock.unlock();
+ // Unlock due to VibratorService will lock for this call and will
+ // call Tracks.mute/unmute which also require thread's lock.
+ AudioFlinger::onExternalVibrationStop(track->getExternalVibration());
+ mLock.lock();
}
}
}
@@ -3985,6 +4011,7 @@
// mPipeSink below
// mNormalSink below
{
+ setMasterBalance(audioFlinger->getMasterBalance_l());
ALOGV("MixerThread() id=%d device=%#x type=%d", id, device, type);
ALOGV("mSampleRate=%u, mChannelMask=%#x, mChannelCount=%u, mFormat=%#x, mFrameSize=%zu, "
"mFrameCount=%zu, mNormalFrameCount=%zu",
@@ -4615,6 +4642,7 @@
fastTrack->mChannelMask = track->mChannelMask;
fastTrack->mFormat = track->mFormat;
fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled();
+ fastTrack->mHapticIntensity = track->getHapticIntensity();
fastTrack->mGeneration++;
state->mTrackMask |= 1 << j;
didModify = true;
@@ -4937,6 +4965,10 @@
trackId,
AudioMixer::TRACK,
AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)track->getHapticPlaybackEnabled());
+ mAudioMixer->setParameter(
+ trackId,
+ AudioMixer::TRACK,
+ AudioMixer::HAPTIC_INTENSITY, (void *)(uintptr_t)track->getHapticIntensity());
// reset retry count
track->mRetryCount = kMaxTrackRetries;
@@ -5266,6 +5298,9 @@
dprintf(fd, " Thread throttle time (msecs): %u\n", mThreadThrottleTimeMs);
dprintf(fd, " AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str());
dprintf(fd, " Master mono: %s\n", mMasterMono ? "on" : "off");
+ dprintf(fd, " Master balance: %f (%s)\n", mMasterBalance.load(),
+ (hasFastMixer() ? std::to_string(mFastMixer->getMasterBalance())
+ : mBalance.toString()).c_str());
const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate);
if (latencyMs != 0.) {
dprintf(fd, " NormalMixer latency ms: %.2lf\n", latencyMs);
@@ -5333,12 +5368,30 @@
ThreadBase::type_t type, bool systemReady)
: PlaybackThread(audioFlinger, output, id, device, type, systemReady)
{
+ setMasterBalance(audioFlinger->getMasterBalance_l());
}
AudioFlinger::DirectOutputThread::~DirectOutputThread()
{
}
+void AudioFlinger::DirectOutputThread::dumpInternals(int fd, const Vector<String16>& args)
+{
+ PlaybackThread::dumpInternals(fd, args);
+ dprintf(fd, " Master balance: %f Left: %f Right: %f\n",
+ mMasterBalance.load(), mMasterBalanceLeft, mMasterBalanceRight);
+}
+
+void AudioFlinger::DirectOutputThread::setMasterBalance(float balance)
+{
+ Mutex::Autolock _l(mLock);
+ if (mMasterBalance != balance) {
+ mMasterBalance.store(balance);
+ mBalance.computeStereoBalance(balance, &mMasterBalanceLeft, &mMasterBalanceRight);
+ broadcast_l();
+ }
+}
+
void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack)
{
float left, right;
@@ -5362,12 +5415,12 @@
if (left > GAIN_FLOAT_UNITY) {
left = GAIN_FLOAT_UNITY;
}
- left *= v;
+ left *= v * mMasterBalanceLeft; // DirectOutputThread balance applied as track volume
right = float_from_gain(gain_minifloat_unpack_right(vlr));
if (right > GAIN_FLOAT_UNITY) {
right = GAIN_FLOAT_UNITY;
}
- right *= v;
+ right *= v * mMasterBalanceRight;
}
if (lastTrack) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 8b8222c..1131b26 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -733,6 +733,7 @@
// VolumeInterface
virtual void setMasterVolume(float value);
+ virtual void setMasterBalance(float balance);
virtual void setMasterMute(bool muted);
virtual void setStreamVolume(audio_stream_type_t stream, float value);
virtual void setStreamMute(audio_stream_type_t stream, bool muted);
@@ -1027,6 +1028,8 @@
AudioStreamOut *mOutput;
float mMasterVolume;
+ std::atomic<float> mMasterBalance{};
+ audio_utils::Balance mBalance;
nsecs_t mLastWriteTime;
int mNumWrites;
int mNumDelayedWrites;
@@ -1199,6 +1202,13 @@
// Blending with limiter is not idempotent,
// and blending without limiter is idempotent but inefficient to do twice.
virtual bool requireMonoBlend() { return mMasterMono.load() && !hasFastMixer(); }
+
+ void setMasterBalance(float balance) override {
+ mMasterBalance.store(balance);
+ if (hasFastMixer()) {
+ mFastMixer->setMasterBalance(balance);
+ }
+ }
};
class DirectOutputThread : public PlaybackThread {
@@ -1216,8 +1226,13 @@
virtual bool checkForNewParameter_l(const String8& keyValuePair,
status_t& status);
+
+ void dumpInternals(int fd, const Vector<String16>& args) override;
+
virtual void flushHw_l();
+ void setMasterBalance(float balance) override;
+
protected:
virtual uint32_t activeSleepTimeUs() const;
virtual uint32_t idleSleepTimeUs() const;
@@ -1245,6 +1260,10 @@
wp<Track> mPreviousTrack; // used to detect track switch
+ // This must be initialized for initial condition of mMasterBalance = 0 (disabled).
+ float mMasterBalanceLeft = 1.f;
+ float mMasterBalanceRight = 1.f;
+
public:
virtual bool hasFastMixer() const { return false; }
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index d23d19d..22d34b2 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -451,6 +451,12 @@
mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
+ "_" + std::to_string(mId));
#endif
+
+ if (channelMask & AUDIO_CHANNEL_HAPTIC_ALL) {
+ mAudioVibrationController = new AudioVibrationController(this);
+ mExternalVibration = new os::ExternalVibration(
+ mUid, "" /* pkg */, mAttr, mAudioVibrationController);
+ }
}
AudioFlinger::PlaybackThread::Track::~Track()
@@ -1336,6 +1342,40 @@
mServerLatencyMs.store(latencyMs);
}
+binder::Status AudioFlinger::PlaybackThread::Track::AudioVibrationController::mute(
+ /*out*/ bool *ret) {
+ *ret = false;
+ sp<ThreadBase> thread = mTrack->mThread.promote();
+ if (thread != 0) {
+ // Lock for updating mHapticPlaybackEnabled.
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
+ && playbackThread->mHapticChannelCount > 0) {
+ mTrack->setHapticPlaybackEnabled(false);
+ *ret = true;
+ }
+ }
+ return binder::Status::ok();
+}
+
+binder::Status AudioFlinger::PlaybackThread::Track::AudioVibrationController::unmute(
+ /*out*/ bool *ret) {
+ *ret = false;
+ sp<ThreadBase> thread = mTrack->mThread.promote();
+ if (thread != 0) {
+ // Lock for updating mHapticPlaybackEnabled.
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
+ && playbackThread->mHapticChannelCount > 0) {
+ mTrack->setHapticPlaybackEnabled(true);
+ *ret = true;
+ }
+ }
+ return binder::Status::ok();
+}
+
// ----------------------------------------------------------------------------
#undef LOG_TAG
#define LOG_TAG "AF::OutputTrack"
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 3b9411a..cd10c82 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -411,11 +411,11 @@
if ((rule == RULE_EXCLUDE_UID || rule == RULE_MATCH_UID)
&& uid == mix->mCriteria[j].mValue.mUid) {
foundUidRule = true;
- criteriaToRemove.push_back(j);
+ criteriaToRemove.insert(criteriaToRemove.begin(), j);
}
}
if (foundUidRule) {
- for (size_t j = criteriaToRemove.size() - 1; j >= 0; j--) {
+ for (size_t j = 0; j < criteriaToRemove.size(); j++) {
mix->mCriteria.removeAt(criteriaToRemove[j]);
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index dc5b238..4cb1e17 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -129,7 +129,7 @@
ssize_t DeviceVector::indexOf(const sp<DeviceDescriptor>& item) const
{
for (size_t i = 0; i < size(); i++) {
- if (item->equals(itemAt(i))) {
+ if (itemAt(i)->equals(item)) { // item may be null sp<>, i.e. AUDIO_DEVICE_NONE
return i;
}
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 46f51e5..d7c7b4d 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -5062,7 +5062,7 @@
}
// If we are not in call and no client is active on this input, this methods returns
- // AUDIO_DEVICE_NONE, causing the patch on the input stream to be released.
+ // a null sp<>, causing the patch on the input stream to be released.
audio_attributes_t attributes = inputDesc->getHighestPriorityAttributes();
if (attributes.source == AUDIO_SOURCE_DEFAULT && isInCall()) {
attributes.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 49c541c..45fb174 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -525,7 +525,7 @@
static constexpr char kAudioPolicyActiveDevice[] =
"android.media.audiopolicy.active.device";
- MediaAnalyticsItem *item = new MediaAnalyticsItem(kAudioPolicy);
+ MediaAnalyticsItem *item = MediaAnalyticsItem::create(kAudioPolicy);
if (item != NULL) {
item->setInt32(kAudioPolicyStatus, status);
diff --git a/services/mediaanalytics/Android.bp b/services/mediaanalytics/Android.bp
new file mode 100644
index 0000000..c93c120
--- /dev/null
+++ b/services/mediaanalytics/Android.bp
@@ -0,0 +1,49 @@
+// Media Statistics service
+//
+
+cc_binary {
+ name: "mediametrics",
+
+ srcs: [
+ "main_mediametrics.cpp",
+ "MediaAnalyticsService.cpp",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libmedia",
+ "libutils",
+ "libbinder",
+ "libdl",
+ "libgui",
+ "libmedia",
+ "libmediautils",
+ "libmediametrics",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ static_libs: ["libregistermsext"],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/include",
+ "frameworks/av/media/libstagefright/rtsp",
+ "frameworks/av/media/libstagefright/webm",
+ "frameworks/av/include/media",
+ "frameworks/av/include/camera",
+ "frameworks/native/include/media/openmax",
+ "frameworks/native/include/media/hardware",
+ "external/tremolo/Tremolo",
+ ],
+
+ init_rc: ["mediametrics.rc"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wno-error=deprecated-declarations",
+ ],
+ clang: true,
+
+}
diff --git a/services/mediaanalytics/Android.mk b/services/mediaanalytics/Android.mk
deleted file mode 100644
index 5b20e61..0000000
--- a/services/mediaanalytics/Android.mk
+++ /dev/null
@@ -1,47 +0,0 @@
-# Media Statistics service
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- main_mediametrics.cpp \
- MediaAnalyticsService.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog \
- libmedia \
- libutils \
- libbinder \
- libdl \
- libgui \
- libmedia \
- libmediautils \
- libmediametrics \
- libstagefright_foundation \
- libutils
-
-LOCAL_STATIC_LIBRARIES := \
- libregistermsext
-
-LOCAL_C_INCLUDES := \
- $(TOP)/frameworks/av/media/libstagefright/include \
- $(TOP)/frameworks/av/media/libstagefright/rtsp \
- $(TOP)/frameworks/av/media/libstagefright/wifi-display \
- $(TOP)/frameworks/av/media/libstagefright/webm \
- $(TOP)/frameworks/av/include/media \
- $(TOP)/frameworks/av/include/camera \
- $(TOP)/frameworks/native/include/media/openmax \
- $(TOP)/frameworks/native/include/media/hardware \
- $(TOP)/external/tremolo/Tremolo
-
-
-LOCAL_MODULE:= mediametrics
-
-LOCAL_INIT_RC := mediametrics.rc
-
-LOCAL_CFLAGS := -Werror -Wall -Wno-error=deprecated-declarations
-LOCAL_CLANG := true
-
-include $(BUILD_EXECUTABLE)
diff --git a/services/mediaresourcemanager/Android.bp b/services/mediaresourcemanager/Android.bp
new file mode 100644
index 0000000..1c63f64
--- /dev/null
+++ b/services/mediaresourcemanager/Android.bp
@@ -0,0 +1,28 @@
+
+
+cc_library_shared {
+ name: "libresourcemanagerservice",
+
+ srcs: [
+ "ResourceManagerService.cpp",
+ "ServiceLog.cpp",
+ ],
+
+ shared_libs: [
+ "libmedia",
+ "libmediautils",
+ "libbinder",
+ "libutils",
+ "liblog",
+ ],
+
+ compile_multilib: "32",
+
+ include_dirs: ["frameworks/av/include"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+}
diff --git a/services/mediaresourcemanager/Android.mk b/services/mediaresourcemanager/Android.mk
deleted file mode 100644
index 5823036..0000000
--- a/services/mediaresourcemanager/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := ResourceManagerService.cpp ServiceLog.cpp
-
-LOCAL_SHARED_LIBRARIES := libmedia libmediautils libbinder libutils liblog
-
-LOCAL_MODULE:= libresourcemanagerservice
-
-LOCAL_32_BIT_ONLY := true
-
-LOCAL_C_INCLUDES += \
- frameworks/av/include
-
-LOCAL_CFLAGS += -Werror -Wall
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))