diff options
54 files changed, 2053 insertions, 598 deletions
@@ -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; + } } |