diff options
34 files changed, 1437 insertions, 471 deletions
diff --git a/Android.mk b/Android.mk index 7c7bb6275d4e..d6beac5e64f6 100644 --- a/Android.mk +++ b/Android.mk @@ -111,6 +111,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/ICheckinService.aidl \ core/java/android/os/IMessenger.aidl \ core/java/android/os/IMountService.aidl \ + core/java/android/os/INetworkManagementService.aidl \ core/java/android/os/INetStatService.aidl \ core/java/android/os/IParentalControlCallback.aidl \ core/java/android/os/IPermissionController.aidl \ diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index ad6540a23391..e65cdf11545b 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -34,12 +34,14 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> +#include <media/mediametadataretriever.h> using namespace android; static long gNumRepetitions; static long gMaxNumFrames; // 0 means decode all available. static long gReproduceBug; // if not -1. +static bool gPreferSoftwareCodec; static int64_t getNowUs() { struct timeval tv; @@ -59,7 +61,9 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) { rawSource = source; } else { rawSource = OMXCodec::Create( - client->interface(), meta, false /* createEncoder */, source); + client->interface(), meta, false /* createEncoder */, source, + NULL /* matchComponentName */, + gPreferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0); if (rawSource == NULL) { fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime); @@ -219,6 +223,8 @@ static void usage(const char *me) { 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, " -t(humbnail) extract video thumbnail\n"); + fprintf(stderr, " -s(oftware) prefer software codec\n"); } int main(int argc, char **argv) { @@ -227,12 +233,14 @@ int main(int argc, char **argv) { bool audioOnly = false; bool listComponents = false; bool dumpProfiles = false; + bool extractThumbnail = false; gNumRepetitions = 1; gMaxNumFrames = 0; gReproduceBug = -1; + gPreferSoftwareCodec = false; int res; - while ((res = getopt(argc, argv, "han:lm:b:p")) >= 0) { + while ((res = getopt(argc, argv, "han:lm:b:pts")) >= 0) { switch (res) { case 'a': { @@ -274,6 +282,18 @@ int main(int argc, char **argv) { break; } + case 't': + { + extractThumbnail = true; + break; + } + + case 's': + { + gPreferSoftwareCodec = true; + break; + } + case '?': case 'h': default: @@ -288,6 +308,34 @@ int main(int argc, char **argv) { argc -= optind; argv += optind; + if (extractThumbnail) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("media.player")); + sp<IMediaPlayerService> service = + interface_cast<IMediaPlayerService>(binder); + + CHECK(service.get() != NULL); + + sp<IMediaMetadataRetriever> retriever = + service->createMetadataRetriever(getpid()); + + CHECK(retriever != NULL); + + for (int k = 0; k < argc; ++k) { + const char *filename = argv[k]; + + CHECK_EQ(retriever->setDataSource(filename), OK); + CHECK_EQ(retriever->setMode(METADATA_MODE_FRAME_CAPTURE_ONLY), OK); + + sp<IMemory> mem = retriever->captureFrame(); + + printf("captureFrame(%s) => %s\n", + filename, mem != NULL ? "OK" : "FAILED"); + } + + return 0; + } + if (dumpProfiles) { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("media.player")); @@ -389,7 +437,8 @@ int main(int argc, char **argv) { sp<MetaData> meta; size_t i; for (i = 0; i < numTracks; ++i) { - meta = extractor->getTrackMetaData(i); + meta = extractor->getTrackMetaData( + i, MediaExtractor::kIncludeExtensiveMetaData); const char *mime; meta->findCString(kKeyMIMEType, &mime); @@ -403,6 +452,12 @@ int main(int argc, char **argv) { } } + int64_t thumbTimeUs; + if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) { + printf("thumbnailTime: %lld us (%.2f secs)\n", + thumbTimeUs, thumbTimeUs / 1E6); + } + mediaSource = extractor->getTrack(i); } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl new file mode 100644 index 000000000000..bd6cabb4f060 --- /dev/null +++ b/core/java/android/os/INetworkManagementService.aidl @@ -0,0 +1,105 @@ +/* //device/java/android/android/os/INetworkManagementService.aidl +** +** Copyright 2007, 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.os; + +/** + * @hide + */ +interface INetworkManagementService +{ + /** + ** GENERAL + **/ + + /** + * Returns a list of currently known network interfaces + */ + String[] listInterfaces(); + + /** + * Shuts down the service + */ + void shutdown(); + + /** + ** TETHERING RELATED + **/ + + + /** + * Returns true if IP forwarding is enabled + */ + boolean getIpForwardingEnabled(); + + /** + * Enables/Disables IP Forwarding + */ + void setIpForwardingEnabled(boolean enabled); + + /** + * Start tethering services with the specified dhcp server range + */ + void startTethering(String dhcpRangeStart, String dhcpRangeEnd); + + /** + * Stop currently running tethering services + */ + void stopTethering(); + + /** + * Returns true if tethering services are started + */ + boolean isTetheringStarted(); + + /** + * Tethers the specified interface + */ + void tetherInterface(String iface); + + /** + * Untethers the specified interface + */ + void untetherInterface(String iface); + + /** + * Returns a list of currently tethered interfaces + */ + String[] listTetheredInterfaces(); + + /** + * Sets the list of DNS forwarders (in order of priority) + */ + void setDnsForwarders(in String[] dns); + + /** + * Returns the list of DNS fowarders (in order of priority) + */ + String[] getDnsForwarders(); + + /** + * Enables Network Address Translation between two interfaces. + * The address and netmask of the external interface is used for + * the NAT'ed network. + */ + void enableNat(String internalInterface, String externalInterface); + + /** + * Disables Network Address Translation between two interfaces. + */ + void disableNat(String internalInterface, String externalInterface); +} diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index b876f05a67e3..d67169f6e51d 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -173,9 +173,32 @@ public class Browser { c.startActivity(i); } + /** + * Stores a String extra in an {@link Intent} representing the title of a + * page to share. When receiving an {@link Intent#ACTION_SEND} from the + * Browser, use this to access the title. + * @hide + */ + public final static String EXTRA_SHARE_TITLE = "share_title"; + + /** + * Stores a Bitmap extra in an {@link Intent} representing the screenshot of + * a page to share. When receiving an {@link Intent#ACTION_SEND} from the + * Browser, use this to access the screenshot. + * @hide + */ + public final static String EXTRA_SHARE_SCREENSHOT = "share_screenshot"; + + /** + * Stores a Bitmap extra in an {@link Intent} representing the favicon of a + * page to share. When receiving an {@link Intent#ACTION_SEND} from the + * Browser, use this to access the favicon. + * @hide + */ + public final static String EXTRA_SHARE_FAVICON = "share_favicon"; + public static final void sendString(Context c, String s) { - sendString(c, s, - c.getText(com.android.internal.R.string.sendText).toString()); + sendString(c, s, c.getString(com.android.internal.R.string.sendText)); } /** diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 93b5b4d3fa6f..ece23a7ef96c 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -474,18 +474,44 @@ public final class ContactsContract { public static final String DISPLAY_NAME_SOURCE = "display_name_source"; /** - * The default text shown as the contact's display name. It is based on - * available data, see {@link #DISPLAY_NAME_SOURCE}. + * <p> + * The standard text shown as the contact's display name, based on the best + * available information for the contact (for example, it might be the email address + * if the name is not available). + * The information actually used to compute the name is stored in + * {@link #DISPLAY_NAME_SOURCE}. + * </p> + * <p> + * A contacts provider is free to choose whatever representation makes most + * sense for its target market. + * For example in the default Android Open Source Project implementation, + * if the display name is + * based on the structured name and the structured name follows + * the Western full-name style, then this field contains the "given name first" + * version of the full name. + * <p> * * @see ContactsContract.ContactNameColumns#DISPLAY_NAME_ALTERNATIVE */ public static final String DISPLAY_NAME_PRIMARY = "display_name"; /** - * An alternative representation of the display name. If display name is + * <p> + * An alternative representation of the display name, such as "family name first" + * instead of "given name first" for Western names. If an alternative is not + * available, the values should be the same as {@link #DISPLAY_NAME_PRIMARY}. + * </p> + * <p> + * A contacts provider is free to provide alternatives as necessary for + * its target market. + * For example the default Android Open Source Project contacts provider + * currently provides an + * alternative in a single case: if the display name is * based on the structured name and the structured name follows - * the Western full name style, then this field contains the "family name first" - * version of the full name. Otherwise, it is the same as DISPLAY_NAME_PRIMARY. + * the Western full name style, then the field contains the "family name first" + * version of the full name. + * Other cases may be added later. + * </p> */ public static final String DISPLAY_NAME_ALTERNATIVE = "display_name_alt"; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 763f273f93ad..49c2d0e16a6a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2268,7 +2268,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager addInArray(child, index); child.mParent = this; - child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; + child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) | DRAWN; if (child.hasFocus()) { requestChildFocus(child, child.findFocus()); diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 53bb5864fd87..3dbfa259f2d8 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -131,7 +131,8 @@ the first component from this list which is found to be installed is set as the preferred activity. --> <string-array name="default_web_search_providers"> - <item>com.android.quicksearchbox/com.android.googlesearch.GoogleSearch</item> + <item>com.google.android.googlequicksearchbox/.google.GoogleSearch</item> + <item>com.android.quicksearchbox/.google.GoogleSearch</item> <item>com.google.android.providers.enhancedgooglesearch/.Launcher</item> <item>com.android.googlesearch/.GoogleSearch</item> <item>com.android.websearch/.Search.1</item> diff --git a/include/media/IMediaDeathNotifier.h b/include/media/IMediaDeathNotifier.h new file mode 100644 index 000000000000..bb3d0d845aae --- /dev/null +++ b/include/media/IMediaDeathNotifier.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef ANDROID_IMEDIADEATHNOTIFIER_H +#define ANDROID_IMEDIADEATHNOTIFIER_H + +#include <utils/threads.h> +#include <media/IMediaPlayerService.h> +#include <utils/SortedVector.h> + +namespace android { + +class IMediaDeathNotifier: virtual public RefBase +{ +public: + IMediaDeathNotifier() { addObitRecipient(this); } + virtual ~IMediaDeathNotifier() { removeObitRecipient(this); } + + virtual void died() = 0; + static const sp<IMediaPlayerService>& getMediaPlayerService(); + +private: + IMediaDeathNotifier &operator=(const IMediaDeathNotifier &); + IMediaDeathNotifier(const IMediaDeathNotifier &); + + static void addObitRecipient(const wp<IMediaDeathNotifier>& recipient); + static void removeObitRecipient(const wp<IMediaDeathNotifier>& recipient); + + class DeathNotifier: public IBinder::DeathRecipient + { + public: + DeathNotifier() {} + virtual ~DeathNotifier(); + + virtual void binderDied(const wp<IBinder>& who); + }; + + friend class DeathNotifier; + + static Mutex sServiceLock; + static sp<IMediaPlayerService> sMediaPlayerService; + static sp<DeathNotifier> sDeathNotifier; + static SortedVector< wp<IMediaDeathNotifier> > sObitRecipients; +}; + +}; // namespace android + +#endif // ANDROID_IMEDIADEATHNOTIFIER_H diff --git a/include/media/IOMX.h b/include/media/IOMX.h index 39bd5b17d268..d38c17759b05 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -42,6 +42,11 @@ public: typedef void *buffer_id; typedef void *node_id; + // Given the calling process' pid, returns true iff + // the implementation of the OMX interface lives in the same + // process. + virtual bool livesLocally(pid_t pid) = 0; + struct ComponentInfo { String8 mName; List<String8> mRoles; diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h index 7132b18ced01..87d23f617274 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -21,8 +21,7 @@ #include <ui/Surface.h> #include <media/IMediaPlayerClient.h> #include <media/IMediaPlayer.h> -#include <media/IMediaPlayerService.h> -#include <utils/SortedVector.h> +#include <media/IMediaDeathNotifier.h> namespace android { @@ -123,12 +122,13 @@ public: virtual void notify(int msg, int ext1, int ext2) = 0; }; -class MediaPlayer : public BnMediaPlayerClient +class MediaPlayer : public BnMediaPlayerClient, + public virtual IMediaDeathNotifier { public: MediaPlayer(); ~MediaPlayer(); - void onFirstRef(); + void died(); void disconnect(); status_t setDataSource(const char *url); status_t setDataSource(int fd, int64_t offset, int64_t length); @@ -164,19 +164,6 @@ private: status_t getDuration_l(int *msec); status_t setDataSource(const sp<IMediaPlayer>& player); - static const sp<IMediaPlayerService>& getMediaPlayerService(); - static void addObitRecipient(const wp<MediaPlayer>& recipient); - static void removeObitRecipient(const wp<MediaPlayer>& recipient); - - class DeathNotifier: public IBinder::DeathRecipient - { - public: - DeathNotifier() {} - virtual ~DeathNotifier(); - - virtual void binderDied(const wp<IBinder>& who); - }; - sp<IMediaPlayer> mPlayer; thread_id_t mLockThreadId; Mutex mLock; @@ -196,13 +183,6 @@ private: float mRightVolume; int mVideoWidth; int mVideoHeight; - - friend class DeathNotifier; - - static Mutex sServiceLock; - static sp<IMediaPlayerService> sMediaPlayerService; - static sp<DeathNotifier> sDeathNotifier; - static SortedVector< wp<MediaPlayer> > sObitRecipients; }; }; // namespace android diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index 8c7392b026f4..9ea6c7bb4560 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -23,6 +23,7 @@ #include <utils/List.h> #include <utils/Errors.h> #include <media/IMediaPlayerClient.h> +#include <media/IMediaDeathNotifier.h> namespace android { @@ -145,12 +146,14 @@ public: virtual void notify(int msg, int ext1, int ext2) = 0; }; -class MediaRecorder : public BnMediaPlayerClient +class MediaRecorder : public BnMediaPlayerClient, + public virtual IMediaDeathNotifier { public: MediaRecorder(); ~MediaRecorder(); + void died(); status_t initCheck(); status_t setCamera(const sp<ICamera>& camera); status_t setPreviewSurface(const sp<Surface>& surface); diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 351763cbefbc..2c323866bb5e 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -109,6 +109,7 @@ private: }; sp<IOMX> mOMX; + bool mOMXLivesLocally; IOMX::node_id mNode; uint32_t mQuirks; bool mIsEncoder; diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index fc234ee0e372..4ae4ec9b493f 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -24,7 +24,8 @@ LOCAL_SRC_FILES:= \ IAudioPolicyService.cpp \ MediaScanner.cpp \ MediaScannerClient.cpp \ - autodetect.cpp + autodetect.cpp \ + IMediaDeathNotifier.cpp LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc diff --git a/media/libmedia/IMediaDeathNotifier.cpp b/media/libmedia/IMediaDeathNotifier.cpp new file mode 100644 index 000000000000..39ac076d764b --- /dev/null +++ b/media/libmedia/IMediaDeathNotifier.cpp @@ -0,0 +1,111 @@ +/* +** Copyright 2010, 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "IMediaDeathNotifier" +#include <utils/Log.h> + +#include <binder/IServiceManager.h> +#include <binder/IPCThreadState.h> +#include <media/IMediaDeathNotifier.h> + +namespace android { + +// client singleton for binder interface to services +Mutex IMediaDeathNotifier::sServiceLock; +sp<IMediaPlayerService> IMediaDeathNotifier::sMediaPlayerService; +sp<IMediaDeathNotifier::DeathNotifier> IMediaDeathNotifier::sDeathNotifier; +SortedVector< wp<IMediaDeathNotifier> > IMediaDeathNotifier::sObitRecipients; + +// establish binder interface to MediaPlayerService +/*static*/const sp<IMediaPlayerService>& +IMediaDeathNotifier::getMediaPlayerService() +{ + LOGV("getMediaPlayerService"); + Mutex::Autolock _l(sServiceLock); + if (sMediaPlayerService.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16("media.player")); + if (binder != 0) { + break; + } + LOGW("Media player service not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + + if (sDeathNotifier == NULL) { + sDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(sDeathNotifier); + sMediaPlayerService = interface_cast<IMediaPlayerService>(binder); + } + LOGE_IF(sMediaPlayerService == 0, "no media player service!?"); + return sMediaPlayerService; +} + +/*static*/ void +IMediaDeathNotifier::addObitRecipient(const wp<IMediaDeathNotifier>& recipient) +{ + LOGV("addObitRecipient"); + Mutex::Autolock _l(sServiceLock); + sObitRecipients.add(recipient); +} + +/*static*/ void +IMediaDeathNotifier::removeObitRecipient(const wp<IMediaDeathNotifier>& recipient) +{ + LOGV("removeObitRecipient"); + Mutex::Autolock _l(sServiceLock); + sObitRecipients.remove(recipient); +} + +void +IMediaDeathNotifier::DeathNotifier::binderDied(const wp<IBinder>& who) { + LOGW("media server died"); + + // Need to do this with the lock held + SortedVector< wp<IMediaDeathNotifier> > list; + { + Mutex::Autolock _l(sServiceLock); + sMediaPlayerService.clear(); + list = sObitRecipients; + } + + // Notify application when media server dies. + // Don't hold the static lock during callback in case app + // makes a call that needs the lock. + size_t count = list.size(); + for (size_t iter = 0; iter < count; ++iter) { + sp<IMediaDeathNotifier> notifier = list[iter].promote(); + if (notifier != 0) { + notifier->died(); + } + } +} + +IMediaDeathNotifier::DeathNotifier::~DeathNotifier() +{ + LOGV("DeathNotifier::~DeathNotifier"); + Mutex::Autolock _l(sServiceLock); + sObitRecipients.clear(); + if (sMediaPlayerService != 0) { + sMediaPlayerService->asBinder()->unlinkToDeath(this); + } +} + +}; // namespace android diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index b43e48fe8591..277bce167d99 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -12,6 +12,7 @@ namespace android { enum { CONNECT = IBinder::FIRST_CALL_TRANSACTION, + LIVES_LOCALLY, LIST_NODES, ALLOCATE_NODE, FREE_NODE, @@ -75,6 +76,15 @@ public: : BpInterface<IOMX>(impl) { } + virtual bool livesLocally(pid_t pid) { + Parcel data, reply; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + data.writeInt32(pid); + remote()->transact(LIVES_LOCALLY, data, &reply); + + return reply.readInt32() != 0; + } + virtual status_t listNodes(List<ComponentInfo> *list) { list->clear(); @@ -369,6 +379,14 @@ IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX"); status_t BnOMX::onTransact( uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { switch (code) { + case LIVES_LOCALLY: + { + CHECK_INTERFACE(IOMX, data, reply); + reply->writeInt32(livesLocally((pid_t)data.readInt32())); + + return OK; + } + case LIST_NODES: { CHECK_INTERFACE(IOMX, data, reply); @@ -408,7 +426,7 @@ status_t BnOMX::onTransact( if (err == OK) { reply->writeIntPtr((intptr_t)node); } - + return NO_ERROR; } @@ -419,7 +437,7 @@ status_t BnOMX::onTransact( node_id node = (void*)data.readIntPtr(); reply->writeInt32(freeNode(node)); - + return NO_ERROR; } @@ -631,7 +649,7 @@ status_t BnOMX::onTransact( node_id node = (void*)data.readIntPtr(); const char *parameter_name = data.readCString(); - + OMX_INDEXTYPE index; status_t err = getExtensionIndex(node, parameter_name, &index); diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 040366be6814..c0664f3cda92 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -34,48 +34,6 @@ namespace android { -// client singleton for binder interface to service -Mutex MediaPlayer::sServiceLock; -sp<IMediaPlayerService> MediaPlayer::sMediaPlayerService; -sp<MediaPlayer::DeathNotifier> MediaPlayer::sDeathNotifier; -SortedVector< wp<MediaPlayer> > MediaPlayer::sObitRecipients; - -// establish binder interface to service -const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService() -{ - Mutex::Autolock _l(sServiceLock); - if (sMediaPlayerService.get() == 0) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder; - do { - binder = sm->getService(String16("media.player")); - if (binder != 0) - break; - LOGW("MediaPlayerService not published, waiting..."); - usleep(500000); // 0.5 s - } while(true); - if (sDeathNotifier == NULL) { - sDeathNotifier = new DeathNotifier(); - } - binder->linkToDeath(sDeathNotifier); - sMediaPlayerService = interface_cast<IMediaPlayerService>(binder); - } - LOGE_IF(sMediaPlayerService==0, "no MediaPlayerService!?"); - return sMediaPlayerService; -} - -void MediaPlayer::addObitRecipient(const wp<MediaPlayer>& recipient) -{ - Mutex::Autolock _l(sServiceLock); - sObitRecipients.add(recipient); -} - -void MediaPlayer::removeObitRecipient(const wp<MediaPlayer>& recipient) -{ - Mutex::Autolock _l(sServiceLock); - sObitRecipients.remove(recipient); -} - MediaPlayer::MediaPlayer() { LOGV("constructor"); @@ -94,15 +52,9 @@ MediaPlayer::MediaPlayer() mLockThreadId = 0; } -void MediaPlayer::onFirstRef() -{ - addObitRecipient(this); -} - MediaPlayer::~MediaPlayer() { LOGV("destructor"); - removeObitRecipient(this); disconnect(); IPCThreadState::self()->flushCommands(); } @@ -630,45 +582,13 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) } } -void MediaPlayer::DeathNotifier::binderDied(const wp<IBinder>& who) { - LOGW("MediaPlayer server died!"); - - // Need to do this with the lock held - SortedVector< wp<MediaPlayer> > list; - { - Mutex::Autolock _l(MediaPlayer::sServiceLock); - MediaPlayer::sMediaPlayerService.clear(); - list = sObitRecipients; - } - - // Notify application when media server dies. - // Don't hold the static lock during callback in case app - // makes a call that needs the lock. - size_t count = list.size(); - for (size_t iter = 0; iter < count; ++iter) { - sp<MediaPlayer> player = list[iter].promote(); - if ((player != 0) && (player->mPlayer != 0)) { - player->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); - } - } -} - -MediaPlayer::DeathNotifier::~DeathNotifier() -{ - Mutex::Autolock _l(sServiceLock); - sObitRecipients.clear(); - if (sMediaPlayerService != 0) { - sMediaPlayerService->asBinder()->unlinkToDeath(this); - } -} - /*static*/ sp<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%s)", url); sp<IMemory> p; const sp<IMediaPlayerService>& service = getMediaPlayerService(); if (service != 0) { - p = sMediaPlayerService->decode(url, pSampleRate, pNumChannels, pFormat); + p = service->decode(url, pSampleRate, pNumChannels, pFormat); } else { LOGE("Unable to locate media service"); } @@ -676,13 +596,19 @@ MediaPlayer::DeathNotifier::~DeathNotifier() } +void MediaPlayer::died() +{ + LOGV("died"); + notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); +} + /*static*/ sp<IMemory> MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%d, %lld, %lld)", fd, offset, length); sp<IMemory> p; const sp<IMediaPlayerService>& service = getMediaPlayerService(); if (service != 0) { - p = sMediaPlayerService->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat); + p = service->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat); } else { LOGE("Unable to locate media service"); } diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 6b6393164fe2..7b5dabb542b6 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -24,6 +24,7 @@ #include <utils/String8.h> #include <media/IMediaPlayerService.h> #include <media/IMediaRecorder.h> +#include <media/mediaplayer.h> // for MEDIA_ERROR_SERVER_DIED namespace android { @@ -576,19 +577,8 @@ status_t MediaRecorder::release() MediaRecorder::MediaRecorder() { LOGV("constructor"); - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder; - do { - binder = sm->getService(String16("media.player")); - if (binder != NULL) { - break; - } - LOGW("MediaPlayerService not published, waiting..."); - usleep(500000); // 0.5 s - } while(true); - - sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); + const sp<IMediaPlayerService>& service(getMediaPlayerService()); if (service != NULL) { mMediaRecorder = service->createMediaRecorder(getpid()); } @@ -637,5 +627,11 @@ void MediaRecorder::notify(int msg, int ext1, int ext2) } } +void MediaRecorder::died() +{ + LOGV("died"); + notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_ERROR_SERVER_DIED, 0); +} + }; // namespace android diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index e36e78c584a5..26b93573fe06 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -29,6 +29,7 @@ LOCAL_SRC_FILES += \ MPEG4Extractor.cpp \ MPEG4Writer.cpp \ MediaExtractor.cpp \ + SampleIterator.cpp \ SampleTable.cpp \ ShoutcastSource.cpp \ StagefrightMediaScanner.cpp \ diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 07a5a8263559..0e9900b9170a 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -232,8 +232,9 @@ sp<MetaData> MPEG4Extractor::getTrackMetaData( uint32_t sampleIndex; uint32_t sampleTime; if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK - && track->sampleTable->getDecodingTime( - sampleIndex, &sampleTime) == OK) { + && track->sampleTable->getMetaDataForSample( + sampleIndex, NULL /* offset */, NULL /* size */, + &sampleTime) == OK) { track->meta->setInt64( kKeyThumbnailTime, ((int64_t)sampleTime * 1000000) / track->timescale); @@ -929,20 +930,16 @@ status_t MPEG4Source::read( if (mBuffer == NULL) { newBuffer = true; - status_t err = mSampleTable->getSampleOffsetAndSize( - mCurrentSampleIndex, &offset, &size); - - if (err != OK) { - return err; - } - - err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts); + status_t err = + mSampleTable->getMetaDataForSample( + mCurrentSampleIndex, &offset, &size, &dts); if (err != OK) { return err; } err = mGroup->acquire_buffer(&mBuffer); + if (err != OK) { CHECK_EQ(mBuffer, NULL); return err; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index c4d3b5dfa03a..c583b930e067 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1016,6 +1016,7 @@ OMXCodec::OMXCodec( const char *componentName, const sp<MediaSource> &source) : mOMX(omx), + mOMXLivesLocally(omx->livesLocally(getpid())), mNode(node), mQuirks(quirks), mIsEncoder(isEncoder), @@ -1191,12 +1192,22 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { IOMX::buffer_id buffer; if (portIndex == kPortIndexInput && (mQuirks & kRequiresAllocateBufferOnInputPorts)) { - err = mOMX->allocateBufferWithBackup( - mNode, portIndex, mem, &buffer); + if (mOMXLivesLocally) { + err = mOMX->allocateBuffer( + mNode, portIndex, def.nBufferSize, &buffer); + } else { + err = mOMX->allocateBufferWithBackup( + mNode, portIndex, mem, &buffer); + } } else if (portIndex == kPortIndexOutput && (mQuirks & kRequiresAllocateBufferOnOutputPorts)) { - err = mOMX->allocateBufferWithBackup( - mNode, portIndex, mem, &buffer); + if (mOMXLivesLocally) { + err = mOMX->allocateBuffer( + mNode, portIndex, def.nBufferSize, &buffer); + } else { + err = mOMX->allocateBufferWithBackup( + mNode, portIndex, mem, &buffer); + } } else { err = mOMX->useBuffer(mNode, portIndex, mem, &buffer); } diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp new file mode 100644 index 000000000000..faad42ba5c54 --- /dev/null +++ b/media/libstagefright/SampleIterator.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2010 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. + */ + +#define LOG_TAG "SampleIterator" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include "include/SampleIterator.h" + +#include <arpa/inet.h> + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/Utils.h> + +#include "include/SampleTable.h" + +namespace android { + +SampleIterator::SampleIterator(SampleTable *table) + : mTable(table), + mInitialized(false), + mTimeToSampleIndex(0), + mTTSSampleIndex(0), + mTTSSampleTime(0), + mTTSCount(0), + mTTSDuration(0) { + reset(); +} + +void SampleIterator::reset() { + mSampleToChunkIndex = 0; + mFirstChunk = 0; + mFirstChunkSampleIndex = 0; + mStopChunk = 0; + mStopChunkSampleIndex = 0; + mSamplesPerChunk = 0; + mChunkDesc = 0; +} + +status_t SampleIterator::seekTo(uint32_t sampleIndex) { + LOGV("seekTo(%d)", sampleIndex); + + if (mTable->mSampleToChunkOffset < 0 + || mTable->mChunkOffsetOffset < 0 + || mTable->mSampleSizeOffset < 0 + || mTable->mTimeToSampleCount == 0) { + + return ERROR_MALFORMED; + } + + if (mInitialized && mCurrentSampleIndex == sampleIndex) { + return OK; + } + + if (!mInitialized || sampleIndex < mFirstChunkSampleIndex) { + reset(); + } + + if (sampleIndex >= mStopChunkSampleIndex) { + status_t err; + if ((err = findChunkRange(sampleIndex)) != OK) { + LOGE("findChunkRange failed"); + return err; + } + } + + CHECK(sampleIndex < mStopChunkSampleIndex); + + uint32_t chunk = + (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk + + mFirstChunk; + + if (!mInitialized || chunk != mCurrentChunkIndex) { + mCurrentChunkIndex = chunk; + + status_t err; + if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) { + LOGE("getChunkOffset return error"); + return err; + } + + mCurrentChunkSampleSizes.clear(); + + uint32_t firstChunkSampleIndex = + mFirstChunkSampleIndex + + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk); + + for (uint32_t i = 0; i < mSamplesPerChunk; ++i) { + size_t sampleSize; + if ((err = getSampleSizeDirect( + firstChunkSampleIndex + i, &sampleSize)) != OK) { + LOGE("getSampleSizeDirect return error"); + return err; + } + + mCurrentChunkSampleSizes.push(sampleSize); + } + } + + uint32_t chunkRelativeSampleIndex = + (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk; + + mCurrentSampleOffset = mCurrentChunkOffset; + for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) { + mCurrentSampleOffset += mCurrentChunkSampleSizes[i]; + } + + mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex]; + if (sampleIndex < mTTSSampleIndex) { + mTimeToSampleIndex = 0; + mTTSSampleIndex = 0; + mTTSSampleTime = 0; + mTTSCount = 0; + mTTSDuration = 0; + } + + status_t err; + if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) { + LOGE("findSampleTime return error"); + return err; + } + + mCurrentSampleIndex = sampleIndex; + + mInitialized = true; + + return OK; +} + +status_t SampleIterator::findChunkRange(uint32_t sampleIndex) { + CHECK(sampleIndex >= mFirstChunkSampleIndex); + + while (sampleIndex >= mStopChunkSampleIndex) { + if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) { + return ERROR_OUT_OF_RANGE; + } + + mFirstChunkSampleIndex = mStopChunkSampleIndex; + + const SampleTable::SampleToChunkEntry *entry = + &mTable->mSampleToChunkEntries[mSampleToChunkIndex]; + + mFirstChunk = entry->startChunk; + mSamplesPerChunk = entry->samplesPerChunk; + mChunkDesc = entry->chunkDesc; + + if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) { + mStopChunk = entry[1].startChunk; + + mStopChunkSampleIndex = + mFirstChunkSampleIndex + + (mStopChunk - mFirstChunk) * mSamplesPerChunk; + } else { + mStopChunk = 0xffffffff; + mStopChunkSampleIndex = 0xffffffff; + } + + ++mSampleToChunkIndex; + } + + return OK; +} + +status_t SampleIterator::getChunkOffset(uint32_t chunk, off_t *offset) { + *offset = 0; + + if (chunk >= mTable->mNumChunkOffsets) { + return ERROR_OUT_OF_RANGE; + } + + if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) { + uint32_t offset32; + + if (mTable->mDataSource->readAt( + mTable->mChunkOffsetOffset + 8 + 4 * chunk, + &offset32, + sizeof(offset32)) < (ssize_t)sizeof(offset32)) { + return ERROR_IO; + } + + *offset = ntohl(offset32); + } else { + CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64); + + uint64_t offset64; + if (mTable->mDataSource->readAt( + mTable->mChunkOffsetOffset + 8 + 8 * chunk, + &offset64, + sizeof(offset64)) < (ssize_t)sizeof(offset64)) { + return ERROR_IO; + } + + *offset = ntoh64(offset64); + } + + return OK; +} + +status_t SampleIterator::getSampleSizeDirect( + uint32_t sampleIndex, size_t *size) { + *size = 0; + + if (sampleIndex >= mTable->mNumSampleSizes) { + return ERROR_OUT_OF_RANGE; + } + + if (mTable->mDefaultSampleSize > 0) { + *size = mTable->mDefaultSampleSize; + return OK; + } + + switch (mTable->mSampleSizeFieldSize) { + case 32: + { + if (mTable->mDataSource->readAt( + mTable->mSampleSizeOffset + 12 + 4 * sampleIndex, + size, sizeof(*size)) < (ssize_t)sizeof(*size)) { + return ERROR_IO; + } + + *size = ntohl(*size); + break; + } + + case 16: + { + uint16_t x; + if (mTable->mDataSource->readAt( + mTable->mSampleSizeOffset + 12 + 2 * sampleIndex, + &x, sizeof(x)) < (ssize_t)sizeof(x)) { + return ERROR_IO; + } + + *size = ntohs(x); + break; + } + + case 8: + { + uint8_t x; + if (mTable->mDataSource->readAt( + mTable->mSampleSizeOffset + 12 + sampleIndex, + &x, sizeof(x)) < (ssize_t)sizeof(x)) { + return ERROR_IO; + } + + *size = x; + break; + } + + default: + { + CHECK_EQ(mTable->mSampleSizeFieldSize, 4); + + uint8_t x; + if (mTable->mDataSource->readAt( + mTable->mSampleSizeOffset + 12 + sampleIndex / 2, + &x, sizeof(x)) < (ssize_t)sizeof(x)) { + return ERROR_IO; + } + + *size = (sampleIndex & 1) ? x & 0x0f : x >> 4; + break; + } + } + + return OK; +} + +status_t SampleIterator::findSampleTime( + uint32_t sampleIndex, uint32_t *time) { + if (sampleIndex >= mTable->mNumSampleSizes) { + return ERROR_OUT_OF_RANGE; + } + + while (sampleIndex >= mTTSSampleIndex + mTTSCount) { + if (mTimeToSampleIndex == mTable->mTimeToSampleCount) { + return ERROR_OUT_OF_RANGE; + } + + mTTSSampleIndex += mTTSCount; + mTTSSampleTime += mTTSCount * mTTSDuration; + + mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex]; + mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1]; + + ++mTimeToSampleIndex; + } + + *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex); + + return OK; +} + +} // namespace android + diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp index 2de96d41b7b8..89a522ec8d44 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -15,9 +15,11 @@ */ #define LOG_TAG "SampleTable" +//#define LOG_NDEBUG 0 #include <utils/Log.h> #include "include/SampleTable.h" +#include "include/SampleIterator.h" #include <arpa/inet.h> @@ -27,10 +29,16 @@ namespace android { -static const uint32_t kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o'); -static const uint32_t kChunkOffsetType64 = FOURCC('c', 'o', '6', '4'); -static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z'); -static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2'); +// static +const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o'); +// static +const uint32_t SampleTable::kChunkOffsetType64 = FOURCC('c', 'o', '6', '4'); +// static +const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z'); +// static +const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2'); + +//////////////////////////////////////////////////////////////////////////////// SampleTable::SampleTable(const sp<DataSource> &source) : mDataSource(source), @@ -46,12 +54,20 @@ SampleTable::SampleTable(const sp<DataSource> &source) mTimeToSampleCount(0), mTimeToSample(NULL), mSyncSampleOffset(-1), - mNumSyncSamples(0) { + mNumSyncSamples(0), + mSampleToChunkEntries(NULL) { + mSampleIterator = new SampleIterator(this); } SampleTable::~SampleTable() { + delete[] mSampleToChunkEntries; + mSampleToChunkEntries = NULL; + delete[] mTimeToSample; mTimeToSample = NULL; + + delete mSampleIterator; + mSampleIterator = NULL; } status_t SampleTable::setChunkOffsetParams( @@ -124,6 +140,25 @@ status_t SampleTable::setSampleToChunkParams( return ERROR_MALFORMED; } + mSampleToChunkEntries = + new SampleToChunkEntry[mNumSampleToChunkOffsets]; + + for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) { + uint8_t buffer[12]; + if (mDataSource->readAt( + mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer)) + != (ssize_t)sizeof(buffer)) { + return ERROR_IO; + } + + CHECK(U32_AT(buffer) >= 1); // chunk index is 1 based in the spec. + + // We want the chunk index to be 0-based. + mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1; + mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]); + mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]); + } + return OK; } @@ -250,217 +285,10 @@ uint32_t SampleTable::countChunkOffsets() const { return mNumChunkOffsets; } -status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) { - *offset = 0; - - if (mChunkOffsetOffset < 0) { - return ERROR_MALFORMED; - } - - if (chunk_index >= mNumChunkOffsets) { - return ERROR_OUT_OF_RANGE; - } - - if (mChunkOffsetType == kChunkOffsetType32) { - uint32_t offset32; - - if (mDataSource->readAt( - mChunkOffsetOffset + 8 + 4 * chunk_index, - &offset32, - sizeof(offset32)) < (ssize_t)sizeof(offset32)) { - return ERROR_IO; - } - - *offset = ntohl(offset32); - } else { - CHECK_EQ(mChunkOffsetType, kChunkOffsetType64); - - uint64_t offset64; - if (mDataSource->readAt( - mChunkOffsetOffset + 8 + 8 * chunk_index, - &offset64, - sizeof(offset64)) < (ssize_t)sizeof(offset64)) { - return ERROR_IO; - } - - *offset = ntoh64(offset64); - } - - return OK; -} - -status_t SampleTable::getChunkForSample( - uint32_t sample_index, - uint32_t *chunk_index, - uint32_t *chunk_relative_sample_index, - uint32_t *desc_index) { - *chunk_index = 0; - *chunk_relative_sample_index = 0; - *desc_index = 0; - - if (mSampleToChunkOffset < 0) { - return ERROR_MALFORMED; - } - - if (sample_index >= countSamples()) { - return ERROR_END_OF_STREAM; - } - - uint32_t first_chunk = 0; - uint32_t samples_per_chunk = 0; - uint32_t chunk_desc_index = 0; - - uint32_t index = 0; - while (index < mNumSampleToChunkOffsets) { - uint8_t buffer[12]; - if (mDataSource->readAt(mSampleToChunkOffset + 8 + index * 12, - buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { - return ERROR_IO; - } - - uint32_t stop_chunk = U32_AT(buffer); - if (sample_index < (stop_chunk - first_chunk) * samples_per_chunk) { - break; - } - - sample_index -= (stop_chunk - first_chunk) * samples_per_chunk; - first_chunk = stop_chunk; - samples_per_chunk = U32_AT(&buffer[4]); - chunk_desc_index = U32_AT(&buffer[8]); - - ++index; - } - - *chunk_index = sample_index / samples_per_chunk + first_chunk - 1; - *chunk_relative_sample_index = sample_index % samples_per_chunk; - *desc_index = chunk_desc_index; - - return OK; -} - uint32_t SampleTable::countSamples() const { return mNumSampleSizes; } -status_t SampleTable::getSampleSize( - uint32_t sample_index, size_t *sample_size) { - *sample_size = 0; - - if (mSampleSizeOffset < 0) { - return ERROR_MALFORMED; - } - - if (sample_index >= mNumSampleSizes) { - return ERROR_OUT_OF_RANGE; - } - - if (mDefaultSampleSize > 0) { - *sample_size = mDefaultSampleSize; - return OK; - } - - switch (mSampleSizeFieldSize) { - case 32: - { - if (mDataSource->readAt( - mSampleSizeOffset + 12 + 4 * sample_index, - sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) { - return ERROR_IO; - } - - *sample_size = ntohl(*sample_size); - break; - } - - case 16: - { - uint16_t x; - if (mDataSource->readAt( - mSampleSizeOffset + 12 + 2 * sample_index, - &x, sizeof(x)) < (ssize_t)sizeof(x)) { - return ERROR_IO; - } - - *sample_size = ntohs(x); - break; - } - - case 8: - { - uint8_t x; - if (mDataSource->readAt( - mSampleSizeOffset + 12 + sample_index, - &x, sizeof(x)) < (ssize_t)sizeof(x)) { - return ERROR_IO; - } - - *sample_size = x; - break; - } - - default: - { - CHECK_EQ(mSampleSizeFieldSize, 4); - - uint8_t x; - if (mDataSource->readAt( - mSampleSizeOffset + 12 + sample_index / 2, - &x, sizeof(x)) < (ssize_t)sizeof(x)) { - return ERROR_IO; - } - - *sample_size = (sample_index & 1) ? x & 0x0f : x >> 4; - break; - } - } - - return OK; -} - -status_t SampleTable::getSampleOffsetAndSize( - uint32_t sample_index, off_t *offset, size_t *size) { - Mutex::Autolock autoLock(mLock); - - *offset = 0; - *size = 0; - - uint32_t chunk_index; - uint32_t chunk_relative_sample_index; - uint32_t desc_index; - status_t err = getChunkForSample( - sample_index, &chunk_index, &chunk_relative_sample_index, - &desc_index); - - if (err != OK) { - return err; - } - - err = getChunkOffset(chunk_index, offset); - - if (err != OK) { - return err; - } - - for (uint32_t j = 0; j < chunk_relative_sample_index; ++j) { - size_t sample_size; - err = getSampleSize(sample_index - j - 1, &sample_size); - - if (err != OK) { - return err; - } - - *offset += sample_size; - } - - err = getSampleSize(sample_index, size); - - if (err != OK) { - return err; - } - - return OK; -} - status_t SampleTable::getMaxSampleSize(size_t *max_size) { Mutex::Autolock autoLock(mLock); @@ -468,7 +296,7 @@ status_t SampleTable::getMaxSampleSize(size_t *max_size) { for (uint32_t i = 0; i < mNumSampleSizes; ++i) { size_t sample_size; - status_t err = getSampleSize(i, &sample_size); + status_t err = getSampleSize_l(i, &sample_size); if (err != OK) { return err; @@ -482,34 +310,6 @@ status_t SampleTable::getMaxSampleSize(size_t *max_size) { return OK; } -status_t SampleTable::getDecodingTime(uint32_t sample_index, uint32_t *time) { - // XXX FIXME idiotic (for the common use-case) O(n) algorithm below... - - Mutex::Autolock autoLock(mLock); - - if (sample_index >= mNumSampleSizes) { - return ERROR_OUT_OF_RANGE; - } - - uint32_t cur_sample = 0; - *time = 0; - for (uint32_t i = 0; i < mTimeToSampleCount; ++i) { - uint32_t n = mTimeToSample[2 * i]; - uint32_t delta = mTimeToSample[2 * i + 1]; - - if (sample_index < cur_sample + n) { - *time += delta * (sample_index - cur_sample); - - return OK; - } - - *time += delta * n; - cur_sample += n; - } - - return ERROR_OUT_OF_RANGE; -} - uint32_t abs_difference(uint32_t time1, uint32_t time2) { return time1 > time2 ? time1 - time2 : time2 - time1; } @@ -539,7 +339,7 @@ status_t SampleTable::findClosestSample( } if (flags & kSyncSample_Flag) { - return findClosestSyncSample(*sample_index, sample_index); + return findClosestSyncSample_l(*sample_index, sample_index); } return OK; @@ -552,7 +352,7 @@ status_t SampleTable::findClosestSample( return ERROR_OUT_OF_RANGE; } -status_t SampleTable::findClosestSyncSample( +status_t SampleTable::findClosestSyncSample_l( uint32_t start_sample_index, uint32_t *sample_index) { *sample_index = 0; @@ -590,6 +390,8 @@ status_t SampleTable::findClosestSyncSample( } status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { + Mutex::Autolock autoLock(mLock); + if (mSyncSampleOffset < 0) { // All samples are sync-samples. *sample_index = 0; @@ -620,7 +422,7 @@ status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { // Now x is a sample index. size_t sampleSize; - status_t err = getSampleSize(x, &sampleSize); + status_t err = getSampleSize_l(x, &sampleSize); if (err != OK) { return err; } @@ -636,5 +438,38 @@ status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { return OK; } +status_t SampleTable::getSampleSize_l( + uint32_t sampleIndex, size_t *sampleSize) { + return mSampleIterator->getSampleSizeDirect( + sampleIndex, sampleSize); +} + +status_t SampleTable::getMetaDataForSample( + uint32_t sampleIndex, + off_t *offset, + size_t *size, + uint32_t *decodingTime) { + Mutex::Autolock autoLock(mLock); + + status_t err; + if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) { + return err; + } + + if (offset) { + *offset = mSampleIterator->getSampleOffset(); + } + + if (size) { + *size = mSampleIterator->getSampleSize(); + } + + if (decodingTime) { + *decodingTime = mSampleIterator->getSampleTime(); + } + + return OK; +} + } // namespace android diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 2b3ef1a3c320..6d64717b14b4 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -33,7 +33,11 @@ ID3::ID3(const sp<DataSource> &source) mSize(0), mFirstFrameOffset(0), mVersion(ID3_UNKNOWN) { - mIsValid = parse(source); + mIsValid = parseV2(source); + + if (!mIsValid) { + mIsValid = parseV1(source); + } } ID3::~ID3() { @@ -51,7 +55,7 @@ ID3::Version ID3::version() const { return mVersion; } -bool ID3::parse(const sp<DataSource> &source) { +bool ID3::parseV2(const sp<DataSource> &source) { struct id3_header { char id[3]; uint8_t version_major; @@ -119,7 +123,7 @@ bool ID3::parse(const sp<DataSource> &source) { } if (header.flags & 0x80) { - LOGI("removing unsynchronization"); + LOGV("removing unsynchronization"); removeUnsynchronization(); } @@ -128,12 +132,18 @@ bool ID3::parse(const sp<DataSource> &source) { // Version 2.3 has an optional extended header. if (mSize < 4) { + free(mData); + mData = NULL; + return false; } size_t extendedHeaderSize = U32_AT(&mData[0]) + 4; if (extendedHeaderSize > mSize) { + free(mData); + mData = NULL; + return false; } @@ -147,6 +157,9 @@ bool ID3::parse(const sp<DataSource> &source) { size_t paddingSize = U32_AT(&mData[6]); if (mFirstFrameOffset + paddingSize > mSize) { + free(mData); + mData = NULL; + return false; } @@ -154,7 +167,7 @@ bool ID3::parse(const sp<DataSource> &source) { } if (extendedFlags & 0x8000) { - LOGI("have crc"); + LOGV("have crc"); } } } @@ -221,9 +234,37 @@ void ID3::Iterator::getID(String8 *id) const { if (mParent.mVersion == ID3_V2_2) { id->setTo((const char *)&mParent.mData[mOffset], 3); - } else { - CHECK_EQ(mParent.mVersion, ID3_V2_3); + } else if (mParent.mVersion == ID3_V2_3) { id->setTo((const char *)&mParent.mData[mOffset], 4); + } else { + CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1); + + switch (mOffset) { + case 3: + id->setTo("TT2"); + break; + case 33: + id->setTo("TP1"); + break; + case 63: + id->setTo("TAL"); + break; + case 93: + id->setTo("TYE"); + break; + case 97: + id->setTo("COM"); + break; + case 126: + id->setTo("TRK"); + break; + case 127: + id->setTo("TCO"); + break; + default: + CHECK(!"should not be here."); + break; + } } } @@ -273,6 +314,20 @@ void ID3::Iterator::getString(String8 *id) const { return; } + if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) { + if (mOffset == 126 || mOffset == 127) { + // Special treatment for the track number and genre. + char tmp[16]; + sprintf(tmp, "%d", (int)*mFrameData); + + id->setTo(tmp); + return; + } + + id->setTo((const char *)mFrameData, mFrameSize); + return; + } + size_t n = mFrameSize - getHeaderLength() - 1; if (*mFrameData == 0x00) { @@ -280,7 +335,8 @@ void ID3::Iterator::getString(String8 *id) const { convertISO8859ToString8(mFrameData + 1, n, id); } else { // UCS-2 - id->setTo((const char16_t *)(mFrameData + 1), n); + // API wants number of characters, not number of bytes... + id->setTo((const char16_t *)(mFrameData + 1), n / 2); } } @@ -299,9 +355,11 @@ const uint8_t *ID3::Iterator::getData(size_t *length) const { size_t ID3::Iterator::getHeaderLength() const { if (mParent.mVersion == ID3_V2_2) { return 6; - } else { - CHECK_EQ(mParent.mVersion, ID3_V2_3); + } else if (mParent.mVersion == ID3_V2_3) { return 10; + } else { + CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1); + return 0; } } @@ -345,9 +403,7 @@ void ID3::Iterator::findFrame() { if (!strcmp(id, mID)) { break; } - } else { - CHECK_EQ(mParent.mVersion, ID3_V2_3); - + } else if (mParent.mVersion == ID3_V2_3) { if (mOffset + 10 > mParent.mSize) { return; } @@ -377,6 +433,52 @@ void ID3::Iterator::findFrame() { if (!strcmp(id, mID)) { break; } + } else { + CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1); + + if (mOffset >= mParent.mSize) { + return; + } + + mFrameData = &mParent.mData[mOffset]; + + switch (mOffset) { + case 3: + case 33: + case 63: + mFrameSize = 30; + break; + case 93: + mFrameSize = 4; + break; + case 97: + if (mParent.mVersion == ID3_V1) { + mFrameSize = 30; + } else { + mFrameSize = 29; + } + break; + case 126: + mFrameSize = 1; + break; + case 127: + mFrameSize = 1; + break; + default: + CHECK(!"Should not be here, invalid offset."); + break; + } + + if (!mID) { + break; + } + + String8 id; + getID(&id); + + if (id == mID) { + break; + } } mOffset += mFrameSize; @@ -461,5 +563,40 @@ ID3::getAlbumArt(size_t *length, String8 *mime) const { return NULL; } +bool ID3::parseV1(const sp<DataSource> &source) { + const size_t V1_TAG_SIZE = 128; + + off_t size; + if (source->getSize(&size) != OK || size < (off_t)V1_TAG_SIZE) { + return false; + } + + mData = (uint8_t *)malloc(V1_TAG_SIZE); + if (source->readAt(size - V1_TAG_SIZE, mData, V1_TAG_SIZE) + != (ssize_t)V1_TAG_SIZE) { + free(mData); + mData = NULL; + + return false; + } + + if (memcmp("TAG", mData, 3)) { + free(mData); + mData = NULL; + + return false; + } + + mSize = V1_TAG_SIZE; + mFirstFrameOffset = 3; + + if (mData[V1_TAG_SIZE - 3] != 0) { + mVersion = ID3_V1; + } else { + mVersion = ID3_V1_1; + } + + return true; +} } // namespace android diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp index 305b065cbff8..07410457e55e 100644 --- a/media/libstagefright/id3/testid3.cpp +++ b/media/libstagefright/id3/testid3.cpp @@ -16,6 +16,8 @@ #include "../include/ID3.h" +#include <sys/stat.h> + #include <ctype.h> #include <dirent.h> @@ -108,6 +110,12 @@ void scanFile(const char *path) { } void scan(const char *path) { + struct stat st; + if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) { + scanFile(path); + return; + } + DIR *dir = opendir(path); if (dir == NULL) { diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h index 79931ac91b29..da042a38f4d5 100644 --- a/media/libstagefright/include/ID3.h +++ b/media/libstagefright/include/ID3.h @@ -28,6 +28,8 @@ struct String8; struct ID3 { enum Version { ID3_UNKNOWN, + ID3_V1, + ID3_V1_1, ID3_V2_2, ID3_V2_3 }; @@ -74,7 +76,8 @@ private: size_t mFirstFrameOffset; Version mVersion; - bool parse(const sp<DataSource> &source); + bool parseV1(const sp<DataSource> &source); + bool parseV2(const sp<DataSource> &source); void removeUnsynchronization(); ID3(const ID3 &); diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index ce0b0d55aa03..b5591011a08a 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -31,6 +31,8 @@ class OMX : public BnOMX, public: OMX(); + virtual bool livesLocally(pid_t pid); + virtual status_t listNodes(List<ComponentInfo> *list); virtual status_t allocateNode( diff --git a/media/libstagefright/include/SampleIterator.h b/media/libstagefright/include/SampleIterator.h new file mode 100644 index 000000000000..a5eaed9124e8 --- /dev/null +++ b/media/libstagefright/include/SampleIterator.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 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. + */ + +#include <utils/Vector.h> + +namespace android { + +struct SampleTable; + +struct SampleIterator { + SampleIterator(SampleTable *table); + + status_t seekTo(uint32_t sampleIndex); + + uint32_t getChunkIndex() const { return mCurrentChunkIndex; } + uint32_t getDescIndex() const { return mChunkDesc; } + off_t getSampleOffset() const { return mCurrentSampleOffset; } + size_t getSampleSize() const { return mCurrentSampleSize; } + uint32_t getSampleTime() const { return mCurrentSampleTime; } + + status_t getSampleSizeDirect( + uint32_t sampleIndex, size_t *size); + +private: + SampleTable *mTable; + + bool mInitialized; + + uint32_t mSampleToChunkIndex; + uint32_t mFirstChunk; + uint32_t mFirstChunkSampleIndex; + uint32_t mStopChunk; + uint32_t mStopChunkSampleIndex; + uint32_t mSamplesPerChunk; + uint32_t mChunkDesc; + + uint32_t mCurrentChunkIndex; + off_t mCurrentChunkOffset; + Vector<size_t> mCurrentChunkSampleSizes; + + uint32_t mTimeToSampleIndex; + uint32_t mTTSSampleIndex; + uint32_t mTTSSampleTime; + uint32_t mTTSCount; + uint32_t mTTSDuration; + + uint32_t mCurrentSampleIndex; + off_t mCurrentSampleOffset; + size_t mCurrentSampleSize; + uint32_t mCurrentSampleTime; + + void reset(); + status_t findChunkRange(uint32_t sampleIndex); + status_t getChunkOffset(uint32_t chunk, off_t *offset); + status_t findSampleTime(uint32_t sampleIndex, uint32_t *time); + + SampleIterator(const SampleIterator &); + SampleIterator &operator=(const SampleIterator &); +}; + +} // namespace android + diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h index ead34311d35a..533ce84f47ff 100644 --- a/media/libstagefright/include/SampleTable.h +++ b/media/libstagefright/include/SampleTable.h @@ -28,6 +28,7 @@ namespace android { class DataSource; +struct SampleIterator; class SampleTable : public RefBase { public: @@ -50,21 +51,16 @@ public: //////////////////////////////////////////////////////////////////////////// uint32_t countChunkOffsets() const; - status_t getChunkOffset(uint32_t chunk_index, off_t *offset); - - status_t getChunkForSample( - uint32_t sample_index, uint32_t *chunk_index, - uint32_t *chunk_relative_sample_index, uint32_t *desc_index); uint32_t countSamples() const; - status_t getSampleSize(uint32_t sample_index, size_t *sample_size); - - status_t getSampleOffsetAndSize( - uint32_t sample_index, off_t *offset, size_t *size); status_t getMaxSampleSize(size_t *size); - status_t getDecodingTime(uint32_t sample_index, uint32_t *time); + status_t getMetaDataForSample( + uint32_t sampleIndex, + off_t *offset, + size_t *size, + uint32_t *decodingTime); enum { kSyncSample_Flag = 1 @@ -72,15 +68,17 @@ public: status_t findClosestSample( uint32_t req_time, uint32_t *sample_index, uint32_t flags); - status_t findClosestSyncSample( - uint32_t start_sample_index, uint32_t *sample_index); - status_t findThumbnailSample(uint32_t *sample_index); protected: ~SampleTable(); private: + static const uint32_t kChunkOffsetType32; + static const uint32_t kChunkOffsetType64; + static const uint32_t kSampleSizeType32; + static const uint32_t kSampleSizeTypeCompact; + sp<DataSource> mDataSource; Mutex mLock; @@ -102,6 +100,22 @@ private: off_t mSyncSampleOffset; uint32_t mNumSyncSamples; + SampleIterator *mSampleIterator; + + struct SampleToChunkEntry { + uint32_t startChunk; + uint32_t samplesPerChunk; + uint32_t chunkDesc; + }; + SampleToChunkEntry *mSampleToChunkEntries; + + friend struct SampleIterator; + + status_t findClosestSyncSample_l( + uint32_t start_sample_index, uint32_t *sample_index); + + status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size); + SampleTable(const SampleTable &); SampleTable &operator=(const SampleTable &); }; diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 8c3f2520115e..21213218cbfd 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -135,50 +135,6 @@ void OMX::CallbackDispatcher::threadEntry() { //////////////////////////////////////////////////////////////////////////////// -class BufferMeta { -public: - BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false) - : mOwner(owner), - mMem(mem), - mIsBackup(is_backup) { - } - - BufferMeta(OMX *owner, size_t size) - : mOwner(owner), - mSize(size), - mIsBackup(false) { - } - - void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) { - if (!mIsBackup) { - return; - } - - memcpy((OMX_U8 *)mMem->pointer() + header->nOffset, - header->pBuffer + header->nOffset, - header->nFilledLen); - } - - void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) { - if (!mIsBackup) { - return; - } - - memcpy(header->pBuffer + header->nOffset, - (const OMX_U8 *)mMem->pointer() + header->nOffset, - header->nFilledLen); - } - -private: - OMX *mOwner; - sp<IMemory> mMem; - size_t mSize; - bool mIsBackup; - - BufferMeta(const BufferMeta &); - BufferMeta &operator=(const BufferMeta &); -}; - OMX::OMX() : mMaster(new OMXMaster), mDispatcher(new CallbackDispatcher(this)), @@ -208,6 +164,10 @@ void OMX::binderDied(const wp<IBinder> &the_late_who) { instance->onObserverDied(mMaster); } +bool OMX::livesLocally(pid_t pid) { + return pid == getpid(); +} + status_t OMX::listNodes(List<ComponentInfo> *list) { list->clear(); diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index 2e238991555c..5b45c1ce973d 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -586,14 +586,14 @@ status_t Harness::testSeek( double r = uniform_rand(); - if (r < 0.5) { + if (i > 0 && r < 0.5) { // 50% chance of just continuing to decode from last position. requestedSeekTimeUs = -1; LOGI("requesting linear read"); } else { - if (r < 0.55) { + if (i > 0 && r < 0.55) { // 5% chance of seeking beyond end of stream. requestedSeekTimeUs = durationUs; diff --git a/media/tests/omxjpegdecoder/Android.mk b/media/tests/omxjpegdecoder/Android.mk index f679f191a345..b7c18bc4af6d 100644 --- a/media/tests/omxjpegdecoder/Android.mk +++ b/media/tests/omxjpegdecoder/Android.mk @@ -33,7 +33,8 @@ LOCAL_SHARED_LIBRARIES := \ libskia \ libstagefright \ libbinder \ - libutils + libutils \ + libjpeg LOCAL_C_INCLUDES := \ $(JNI_H_INCLUDE) \ diff --git a/preloaded-classes b/preloaded-classes index ec4d74c599ce..90bbb3746c46 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -753,10 +753,8 @@ java.math.BigInteger java.math.Multiplication java.net.ContentHandler java.net.InetAddress -java.net.InetAddress$CacheElement java.net.InetAddress$WaitReachable java.net.JarURLConnection -java.net.NegativeCache java.net.NetPermission java.net.ProxySelectorImpl java.net.Socket$ConnectLock diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 406897d7aeaa..1c82c94e77ae 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -1203,12 +1203,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Remove expired alerts if (intentsToRemove != null) { for (PendingIntent i : intentsToRemove) { - mProximityAlerts.remove(i); - ProximityAlert alert = mProximityAlerts.get(i); + ProximityAlert alert = mProximityAlerts.remove(i); mProximitiesEntered.remove(alert); } } - } // Note: this is called with the lock held. diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java new file mode 100644 index 000000000000..97fa0cc229ef --- /dev/null +++ b/services/java/com/android/server/NetworkManagementService.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.INetworkManagementService; +import android.os.Handler; +import android.text.TextUtils; +import android.util.Log; +import java.util.ArrayList; + +import android.provider.Settings; +import android.content.ContentResolver; +import android.database.ContentObserver; + +import java.io.File; +import java.io.FileReader; +import java.lang.IllegalStateException; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @hide + */ +class NetworkManagementService extends INetworkManagementService.Stub { + + private static final String TAG = "NetworkManagmentService"; + + class NetdResponseCode { + public static final int InterfaceListResult = 110; + public static final int TetherInterfaceListResult = 111; + public static final int TetherDnsFwdTgtListResult = 112; + + public static final int TetherStatusResult = 210; + public static final int IpFwdStatusResult = 211; + } + + /** + * Binder context for this service + */ + private Context mContext; + + /** + * connector object for communicating with netd + */ + private NativeDaemonConnector mConnector; + + /** + * Constructs a new NetworkManagementService instance + * + * @param context Binder context for this service + */ + private NetworkManagementService(Context context) { + mContext = context; + + mConnector = new NativeDaemonConnector( + new NetdCallbackReceiver(), "netd", 10, "NetdConnector"); + Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName()); + thread.start(); + } + + // + // Netd Callback handling + // + + class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { + public void onDaemonConnected() { + new Thread() { + public void run() { + // XXX: Run some tests + } + }.start(); + } + public boolean onEvent(int code, String raw, String[] cooked) { + return false; + } + } + + // + // INetworkManagementService members + // + + public String[] listInterfaces() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList<String> rsp = mConnector.doCommand("list_interfaces"); + + String[] rdata = new String[rsp.size()]; + int idx = 0; + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.InterfaceListResult) { + if (tok.length !=2) { + throw new IllegalStateException( + String.format("Malformatted list entry '%s'", line)); + } + rdata[idx++] = tok[1]; + } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { + return rdata; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void shutdown() { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.SHUTDOWN) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires SHUTDOWN permission"); + } + + Log.d(TAG, "Shutting down"); + } + + public boolean getIpForwardingEnabled() throws IllegalStateException{ + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList<String> rsp = mConnector.doCommand("ipfwd status"); + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.IpFwdStatusResult) { + // 211 Forwarding <enabled/disabled> + if (tok.length !=2) { + throw new IllegalStateException( + String.format("Malformatted list entry '%s'", line)); + } + if (tok[2].equals("enabled")) + return true; + return false; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void setIpForwardingEnabled(boolean enable) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); + } + + public void startTethering(String dhcpRangeStart, String dhcpRangeEnd) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format("tether start %s %s", dhcpRangeStart, dhcpRangeEnd)); + } + + public void stopTethering() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("tether stop"); + } + + public boolean isTetheringStarted() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList<String> rsp = mConnector.doCommand("tether status"); + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.TetherStatusResult) { + // XXX: Tethering services <started/stopped> <TBD>... + if (tok[2].equals("started")) + return true; + return false; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void tetherInterface(String iface) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("tether interface add " + iface); + } + + public void untetherInterface(String iface) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("tether interface remove " + iface); + } + + public String[] listTetheredInterfaces() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList<String> rsp = mConnector.doCommand("tether interface list"); + + String[] rdata = new String[rsp.size()]; + int idx = 0; + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.TetherInterfaceListResult) { + if (tok.length !=2) { + throw new IllegalStateException( + String.format("Malformatted list entry '%s'", line)); + } + rdata[idx++] = tok[1]; + } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { + return rdata; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void setDnsForwarders(String[] dns) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + try { + String cmd = "tether dns set "; + for (String s : dns) { + cmd += InetAddress.getByName(s).toString() + " "; + } + mConnector.doCommand(cmd); + } catch (UnknownHostException e) { + throw new IllegalStateException("Error resolving dns name", e); + } + } + + public String[] getDnsForwarders() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList<String> rsp = mConnector.doCommand("tether dns list"); + + String[] rdata = new String[rsp.size()]; + int idx = 0; + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.TetherDnsFwdTgtListResult) { + if (tok.length !=2) { + throw new IllegalStateException( + String.format("Malformatted list entry '%s'", line)); + } + rdata[idx++] = tok[1]; + } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { + return rdata; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void enableNat(String internalInterface, String externalInterface) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand( + String.format("nat enable %s %s", internalInterface, externalInterface)); + } + + public void disableNat(String internalInterface, String externalInterface) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand( + String.format("nat disable %s %s", internalInterface, externalInterface)); + } +} + |