summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java40
-rw-r--r--cmds/stagefright/stagefright.cpp61
-rw-r--r--core/java/android/app/Notification.java6
-rw-r--r--core/java/android/os/INetworkManagementService.aidl105
-rw-r--r--core/java/android/provider/Browser.java27
-rw-r--r--core/java/android/provider/ContactsContract.java39
-rw-r--r--core/java/android/view/ViewGroup.java2
-rw-r--r--core/java/android/widget/AutoCompleteTextView.java26
-rw-r--r--core/java/android/widget/DateTimeView.java258
-rw-r--r--core/java/android/widget/ListView.java8
-rw-r--r--core/res/res/layout/status_bar_latest_event_content.xml2
-rw-r--r--core/res/res/values/arrays.xml3
-rw-r--r--docs/html/sdk/download.jd6
-rw-r--r--include/media/IMediaDeathNotifier.h61
-rw-r--r--include/media/IOMX.h5
-rw-r--r--include/media/mediaplayer.h28
-rw-r--r--include/media/mediarecorder.h5
-rw-r--r--include/media/stagefright/OMXCodec.h1
-rw-r--r--libs/rs/rsAllocation.cpp22
-rw-r--r--media/libmedia/Android.mk3
-rw-r--r--media/libmedia/IMediaDeathNotifier.cpp111
-rw-r--r--media/libmedia/IOMX.cpp24
-rw-r--r--media/libmedia/mediaplayer.cpp90
-rw-r--r--media/libmedia/mediarecorder.cpp20
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp17
-rw-r--r--media/libstagefright/OMXCodec.cpp19
-rw-r--r--media/libstagefright/SampleIterator.cpp310
-rw-r--r--media/libstagefright/SampleTable.cpp323
-rw-r--r--media/libstagefright/id3/ID3.cpp161
-rw-r--r--media/libstagefright/id3/testid3.cpp8
-rw-r--r--media/libstagefright/include/ID3.h5
-rw-r--r--media/libstagefright/include/OMX.h2
-rw-r--r--media/libstagefright/include/SampleIterator.h75
-rw-r--r--media/libstagefright/include/SampleTable.h40
-rw-r--r--media/libstagefright/omx/OMX.cpp48
-rw-r--r--media/libstagefright/omx/tests/OMXHarness.cpp4
-rw-r--r--media/tests/omxjpegdecoder/Android.mk3
-rw-r--r--preloaded-classes2
-rw-r--r--services/java/com/android/server/LocationManagerService.java4
-rw-r--r--services/java/com/android/server/MountService.java10
-rw-r--r--services/java/com/android/server/NetworkManagementService.java303
-rw-r--r--services/java/com/android/server/PackageManagerService.java123
-rw-r--r--services/java/com/android/server/status/StatusBarPolicy.java61
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java21
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/BitmapShader.java11
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas.java19
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/ComposeShader.java7
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/LinearGradient.java78
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Paint.java9
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/RadialGradient.java8
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Shader.java15
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/SweepGradient.java10
54 files changed, 2053 insertions, 598 deletions
diff --git a/Android.mk b/Android.mk
index 7c7bb62..d6beac5 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/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 4953f5d..68373cb 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -92,6 +92,11 @@ public final class Pm {
return;
}
+ if ("mountsd".equals(op)) {
+ runMountSd();
+ return;
+ }
+
if ("uninstall".equals(op)) {
runUninstall();
return;
@@ -637,6 +642,37 @@ public final class Pm {
}
}
+ private void runMountSd() {
+ String opt;
+ boolean mount = false;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("-m")) {
+ String mountStr = nextOptionData();
+ if (mountStr == null) {
+ System.err.println("Error: no value specified for -m");
+ showUsage();
+ return;
+ }
+ if ("true".equalsIgnoreCase(mountStr)) {
+ mount = true;
+ } else if ("false".equalsIgnoreCase(mountStr)) {
+ mount = false;
+ } else {
+ System.err.println("Error: no value specified for -m");
+ showUsage();
+ return;
+ }
+ }
+ }
+
+ try {
+ mPm.updateExternalMediaStatus(mount);
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
boolean finished;
boolean result;
@@ -826,6 +862,7 @@ public final class Pm {
System.err.println(" pm path PACKAGE");
System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] PATH");
System.err.println(" pm uninstall [-k] PACKAGE");
+ System.err.println(" pm mountsd [-m true/false]");
System.err.println(" pm enable PACKAGE_OR_COMPONENT");
System.err.println(" pm disable PACKAGE_OR_COMPONENT");
System.err.println("");
@@ -862,6 +899,9 @@ public final class Pm {
System.err.println(" -k: keep the data and cache directories around.");
System.err.println("after the package removal.");
System.err.println("");
+ System.err.println("The mountsd command simulates mounting/unmounting sdcard.Options:");
+ System.err.println(" -m: true or false.");
+ System.err.println("");
System.err.println("The enable and disable commands change the enabled state of");
System.err.println("a given package or component (written as \"package/class\").");
}
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index ad6540a..e65cdf1 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/app/Notification.java b/core/java/android/app/Notification.java
index be5a7d3..4d72f73 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -443,11 +443,7 @@ public class Notification implements Parcelable
contentView.setTextViewText(com.android.internal.R.id.text, contentText);
}
if (this.when != 0) {
- Date date = new Date(when);
- CharSequence str =
- DateUtils.isToday(when) ? DateFormat.getTimeFormat(context).format(date)
- : DateFormat.getDateFormat(context).format(date);
- contentView.setTextViewText(com.android.internal.R.id.time, str);
+ contentView.setLong(com.android.internal.R.id.time, "setTime", when);
}
this.contentView = contentView;
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
new file mode 100644
index 0000000..bd6cabb
--- /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 b876f05..d67169f 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 93b5b4d..5819b59 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";
@@ -1239,9 +1265,6 @@ public final class ContactsContract {
* </dd>
* </dl>
* <h2>Columns</h2>
- * TODO: include {@link #DISPLAY_NAME_PRIMARY}, {@link #DISPLAY_NAME_ALTERNATIVE},
- * {@link #DISPLAY_NAME_SOURCE}, {@link #PHONETIC_NAME}, {@link #PHONETIC_NAME_STYLE},
- * {@link #SORT_KEY_PRIMARY}, {@link #SORT_KEY_ALTERNATIVE}?
*
* <table class="jd-sumtable">
* <tr>
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 763f273..49c2d0e 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/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index bb9a672..d53a442 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1331,20 +1331,26 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
final int maxHeight = mPopup.getMaxAvailableHeight(
getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
+ // getMaxAvailableHeight() subtracts the padding, so we put it back,
+ // to get the available height for the whole window
+ int padding = 0;
+ Drawable background = mPopup.getBackground();
+ if (background != null) {
+ background.getPadding(mTempRect);
+ padding = mTempRect.top + mTempRect.bottom;
+ }
+
if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
- // getMaxAvailableHeight() subtracts the padding, so we put it back,
- // to get the available height for the whole window
- int padding = 0;
- Drawable background = mPopup.getBackground();
- if (background != null) {
- background.getPadding(mTempRect);
- padding = mTempRect.top + mTempRect.bottom;
- }
return maxHeight + padding;
}
- return mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
- 0, ListView.NO_POSITION, maxHeight - otherHeights, 2) + otherHeights;
+ final int listContent = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
+ 0, ListView.NO_POSITION, maxHeight - otherHeights, 2);
+ // add padding only if the list has items in it, that way we don't show
+ // the popup if it is not needed
+ if (listContent > 0) otherHeights += padding;
+
+ return listContent + otherHeights;
}
private View getHintView(Context context) {
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
new file mode 100644
index 0000000..9067e26
--- /dev/null
+++ b/core/java/android/widget/DateTimeView.java
@@ -0,0 +1,258 @@
+/*
+ * 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.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.BroadcastReceiver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.text.format.Time;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.widget.TextView;
+import android.widget.RemoteViews.RemoteView;
+
+import com.android.internal.R;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+//
+// TODO
+// - listen for the next threshold time to update the view.
+// - listen for date format pref changed
+// - put the AM/PM in a smaller font
+//
+
+/**
+ * Displays a given time in a convenient human-readable foramt.
+ *
+ * @hide
+ */
+@RemoteView
+public class DateTimeView extends TextView {
+ private static final String TAG = "DateTimeView";
+
+ private static final long TWELVE_HOURS_IN_MINUTES = 12 * 60;
+ private static final long TWENTY_FOUR_HOURS_IN_MILLIS = 24 * 60 * 60 * 1000;
+
+ private static final int SHOW_TIME = 0;
+ private static final int SHOW_MONTH_DAY_YEAR = 1;
+
+ Date mTime;
+ long mTimeMillis;
+
+ int mLastDisplay = -1;
+ DateFormat mLastFormat;
+
+ private boolean mAttachedToWindow;
+ private long mUpdateTimeMillis;
+
+ public DateTimeView(Context context) {
+ super(context);
+ }
+
+ public DateTimeView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onDetachedFromWindow();
+ registerReceivers();
+ mAttachedToWindow = true;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ unregisterReceivers();
+ mAttachedToWindow = false;
+ }
+
+ @android.view.RemotableViewMethod
+ public void setTime(long time) {
+ Time t = new Time();
+ t.set(time);
+ t.second = 0;
+ mTimeMillis = t.toMillis(false);
+ mTime = new Date(t.year-1900, t.month, t.monthDay, t.hour, t.minute, 0);
+ update();
+ }
+
+ void update() {
+ if (mTime == null) {
+ return;
+ }
+
+ long start = System.nanoTime();
+
+ int display;
+ Date time = mTime;
+
+ Time t = new Time();
+ t.set(mTimeMillis);
+ t.second = 0;
+
+ t.hour -= 12;
+ long twelveHoursBefore = t.toMillis(false);
+ t.hour += 12;
+ long twelveHoursAfter = t.toMillis(false);
+ t.hour = 0;
+ t.minute = 0;
+ long midnightBefore = t.toMillis(false);
+ t.monthDay++;
+ long midnightAfter = t.toMillis(false);
+
+ long nowMillis = System.currentTimeMillis();
+ t.set(nowMillis);
+ t.second = 0;
+ nowMillis = t.normalize(false);
+
+ // Choose the display mode
+ choose_display: {
+ if ((nowMillis >= midnightBefore && nowMillis < midnightAfter)
+ || (nowMillis >= twelveHoursBefore && nowMillis < twelveHoursAfter)) {
+ display = SHOW_TIME;
+ break choose_display;
+ }
+ // Else, show month day and year.
+ display = SHOW_MONTH_DAY_YEAR;
+ break choose_display;
+ }
+
+ // Choose the format
+ DateFormat format;
+ if (display == mLastDisplay && mLastFormat != null) {
+ // use cached format
+ format = mLastFormat;
+ } else {
+ switch (display) {
+ case SHOW_TIME:
+ format = getTimeFormat();
+ break;
+ case SHOW_MONTH_DAY_YEAR:
+ format = getDateFormat();
+ break;
+ default:
+ throw new RuntimeException("unknown display value: " + display);
+ }
+ mLastFormat = format;
+ }
+
+ // Set the text
+ String text = format.format(mTime);
+ setText(text);
+
+ // Schedule the next update
+ if (display == SHOW_TIME) {
+ // Currently showing the time, update at the later of twelve hours after or midnight.
+ mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter;
+ } else {
+ // Currently showing the date
+ if (mTimeMillis < nowMillis) {
+ // If the time is in the past, don't schedule an update
+ mUpdateTimeMillis = 0;
+ } else {
+ // If hte time is in the future, schedule one at the earlier of twelve hours
+ // before or midnight before.
+ mUpdateTimeMillis = twelveHoursBefore < midnightBefore
+ ? twelveHoursBefore : midnightBefore;
+ }
+ }
+ if (false) {
+ Log.d(TAG, "update needed for '" + time + "' at '" + new Date(mUpdateTimeMillis)
+ + "' - text=" + text);
+ }
+
+ long finish = System.nanoTime();
+ }
+
+ private DateFormat getTimeFormat() {
+ int res;
+ Context context = getContext();
+ if (android.text.format.DateFormat.is24HourFormat(context)) {
+ res = R.string.twenty_four_hour_time_format;
+ } else {
+ res = R.string.twelve_hour_time_format;
+ }
+ String format = context.getString(res);
+ return new SimpleDateFormat(format);
+ }
+
+ private DateFormat getDateFormat() {
+ String format = Settings.System.getString(getContext().getContentResolver(),
+ Settings.System.DATE_FORMAT);
+ if ("".equals(format)) {
+ return DateFormat.getDateInstance(DateFormat.SHORT);
+ } else {
+ return new SimpleDateFormat(format);
+ }
+ }
+
+ private void registerReceivers() {
+ Context context = getContext();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ context.registerReceiver(mBroadcastReceiver, filter);
+
+ Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT);
+ context.getContentResolver().registerContentObserver(uri, true, mContentObserver);
+ }
+
+ private void unregisterReceivers() {
+ Context context = getContext();
+ context.unregisterReceiver(mBroadcastReceiver);
+ context.getContentResolver().unregisterContentObserver(mContentObserver);
+ }
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_TIME_TICK.equals(action)) {
+ if (System.currentTimeMillis() < mUpdateTimeMillis) {
+ // The update() function takes a few milliseconds to run because of
+ // all of the time conversions it needs to do, so we can't do that
+ // every minute.
+ return;
+ }
+ }
+ // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format.
+ mLastFormat = null;
+ update();
+ }
+ };
+
+ private ContentObserver mContentObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mLastFormat = null;
+ update();
+ }
+ };
+}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index c63774a..401e7ff 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2781,10 +2781,10 @@ public class ListView extends AbsListView {
while (first.getBottom() < listTop) {
AbsListView.LayoutParams layoutParams = (LayoutParams) first.getLayoutParams();
if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
- removeViewInLayout(first);
+ detachViewFromParent(first);
recycleBin.addScrapView(first);
} else {
- detachViewFromParent(first);
+ removeViewInLayout(first);
}
first = getChildAt(0);
mFirstPosition++;
@@ -2812,10 +2812,10 @@ public class ListView extends AbsListView {
while (last.getTop() > listBottom) {
AbsListView.LayoutParams layoutParams = (LayoutParams) last.getLayoutParams();
if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
- removeViewInLayout(last);
+ detachViewFromParent(last);
recycleBin.addScrapView(last);
} else {
- detachViewFromParent(last);
+ removeViewInLayout(last);
}
last = getChildAt(--lastIndex);
}
diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml
index 2f7036f..c3aa041 100644
--- a/core/res/res/layout/status_bar_latest_event_content.xml
+++ b/core/res/res/layout/status_bar_latest_event_content.xml
@@ -45,7 +45,7 @@
android:textSize="14sp"
android:paddingLeft="4dp"
/>
- <TextView android:id="@+id/time"
+ <android.widget.DateTimeView android:id="@+id/time"
android:layout_marginLeft="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 53bb586..3dbfa25 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/docs/html/sdk/download.jd b/docs/html/sdk/download.jd
index 47505e6..029de21 100644
--- a/docs/html/sdk/download.jd
+++ b/docs/html/sdk/download.jd
@@ -58,10 +58,8 @@ The License Agreement constitutes a contract between you and Google with respect
<h2>Thank you for downloading the Android SDK!</h2>
<p>Your download should be underway. If not, <a id="click-download">click here to start the download</a>.</p>
<p>To set up your Android development environment, please read the guide to
- <a href="installing.html">Installing the Android SDK</a>.
- Once you have completed the installation, see the
- <a href="/guide/index.html">Dev Guide</a> for documentation about
- developing Android applications.</p>
+ <a href="installing.html">Installing the Android SDK</a> and ensure that your development
+ machine meets the system requirements linked on that page.</p>
</div>
<script type="text/javascript">
diff --git a/include/media/IMediaDeathNotifier.h b/include/media/IMediaDeathNotifier.h
new file mode 100644
index 0000000..bb3d0d8
--- /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 39bd5b1..d38c177 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 7132b18..87d23f6 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 8c7392b..9ea6c7b 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 351763c..2c32386 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/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index b4ec1a2..1ae2317 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -366,6 +366,25 @@ static void mip8888(const Adapter2D &out, const Adapter2D &in)
}
}
+static void mip8(const Adapter2D &out, const Adapter2D &in)
+{
+ uint32_t w = out.getDimX();
+ uint32_t h = out.getDimY();
+
+ for (uint32_t y=0; y < h; y++) {
+ uint8_t *oPtr = static_cast<uint8_t *>(out.getElement(0, y));
+ const uint8_t *i1 = static_cast<uint8_t *>(in.getElement(0, y*2));
+ const uint8_t *i2 = static_cast<uint8_t *>(in.getElement(0, y*2+1));
+
+ for (uint32_t x=0; x < w; x++) {
+ *oPtr = (uint8_t)(((uint32_t)i1[0] + i1[1] + i2[0] + i2[1]) * 0.25f);
+ oPtr ++;
+ i1 += 2;
+ i2 += 2;
+ }
+ }
+}
+
static void mip(const Adapter2D &out, const Adapter2D &in)
{
switch(out.getBaseType()->getElement()->getSizeBits()) {
@@ -375,6 +394,9 @@ static void mip(const Adapter2D &out, const Adapter2D &in)
case 16:
mip565(out, in);
break;
+ case 8:
+ mip8(out, in);
+ break;
}
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index fc234ee..4ae4ec9b 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 0000000..39ac076
--- /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 b43e48f..277bce1 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 040366b..c0664f3 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 6b63931..7b5dabb 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 e36e78c..26b9357 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 07a5a82..0e9900b 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 c4d3b5d..c583b93 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 0000000..faad42b
--- /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 2de96d4..89a522e 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 2b3ef1a..6d64717 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 305b065..0741045 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 79931ac..da042a3 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 ce0b0d5..b559101 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 0000000..a5eaed9
--- /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 ead3431..533ce84 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 8c3f252..2121321 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 2e23899..5b45c1c 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 f679f19..b7c18bc 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 ec4d74c..90bbb37 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 406897d..1c82c94 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/MountService.java b/services/java/com/android/server/MountService.java
index f8f8742..0d9f98f 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -28,6 +28,7 @@ import android.content.res.Resources;
import android.net.Uri;
import android.os.IMountService;
import android.os.Environment;
+import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UEventObserver;
import android.os.Handler;
@@ -131,6 +132,8 @@ class MountService extends IMountService.Stub
private String mLegacyState = Environment.MEDIA_REMOVED;
+ private PackageManagerService mPms;
+
/**
* Constructs a new MountService instance
*
@@ -139,6 +142,7 @@ class MountService extends IMountService.Stub
public MountService(Context context) {
mContext = context;
+ mPms = (PackageManagerService) ServiceManager.getService("package");
// Register a BOOT_COMPLETED handler so that we can start
// our NativeDaemonConnector. We defer the startup so that we don't
// start processing events before we ought-to
@@ -509,6 +513,8 @@ class MountService extends IMountService.Stub
void handlePossibleExplicitUnmountBroadcast(String path) {
if (mMounted) {
mMounted = false;
+ // Update media status on PackageManagerService to unmount packages on sdcard
+ mPms.updateExternalMediaStatus(false);
Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
Uri.parse("file://" + path));
mContext.sendBroadcast(intent);
@@ -739,6 +745,8 @@ class MountService extends IMountService.Stub
updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+ // Update media status on PackageManagerService to unmount packages on sdcard
+ mPms.updateExternalMediaStatus(false);
if (mShowSafeUnmountNotificationWhenUnmounted) {
setMediaStorageNotification(
com.android.internal.R.string.ext_media_safe_unmount_notification_title,
@@ -800,6 +808,8 @@ class MountService extends IMountService.Stub
void notifyMediaMounted(String path, boolean readOnly) {
updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
+ // Update media status on PackageManagerService to mount packages on sdcard
+ mPms.updateExternalMediaStatus(true);
setMediaStorageNotification(0, 0, 0, false, false, null);
updateUsbMassStorageNotification(false, false);
Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
new file mode 100644
index 0000000..97fa0cc
--- /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));
+ }
+}
+
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1b6a56a..cafc804 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -4753,13 +4753,18 @@ class PackageManagerService extends IPackageManager.Stub {
ret = deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
}
if (ret && onSd) {
- // Post a delayed destroy on the container since there might
- // be active processes holding open file handles to package
- // resources which will get killed by the process killer when
- // destroying the container. This might even kill the current
- // process and crash the system. Delay the destroy a bit so
- // that the active processes get to handle the uninstall broadcasts.
- sendDelayedDestroySdDir(packageName);
+ if (deleteCodeAndResources) {
+ // Post a delayed destroy on the container since there might
+ // be active processes holding open file handles to package
+ // resources which will get killed by the process killer when
+ // destroying the container. This might even kill the current
+ // process and crash the system. Delay the destroy a bit so
+ // that the active processes get to handle the uninstall broadcasts.
+ sendDelayedDestroySdDir(packageName);
+ } else {
+ // Just unmount the directory
+ unMountSdDir(packageName);
+ }
}
return ret;
}
@@ -5866,7 +5871,9 @@ class PackageManagerService extends IPackageManager.Stub {
HashSet<String> loadedPermissions = new HashSet<String>();
GrantedPermissions(int pkgFlags) {
- this.pkgFlags = pkgFlags & ApplicationInfo.FLAG_SYSTEM;
+ this.pkgFlags = (pkgFlags & ApplicationInfo.FLAG_SYSTEM) |
+ (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
+ (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD);
}
}
@@ -6672,9 +6679,8 @@ class PackageManagerService extends IPackageManager.Stub {
if (!pkg.resourcePathString.equals(pkg.codePathString)) {
serializer.attribute(null, "resourcePath", pkg.resourcePathString);
}
- serializer.attribute(null, "system",
- (pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0
- ? "true" : "false");
+ serializer.attribute(null, "flags",
+ Integer.toString(pkg.pkgFlags));
serializer.attribute(null, "ts", pkg.getTimeStampStr());
serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
if (pkg.sharedUser == null) {
@@ -7065,16 +7071,24 @@ class PackageManagerService extends IPackageManager.Stub {
} catch (NumberFormatException e) {
}
}
- systemStr = parser.getAttributeValue(null, "system");
installerPackageName = parser.getAttributeValue(null, "installer");
+
+ systemStr = parser.getAttributeValue(null, "flags");
if (systemStr != null) {
- if ("true".equals(systemStr)) {
- pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ try {
+ pkgFlags = Integer.parseInt(systemStr);
+ } catch (NumberFormatException e) {
}
} else {
- // Old settings that don't specify system... just treat
- // them as system, good enough.
- pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ // For backward compatibility
+ systemStr = parser.getAttributeValue(null, "system");
+ if (systemStr != null) {
+ pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM : 0;
+ } else {
+ // Old settings that don't specify system... just treat
+ // them as system, good enough.
+ pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ }
}
timeStampStr = parser.getAttributeValue(null, "ts");
if (timeStampStr != null) {
@@ -7443,6 +7457,7 @@ class PackageManagerService extends IPackageManager.Stub {
static final boolean DEBUG_SD_INSTALL = false;
final private String mSdEncryptKey = "AppsOnSD";
final private String mSdEncryptAlg = "AES";
+ private boolean mMediaMounted = false;
private MountService getMountService() {
return (MountService) ServiceManager.getService("mount");
@@ -7518,6 +7533,11 @@ class PackageManagerService extends IPackageManager.Stub {
return null;
}
+ private boolean unMountSdDir(String pkgName) {
+ // STOPSHIP unmount directory
+ return true;
+ }
+
private String getSdDir(String pkgName) {
String cachePath = null;
try {
@@ -7553,6 +7573,15 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ private String[] getSecureContainerList() {
+ try {
+ return getMountService().getSecureContainerList();
+ } catch (IllegalStateException e) {
+ Log.i(TAG, "Failed to getSecureContainerList");
+ }
+ return null;
+ }
+
private void sendDelayedDestroySdDir(String pkgName) {
if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) {
// Don't have to send message again
@@ -7562,7 +7591,63 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessageDelayed(msg, DESTROY_SD_CONTAINER_DELAY);
}
- public void updateExternalMediaStatus(boolean mediaStatus) {
- // TODO
+ public void updateExternalMediaStatus(final boolean mediaStatus) {
+ if (mediaStatus == mMediaMounted) {
+ return;
+ }
+ mMediaMounted = mediaStatus;
+ // Queue up an async operation since the package installation may take a little while.
+ mHandler.post(new Runnable() {
+ public void run() {
+ mHandler.removeCallbacks(this);
+ final String list[] = getSecureContainerList();
+ if (list == null || list.length == 0) {
+ return;
+ }
+ for (int i = 0; i < list.length; i++) {
+ String mountPkg = list[i];
+ // TODO compare with default package
+ synchronized (mPackages) {
+ PackageSetting ps = mSettings.mPackages.get(mountPkg);
+ if (ps != null && (ps.pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) != 0) {
+ if (mediaStatus) {
+ String pkgPath = getSdDir(mountPkg);
+ if (pkgPath == null) {
+ continue;
+ }
+ pkgPath = ps.codePathString;
+ int parseFlags = PackageParser.PARSE_CHATTY |
+ PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+ PackageParser pp = new PackageParser(pkgPath);
+ pp.setSeparateProcesses(mSeparateProcesses);
+ final PackageParser.Package pkg = pp.parsePackage(new File(pkgPath),
+ null, mMetrics, parseFlags);
+ if (pkg == null) {
+ Log.w(TAG, "Failed to install package : " + mountPkg + " from sd card");
+ continue;
+ }
+ int scanMode = SCAN_MONITOR;
+ // Scan the package
+ if (scanPackageLI(pkg, parseFlags, scanMode) != null) {
+ // Grant permissions
+ grantPermissionsLP(pkg, false);
+ // Persist settings
+ mSettings.writeLP();
+ } else {
+ Log.i(TAG, "Failed to install package: " + mountPkg + " from sdcard");
+ }
+ } else {
+ // Delete package
+ PackageRemovedInfo outInfo = new PackageRemovedInfo();
+ boolean res = deletePackageLI(mountPkg, false, PackageManager.DONT_DELETE_DATA, outInfo);
+ if (!res) {
+ Log.e(TAG, "Failed to delete pkg from sdcard : " + mountPkg);
+ }
+ }
+ }
+ }
+ }
+ }
+ });
}
}
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 42c0254..f5aeaf0 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -88,6 +88,8 @@ public class StatusBarPolicy {
// clock
private Calendar mCalendar;
+ private String mClockFormatString;
+ private SimpleDateFormat mClockFormat;
private IBinder mClockIcon;
private IconData mClockData;
@@ -546,37 +548,48 @@ public class StatusBarPolicy {
res = R.string.twelve_hour_time_format;
}
- String format = mContext.getString(res);
+ final char MAGIC1 = '\uEF00';
+ final char MAGIC2 = '\uEF01';
- /*
- * Search for an unquoted "a" in the format string, so we can
- * add dummy characters around it to let us find it again after
- * formatting and change its size.
- */
- int a = -1;
- boolean quoted = false;
- for (int i = 0; i < format.length(); i++) {
- char c = format.charAt(i);
+ SimpleDateFormat sdf;
+ String format = mContext.getString(res);
+ if (!format.equals(mClockFormatString)) {
+ /*
+ * Search for an unquoted "a" in the format string, so we can
+ * add dummy characters around it to let us find it again after
+ * formatting and change its size.
+ */
+ int a = -1;
+ boolean quoted = false;
+ for (int i = 0; i < format.length(); i++) {
+ char c = format.charAt(i);
+
+ if (c == '\'') {
+ quoted = !quoted;
+ }
- if (c == '\'') {
- quoted = !quoted;
+ if (!quoted && c == 'a') {
+ a = i;
+ break;
+ }
}
- if (!quoted && c == 'a') {
- a = i;
- break;
+ if (a >= 0) {
+ // Move a back so any whitespace before the AM/PM is also in the alternate size.
+ final int b = a;
+ while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
+ a--;
+ }
+ format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
+ + "a" + MAGIC2 + format.substring(b + 1);
}
- }
-
- final char MAGIC1 = '\uEF00';
- final char MAGIC2 = '\uEF01';
- if (a >= 0) {
- format = format.substring(0, a) + MAGIC1 + "a" + MAGIC2 +
- format.substring(a + 1);
+ mClockFormat = sdf = new SimpleDateFormat(format);
+ mClockFormatString = format;
+ } else {
+ sdf = mClockFormat;
}
-
- String result = new SimpleDateFormat(format).format(mCalendar.getTime());
+ String result = sdf.format(mCalendar.getTime());
int magic1 = result.indexOf(MAGIC1);
int magic2 = result.indexOf(MAGIC2);
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index f2ddd0f..ffc2cbc 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -121,6 +121,20 @@ public class NotificationTestList extends TestActivity
}
},
+ new Test("Times") {
+ public void run()
+ {
+ long now = System.currentTimeMillis();
+
+ timeNotification(7, "24 hours from now", now+(1000*60*60*24));
+ timeNotification(6, "12:01:00 from now", now+(1000*60*60*12)+(60*1000));
+ timeNotification(5, "12 hours from now", now+(1000*60*60*12));
+ timeNotification(4, "now", now);
+ timeNotification(3, "11:59:00 ago", now-((1000*60*60*12)-(60*1000)));
+ timeNotification(2, "12 hours ago", now-(1000*60*60*12));
+ timeNotification(1, "24 hours ago", now-(1000*60*60*24));
+ }
+ },
new StateStress("Stress - Ongoing / Latest", 100, 100, new Runnable[] {
new Runnable() {
public void run() {
@@ -590,5 +604,12 @@ public class NotificationTestList extends TestActivity
mHandler.postDelayed(mRunnable, mPause);
}
}
+
+ void timeNotification(int n, String label, long time) {
+ mNM.notify(n, new Notification(NotificationTestList.this,
+ R.drawable.ic_statusbar_missedcall, null,
+ time, label, "" + new java.util.Date(time), null));
+
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
index 8bf7fcc..ad3974c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
@@ -16,6 +16,8 @@
package android.graphics;
+import java.awt.Paint;
+
public class BitmapShader extends Shader {
// we hold on just for the GC, since our native counterpart is using it
@@ -31,11 +33,16 @@ public class BitmapShader extends Shader {
public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
mBitmap = bitmap;
}
-
+
//---------- Custom methods
-
+
public Bitmap getBitmap() {
return mBitmap;
}
+
+ @Override
+ Paint getJavaPaint() {
+ return null;
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index 02e3220..9f4dfd0 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -125,13 +125,16 @@ public class Canvas extends _Original_Canvas {
}
Shader shader = paint.getShader();
- if (shader instanceof LinearGradient) {
- g.setPaint(((LinearGradient)shader).getPaint());
- } else {
- if (mLogger != null && shader != null) {
- mLogger.warning(String.format(
- "Shader '%1$s' is not supported in the Layout Editor.",
- shader.getClass().getCanonicalName()));
+ if (shader != null) {
+ java.awt.Paint shaderPaint = shader.getJavaPaint();
+ if (shaderPaint != null) {
+ g.setPaint(shaderPaint);
+ } else {
+ if (mLogger != null) {
+ mLogger.warning(String.format(
+ "Shader '%1$s' is not supported in the Layout Editor.",
+ shader.getClass().getCanonicalName()));
+ }
}
}
@@ -409,7 +412,7 @@ public class Canvas extends _Original_Canvas {
g.setColor(new Color(color));
- getGraphics2d().fillRect(0, 0, getWidth(), getHeight());
+ g.fillRect(0, 0, getWidth(), getHeight());
g.setComposite(composite);
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
index 968a597..863d64a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
@@ -16,6 +16,8 @@
package android.graphics;
+import java.awt.Paint;
+
/** A subclass of shader that returns the composition of two other shaders, combined by
an {@link android.graphics.Xfermode} subclass.
*/
@@ -42,5 +44,10 @@ public class ComposeShader extends Shader {
public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
// FIXME Implement shader
}
+
+ @Override
+ Paint getJavaPaint() {
+ return null;
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
index 1a0dc05..7cb8f26 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
@@ -21,51 +21,63 @@ import java.awt.Color;
import java.awt.Paint;
public class LinearGradient extends Shader {
-
+
private GradientPaint mGradientPaint;
- /** Create a shader that draws a linear gradient along a line.
- @param x0 The x-coordinate for the start of the gradient line
- @param y0 The y-coordinate for the start of the gradient line
- @param x1 The x-coordinate for the end of the gradient line
- @param y1 The y-coordinate for the end of the gradient line
- @param colors The colors to be distributed along the gradient line
- @param positions May be null. The relative positions [0..1] of
- each corresponding color in the colors array. If this is null,
- the the colors are distributed evenly along the gradient line.
- @param tile The Shader tiling mode
- */
- public LinearGradient(float x0, float y0, float x1, float y1,
- int colors[], float positions[], TileMode tile) {
+ /**
+ * Create a shader that draws a linear gradient along a line.
+ *
+ * @param x0 The x-coordinate for the start of the gradient line
+ * @param y0 The y-coordinate for the start of the gradient line
+ * @param x1 The x-coordinate for the end of the gradient line
+ * @param y1 The y-coordinate for the end of the gradient line
+ * @param colors The colors to be distributed along the gradient line
+ * @param positions May be null. The relative positions [0..1] of each
+ * corresponding color in the colors array. If this is null, the
+ * the colors are distributed evenly along the gradient line.
+ * @param tile The Shader tiling mode
+ */
+ public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
+ TileMode tile) {
if (colors.length < 2) {
throw new IllegalArgumentException("needs >= 2 number of colors");
}
if (positions != null && colors.length != positions.length) {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
-
+
// FIXME implement multi color linear gradient
+ if (colors.length == 2) {
+ // The hasAlpha flag in Color() is only used to enforce alpha to 0xFF if false.
+ // If true the alpha is read from the int.
+ mGradientPaint = new GradientPaint(x0, y0, new Color(colors[0], true /* hasalpha */),
+ x1, y1, new Color(colors[1], true /* hasalpha */), tile != TileMode.CLAMP);
+ }
}
- /** Create a shader that draws a linear gradient along a line.
- @param x0 The x-coordinate for the start of the gradient line
- @param y0 The y-coordinate for the start of the gradient line
- @param x1 The x-coordinate for the end of the gradient line
- @param y1 The y-coordinate for the end of the gradient line
- @param color0 The color at the start of the gradient line.
- @param color1 The color at the end of the gradient line.
- @param tile The Shader tiling mode
- */
- public LinearGradient(float x0, float y0, float x1, float y1,
- int color0, int color1, TileMode tile) {
- mGradientPaint = new GradientPaint(x0, y0, new Color(color0, true /* hasalpha */),
- x1,y1, new Color(color1, true /* hasalpha */), tile != TileMode.CLAMP);
+ /**
+ * Create a shader that draws a linear gradient along a line.
+ *
+ * @param x0 The x-coordinate for the start of the gradient line
+ * @param y0 The y-coordinate for the start of the gradient line
+ * @param x1 The x-coordinate for the end of the gradient line
+ * @param y1 The y-coordinate for the end of the gradient line
+ * @param color0 The color at the start of the gradient line.
+ * @param color1 The color at the end of the gradient line.
+ * @param tile The Shader tiling mode
+ */
+ public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
+ TileMode tile) {
+ // The hasAlpha flag in Color() is only used to enforce alpha to 0xFF if false.
+ // If true the alpha is read from the int.
+ mGradientPaint = new GradientPaint(x0, y0, new Color(color0, true /* hasalpha */), x1, y1,
+ new Color(color1, true /* hasalpha */), tile != TileMode.CLAMP);
}
-
- //---------- Custom Methods
-
- public Paint getPaint() {
+
+ // ---------- Custom Methods
+
+ @Override
+ public Paint getJavaPaint() {
return mGradientPaint;
}
}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
index f3af133..312dab3 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java
@@ -59,23 +59,14 @@ public class Paint extends _Original_Paint {
private final FontRenderContext mFontContext = new FontRenderContext(
new AffineTransform(), true, true);
- @SuppressWarnings("hiding")
public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG;
- @SuppressWarnings("hiding")
public static final int FILTER_BITMAP_FLAG = _Original_Paint.FILTER_BITMAP_FLAG;
- @SuppressWarnings("hiding")
public static final int DITHER_FLAG = _Original_Paint.DITHER_FLAG;
- @SuppressWarnings("hiding")
public static final int UNDERLINE_TEXT_FLAG = _Original_Paint.UNDERLINE_TEXT_FLAG;
- @SuppressWarnings("hiding")
public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
- @SuppressWarnings("hiding")
public static final int FAKE_BOLD_TEXT_FLAG = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
- @SuppressWarnings("hiding")
public static final int LINEAR_TEXT_FLAG = _Original_Paint.LINEAR_TEXT_FLAG;
- @SuppressWarnings("hiding")
public static final int SUBPIXEL_TEXT_FLAG = _Original_Paint.SUBPIXEL_TEXT_FLAG;
- @SuppressWarnings("hiding")
public static final int DEV_KERN_TEXT_FLAG = _Original_Paint.DEV_KERN_TEXT_FLAG;
public static class FontMetrics extends _Original_Paint.FontMetrics {
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
index 61b693a..13848c5 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
@@ -16,6 +16,8 @@
package android.graphics;
+import java.awt.Paint;
+
public class RadialGradient extends Shader {
/** Create a shader that draws a radial gradient given the center and radius.
@@ -58,5 +60,11 @@ public class RadialGradient extends Shader {
}
// FIXME Implement shader
}
+
+ @Override
+ Paint getJavaPaint() {
+ // TODO Auto-generated method stub
+ return null;
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader.java b/tools/layoutlib/bridge/src/android/graphics/Shader.java
index 3a9fda5..0cc5940 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Shader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader.java
@@ -16,14 +16,16 @@
package android.graphics;
+
+
/**
* Shader is the based class for objects that return horizontal spans of colors
* during drawing. A subclass of Shader is installed in a Paint calling
* paint.setShader(shader). After that any object (other than a bitmap) that is
* drawn with that paint will get its color(s) from the shader.
*/
-public class Shader {
-
+public abstract class Shader {
+
private final Matrix mMatrix = new Matrix();
public enum TileMode {
@@ -41,7 +43,7 @@ public class Shader {
* mirror images so that adjacent images always seam
*/
MIRROR (2);
-
+
TileMode(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -57,7 +59,7 @@ public class Shader {
if (localM != null) {
localM.set(mMatrix);
}
-
+
return !mMatrix.isIdentity();
}
@@ -73,4 +75,9 @@ public class Shader {
mMatrix.reset();
}
}
+
+ /**
+ * Returns a java.awt.Paint object matching this shader.
+ */
+ abstract java.awt.Paint getJavaPaint();
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
index e79e970..21d8244 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
@@ -16,6 +16,8 @@
package android.graphics;
+import java.awt.Paint;
+
public class SweepGradient extends Shader {
/**
@@ -41,7 +43,7 @@ public class SweepGradient extends Shader {
throw new IllegalArgumentException(
"color and position arrays must be of equal length");
}
-
+
// FIXME Implement shader
}
@@ -56,5 +58,11 @@ public class SweepGradient extends Shader {
public SweepGradient(float cx, float cy, int color0, int color1) {
// FIXME Implement shader
}
+
+ @Override
+ Paint getJavaPaint() {
+ // TODO Auto-generated method stub
+ return null;
+ }
}