diff options
501 files changed, 11096 insertions, 14573 deletions
@@ -36,6 +36,7 @@ LOCAL_SRC_FILES := $(call find-other-java-files,$(FRAMEWORKS_BASE_SUBDIRS)) # EventLogTags files. LOCAL_SRC_FILES += \ core/java/android/content/EventLogTags.logtags \ + core/java/android/speech/tts/EventLogTags.logtags \ core/java/android/webkit/EventLogTags.logtags \ telephony/java/com/android/internal/telephony/EventLogTags.logtags \ diff --git a/api/current.txt b/api/current.txt index 1e13e24..6ded422 100644 --- a/api/current.txt +++ b/api/current.txt @@ -312,10 +312,15 @@ package android { field public static final int codes = 16843330; // 0x1010242 field public static final int collapseColumns = 16843083; // 0x101014b field public static final int color = 16843173; // 0x10101a5 + field public static final int colorActivatedHighlight = 16843684; // 0x10103a4 field public static final int colorBackground = 16842801; // 0x1010031 field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab + field public static final int colorFocusedHighlight = 16843683; // 0x10103a3 field public static final int colorForeground = 16842800; // 0x1010030 field public static final int colorForegroundInverse = 16843270; // 0x1010206 + field public static final int colorLongPressedHighlight = 16843682; // 0x10103a2 + field public static final int colorMultiSelectHighlight = 16843685; // 0x10103a5 + field public static final int colorPressedHighlight = 16843681; // 0x10103a1 field public static final int columnCount = 16843638; // 0x1010376 field public static final int columnDelay = 16843215; // 0x10101cf field public static final int columnOrderPreserved = 16843639; // 0x1010377 @@ -372,9 +377,11 @@ package android { field public static final int drawSelectorOnTop = 16843004; // 0x10100fc field public static final int drawable = 16843161; // 0x1010199 field public static final int drawableBottom = 16843118; // 0x101016e + field public static final int drawableEnd = 16843687; // 0x10103a7 field public static final int drawableLeft = 16843119; // 0x101016f field public static final int drawablePadding = 16843121; // 0x1010171 field public static final int drawableRight = 16843120; // 0x1010170 + field public static final int drawableStart = 16843686; // 0x10103a6 field public static final int drawableTop = 16843117; // 0x101016d field public static final int drawingCacheQuality = 16842984; // 0x10100e8 field public static final int dropDownAnchor = 16843363; // 0x1010263 @@ -941,6 +948,7 @@ package android { field public static final int tension = 16843370; // 0x101026a field public static final int testOnly = 16843378; // 0x1010272 field public static final int text = 16843087; // 0x101014f + field public static final int textAllCaps = 16843680; // 0x10103a0 field public static final int textAppearance = 16842804; // 0x1010034 field public static final int textAppearanceButton = 16843271; // 0x1010207 field public static final int textAppearanceInverse = 16842805; // 0x1010035 @@ -1120,6 +1128,16 @@ package android { field public static final int background_light = 17170447; // 0x106000f field public static final int black = 17170444; // 0x106000c field public static final int darker_gray = 17170432; // 0x1060000 + field public static final int holo_blue_bright = 17170459; // 0x106001b + field public static final int holo_blue_dark = 17170451; // 0x1060013 + field public static final int holo_blue_light = 17170450; // 0x1060012 + field public static final int holo_green_dark = 17170453; // 0x1060015 + field public static final int holo_green_light = 17170452; // 0x1060014 + field public static final int holo_orange_dark = 17170457; // 0x1060019 + field public static final int holo_orange_light = 17170456; // 0x1060018 + field public static final int holo_purple = 17170458; // 0x106001a + field public static final int holo_red_dark = 17170455; // 0x1060017 + field public static final int holo_red_light = 17170454; // 0x1060016 field public static final int primary_text_dark = 17170433; // 0x1060001 field public static final int primary_text_dark_nodisable = 17170434; // 0x1060002 field public static final int primary_text_light = 17170435; // 0x1060003 @@ -2238,7 +2256,6 @@ package android.app { method public abstract void setCustomView(android.view.View); method public abstract void setCustomView(android.view.View, android.app.ActionBar.LayoutParams); method public abstract void setCustomView(int); - method public abstract void setDisplayDisableHomeEnabled(boolean); method public abstract void setDisplayHomeAsUpEnabled(boolean); method public abstract void setDisplayOptions(int); method public abstract void setDisplayOptions(int, int); @@ -2246,6 +2263,7 @@ package android.app { method public abstract void setDisplayShowHomeEnabled(boolean); method public abstract void setDisplayShowTitleEnabled(boolean); method public abstract void setDisplayUseLogoEnabled(boolean); + method public abstract void setHomeButtonEnabled(boolean); method public abstract void setIcon(int); method public abstract void setIcon(android.graphics.drawable.Drawable); method public abstract void setListNavigationCallbacks(android.widget.SpinnerAdapter, android.app.ActionBar.OnNavigationListener); @@ -2258,7 +2276,6 @@ package android.app { method public abstract void setTitle(java.lang.CharSequence); method public abstract void setTitle(int); method public abstract void show(); - field public static final int DISPLAY_DISABLE_HOME = 32; // 0x20 field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4 field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10 field public static final int DISPLAY_SHOW_HOME = 2; // 0x2 @@ -11940,6 +11957,8 @@ package android.nfc { public final class NdefRecord implements android.os.Parcelable { ctor public NdefRecord(short, byte[], byte[], byte[]); ctor public NdefRecord(byte[]) throws android.nfc.FormatException; + method public static android.nfc.NdefRecord createUri(android.net.Uri); + method public static android.nfc.NdefRecord createUri(java.lang.String); method public int describeContents(); method public byte[] getId(); method public byte[] getPayload(); @@ -14376,7 +14395,7 @@ package android.os { method public static final void sendSignal(int, int); method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException; method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException; - method public static final boolean supportsProcesses(); + method public static final deprecated boolean supportsProcesses(); field public static final int BLUETOOTH_GID = 2000; // 0x7d0 field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710 field public static final int LAST_APPLICATION_UID = 99999; // 0x1869f @@ -14998,6 +15017,257 @@ package android.provider { field public static final deprecated java.lang.String URL = "url"; } + public final class CalendarContract { + field public static final java.lang.String ACCOUNT_TYPE_LOCAL = "LOCAL"; + field public static final java.lang.String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER"; + field public static final java.lang.String AUTHORITY = "com.android.calendar"; + field public static final java.lang.String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String EXTRA_EVENT_BEGIN_TIME = "beginTime"; + field public static final java.lang.String EXTRA_EVENT_END_TIME = "endTime"; + } + + public static final class CalendarContract.Attendees implements android.provider.BaseColumns android.provider.CalendarContract.AttendeesColumns android.provider.CalendarContract.EventsColumns { + method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]); + field public static final android.net.Uri CONTENT_URI; + } + + protected static abstract interface CalendarContract.AttendeesColumns { + field public static final java.lang.String ATTENDEE_EMAIL = "attendeeEmail"; + field public static final java.lang.String ATTENDEE_NAME = "attendeeName"; + field public static final java.lang.String ATTENDEE_RELATIONSHIP = "attendeeRelationship"; + field public static final java.lang.String ATTENDEE_STATUS = "attendeeStatus"; + field public static final int ATTENDEE_STATUS_ACCEPTED = 1; // 0x1 + field public static final int ATTENDEE_STATUS_DECLINED = 2; // 0x2 + field public static final int ATTENDEE_STATUS_INVITED = 3; // 0x3 + field public static final int ATTENDEE_STATUS_NONE = 0; // 0x0 + field public static final int ATTENDEE_STATUS_TENTATIVE = 4; // 0x4 + field public static final java.lang.String ATTENDEE_TYPE = "attendeeType"; + field public static final java.lang.String EVENT_ID = "event_id"; + field public static final int RELATIONSHIP_ATTENDEE = 1; // 0x1 + field public static final int RELATIONSHIP_NONE = 0; // 0x0 + field public static final int RELATIONSHIP_ORGANIZER = 2; // 0x2 + field public static final int RELATIONSHIP_PERFORMER = 3; // 0x3 + field public static final int RELATIONSHIP_SPEAKER = 4; // 0x4 + field public static final int TYPE_NONE = 0; // 0x0 + field public static final int TYPE_OPTIONAL = 2; // 0x2 + field public static final int TYPE_REQUIRED = 1; // 0x1 + } + + public static final class CalendarContract.CalendarAlerts implements android.provider.BaseColumns android.provider.CalendarContract.CalendarAlertsColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final android.net.Uri CONTENT_URI_BY_INSTANCE; + } + + protected static abstract interface CalendarContract.CalendarAlertsColumns { + field public static final java.lang.String ALARM_TIME = "alarmTime"; + field public static final java.lang.String BEGIN = "begin"; + field public static final java.lang.String CREATION_TIME = "creationTime"; + field public static final java.lang.String DEFAULT_SORT_ORDER = "begin ASC,title ASC"; + field public static final java.lang.String END = "end"; + field public static final java.lang.String EVENT_ID = "event_id"; + field public static final java.lang.String MINUTES = "minutes"; + field public static final java.lang.String NOTIFY_TIME = "notifyTime"; + field public static final java.lang.String RECEIVED_TIME = "receivedTime"; + field public static final java.lang.String STATE = "state"; + field public static final int STATE_DISMISSED = 2; // 0x2 + field public static final int STATE_FIRED = 1; // 0x1 + field public static final int STATE_SCHEDULED = 0; // 0x0 + } + + public static final class CalendarContract.CalendarCache implements android.provider.CalendarContract.CalendarCacheColumns { + field public static final java.lang.String KEY_TIMEZONE_INSTANCES = "timezoneInstances"; + field public static final java.lang.String KEY_TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious"; + field public static final java.lang.String KEY_TIMEZONE_TYPE = "timezoneType"; + field public static final java.lang.String TIMEZONE_TYPE_AUTO = "auto"; + field public static final java.lang.String TIMEZONE_TYPE_HOME = "home"; + field public static final android.net.Uri URI; + } + + protected static abstract interface CalendarContract.CalendarCacheColumns { + field public static final java.lang.String KEY = "key"; + field public static final java.lang.String VALUE = "value"; + } + + protected static abstract interface CalendarContract.CalendarColumns { + field public static final java.lang.String ALLOWED_REMINDERS = "allowedReminders"; + field public static final java.lang.String CALENDAR_ACCESS_LEVEL = "calendar_access_level"; + field public static final java.lang.String CALENDAR_COLOR = "calendar_color"; + field public static final java.lang.String CALENDAR_DISPLAY_NAME = "calendar_displayName"; + field public static final java.lang.String CALENDAR_TIME_ZONE = "calendar_timezone"; + field public static final int CAL_ACCESS_CONTRIBUTOR = 500; // 0x1f4 + field public static final int CAL_ACCESS_EDITOR = 600; // 0x258 + field public static final int CAL_ACCESS_FREEBUSY = 100; // 0x64 + field public static final int CAL_ACCESS_NONE = 0; // 0x0 + field public static final int CAL_ACCESS_OVERRIDE = 400; // 0x190 + field public static final int CAL_ACCESS_OWNER = 700; // 0x2bc + field public static final int CAL_ACCESS_READ = 200; // 0xc8 + field public static final int CAL_ACCESS_RESPOND = 300; // 0x12c + field public static final int CAL_ACCESS_ROOT = 800; // 0x320 + field public static final java.lang.String CAN_MODIFY_TIME_ZONE = "canModifyTimeZone"; + field public static final java.lang.String CAN_ORGANIZER_RESPOND = "canOrganizerRespond"; + field public static final java.lang.String MAX_REMINDERS = "maxReminders"; + field public static final java.lang.String OWNER_ACCOUNT = "ownerAccount"; + field public static final java.lang.String SYNC_EVENTS = "sync_events"; + field public static final java.lang.String VISIBLE = "visible"; + } + + public static final class CalendarContract.CalendarEntity implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.SyncColumns { + method public static android.content.EntityIterator newEntityIterator(android.database.Cursor); + field public static final android.net.Uri CONTENT_URI; + } + + protected static abstract interface CalendarContract.CalendarSyncColumns { + field public static final java.lang.String CAL_SYNC1 = "cal_sync1"; + field public static final java.lang.String CAL_SYNC10 = "cal_sync10"; + field public static final java.lang.String CAL_SYNC2 = "cal_sync2"; + field public static final java.lang.String CAL_SYNC3 = "cal_sync3"; + field public static final java.lang.String CAL_SYNC4 = "cal_sync4"; + field public static final java.lang.String CAL_SYNC5 = "cal_sync5"; + field public static final java.lang.String CAL_SYNC6 = "cal_sync6"; + field public static final java.lang.String CAL_SYNC7 = "cal_sync7"; + field public static final java.lang.String CAL_SYNC8 = "cal_sync8"; + field public static final java.lang.String CAL_SYNC9 = "cal_sync9"; + } + + public static final class CalendarContract.Calendars implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.SyncColumns { + field public static final java.lang.String CALENDAR_LOCATION = "calendar_location"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "displayName"; + field public static final java.lang.String NAME = "name"; + } + + public static final class CalendarContract.EventDays implements android.provider.CalendarContract.EventDaysColumns { + method public static final android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]); + field public static final android.net.Uri CONTENT_URI; + } + + protected static abstract interface CalendarContract.EventDaysColumns { + field public static final java.lang.String ENDDAY = "endDay"; + field public static final java.lang.String STARTDAY = "startDay"; + } + + public static final class CalendarContract.Events implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.SyncColumns { + field public static final android.net.Uri CONTENT_EXCEPTION_URI; + field public static final android.net.Uri CONTENT_URI; + } + + protected static abstract interface CalendarContract.EventsColumns { + field public static final int ACCESS_CONFIDENTIAL = 1; // 0x1 + field public static final int ACCESS_DEFAULT = 0; // 0x0 + field public static final java.lang.String ACCESS_LEVEL = "accessLevel"; + field public static final int ACCESS_PRIVATE = 2; // 0x2 + field public static final int ACCESS_PUBLIC = 3; // 0x3 + field public static final java.lang.String ALL_DAY = "allDay"; + field public static final java.lang.String AVAILABILITY = "availability"; + field public static final int AVAILABILITY_BUSY = 0; // 0x0 + field public static final int AVAILABILITY_FREE = 1; // 0x1 + field public static final java.lang.String CALENDAR_ID = "calendar_id"; + field public static final java.lang.String CAN_INVITE_OTHERS = "canInviteOthers"; + field public static final java.lang.String DESCRIPTION = "description"; + field public static final java.lang.String DTEND = "dtend"; + field public static final java.lang.String DTSTART = "dtstart"; + field public static final java.lang.String DURATION = "duration"; + field public static final java.lang.String EVENT_COLOR = "eventColor"; + field public static final java.lang.String EVENT_END_TIMEZONE = "eventEndTimezone"; + field public static final java.lang.String EVENT_LOCATION = "eventLocation"; + field public static final java.lang.String EVENT_TIMEZONE = "eventTimezone"; + field public static final java.lang.String EXDATE = "exdate"; + field public static final java.lang.String EXRULE = "exrule"; + field public static final java.lang.String GUESTS_CAN_INVITE_OTHERS = "guestsCanInviteOthers"; + field public static final java.lang.String GUESTS_CAN_MODIFY = "guestsCanModify"; + field public static final java.lang.String GUESTS_CAN_SEE_GUESTS = "guestsCanSeeGuests"; + field public static final java.lang.String HAS_ALARM = "hasAlarm"; + field public static final java.lang.String HAS_ATTENDEE_DATA = "hasAttendeeData"; + field public static final java.lang.String HAS_EXTENDED_PROPERTIES = "hasExtendedProperties"; + field public static final java.lang.String LAST_DATE = "lastDate"; + field public static final java.lang.String LAST_SYNCED = "lastSynced"; + field public static final java.lang.String ORGANIZER = "organizer"; + field public static final java.lang.String ORIGINAL_ALL_DAY = "originalAllDay"; + field public static final java.lang.String ORIGINAL_ID = "original_id"; + field public static final java.lang.String ORIGINAL_INSTANCE_TIME = "originalInstanceTime"; + field public static final java.lang.String ORIGINAL_SYNC_ID = "original_sync_id"; + field public static final java.lang.String RDATE = "rdate"; + field public static final java.lang.String RRULE = "rrule"; + field public static final java.lang.String SELF_ATTENDEE_STATUS = "selfAttendeeStatus"; + field public static final java.lang.String STATUS = "eventStatus"; + field public static final int STATUS_CANCELED = 2; // 0x2 + field public static final int STATUS_CONFIRMED = 1; // 0x1 + field public static final int STATUS_TENTATIVE = 0; // 0x0 + field public static final java.lang.String SYNC_DATA1 = "sync_data1"; + field public static final java.lang.String SYNC_DATA10 = "sync_data10"; + field public static final java.lang.String SYNC_DATA2 = "sync_data2"; + field public static final java.lang.String SYNC_DATA3 = "sync_data3"; + field public static final java.lang.String SYNC_DATA4 = "sync_data4"; + field public static final java.lang.String SYNC_DATA5 = "sync_data5"; + field public static final java.lang.String SYNC_DATA6 = "sync_data6"; + field public static final java.lang.String SYNC_DATA7 = "sync_data7"; + field public static final java.lang.String SYNC_DATA8 = "sync_data8"; + field public static final java.lang.String SYNC_DATA9 = "sync_data9"; + field public static final java.lang.String TITLE = "title"; + } + + public static final class CalendarContract.EventsEntity implements android.provider.BaseColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.SyncColumns { + method public static android.content.EntityIterator newEntityIterator(android.database.Cursor, android.content.ContentResolver); + method public static android.content.EntityIterator newEntityIterator(android.database.Cursor, android.content.ContentProviderClient); + field public static final android.net.Uri CONTENT_URI; + } + + public static final class CalendarContract.ExtendedProperties implements android.provider.BaseColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.ExtendedPropertiesColumns { + field public static final android.net.Uri CONTENT_URI; + } + + protected static abstract interface CalendarContract.ExtendedPropertiesColumns { + field public static final java.lang.String EVENT_ID = "event_id"; + field public static final java.lang.String NAME = "name"; + field public static final java.lang.String VALUE = "value"; + } + + public static final class CalendarContract.Instances implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns { + method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long); + method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long, java.lang.String); + field public static final java.lang.String BEGIN = "begin"; + field public static final android.net.Uri CONTENT_BY_DAY_URI; + field public static final android.net.Uri CONTENT_SEARCH_BY_DAY_URI; + field public static final android.net.Uri CONTENT_SEARCH_URI; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String END = "end"; + field public static final java.lang.String END_DAY = "endDay"; + field public static final java.lang.String END_MINUTE = "endMinute"; + field public static final java.lang.String EVENT_ID = "event_id"; + field public static final java.lang.String START_DAY = "startDay"; + field public static final java.lang.String START_MINUTE = "startMinute"; + } + + public static final class CalendarContract.Reminders implements android.provider.BaseColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.RemindersColumns { + method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]); + field public static final android.net.Uri CONTENT_URI; + } + + protected static abstract interface CalendarContract.RemindersColumns { + field public static final java.lang.String EVENT_ID = "event_id"; + field public static final java.lang.String METHOD = "method"; + field public static final int METHOD_ALERT = 1; // 0x1 + field public static final int METHOD_DEFAULT = 0; // 0x0 + field public static final int METHOD_EMAIL = 2; // 0x2 + field public static final int METHOD_SMS = 3; // 0x3 + field public static final java.lang.String MINUTES = "minutes"; + field public static final int MINUTES_DEFAULT = -1; // 0xffffffff + } + + protected static abstract interface CalendarContract.SyncColumns implements android.provider.CalendarContract.CalendarSyncColumns { + field public static final java.lang.String ACCOUNT_NAME = "account_name"; + field public static final java.lang.String ACCOUNT_TYPE = "account_type"; + field public static final java.lang.String CAN_PARTIALLY_UPDATE = "canPartiallyUpdate"; + field public static final java.lang.String DELETED = "deleted"; + field public static final java.lang.String DIRTY = "dirty"; + field public static final java.lang.String _SYNC_ID = "_sync_id"; + } + + public static final class CalendarContract.SyncState implements android.provider.SyncStateContract.Columns { + field public static final android.net.Uri CONTENT_URI; + } + public class CallLog { ctor public CallLog(); field public static final java.lang.String AUTHORITY = "call_log"; @@ -15481,6 +15751,7 @@ package android.provider { public static final class ContactsContract.CommonDataKinds.Photo implements android.provider.ContactsContract.DataColumnsWithJoins { field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo"; field public static final java.lang.String PHOTO = "data15"; + field public static final java.lang.String PHOTO_FILE_ID = "data14"; } public static final class ContactsContract.CommonDataKinds.Relation implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { @@ -15620,7 +15891,9 @@ package android.provider { public static final class ContactsContract.Contacts.Photo implements android.provider.BaseColumns android.provider.ContactsContract.DataColumnsWithJoins { field public static final java.lang.String CONTENT_DIRECTORY = "photo"; + field public static final java.lang.String DISPLAY_PHOTO = "display_photo"; field public static final java.lang.String PHOTO = "data15"; + field public static final java.lang.String PHOTO_FILE_ID = "data14"; } public static final class ContactsContract.Contacts.StreamItems implements android.provider.ContactsContract.StreamItemsColumns { @@ -15633,6 +15906,7 @@ package android.provider { field public static final java.lang.String IN_VISIBLE_GROUP = "in_visible_group"; field public static final java.lang.String IS_USER_PROFILE = "is_user_profile"; field public static final java.lang.String LOOKUP_KEY = "lookup"; + field public static final java.lang.String PHOTO_FILE_ID = "photo_file_id"; field public static final java.lang.String PHOTO_ID = "photo_id"; field public static final java.lang.String PHOTO_THUMBNAIL_URI = "photo_thumb_uri"; field public static final java.lang.String PHOTO_URI = "photo_uri"; @@ -15721,6 +15995,13 @@ package android.provider { field public static final int UNDEFINED = 0; // 0x0 } + public static final class ContactsContract.DisplayPhoto { + field public static final android.net.Uri CONTENT_MAX_DIMENSIONS_URI; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DISPLAY_MAX_DIM = "display_max_dim"; + field public static final java.lang.String THUMBNAIL_MAX_DIM = "thumbnail_max_dim"; + } + public static abstract interface ContactsContract.FullNameStyle { field public static final int CHINESE = 3; // 0x3 field public static final int CJK = 2; // 0x2 @@ -15739,7 +16020,10 @@ package android.provider { } protected static abstract interface ContactsContract.GroupsColumns { + field public static final java.lang.String ACTION = "action"; + field public static final java.lang.String ACTION_URI = "action_uri"; field public static final java.lang.String AUTO_ADD = "auto_add"; + field public static final java.lang.String DATA_SET = "data_set"; field public static final java.lang.String DELETED = "deleted"; field public static final java.lang.String FAVORITES = "favorites"; field public static final java.lang.String GROUP_IS_READ_ONLY = "group_is_read_only"; @@ -15855,6 +16139,10 @@ package android.provider { field public static final java.lang.String CONTENT_DIRECTORY = "data"; } + public static final class ContactsContract.RawContacts.DisplayPhoto { + field public static final java.lang.String CONTENT_DIRECTORY = "display_photo"; + } + public static final class ContactsContract.RawContacts.Entity implements android.provider.BaseColumns android.provider.ContactsContract.DataColumns { field public static final java.lang.String CONTENT_DIRECTORY = "entity"; field public static final java.lang.String DATA_ID = "data_id"; @@ -15867,6 +16155,7 @@ package android.provider { protected static abstract interface ContactsContract.RawContactsColumns { field public static final java.lang.String AGGREGATION_MODE = "aggregation_mode"; field public static final java.lang.String CONTACT_ID = "contact_id"; + field public static final java.lang.String DATA_SET = "data_set"; field public static final java.lang.String DELETED = "deleted"; field public static final java.lang.String RAW_CONTACT_IS_READ_ONLY = "raw_contact_is_read_only"; field public static final java.lang.String RAW_CONTACT_IS_USER_PROFILE = "raw_contact_is_user_profile"; @@ -20635,8 +20924,10 @@ package android.view { public abstract class ActionProvider { ctor public ActionProvider(android.content.Context); + method public boolean hasSubMenu(); method public abstract android.view.View onCreateActionView(); - method public void onPerformDefaultAction(android.view.View); + method public boolean onPerformDefaultAction(); + method public void onPrepareSubMenu(android.view.SubMenu); } public abstract interface ContextMenu implements android.view.Menu { @@ -22473,7 +22764,7 @@ package android.view { method public void requestDisallowInterceptTouchEvent(boolean); method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); method public void requestTransparentRegion(android.view.View); - method protected void resetLayoutDirectionResolution(); + method protected void resetResolvedLayoutDirection(); method public void scheduleLayoutAnimation(); method public void setAddStatesFromChildren(boolean); method public void setAlwaysDrawnWithCacheEnabled(boolean); @@ -24984,6 +25275,10 @@ package android.widget { method public void setRowCount(int); method public void setRowOrderPreserved(boolean); method public void setUseDefaultMargins(boolean); + method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment, int); + method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment, int); + method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment); + method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment); field public static final int ALIGN_BOUNDS = 0; // 0x0 field public static final int ALIGN_MARGINS = 1; // 0x1 field public static final android.widget.GridLayout.Alignment BASELINE; @@ -25002,23 +25297,19 @@ package android.widget { public static abstract class GridLayout.Alignment { } - public static class GridLayout.Group { - ctor public GridLayout.Group(int, int, android.widget.GridLayout.Alignment); - ctor public GridLayout.Group(int, android.widget.GridLayout.Alignment); - field public final android.widget.GridLayout.Alignment alignment; - field public int flexibility; - } - public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams { - ctor public GridLayout.LayoutParams(android.widget.GridLayout.Group, android.widget.GridLayout.Group); + ctor public GridLayout.LayoutParams(android.widget.GridLayout.Spec, android.widget.GridLayout.Spec); ctor public GridLayout.LayoutParams(); ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams); ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams); ctor public GridLayout.LayoutParams(android.widget.GridLayout.LayoutParams); ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet); method public void setGravity(int); - field public android.widget.GridLayout.Group columnGroup; - field public android.widget.GridLayout.Group rowGroup; + field public android.widget.GridLayout.Spec columnSpec; + field public android.widget.GridLayout.Spec rowSpec; + } + + public static class GridLayout.Spec { } public class GridView extends android.widget.AbsListView { @@ -25809,7 +26100,7 @@ package android.widget { ctor public ShareActionProvider(android.content.Context); method public android.view.View onCreateActionView(); method public void setShareHistoryFileName(java.lang.String); - method public void setShareIntent(android.view.View, android.content.Intent); + method public void setShareIntent(android.content.Intent); field public static final java.lang.String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml"; } @@ -26150,7 +26441,10 @@ package android.widget { method protected void onTextChanged(java.lang.CharSequence, int, int, int); method public boolean onTextContextMenuItem(int); method public void removeTextChangedListener(android.text.TextWatcher); - method protected void resetLayoutDirectionResolution(); + method protected void resetResolvedDrawables(); + method protected void resetResolvedLayoutDirection(); + method protected void resolveDrawables(); + method public void setAllCaps(boolean); method public final void setAutoLinkMask(int); method public void setCompoundDrawablePadding(int); method public void setCompoundDrawables(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 479b70a..3fb1736 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -157,6 +157,11 @@ public class Am { String value = nextArgRequired(); intent.putExtra(key, Integer.valueOf(value)); hasIntentInfo = true; + } else if (opt.equals("--eu")) { + String key = nextArgRequired(); + String value = nextArgRequired(); + intent.putExtra(key, Uri.parse(value)); + hasIntentInfo = true; } else if (opt.equals("--eia")) { String key = nextArgRequired(); String value = nextArgRequired(); @@ -1119,6 +1124,7 @@ public class Am { " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" + " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" + " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" + + " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" + " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" + " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" + " [-n <COMPONENT>] [-f <FLAGS>]\n" + diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 152a7cb..f2be29f 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -82,35 +82,27 @@ public: virtual void onStarted() { sp<ProcessState> proc = ProcessState::self(); - if (proc->supportsProcesses()) { - LOGV("App process: starting thread pool.\n"); - proc->startThreadPool(); - } + LOGV("App process: starting thread pool.\n"); + proc->startThreadPool(); AndroidRuntime* ar = AndroidRuntime::getRuntime(); ar->callMain(mClassName, mClass, mArgC, mArgV); - if (ProcessState::self()->supportsProcesses()) { - IPCThreadState::self()->stopProcess(); - } + IPCThreadState::self()->stopProcess(); } virtual void onZygoteInit() { sp<ProcessState> proc = ProcessState::self(); - if (proc->supportsProcesses()) { - LOGV("App process: starting thread pool.\n"); - proc->startThreadPool(); - } + LOGV("App process: starting thread pool.\n"); + proc->startThreadPool(); } virtual void onExit(int code) { if (mClassName == NULL) { // if zygote - if (ProcessState::self()->supportsProcesses()) { - IPCThreadState::self()->stopProcess(); - } + IPCThreadState::self()->stopProcess(); } AndroidRuntime::onExit(code); diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 38d0d2a..2666b41 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -23,6 +23,8 @@ import android.app.backup.IRestoreSession; import android.os.RemoteException; import android.os.ServiceManager; +import java.util.HashSet; + public final class Bmgr { IBackupManager mBmgr; IRestoreSession mRestore; @@ -45,7 +47,6 @@ public final class Bmgr { } public void run(String[] args) { - boolean validCommand = false; if (args.length < 1) { showUsage(); return; @@ -329,7 +330,13 @@ public final class Bmgr { } else { try { long token = Long.parseLong(arg, 16); - doRestoreAll(token); + HashSet<String> filter = null; + while ((arg = nextArg()) != null) { + if (filter == null) filter = new HashSet<String>(); + filter.add(arg); + } + + doRestoreAll(token, filter); } catch (NumberFormatException e) { showUsage(); return; @@ -364,7 +371,7 @@ public final class Bmgr { } } - private void doRestoreAll(long token) { + private void doRestoreAll(long token, HashSet<String> filter) { RestoreObserver observer = new RestoreObserver(); try { @@ -383,7 +390,13 @@ public final class Bmgr { for (RestoreSet s : sets) { if (s.token == token) { System.out.println("Scheduling restore: " + s.name); - didRestore = (mRestore.restoreAll(token, observer) == 0); + if (filter == null) { + didRestore = (mRestore.restoreAll(token, observer) == 0); + } else { + String[] names = new String[filter.size()]; + filter.toArray(names); + didRestore = (mRestore.restoreSome(token, observer, names) == 0); + } break; } } @@ -430,6 +443,7 @@ public final class Bmgr { System.err.println(" bmgr list sets"); System.err.println(" bmgr transport WHICH"); System.err.println(" bmgr restore TOKEN"); + System.err.println(" bmgr restore TOKEN PACKAGE..."); System.err.println(" bmgr restore PACKAGE"); System.err.println(" bmgr run"); System.err.println(" bmgr wipe PACKAGE"); @@ -457,12 +471,18 @@ public final class Bmgr { System.err.println("The 'transport' command designates the named transport as the currently"); System.err.println("active one. This setting is persistent across reboots."); System.err.println(""); - System.err.println("The 'restore' command when given a restore token initiates a full-system"); + System.err.println("The 'restore' command when given just a restore token initiates a full-system"); System.err.println("restore operation from the currently active transport. It will deliver"); System.err.println("the restore set designated by the TOKEN argument to each application"); System.err.println("that had contributed data to that restore set."); System.err.println(""); - System.err.println("The 'restore' command when given a package name intiates a restore of"); + System.err.println("The 'restore' command when given a token and one or more package names"); + System.err.println("initiates a restore operation of just those given packages from the restore"); + System.err.println("set designated by the TOKEN argument. It is effectively the same as the"); + System.err.println("'restore' operation supplying only a token, but applies a filter to the"); + System.err.println("set of applications to be restored."); + System.err.println(""); + System.err.println("The 'restore' command when given just a package name intiates a restore of"); System.err.println("just that one package according to the restore set selection algorithm"); System.err.println("used by the RestoreSession.restorePackage() method."); System.err.println(""); diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index a00a212..7d39912 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -5,13 +5,6 @@ LOCAL_SRC_FILES:= \ bootanimation_main.cpp \ BootAnimation.cpp -# need "-lrt" on Linux simulator to pick up clock_gettime -ifeq ($(TARGET_SIMULATOR),true) - ifeq ($(HOST_OS),linux) - LOCAL_LDLIBS += -lrt - endif -endif - LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES LOCAL_SHARED_LIBRARIES := \ diff --git a/cmds/bugreport/Android.mk b/cmds/bugreport/Android.mk index 631c219..f476f5e 100644 --- a/cmds/bugreport/Android.mk +++ b/cmds/bugreport/Android.mk @@ -1,5 +1,3 @@ -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -10,5 +8,3 @@ LOCAL_MODULE:= bugreport LOCAL_SHARED_LIBRARIES := libcutils include $(BUILD_EXECUTABLE) - -endif diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk index 431a556..56f1324 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -1,5 +1,3 @@ -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -14,5 +12,3 @@ LOCAL_MODULE := dumpstate LOCAL_SHARED_LIBRARIES := libcutils include $(BUILD_EXECUTABLE) - -endif diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index d7a9ef6..f277339 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -1,5 +1,3 @@ -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH := $(call my-dir) common_src_files := \ @@ -41,5 +39,3 @@ LOCAL_MODULE := installd LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) - -endif # !simulator diff --git a/cmds/installd/tests/Android.mk b/cmds/installd/tests/Android.mk index e53378d..315acdb 100644 --- a/cmds/installd/tests/Android.mk +++ b/cmds/installd/tests/Android.mk @@ -2,8 +2,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -ifneq ($(TARGET_SIMULATOR),true) - # Build the unit tests. test_src_files := \ installd_utils_test.cpp @@ -38,5 +36,3 @@ $(foreach file,$(test_src_files), \ $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ $(eval include $(BUILD_EXECUTABLE)) \ ) - -endif diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk index 67dd9f8..5a9b979 100644 --- a/cmds/keystore/Android.mk +++ b/cmds/keystore/Android.mk @@ -14,8 +14,6 @@ # limitations under the License. # -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -32,5 +30,3 @@ LOCAL_SHARED_LIBRARIES := libcutils libcrypto LOCAL_MODULE:= keystore_cli LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) - -endif diff --git a/cmds/rawbu/Android.mk b/cmds/rawbu/Android.mk index c1be8a4..b580390 100644 --- a/cmds/rawbu/Android.mk +++ b/cmds/rawbu/Android.mk @@ -1,7 +1,5 @@ # Copyright 2009 The Android Open Source Project -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -15,5 +13,3 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) - -endif diff --git a/cmds/runtime/Android.mk b/cmds/runtime/Android.mk deleted file mode 100644 index 6a72d10..0000000 --- a/cmds/runtime/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -ifeq ($(TARGET_SIMULATOR),true) - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - ServiceManager.cpp \ - SignalHandler.cpp \ - main_runtime.cpp - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libbinder \ - libandroid_runtime \ - libcutils \ - libui \ - libsystem_server \ - libhardware_legacy - -LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) - -ifeq ($(TARGET_OS),linux) - LOCAL_CFLAGS += -DXP_UNIX -endif - -LOCAL_MODULE:= runtime - -include $(BUILD_EXECUTABLE) -endif diff --git a/cmds/runtime/MODULE_LICENSE_APACHE2 b/cmds/runtime/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29..0000000 --- a/cmds/runtime/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/cmds/runtime/NOTICE b/cmds/runtime/NOTICE deleted file mode 100644 index c5b1efa..0000000 --- a/cmds/runtime/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-2008, 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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/cmds/runtime/ServiceManager.cpp b/cmds/runtime/ServiceManager.cpp deleted file mode 100644 index b2bef07..0000000 --- a/cmds/runtime/ServiceManager.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// - -#define LOG_TAG "ServiceManager" - -#include "ServiceManager.h" -#include "SignalHandler.h" - -#include <utils/Debug.h> -#include <utils/Log.h> -#include <binder/Parcel.h> -#include <utils/String8.h> -#include <binder/ProcessState.h> - -#include <private/utils/Static.h> - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> - -namespace android { - -BServiceManager::BServiceManager() -{ -} - -sp<IBinder> BServiceManager::getService(const String16& name) const -{ - AutoMutex _l(mLock); - ssize_t i = mServices.indexOfKey(name); - LOGV("ServiceManager: getService(%s) -> %d\n", String8(name).string(), i); - if (i >= 0) return mServices.valueAt(i); - return NULL; -} - -sp<IBinder> BServiceManager::checkService(const String16& name) const -{ - AutoMutex _l(mLock); - ssize_t i = mServices.indexOfKey(name); - LOGV("ServiceManager: getService(%s) -> %d\n", String8(name).string(), i); - if (i >= 0) return mServices.valueAt(i); - return NULL; -} - -status_t BServiceManager::addService(const String16& name, const sp<IBinder>& service) -{ - AutoMutex _l(mLock); - LOGI("ServiceManager: addService(%s, %p)\n", String8(name).string(), service.get()); - const ssize_t res = mServices.add(name, service); - if (res >= NO_ERROR) { - mChanged.broadcast(); - return NO_ERROR; - } - return res; -} - -Vector<String16> BServiceManager::listServices() -{ - Vector<String16> res; - - AutoMutex _l(mLock); - const size_t N = mServices.size(); - for (size_t i=0; i<N; i++) { - res.add(mServices.keyAt(i)); - } - - return res; -} - -}; // namespace android diff --git a/cmds/runtime/ServiceManager.h b/cmds/runtime/ServiceManager.h deleted file mode 100644 index 090ca6d..0000000 --- a/cmds/runtime/ServiceManager.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -#ifndef ANDROID_SERVICE_MANAGER_H -#define ANDROID_SERVICE_MANAGER_H - -#include <binder/IServiceManager.h> -#include <utils/KeyedVector.h> -#include <utils/threads.h> - -namespace android { - -// ---------------------------------------------------------------------- - -class BServiceManager : public BnServiceManager -{ -public: - BServiceManager(); - - virtual sp<IBinder> getService( const String16& name) const; - virtual sp<IBinder> checkService( const String16& name) const; - virtual status_t addService( const String16& name, - const sp<IBinder>& service); - virtual Vector<String16> listServices(); - - -private: - mutable Mutex mLock; - mutable Condition mChanged; - sp<IPermissionController> mPermissionController; - KeyedVector<String16, sp<IBinder> > mServices; -}; - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_SERVICE_MANAGER_H diff --git a/cmds/runtime/SignalHandler.cpp b/cmds/runtime/SignalHandler.cpp deleted file mode 100644 index cccaabf..0000000 --- a/cmds/runtime/SignalHandler.cpp +++ /dev/null @@ -1,249 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// - -#define LOG_TAG "SignalHandler" - -#include "SignalHandler.h" - -#include <utils/Atomic.h> -#include <utils/Debug.h> -#include <utils/Log.h> - -#include <errno.h> -#include <sys/wait.h> -#include <unistd.h> - -namespace android { - -class SignalHandler::ProcessThread : public Thread -{ -public: - ProcessThread(SignalHandler& sh) - : Thread(false) - , mOwner(sh) - { - } - - virtual bool threadLoop() - { - char buffer[32]; - read(mOwner.mAvailMsg[0], buffer, sizeof(buffer)); - - LOGV("Signal command processing thread woke up!"); - - if (mOwner.mLostCommands) { - LOGE("Lost %d signals!", mOwner.mLostCommands); - mOwner.mLostCommands = 0; - } - - int cur; - while ((cur=mOwner.mCommandBottom) != mOwner.mCommandTop) { - if (mOwner.mCommands[cur].filled == 0) { - LOGV("Command at %d is not yet filled", cur); - break; - } - - LOGV("Processing command at %d, top is %d", - cur, mOwner.mCommandTop); - processCommand(mOwner.mCommands[cur]); - mOwner.mCommands[cur].filled = 0; - - int next = mOwner.mCommandBottom+1; - if (next >= COMMAND_QUEUE_SIZE) { - next = 0; - } - - mOwner.mCommandBottom = next; - } - - return true; - } - - void processCommand(const CommandEntry& entry) - { - switch (entry.signum) { - case SIGCHLD: { - mOwner.mLock.lock(); - ssize_t i = mOwner.mChildHandlers.indexOfKey(entry.info.si_pid); - ChildHandler ch; - if (i >= 0) { - ch = mOwner.mChildHandlers.valueAt(i); - mOwner.mChildHandlers.removeItemsAt(i); - } - mOwner.mLock.unlock(); - - LOGD("SIGCHLD: pid=%d, handle index=%d", entry.info.si_pid, i); - - if (i >= 0) { - int res = waitpid(entry.info.si_pid, NULL, WNOHANG); - LOGW_IF(res == 0, - "Received SIGCHLD, but pid %d is not yet stopped", - entry.info.si_pid); - if (ch.handler) { - ch.handler(entry.info.si_pid, ch.userData); - } - } else { - LOGW("Unhandled SIGCHLD for pid %d", entry.info.si_pid); - } - } break; - } - } - - SignalHandler& mOwner; -}; - - -Mutex SignalHandler::mInstanceLock; -SignalHandler* SignalHandler::mInstance = NULL; - -status_t SignalHandler::setChildHandler(pid_t childPid, - int tag, - child_callback_t handler, - void* userData) -{ - SignalHandler* const self = getInstance(); - - self->mLock.lock(); - - // First make sure this child hasn't already exited. - pid_t res = waitpid(childPid, NULL, WNOHANG); - if (res != 0) { - if (res < 0) { - LOGW("setChildHandler waitpid of %d failed: %d (%s)", - childPid, res, strerror(errno)); - } else { - LOGW("setChildHandler waitpid of %d said %d already dead", - childPid, res); - } - - // Some kind of error... just handle the exit now. - self->mLock.unlock(); - - if (handler) { - handler(childPid, userData); - } - - // Return an error code -- 0 means it already exited. - return (status_t)res; - } - - ChildHandler entry; - entry.childPid = childPid; - entry.tag = tag; - entry.handler = handler; - entry.userData = userData; - - // Note: this replaces an existing entry for this pid, if there already - // is one. This is the required behavior. - LOGD("setChildHandler adding pid %d, tag %d, handler %p, data %p", - childPid, tag, handler, userData); - self->mChildHandlers.add(childPid, entry); - - self->mLock.unlock(); - - return NO_ERROR; -} - -void SignalHandler::killAllChildren(int tag) -{ - SignalHandler* const self = getInstance(); - - AutoMutex _l (self->mLock); - const size_t N = self->mChildHandlers.size(); - for (size_t i=0; i<N; i++) { - const ChildHandler& ch(self->mChildHandlers.valueAt(i)); - if (tag == 0 || ch.tag == tag) { - const pid_t pid = ch.childPid; - LOGI("Killing child %d (tag %d)\n", pid, ch.tag); - kill(pid, SIGKILL); - } - } -} - -SignalHandler::SignalHandler() - : mCommandTop(0) - , mCommandBottom(0) - , mLostCommands(0) -{ - memset(mCommands, 0, sizeof(mCommands)); - - int res = pipe(mAvailMsg); - LOGE_IF(res != 0, "Unable to create signal handler pipe: %s", strerror(errno)); - - mProcessThread = new ProcessThread(*this); - mProcessThread->run("SignalHandler", PRIORITY_HIGHEST); - - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = sigAction; - sa.sa_flags = SA_NOCLDSTOP|SA_SIGINFO; - sigaction(SIGCHLD, &sa, NULL); -} - -SignalHandler::~SignalHandler() -{ -} - -SignalHandler* SignalHandler::getInstance() -{ - AutoMutex _l(mInstanceLock); - if (mInstance == NULL) { - mInstance = new SignalHandler(); - } - return mInstance; -} - -void SignalHandler::sigAction(int signum, siginfo_t* info, void*) -{ - static const char wakeupMsg[1] = { 0xff }; - - // If our signal handler is being called, then we know we have - // already initialized the SignalHandler class and thus mInstance - // is valid. - SignalHandler* const self = mInstance; - - // XXX This is not safe! - #if 0 - LOGV("Signal %d: signo=%d, errno=%d, code=%d, pid=%d\n", - signum, - info->si_signo, info->si_errno, info->si_code, - info->si_pid); - #endif - - int32_t oldTop, newTop; - - // Find the next command slot... - do { - oldTop = self->mCommandTop; - - newTop = oldTop + 1; - if (newTop >= COMMAND_QUEUE_SIZE) { - newTop = 0; - } - - if (newTop == self->mCommandBottom) { - // The buffer is filled up! Ouch! - // XXX This is not safe! - #if 0 - LOGE("Command buffer overflow! newTop=%d\n", newTop); - #endif - android_atomic_add(1, &self->mLostCommands); - write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg)); - return; - } - } while(android_atomic_cmpxchg(oldTop, newTop, &(self->mCommandTop))); - - // Fill in the command data... - self->mCommands[oldTop].signum = signum; - self->mCommands[oldTop].info = *info; - - // And now make this command available. - self->mCommands[oldTop].filled = 1; - - // Wake up the processing thread. - write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg)); -} - -}; // namespace android - diff --git a/cmds/runtime/SignalHandler.h b/cmds/runtime/SignalHandler.h deleted file mode 100644 index 7f4ef8e..0000000 --- a/cmds/runtime/SignalHandler.h +++ /dev/null @@ -1,137 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -#ifndef ANDROID_SIGNAL_HANDLER_H -#define ANDROID_SIGNAL_HANDLER_H - -#include <utils/KeyedVector.h> -#include <utils/threads.h> - -#include <signal.h> - -namespace android { - -// ---------------------------------------------------------------------- - -enum { - DEFAULT_PROCESS_TAG = 1 -}; - -class SignalHandler -{ -public: - typedef void (*child_callback_t)(pid_t child, void* userData); - - /** - * Set a handler for when a child process exits. By calling - * this, a waitpid() will be done when the child exits to remove - * it from the zombie state. You can also optionally specify a - * handler to be called when the child exits. - * - * If there is already a handler for this child process, it is - * replaced by this new handler. In this case the old handler's - * function is not called. - * - * @param childPid Process ID of child to watch. - * @param childTag User-defined tag for this child. Must be - * greater than zero. - * @param handler If non-NULL, this will be called when the - * child exits. It may be called in either a - * separate signal handling thread, or - * immediately if the child has already exited. - * @param userData Propageted as-is to handler. - * - * @return status_t NO_ERROR if all is well. - */ - static status_t setChildHandler(pid_t childPid, - int childTag = DEFAULT_PROCESS_TAG, - child_callback_t handler = NULL, - void* userData = NULL); - - /** - * Kill all of the child processes for which we have a waiting - * handler, whose tag is the given value. If tag is 0, all - * children are killed. - * - * @param tag - */ - static void killAllChildren(int tag = 0); - -private: - SignalHandler(); - ~SignalHandler(); - - static SignalHandler* getInstance(); - - static void sigAction(int, siginfo_t*, void*); - - // -------------------------------------------------- - // Shared state... all of this is protected by mLock. - // -------------------------------------------------- - - mutable Mutex mLock; - - struct ChildHandler - { - pid_t childPid; - int tag; - child_callback_t handler; - void* userData; - }; - KeyedVector<pid_t, ChildHandler> mChildHandlers; - - // -------------------------------------------------- - // Commmand queue... data is inserted by the signal - // handler using atomic ops, and retrieved by the - // signal processing thread. Because these are touched - // by the signal handler, no lock is used. - // -------------------------------------------------- - - enum { - COMMAND_QUEUE_SIZE = 64 - }; - struct CommandEntry - { - int filled; - int signum; - siginfo_t info; - }; - - // The top of the queue. This is incremented atomically by the - // signal handler before placing a command in the queue. - volatile int32_t mCommandTop; - - // The bottom of the queue. Only modified by the processing - // thread; the signal handler reads it only to determine if the - // queue is full. - int32_t mCommandBottom; - - // Incremented each time we receive a signal and don't have room - // for it on the command queue. - volatile int32_t mLostCommands; - - // The command processing thread. - class ProcessThread; - sp<Thread> mProcessThread; - - // Pipe used to tell command processing thread when new commands. - // are available. The thread blocks on the read end, the signal - // handler writes when it enqueues new commands. - int mAvailMsg[2]; - - // The commands. - CommandEntry mCommands[COMMAND_QUEUE_SIZE]; - - // -------------------------------------------------- - // Singleton. - // -------------------------------------------------- - - static Mutex mInstanceLock; - static SignalHandler* mInstance; -}; - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_SIGNAL_HANDLER_H diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp deleted file mode 100644 index dbff095..0000000 --- a/cmds/runtime/main_runtime.cpp +++ /dev/null @@ -1,515 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -// Main entry point for runtime. -// - -#include "ServiceManager.h" -#include "SignalHandler.h" - -#include <utils/threads.h> -#include <utils/Errors.h> - -#include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> -#include <utils/Log.h> -#include <cutils/zygote.h> - -#include <cutils/properties.h> - -#include <private/utils/Static.h> - -#include <surfaceflinger/ISurfaceComposer.h> - -#include <android_runtime/AndroidRuntime.h> - -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <getopt.h> -#include <signal.h> -#include <errno.h> -#include <sys/stat.h> -#include <linux/capability.h> -#include <linux/ioctl.h> -#ifdef HAVE_ANDROID_OS -# include <linux/android_alarm.h> -#endif - -#undef LOG_TAG -#define LOG_TAG "runtime" - -static const char* ZYGOTE_ARGV[] = { - "--setuid=1000", - "--setgid=1000", - "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003", - /* CAP_SYS_TTY_CONFIG & CAP_SYS_RESOURCE & CAP_NET_BROADCAST & - * CAP_NET_ADMIN & CAP_NET_RAW & CAP_NET_BIND_SERVICE & CAP_KILL & - * CAP_SYS_BOOT CAP_SYS_NICE - */ - "--capabilities=96549920,96549920", - "--runtime-init", - "--nice-name=system_server", - "com.android.server.SystemServer" -}; - -using namespace android; - -extern "C" status_t system_init(); - -enum { - SYSTEM_PROCESS_TAG = DEFAULT_PROCESS_TAG+1 -}; - -extern Mutex gEventQMutex; -extern Condition gEventQCondition; - -namespace android { - -extern void set_finish_init_func(void (*func)()); - - -/** - * This class is used to kill this process (runtime) when the system_server dies. - */ -class GrimReaper : public IBinder::DeathRecipient { -public: - GrimReaper() { } - - virtual void binderDied(const wp<IBinder>& who) - { - LOGI("Grim Reaper killing runtime..."); - kill(getpid(), SIGKILL); - } -}; - -extern void QuickTests(); - -/* - * Print usage info. - */ -static void usage(const char* argv0) -{ - fprintf(stderr, - "Usage: runtime [-g gamma] [-l logfile] [-n] [-s]\n" - " [-j app-component] [-v app-verb] [-d app-data]\n" - "\n" - "-l: File to send log messages to\n" - "-n: Don't print to stdout/stderr\n" - "-s: Force single-process mode\n" - "-j: Custom home app component name\n" - "-v: Custom home app intent verb\n" - "-d: Custom home app intent data\n" - ); - exit(1); -} - -// Selected application to run. -static const char* gInitialApplication = NULL; -static const char* gInitialVerb = NULL; -static const char* gInitialData = NULL; - -static void writeStringToParcel(Parcel& parcel, const char* str) -{ - if (str) { - parcel.writeString16(String16(str)); - } else { - parcel.writeString16(NULL, 0); - } -} - -/* - * Starting point for program logic. - * - * Returns with an exit status code (0 on success, nonzero on error). - */ -static int run(sp<ProcessState>& proc) -{ - // Temporary hack to call startRunning() on the activity manager. - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> am; - while ((am = sm->getService(String16("activity"))) == NULL) { - LOGI("Waiting for activity manager..."); - } - Parcel data, reply; - // XXX Need to also supply a package name for this to work again. - // IActivityManager::getInterfaceDescriptor() is the token for invoking on this interface; - // hardcoding it here avoids having to link with the full Activity Manager library - data.writeInterfaceToken(String16("android.app.IActivityManager")); - writeStringToParcel(data, NULL); - writeStringToParcel(data, gInitialApplication); - writeStringToParcel(data, gInitialVerb); - writeStringToParcel(data, gInitialData); -LOGI("run() sending FIRST_CALL_TRANSACTION to activity manager"); - am->transact(IBinder::FIRST_CALL_TRANSACTION, data, &reply); - - if (proc->supportsProcesses()) { - // Now we link to the Activity Manager waiting for it to die. If it does kill ourself. - // initd will restart this process and bring the system back up. - sp<GrimReaper> grim = new GrimReaper(); - am->linkToDeath(grim, grim.get(), 0); - - // Now join the thread pool. Note this is needed so that the message enqueued in the driver - // for the linkToDeath gets processed. - IPCThreadState::self()->joinThreadPool(); - } else { - // Keep this thread running forever... - while (1) { - usleep(100000); - } - } - return 1; -} - - -}; // namespace android - - -/* - * Post-system-process initialization. - * - * This function continues initialization after the system process - * has been initialized. It needs to be separate because the system - * initialization needs to care of starting the Android runtime if it is not - * running in its own process, which doesn't return until the runtime is - * being shut down. So it will call back to here from inside of Dalvik, - * to allow us to continue booting up. - */ -static void finish_system_init(sp<ProcessState>& proc) -{ - // If we are running multiprocess, we now need to have the - // thread pool started here. We don't do this in boot_init() - // because when running single process we need to start the - // thread pool after the Android runtime has been started (so - // the pool uses Dalvik threads). - if (proc->supportsProcesses()) { - proc->startThreadPool(); - } -} - - -// This function can be used to enforce security to different -// root contexts. For now, we just give every access. -static bool contextChecker( - const String16& name, const sp<IBinder>& caller, void* userData) -{ - return true; -} - -/* - * Initialization of boot services. - * - * This is where we perform initialization of all of our low-level - * boot services. Most importantly, here we become the context - * manager and use that to publish the service manager that will provide - * access to all other services. - */ -static void boot_init() -{ - LOGI("Entered boot_init()!\n"); - - sp<ProcessState> proc(ProcessState::self()); - LOGD("ProcessState: %p\n", proc.get()); - proc->becomeContextManager(contextChecker, NULL); - - if (proc->supportsProcesses()) { - LOGI("Binder driver opened. Multiprocess enabled.\n"); - } else { - LOGI("Binder driver not found. Processes not supported.\n"); - } - - sp<BServiceManager> sm = new BServiceManager; - proc->setContextObject(sm); -} - -/* - * Redirect stdin/stdout/stderr to /dev/null. - */ -static void redirectStdFds(void) -{ - int fd = open("/dev/null", O_RDWR, 0); - if (fd < 0) { - LOGW("Unable to open /dev/null: %s\n", strerror(errno)); - } else { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - close(fd); - } -} - -static int hasDir(const char* dir) -{ - struct stat s; - int res = stat(dir, &s); - if (res == 0) { - return S_ISDIR(s.st_mode); - } - return 0; -} - -static void validateTime() -{ -#if HAVE_ANDROID_OS - int fd; - int res; - time_t min_time = 1167652800; // jan 1 2007, type 'date -ud "1/1 12:00" +%s' to get value for current year - struct timespec ts; - - fd = open("/dev/alarm", O_RDWR); - if(fd < 0) { - LOGW("Unable to open alarm driver: %s\n", strerror(errno)); - return; - } - res = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_RTC_WAKEUP), &ts); - if(res < 0) { - LOGW("Unable to read rtc, %s\n", strerror(errno)); - } - else if(ts.tv_sec >= min_time) { - goto done; - } - LOGW("Invalid time detected, %ld set to %ld\n", ts.tv_sec, min_time); - ts.tv_sec = min_time; - ts.tv_nsec = 0; - res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); - if(res < 0) { - LOGW("Unable to set rtc to %ld: %s\n", ts.tv_sec, strerror(errno)); - } -done: - close(fd); -#endif -} - -#ifndef HAVE_ANDROID_OS -class QuickRuntime : public AndroidRuntime -{ -public: - QuickRuntime() {} - - virtual void onStarted() - { - printf("QuickRuntime: onStarted\n"); - } -}; -#endif - -static status_t start_process(const char* name); - -static void restart_me(pid_t child, void* userData) -{ - start_process((const char*)userData); -} - -static status_t start_process(const char* name) -{ - String8 path(name); - Vector<const char*> args; - String8 leaf(path.getPathLeaf()); - String8 parentDir(path.getPathDir()); - args.insertAt(leaf.string(), 0); - args.add(parentDir.string()); - args.add(NULL); - pid_t child = fork(); - if (child < 0) { - status_t err = errno; - LOGE("*** fork of child %s failed: %s", leaf.string(), strerror(err)); - return -errno; - } else if (child == 0) { - LOGI("Executing: %s", path.string()); - execv(path.string(), const_cast<char**>(args.array())); - int err = errno; - LOGE("Exec failed: %s\n", strerror(err)); - _exit(err); - } else { - SignalHandler::setChildHandler(child, DEFAULT_PROCESS_TAG, - restart_me, (void*)name); - } - return -errno; -} - -/* - * Application entry point. - * - * Parse arguments, set some values, and pass control off to Run(). - * - * This is redefined to "SDL_main" on SDL simulator builds, and - * "runtime_main" on wxWidgets builds. - */ -extern "C" -int main(int argc, char* const argv[]) -{ - bool singleProcess = false; - const char* logFile = NULL; - int ic; - int result = 1; - pid_t systemPid; - - sp<ProcessState> proc; - -#ifndef HAVE_ANDROID_OS - /* Set stdout/stderr to unbuffered for MinGW/MSYS. */ - //setvbuf(stdout, NULL, _IONBF, 0); - //setvbuf(stderr, NULL, _IONBF, 0); - - LOGI("commandline args:\n"); - for (int i = 0; i < argc; i++) - LOGI(" %2d: '%s'\n", i, argv[i]); -#endif - - while (1) { - ic = getopt(argc, argv, "g:j:v:d:l:ns"); - if (ic < 0) - break; - - switch (ic) { - case 'g': - break; - case 'j': - gInitialApplication = optarg; - break; - case 'v': - gInitialVerb = optarg; - break; - case 'd': - gInitialData = optarg; - break; - case 'l': - logFile = optarg; - break; - case 'n': - redirectStdFds(); - break; - case 's': - singleProcess = true; - break; - case '?': - default: - LOGE("runtime: unrecognized flag -%c\n", ic); - usage(argv[0]); - break; - } - } - if (optind < argc) { - LOGE("runtime: extra stuff: %s\n", argv[optind]); - usage(argv[0]); - } - - if (singleProcess) { - ProcessState::setSingleProcess(true); - } - - if (logFile != NULL) { - android_logToFile(NULL, logFile); - } - - /* - * Set up ANDROID_* environment variables. - * - * TODO: the use of $ANDROID_PRODUCT_OUT will go away soon. - */ - static const char* kSystemDir = "/system"; - static const char* kDataDir = "/data"; - static const char* kAppSubdir = "/app"; - const char* out = NULL; -#ifndef HAVE_ANDROID_OS - //out = getenv("ANDROID_PRODUCT_OUT"); -#endif - if (out == NULL) - out = ""; - - char* systemDir = (char*) malloc(strlen(out) + strlen(kSystemDir) +1); - char* dataDir = (char*) malloc(strlen(out) + strlen(kDataDir) +1); - - sprintf(systemDir, "%s%s", out, kSystemDir); - sprintf(dataDir, "%s%s", out, kDataDir); - setenv("ANDROID_ROOT", systemDir, 1); - setenv("ANDROID_DATA", dataDir, 1); - - char* assetDir = (char*) malloc(strlen(systemDir) + strlen(kAppSubdir) +1); - sprintf(assetDir, "%s%s", systemDir, kAppSubdir); - - LOGI("Startup: sys='%s' asset='%s' data='%s'\n", - systemDir, assetDir, dataDir); - free(systemDir); - free(dataDir); - -#ifdef HAVE_ANDROID_OS - /* set up a process group for easier killing on the device */ - setpgid(0, getpid()); -#endif - - // Change to asset dir. This is only necessary if we've changed to - // a different directory, but there's little harm in doing it regardless. - // - // Expecting assets to live in the current dir is not a great idea, - // because some of our code or one of our libraries could change the - // directory out from under us. Preserve the behavior for now. - if (chdir(assetDir) != 0) { - LOGW("WARNING: could not change dir to '%s': %s\n", - assetDir, strerror(errno)); - } - free(assetDir); - -#if 0 - // Hack to keep libc from beating the filesystem to death. It's - // hitting /etc/localtime frequently, - // - // This statement locks us into Pacific time. We could do better, - // but there's not much point until we're sure that the library - // can't be changed to do more along the lines of what we want. -#ifndef XP_WIN - setenv("TZ", "PST+8PDT,M4.1.0/2,M10.5.0/2", true); -#endif -#endif - - /* track our progress through the boot sequence */ - const int LOG_BOOT_PROGRESS_START = 3000; - LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, - ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); - - validateTime(); - - proc = ProcessState::self(); - - boot_init(); - - /* If we are in multiprocess mode, have zygote spawn the system - * server process and call system_init(). If we are running in - * single process mode just call system_init() directly. - */ - if (proc->supportsProcesses()) { - // If stdio logging is on, system_server should not inherit our stdio - // The dalvikvm instance will copy stdio to the log on its own - char propBuf[PROPERTY_VALUE_MAX]; - bool logStdio = false; - property_get("log.redirect-stdio", propBuf, ""); - logStdio = (strcmp(propBuf, "true") == 0); - - zygote_run_oneshot((int)(!logStdio), - sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]), - ZYGOTE_ARGV); - - //start_process("/system/bin/mediaserver"); - - } else { -#ifndef HAVE_ANDROID_OS - QuickRuntime* runt = new QuickRuntime(); - runt->start("com/android/server/SystemServer", - "" /* spontaneously fork system server from zygote */); -#endif - } - - //printf("+++ post-zygote\n"); - - finish_system_init(proc); - run(proc); - -bail: - if (proc != NULL) { - proc->setContextObject(NULL); - } - - return 0; -} diff --git a/cmds/screenshot/Android.mk b/cmds/screenshot/Android.mk index 99c7aeb..73a8e22 100644 --- a/cmds/screenshot/Android.mk +++ b/cmds/screenshot/Android.mk @@ -1,5 +1,3 @@ -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -12,5 +10,3 @@ LOCAL_STATIC_LIBRARIES := libpng LOCAL_C_INCLUDES += external/zlib include $(BUILD_EXECUTABLE) - -endif diff --git a/cmds/servicemanager/Android.mk b/cmds/servicemanager/Android.mk index fe5929b..ea80c7d 100644 --- a/cmds/servicemanager/Android.mk +++ b/cmds/servicemanager/Android.mk @@ -1,4 +1,3 @@ -ifneq ($(TARGET_SIMULATOR),true) LOCAL_PATH:= $(call my-dir) #include $(CLEAR_VARS) @@ -14,4 +13,3 @@ ifeq ($(BOARD_USE_LVMX),true) LOCAL_CFLAGS += -DLVMX endif include $(BUILD_EXECUTABLE) -endif diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 1a5b7f3..dd1c275 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -793,7 +793,9 @@ int main(int argc, char **argv) { MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4, MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC, MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB, - MEDIA_MIMETYPE_AUDIO_MPEG + MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW, + MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS, + MEDIA_MIMETYPE_VIDEO_VPX }; for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp index a19711e..59360d3 100644 --- a/cmds/system_server/library/system_init.cpp +++ b/cmds/system_server/library/system_init.cpp @@ -76,23 +76,6 @@ extern "C" status_t system_init() SensorService::instantiate(); } - // On the simulator, audioflinger et al don't get started the - // same way as on the device, and we need to start them here - if (!proc->supportsProcesses()) { - - // Start the AudioFlinger - AudioFlinger::instantiate(); - - // Start the media playback service - MediaPlayerService::instantiate(); - - // Start the camera service - CameraService::instantiate(); - - // Start the audio policy service - AudioPolicyService::instantiate(); - } - // And now start the Android runtime. We have to do this bit // of nastiness because the Android runtime initialization requires // some of the core system services to already be started. @@ -117,14 +100,10 @@ extern "C" status_t system_init() } env->CallStaticVoidMethod(clazz, methodId); - // If running in our own process, just go into the thread - // pool. Otherwise, call the initialization finished - // func to let this process continue its initilization. - if (proc->supportsProcesses()) { - LOGI("System server: entering thread pool.\n"); - ProcessState::self()->startThreadPool(); - IPCThreadState::self()->joinThreadPool(); - LOGI("System server: exiting thread pool.\n"); - } + LOGI("System server: entering thread pool.\n"); + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); + LOGI("System server: exiting thread pool.\n"); + return NO_ERROR; } diff --git a/cmds/system_server/system_main.cpp b/cmds/system_server/system_main.cpp index 543f650..d67329d 100644 --- a/cmds/system_server/system_main.cpp +++ b/cmds/system_server/system_main.cpp @@ -52,10 +52,5 @@ int main(int argc, const char* const argv[]) LOGW("*** Current priority: %d\n", getpriority(PRIO_PROCESS, 0)); setpriority(PRIO_PROCESS, 0, -1); - #if HAVE_ANDROID_OS - //setgid(GID_SYSTEM); - //setuid(UID_SYSTEM); - #endif - system_init(); } diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index d77dbdc..e6c2a0f 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -87,11 +87,13 @@ public final class AnimatorSet extends Animator { private AnimatorSetListener mSetListener = null; /** - * Flag indicating that the AnimatorSet has been canceled (by calling cancel() or end()). + * Flag indicating that the AnimatorSet has been manually + * terminated (by calling cancel() or end()). * This flag is used to avoid starting other animations when currently-playing - * child animations of this AnimatorSet end. + * child animations of this AnimatorSet end. It also determines whether cancel/end + * notifications are sent out via the normal AnimatorSetListener mechanism. */ - boolean mCanceled = false; + boolean mTerminated = false; // The amount of time in ms to delay starting the animation after start() is called private long mStartDelay = 0; @@ -271,30 +273,28 @@ public final class AnimatorSet extends Animator { @SuppressWarnings("unchecked") @Override public void cancel() { - mCanceled = true; - if (mListeners != null) { - ArrayList<AnimatorListener> tmpListeners = - (ArrayList<AnimatorListener>) mListeners.clone(); - for (AnimatorListener listener : tmpListeners) { - listener.onAnimationCancel(this); - } - } - if (mDelayAnim != null && mDelayAnim.isRunning()) { - // If we're currently in the startDelay period, just cancel that animator and - // send out the end event to all listeners - mDelayAnim.cancel(); + mTerminated = true; + if (isRunning()) { + ArrayList<AnimatorListener> tmpListeners = null; if (mListeners != null) { - ArrayList<AnimatorListener> tmpListeners = - (ArrayList<AnimatorListener>) mListeners.clone(); + tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); for (AnimatorListener listener : tmpListeners) { - listener.onAnimationEnd(this); + listener.onAnimationCancel(this); } } - return; - } - if (mSortedNodes.size() > 0) { - for (Node node : mSortedNodes) { - node.animation.cancel(); + if (mDelayAnim != null && mDelayAnim.isRunning()) { + // If we're currently in the startDelay period, just cancel that animator and + // send out the end event to all listeners + mDelayAnim.cancel(); + } else if (mSortedNodes.size() > 0) { + for (Node node : mSortedNodes) { + node.animation.cancel(); + } + } + if (tmpListeners != null) { + for (AnimatorListener listener : tmpListeners) { + listener.onAnimationEnd(this); + } } } } @@ -307,23 +307,32 @@ public final class AnimatorSet extends Animator { */ @Override public void end() { - mCanceled = true; - if (mSortedNodes.size() != mNodes.size()) { - // hasn't been started yet - sort the nodes now, then end them - sortNodes(); - for (Node node : mSortedNodes) { - if (mSetListener == null) { - mSetListener = new AnimatorSetListener(this); + mTerminated = true; + if (isRunning()) { + if (mSortedNodes.size() != mNodes.size()) { + // hasn't been started yet - sort the nodes now, then end them + sortNodes(); + for (Node node : mSortedNodes) { + if (mSetListener == null) { + mSetListener = new AnimatorSetListener(this); + } + node.animation.addListener(mSetListener); } - node.animation.addListener(mSetListener); } - } - if (mDelayAnim != null) { - mDelayAnim.cancel(); - } - if (mSortedNodes.size() > 0) { - for (Node node : mSortedNodes) { - node.animation.end(); + if (mDelayAnim != null) { + mDelayAnim.cancel(); + } + if (mSortedNodes.size() > 0) { + for (Node node : mSortedNodes) { + node.animation.end(); + } + } + if (mListeners != null) { + ArrayList<AnimatorListener> tmpListeners = + (ArrayList<AnimatorListener>) mListeners.clone(); + for (AnimatorListener listener : tmpListeners) { + listener.onAnimationEnd(this); + } } } } @@ -424,7 +433,7 @@ public final class AnimatorSet extends Animator { @SuppressWarnings("unchecked") @Override public void start() { - mCanceled = false; + mTerminated = false; // First, sort the nodes (if necessary). This will ensure that sortedNodes // contains the animation nodes in the correct order. @@ -437,7 +446,8 @@ public final class AnimatorSet extends Animator { ArrayList<AnimatorListener> oldListeners = node.animation.getListeners(); if (oldListeners != null && oldListeners.size() > 0) { for (AnimatorListener listener : oldListeners) { - if (listener instanceof DependencyListener) { + if (listener instanceof DependencyListener || + listener instanceof AnimatorSetListener) { node.animation.removeListener(listener); } } @@ -522,7 +532,7 @@ public final class AnimatorSet extends Animator { * and will populate any appropriate lists, when it is started. */ anim.mNeedsSort = true; - anim.mCanceled = false; + anim.mTerminated = false; anim.mPlayingSet = new ArrayList<Animator>(); anim.mNodeMap = new HashMap<Animator, Node>(); anim.mNodes = new ArrayList<Node>(); @@ -640,7 +650,7 @@ public final class AnimatorSet extends Animator { * @param dependencyAnimation the animation that sent the event. */ private void startIfReady(Animator dependencyAnimation) { - if (mAnimatorSet.mCanceled) { + if (mAnimatorSet.mTerminated) { // if the parent AnimatorSet was canceled, then don't start any dependent anims return; } @@ -676,11 +686,15 @@ public final class AnimatorSet extends Animator { } public void onAnimationCancel(Animator animation) { - if (mPlayingSet.size() == 0) { - if (mListeners != null) { - int numListeners = mListeners.size(); - for (int i = 0; i < numListeners; ++i) { - mListeners.get(i).onAnimationCancel(mAnimatorSet); + if (!mTerminated) { + // Listeners are already notified of the AnimatorSet canceling in cancel(). + // The logic below only kicks in when animations end normally + if (mPlayingSet.size() == 0) { + if (mListeners != null) { + int numListeners = mListeners.size(); + for (int i = 0; i < numListeners; ++i) { + mListeners.get(i).onAnimationCancel(mAnimatorSet); + } } } } @@ -692,24 +706,28 @@ public final class AnimatorSet extends Animator { mPlayingSet.remove(animation); Node animNode = mAnimatorSet.mNodeMap.get(animation); animNode.done = true; - ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes; - boolean allDone = true; - int numSortedNodes = sortedNodes.size(); - for (int i = 0; i < numSortedNodes; ++i) { - if (!sortedNodes.get(i).done) { - allDone = false; - break; + if (!mTerminated) { + // Listeners are already notified of the AnimatorSet ending in cancel() or + // end(); the logic below only kicks in when animations end normally + ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes; + boolean allDone = true; + int numSortedNodes = sortedNodes.size(); + for (int i = 0; i < numSortedNodes; ++i) { + if (!sortedNodes.get(i).done) { + allDone = false; + break; + } } - } - if (allDone) { - // If this was the last child animation to end, then notify listeners that this - // AnimatorSet has ended - if (mListeners != null) { - ArrayList<AnimatorListener> tmpListeners = - (ArrayList<AnimatorListener>) mListeners.clone(); - int numListeners = tmpListeners.size(); - for (int i = 0; i < numListeners; ++i) { - tmpListeners.get(i).onAnimationEnd(mAnimatorSet); + if (allDone) { + // If this was the last child animation to end, then notify listeners that this + // AnimatorSet has ended + if (mListeners != null) { + ArrayList<AnimatorListener> tmpListeners = + (ArrayList<AnimatorListener>) mListeners.clone(); + int numListeners = tmpListeners.size(); + for (int i = 0; i < numListeners; ++i) { + tmpListeners.get(i).onAnimationEnd(mAnimatorSet); + } } } } @@ -791,6 +809,8 @@ public final class AnimatorSet extends Animator { } } } + // nodes are 'done' by default; they become un-done when started, and done + // again when ended node.done = false; } } diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 1dcaa04..90d676e 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -933,17 +933,17 @@ public class ValueAnimator extends Animator { @Override public void cancel() { - if (mListeners != null) { - ArrayList<AnimatorListener> tmpListeners = - (ArrayList<AnimatorListener>) mListeners.clone(); - for (AnimatorListener listener : tmpListeners) { - listener.onAnimationCancel(this); - } - } // Only cancel if the animation is actually running or has been started and is about // to run if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) || sDelayedAnims.get().contains(this)) { + if (mListeners != null) { + ArrayList<AnimatorListener> tmpListeners = + (ArrayList<AnimatorListener>) mListeners.clone(); + for (AnimatorListener listener : tmpListeners) { + listener.onAnimationCancel(this); + } + } endAnimation(); } } diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 3ec5edb..36940c2 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -81,6 +81,9 @@ public abstract class ActionBar { * Set this flag if selecting the 'home' button in the action bar to return * up by a single level in your UI rather than back to the top level or front page. * + * <p>Setting this option will implicitly enable interaction with the home/up + * button. See {@link #setHomeButtonEnabled(boolean)}. + * * @see #setDisplayOptions(int) * @see #setDisplayOptions(int, int) */ @@ -107,18 +110,6 @@ public abstract class ActionBar { public static final int DISPLAY_SHOW_CUSTOM = 0x10; /** - * Disable the 'home' element. This may be combined with - * {@link #DISPLAY_SHOW_HOME} to create a non-focusable/non-clickable - * 'home' element. Useful for a level of your app's navigation hierarchy - * where clicking 'home' doesn't do anything. - * - * @see #setDisplayOptions(int) - * @see #setDisplayOptions(int, int) - * @see #setDisplayDisableHomeEnabled(boolean) - */ - public static final int DISPLAY_DISABLE_HOME = 0x20; - - /** * Set the action bar into custom navigation mode, supplying a view * for custom navigation. * @@ -405,21 +396,6 @@ public abstract class ActionBar { public abstract void setDisplayShowCustomEnabled(boolean showCustom); /** - * Set whether the 'home' affordance on the action bar should be disabled. - * If set, the 'home' element will not be focusable or clickable, useful if - * the user is at the top level of the app's navigation hierarchy. - * - * <p>To set several display options at once, see the setDisplayOptions methods. - * - * @param disableHome true to disable the 'home' element. - * - * @see #setDisplayOptions(int) - * @see #setDisplayOptions(int, int) - * @see #DISPLAY_DISABLE_HOME - */ - public abstract void setDisplayDisableHomeEnabled(boolean disableHome); - - /** * Set the ActionBar's background. * * @param d Background drawable @@ -632,6 +608,22 @@ public abstract class ActionBar { public abstract void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener); /** + * Enable or disable the "home" button in the corner of the action bar. (Note that this + * is the application home/up affordance on the action bar, not the systemwide home + * button.) + * + * <p>This defaults to true for packages targeting < API 14. For packages targeting + * API 14 or greater, the application should call this method to enable interaction + * with the home/up affordance. + * + * <p>Setting the {@link #DISPLAY_HOME_AS_UP} display option will automatically enable + * the home button. + * + * @param enabled true to enable the home button, false to disable the home button. + */ + public abstract void setHomeButtonEnabled(boolean enabled); + + /** * Listener interface for ActionBar navigation events. */ public interface OnNavigationListener { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index a6658cc..d207a0a 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1471,6 +1471,24 @@ public class ActivityManager { } /** + * Returns the usage statistics of each installed package. + * + * @hide + */ + public PkgUsageStats[] getAllPackageUsageStats() { + try { + IUsageStats usageStatsService = IUsageStats.Stub.asInterface( + ServiceManager.getService("usagestats")); + if (usageStatsService != null) { + return usageStatsService.getAllPkgUsageStats(); + } + } catch (RemoteException e) { + Log.w(TAG, "Could not query usage stats", e); + } + return new PkgUsageStats[0]; + } + + /** * @param userid the user's id. Zero indicates the default user * @hide */ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index eee14fb..8994b17 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -932,6 +932,10 @@ public final class ActivityThread { ucd.info = info; queueOrSendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd); } + + public void scheduleTrimMemory(int level) { + queueOrSendMessage(H.TRIM_MEMORY, level); + } } private final class H extends Handler { @@ -975,6 +979,7 @@ public final class ActivityThread { public static final int SLEEPING = 137; public static final int SET_CORE_SETTINGS = 138; public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139; + public static final int TRIM_MEMORY = 140; String codeToString(int code) { if (DEBUG_MESSAGES) { switch (code) { @@ -1018,6 +1023,7 @@ public final class ActivityThread { case SLEEPING: return "SLEEPING"; case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS"; case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO"; + case TRIM_MEMORY: return "TRIM_MEMORY"; } } return "(unknown)"; @@ -1158,6 +1164,8 @@ public final class ActivityThread { break; case UPDATE_PACKAGE_COMPATIBILITY_INFO: handleUpdatePackageCompatibilityInfo((UpdateCompatibilityData)msg.obj); + case TRIM_MEMORY: + handleTrimMemory(msg.arg1); } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what); } @@ -3529,6 +3537,9 @@ public final class ActivityThread { BinderInternal.forceGc("mem"); } + final void handleTrimMemory(int level) { + } + private final void handleBindApplication(AppBindData data) { mBoundApplication = data; mConfiguration = new Configuration(data.config); @@ -4093,11 +4104,6 @@ public final class ActivityThread { }); } - private final void detach() - { - sThreadLocal.set(null); - } - public static final ActivityThread systemMain() { HardwareRenderer.disable(); ActivityThread thread = new ActivityThread(); @@ -4105,10 +4111,9 @@ public final class ActivityThread { return thread; } - public final void installSystemProviders(List providers) { + public final void installSystemProviders(List<ProviderInfo> providers) { if (providers != null) { - installContentProviders(mInitialApplication, - (List<ProviderInfo>)providers); + installContentProviders(mInitialApplication, providers); } } @@ -4147,14 +4152,6 @@ public final class ActivityThread { Looper.loop(); - if (Process.supportsProcesses()) { - throw new RuntimeException("Main thread loop unexpectedly exited"); - } - - thread.detach(); - String name = (thread.mInitialApplication != null) - ? thread.mInitialApplication.getPackageName() - : "<unknown>"; - Slog.i(TAG, "Main thread of " + name + " is now exiting"); + throw new RuntimeException("Main thread loop unexpectedly exited"); } } diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index dc0f529..16181e0 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -478,6 +478,13 @@ public abstract class ApplicationThreadNative extends Binder updatePackageCompatibilityInfo(pkg, compat); return true; } + + case SCHEDULE_TRIM_MEMORY_TRANSACTION: { + data.enforceInterface(IApplicationThread.descriptor); + int level = data.readInt(); + scheduleTrimMemory(level); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -989,4 +996,12 @@ class ApplicationThreadProxy implements IApplicationThread { mRemote.transact(UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); } + + public void scheduleTrimMemory(int level) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeInt(level); + mRemote.transact(SCHEDULE_TRIM_MEMORY_TRANSACTION, data, null, + IBinder.FLAG_ONEWAY); + } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 8749d3e..d2323e7 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1163,9 +1163,6 @@ class ContextImpl extends Context { throw new IllegalArgumentException("permission is null"); } - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } try { return ActivityManagerNative.getDefault().checkPermission( permission, pid, uid); @@ -1180,9 +1177,6 @@ class ContextImpl extends Context { throw new IllegalArgumentException("permission is null"); } - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } int pid = Binder.getCallingPid(); if (pid != Process.myPid()) { return checkPermission(permission, pid, @@ -1263,9 +1257,6 @@ class ContextImpl extends Context { @Override public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } try { return ActivityManagerNative.getDefault().checkUriPermission( uri, pid, uid, modeFlags); @@ -1276,9 +1267,6 @@ class ContextImpl extends Context { @Override public int checkCallingUriPermission(Uri uri, int modeFlags) { - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } int pid = Binder.getCallingPid(); if (pid != Process.myPid()) { return checkUriPermission(uri, pid, diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 05a68a8..94c2c86 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -119,6 +119,7 @@ public interface IApplicationThread extends IInterface { throws RemoteException; void setCoreSettings(Bundle coreSettings) throws RemoteException; void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException; + void scheduleTrimMemory(int level) throws RemoteException; String descriptor = "android.app.IApplicationThread"; @@ -162,4 +163,5 @@ public interface IApplicationThread extends IInterface { int SET_HTTP_PROXY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38; int SET_CORE_SETTINGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39; int UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40; + int SCHEDULE_TRIM_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+41; } diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index dce0a97..436fdf8 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -28,6 +28,7 @@ import android.os.RemoteException; import android.util.Log; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.util.HashSet; import java.util.LinkedList; @@ -533,6 +534,16 @@ public abstract class BackupAgent extends ContextWrapper { Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); throw ex; } finally { + // Send the EOD marker indicating that there is no more data + // forthcoming from this agent. + try { + FileOutputStream out = new FileOutputStream(data.getFileDescriptor()); + byte[] buf = new byte[4]; + out.write(buf); + } catch (IOException e) { + Log.e(TAG, "Unable to finalize backup stream!"); + } + Binder.restoreCallingIdentity(ident); try { callbackBinder.opComplete(token); diff --git a/core/java/android/app/backup/IRestoreSession.aidl b/core/java/android/app/backup/IRestoreSession.aidl index 1dddbb0..14731ee 100644 --- a/core/java/android/app/backup/IRestoreSession.aidl +++ b/core/java/android/app/backup/IRestoreSession.aidl @@ -52,6 +52,25 @@ interface IRestoreSession { int restoreAll(long token, IRestoreObserver observer); /** + * Restore select packages from the given set onto the device, replacing the + * current data of any app contained in the set with the data previously + * backed up. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @return Zero on success, nonzero on error. The observer will only receive + * progress callbacks if this method returned zero. + * @param token The token from {@link getAvailableRestoreSets()} corresponding to + * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + * @param packages The set of packages for which to attempt a restore. Regardless of + * the contents of the actual back-end dataset named by {@code token}, only + * applications mentioned in this list will have their data restored. + */ + int restoreSome(long token, IRestoreObserver observer, in String[] packages); + + /** * Restore a single application from backup. The data will be restored from the * current backup dataset if the given package has stored data there, or from * the dataset used during the last full device setup operation if the current diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java index 24ddb99..7181c61 100644 --- a/core/java/android/app/backup/RestoreSession.java +++ b/core/java/android/app/backup/RestoreSession.java @@ -87,6 +87,40 @@ public class RestoreSession { } /** + * Restore select packages from the given set onto the device, replacing the + * current data of any app contained in the set with the data previously + * backed up. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @return Zero on success, nonzero on error. The observer will only receive + * progress callbacks if this method returned zero. + * @param token The token from {@link getAvailableRestoreSets()} corresponding to + * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + * @param packages The set of packages for which to attempt a restore. Regardless of + * the contents of the actual back-end dataset named by {@code token}, only + * applications mentioned in this list will have their data restored. + * + * @hide + */ + public int restoreSome(long token, RestoreObserver observer, String[] packages) { + int err = -1; + if (mObserver != null) { + Log.d(TAG, "restoreAll() called during active restore"); + return -1; + } + mObserver = new RestoreObserverWrapper(mContext, observer); + try { + err = mBinder.restoreSome(token, mObserver, packages); + } catch (RemoteException e) { + Log.d(TAG, "Can't contact server to restore packages"); + } + return err; + } + + /** * Restore a single application from backup. The data will be restored from the * current backup dataset if the given package has stored data there, or from * the dataset used during the last full device setup operation if the current diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java index 52efc07..0a01dcf 100644 --- a/core/java/android/bluetooth/BluetoothHealth.java +++ b/core/java/android/bluetooth/BluetoothHealth.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.annotation.SdkConstant; import android.content.Context; import android.os.IBinder; import android.os.ParcelFileDescriptor; @@ -67,9 +66,6 @@ public final class BluetoothHealth implements BluetoothProfile { */ public static final int CHANNEL_TYPE_ANY = 12; - private final ArrayList<BluetoothHealthAppConfiguration> mAppConfigs = - new ArrayList<BluetoothHealthAppConfiguration>(); - /** * Register an application configuration that acts as a Health SINK. * This is the configuration that will be used to communicate with health devices @@ -86,7 +82,7 @@ public final class BluetoothHealth implements BluetoothProfile { * @return If true, callback will be called. */ public boolean registerSinkAppConfiguration(String name, int dataType, - IBluetoothHealthCallback callback) { + BluetoothHealthCallback callback) { if (!isEnabled() || name == null) return false; if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); @@ -111,18 +107,18 @@ public final class BluetoothHealth implements BluetoothProfile { * @hide */ public boolean registerAppConfiguration(String name, int dataType, int role, - int channelType, IBluetoothHealthCallback callback) { + int channelType, BluetoothHealthCallback callback) { boolean result = false; if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result; if (DBG) log("registerApplication(" + name + ":" + dataType + ")"); + BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback); BluetoothHealthAppConfiguration config = - new BluetoothHealthAppConfiguration(name, dataType, role, channelType, - callback); + new BluetoothHealthAppConfiguration(name, dataType, role, channelType); if (mService != null) { try { - result = mService.registerAppConfiguration(config); + result = mService.registerAppConfiguration(config, wrapper); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -130,8 +126,6 @@ public final class BluetoothHealth implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - - if (result) mAppConfigs.add(config); return result; } @@ -147,7 +141,7 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { boolean result = false; - if (mService != null && isEnabled() && isValidAppConfig(config)) { + if (mService != null && isEnabled() && config != null) { try { result = mService.unregisterAppConfiguration(config); } catch (RemoteException e) { @@ -157,26 +151,26 @@ public final class BluetoothHealth implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - if (result) mAppConfigs.remove(config); + return result; } /** * Connect to a health device which has the {@link #SOURCE_ROLE}. - * This is an asynchrnous call. If this function returns true, the callback + * This is an asynchronous call. If this function returns true, the callback * associated with the application configuration will be called. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registed using - * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param config The application configuration which has been registered using + * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. */ public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.connectChannelToSource(device, config); } catch (RemoteException e) { @@ -197,15 +191,15 @@ public final class BluetoothHealth implements BluetoothProfile { *<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registed using - * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param config The application configuration which has been registered using + * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. * @hide */ public boolean connectChannelToSink(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelType) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.connectChannelToSink(device, config, channelType); } catch (RemoteException e) { @@ -226,8 +220,8 @@ public final class BluetoothHealth implements BluetoothProfile { *<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registed using - * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param config The application configuration which has been registered using + * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @param fd The file descriptor that was associated with the channel. * @return If true, the callback associated with the application config will be called. * @hide @@ -235,7 +229,7 @@ public final class BluetoothHealth implements BluetoothProfile { public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.disconnectChannel(device, config, fd); } catch (RemoteException e) { @@ -262,7 +256,7 @@ public final class BluetoothHealth implements BluetoothProfile { public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.getMainChannelFd(device, config); } catch (RemoteException e) { @@ -290,6 +284,7 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ + @Override public int getConnectionState(BluetoothDevice device) { if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -317,6 +312,7 @@ public final class BluetoothHealth implements BluetoothProfile { * local adapter. * @return List of devices. The list will be empty on error. */ + @Override public List<BluetoothDevice> getConnectedDevices() { if (mService != null && isEnabled()) { try { @@ -348,6 +344,7 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ + @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (mService != null && isEnabled()) { try { @@ -361,6 +358,27 @@ public final class BluetoothHealth implements BluetoothProfile { return new ArrayList<BluetoothDevice>(); } + private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub { + private BluetoothHealthCallback mCallback; + + public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) { + mCallback = callback; + } + + @Override + public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, + int status) { + mCallback.onHealthAppConfigurationStatusChange(config, status); + } + + @Override + public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, + BluetoothDevice device, int prevState, int newState, + ParcelFileDescriptor fd) { + mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd); + } + } + /** Health Channel Connection State - Disconnected */ public static final int STATE_CHANNEL_DISCONNECTED = 0; /** Health Channel Connection State - Connecting */ @@ -379,7 +397,6 @@ public final class BluetoothHealth implements BluetoothProfile { /** Health App Configuration un-registration failure */ public static final int APPLICATION_UNREGISTRATION_FAILURE = 3; - private Context mContext; private ServiceListener mServiceListener; private IBluetooth mService; BluetoothAdapter mAdapter; @@ -420,14 +437,8 @@ public final class BluetoothHealth implements BluetoothProfile { return false; } - private boolean isValidAppConfig(BluetoothHealthAppConfiguration config) { - if (!mAppConfigs.isEmpty() && mAppConfigs.contains(config)) return true; - log("Not a valid config: " + config); - return false; - } - private boolean checkAppParam(String name, int role, int channelType, - IBluetoothHealthCallback callback) { + BluetoothHealthCallback callback) { if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING && diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java index b87aea5..7020249 100644 --- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -17,7 +17,6 @@ package android.bluetooth; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -34,21 +33,18 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { private final int mDataType; private final int mRole; private final int mChannelType; - private final IBluetoothHealthCallback mCallback; /** * Constructor to register the SINK role * * @param name Friendly name associated with the application configuration * @param dataType Data Type of the remote Bluetooth Health device - * @param callback Callback associated with the application configuration. */ - BluetoothHealthAppConfiguration(String name, int dataType, IBluetoothHealthCallback callback) { + BluetoothHealthAppConfiguration(String name, int dataType) { mName = name; mDataType = dataType; mRole = BluetoothHealth.SINK_ROLE; mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY; - mCallback = callback; } /** @@ -56,17 +52,15 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * * @param name Friendly name associated with the application configuration * @param dataType Data Type of the remote Bluetooth Health device - * @param role {@link BluetoothHealth.SOURCE_ROLE} or - * {@link BluetoothHealth.SINK_ROLE} - * @param callback Callback associated with the application configuration. + * @param role {@link BluetoothHealth#SOURCE_ROLE} or + * {@link BluetoothHealth#SINK_ROLE} */ - BluetoothHealthAppConfiguration(String name, int dataType, int role, int channelType, - IBluetoothHealthCallback callback) { + BluetoothHealthAppConfiguration(String name, int dataType, int role, int + channelType) { mName = name; mDataType = dataType; mRole = role; mChannelType = channelType; - mCallback = callback; } @Override @@ -77,8 +71,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { return mName.equals(config.getName()) && mDataType == config.getDataType() && mRole == config.getRole() && - mChannelType == config.getChannelType() && - mCallback.equals(config.getCallback()); + mChannelType == config.getChannelType(); } return false; } @@ -90,7 +83,6 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { result = 31 * result + mDataType; result = 31 * result + mRole; result = 31 * result + mChannelType; - result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0); return result; } @@ -98,9 +90,10 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { public String toString() { return "BluetoothHealthAppConfiguration [mName = " + mName + ",mDataType = " + mDataType + ", mRole = " + mRole + ",mChannelType = " + - mChannelType + ",callback=" + mCallback +"]"; + mChannelType + "]"; } + @Override public int describeContents() { return 0; } @@ -144,37 +137,31 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { return mChannelType; } - /** - * Return the callback associated with this application configuration. - * - * @return IBluetoothHealthCallback - */ - public IBluetoothHealthCallback getCallback() { - return mCallback; - } - public static final Parcelable.Creator<BluetoothHealthAppConfiguration> CREATOR = new Parcelable.Creator<BluetoothHealthAppConfiguration>() { + @Override public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { String name = in.readString(); int type = in.readInt(); int role = in.readInt(); int channelType = in.readInt(); - IBluetoothHealthCallback callback = - IBluetoothHealthCallback.Stub.asInterface(in.readStrongBinder()); - return new BluetoothHealthAppConfiguration(name, type, role, channelType, - callback); + return new BluetoothHealthAppConfiguration(name, type, role, + channelType); } + + @Override public BluetoothHealthAppConfiguration[] newArray(int size) { return new BluetoothHealthAppConfiguration[size]; } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mName); out.writeInt(mDataType); out.writeInt(mRole); out.writeInt(mChannelType); - out.writeStrongInterface(mCallback); } + + } diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java new file mode 100644 index 0000000..0d11bb5 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHealthCallback.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 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.bluetooth; + +import android.os.ParcelFileDescriptor; +import android.util.Log; + +/** + * This class is used for all the {@link BluetoothHealth} callbacks. + * @hide + */ +public abstract class BluetoothHealthCallback { + + private static final String TAG = "BluetoothHealthCallback"; + + public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, + int status) { + Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + " Status:" + status); + } + + public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, + BluetoothDevice device, int prevState, int newState, + ParcelFileDescriptor fd) { + Log.d(TAG, "onHealthChannelStateChange: " + config + " Device:" + device + + "PrevState:" + prevState + "NewState:" + newState + "FileDescriptor:" + fd); + } +} diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index 83e59e2..acce182 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -62,6 +62,7 @@ public final class BluetoothServerSocket implements Closeable { /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; + private final int mChannel; /** * Construct a socket for incoming connections. @@ -74,6 +75,7 @@ public final class BluetoothServerSocket implements Closeable { */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) throws IOException { + mChannel = port; mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); } @@ -125,4 +127,12 @@ public final class BluetoothServerSocket implements Closeable { mHandler = handler; mMessage = message; } + + /** + * Returns the channel on which this socket is bound. + * @hide + */ + public int getChannel() { + return mChannel; + } } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 6ca6c2e..183772d 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothHealthCallback; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHealthAppConfiguration; import android.os.ParcelUuid; @@ -102,7 +103,8 @@ interface IBluetooth boolean disconnectPanDevice(in BluetoothDevice device); // HDP profile APIs - boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config); + boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config, + in IBluetoothHealthCallback callback); boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config); boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, diff --git a/core/java/android/content/ComponentCallbacks.java b/core/java/android/content/ComponentCallbacks.java index dad60b0..92b98fd 100644 --- a/core/java/android/content/ComponentCallbacks.java +++ b/core/java/android/content/ComponentCallbacks.java @@ -51,4 +51,16 @@ public interface ComponentCallbacks { * The system will perform a gc for you after returning from this method. */ void onLowMemory(); + + /** @hide */ + static final int TRIM_MEMORY_COMPLETE = 80; + + /** @hide */ + static final int TRIM_MEMORY_MODERATE = 60; + + /** @hide */ + static final int TRIM_MEMORY_BACKGROUND = 40; + + /** @hide */ + static final int TRIM_MEMORY_INVISIBLE = 20; } diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 19894a0..f2f0e82 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -52,11 +52,26 @@ import java.util.Collections; public class LinkProperties implements Parcelable { String mIfaceName; - private Collection<LinkAddress> mLinkAddresses; - private Collection<InetAddress> mDnses; - private Collection<RouteInfo> mRoutes; + private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>(); + private Collection<InetAddress> mDnses = new ArrayList<InetAddress>(); + private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); private ProxyProperties mHttpProxy; + public static class CompareAddressesResult { + public ArrayList<LinkAddress> removed = new ArrayList<LinkAddress>(); + public ArrayList<LinkAddress> added = new ArrayList<LinkAddress>(); + + @Override + public String toString() { + String retVal = "removedAddresses=["; + for (LinkAddress addr : removed) retVal += addr.toString() + ","; + retVal += "] addedAddresses=["; + for (LinkAddress addr : added) retVal += addr.toString() + ","; + retVal += "]"; + return retVal; + } + } + public LinkProperties() { clear(); } @@ -121,9 +136,9 @@ public class LinkProperties implements Parcelable { public void clear() { mIfaceName = null; - mLinkAddresses = new ArrayList<LinkAddress>(); - mDnses = new ArrayList<InetAddress>(); - mRoutes = new ArrayList<RouteInfo>(); + mLinkAddresses.clear(); + mDnses.clear(); + mRoutes.clear(); mHttpProxy = null; } @@ -155,6 +170,63 @@ public class LinkProperties implements Parcelable { return ifaceName + linkAddresses + routes + dns + proxy; } + /** + * Compares this {@code LinkProperties} interface name against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalInterfaceName(LinkProperties target) { + return TextUtils.equals(getInterfaceName(), target.getInterfaceName()); + } + + /** + * Compares this {@code LinkProperties} interface name against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalAddresses(LinkProperties target) { + Collection<InetAddress> targetAddresses = target.getAddresses(); + Collection<InetAddress> sourceAddresses = getAddresses(); + return (sourceAddresses.size() == targetAddresses.size()) ? + sourceAddresses.containsAll(targetAddresses) : false; + } + + /** + * Compares this {@code LinkProperties} DNS addresses against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalDnses(LinkProperties target) { + Collection<InetAddress> targetDnses = target.getDnses(); + return (mDnses.size() == targetDnses.size()) ? + mDnses.containsAll(targetDnses) : false; + } + + /** + * Compares this {@code LinkProperties} Routes against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalRoutes(LinkProperties target) { + Collection<RouteInfo> targetRoutes = target.getRoutes(); + return (mRoutes.size() == targetRoutes.size()) ? + mRoutes.containsAll(targetRoutes) : false; + } + + /** + * Compares this {@code LinkProperties} HttpProxy against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + */ + public boolean isIdenticalHttpProxy(LinkProperties target) { + return getHttpProxy() == null ? target.getHttpProxy() == null : + getHttpProxy().equals(target.getHttpProxy()); + } @Override /** @@ -176,30 +248,41 @@ public class LinkProperties implements Parcelable { if (!(obj instanceof LinkProperties)) return false; - boolean sameAddresses; - boolean sameDnses; - boolean sameRoutes; - LinkProperties target = (LinkProperties) obj; - Collection<InetAddress> targetAddresses = target.getAddresses(); - Collection<InetAddress> sourceAddresses = getAddresses(); - sameAddresses = (sourceAddresses.size() == targetAddresses.size()) ? - sourceAddresses.containsAll(targetAddresses) : false; - - Collection<InetAddress> targetDnses = target.getDnses(); - sameDnses = (mDnses.size() == targetDnses.size()) ? - mDnses.containsAll(targetDnses) : false; + return isIdenticalInterfaceName(target) && + isIdenticalAddresses(target) && + isIdenticalDnses(target) && + isIdenticalRoutes(target) && + isIdenticalHttpProxy(target); + } - Collection<RouteInfo> targetRoutes = target.getRoutes(); - sameRoutes = (mRoutes.size() == targetRoutes.size()) ? - mRoutes.containsAll(targetRoutes) : false; - - return - sameAddresses && sameDnses && sameRoutes - && TextUtils.equals(getInterfaceName(), target.getInterfaceName()) - && (getHttpProxy() == null ? target.getHttpProxy() == null : - getHttpProxy().equals(target.getHttpProxy())); + /** + * Return two lists, a list of addresses that would be removed from + * mLinkAddresses and a list of addresses that would be added to + * mLinkAddress which would then result in target and mLinkAddresses + * being the same list. + * + * @param target is a new list of addresses + * @return the removed and added lists. + */ + public CompareAddressesResult compareAddresses(LinkProperties target) { + /* + * Duplicate the LinkAddresses into removed, we will be removing + * address which are common between mLinkAddresses and target + * leaving the addresses that are different. And address which + * are in target but not in mLinkAddresses are placed in the + * addedAddresses. + */ + CompareAddressesResult result = new CompareAddressesResult(); + result.removed = new ArrayList<LinkAddress>(mLinkAddresses); + result.added.clear(); + for (LinkAddress newAddress : target.getLinkAddresses()) { + if (! result.removed.remove(newAddress)) { + result.added.add(newAddress); + } + } + return result; } @Override diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 9d40c42..fbff7d8 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -47,13 +47,39 @@ public class NetworkStats implements Parcelable { * {@link SystemClock#elapsedRealtime()} timestamp when this data was * generated. */ - public final long elapsedRealtime; - public int size; - public String[] iface; - public int[] uid; - public int[] tag; - public long[] rx; - public long[] tx; + private final long elapsedRealtime; + private int size; + private String[] iface; + private int[] uid; + private int[] tag; + private long[] rxBytes; + private long[] rxPackets; + private long[] txBytes; + private long[] txPackets; + + public static class Entry { + public String iface; + public int uid; + public int tag; + public long rxBytes; + public long rxPackets; + public long txBytes; + public long txPackets; + + public Entry() { + } + + public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, + long txPackets) { + this.iface = iface; + this.uid = uid; + this.tag = tag; + this.rxBytes = rxBytes; + this.rxPackets = rxPackets; + this.txBytes = txBytes; + this.txPackets = txPackets; + } + } public NetworkStats(long elapsedRealtime, int initialSize) { this.elapsedRealtime = elapsedRealtime; @@ -61,8 +87,10 @@ public class NetworkStats implements Parcelable { this.iface = new String[initialSize]; this.uid = new int[initialSize]; this.tag = new int[initialSize]; - this.rx = new long[initialSize]; - this.tx = new long[initialSize]; + this.rxBytes = new long[initialSize]; + this.rxPackets = new long[initialSize]; + this.txBytes = new long[initialSize]; + this.txPackets = new long[initialSize]; } public NetworkStats(Parcel parcel) { @@ -71,46 +99,93 @@ public class NetworkStats implements Parcelable { iface = parcel.createStringArray(); uid = parcel.createIntArray(); tag = parcel.createIntArray(); - rx = parcel.createLongArray(); - tx = parcel.createLongArray(); + rxBytes = parcel.createLongArray(); + rxPackets = parcel.createLongArray(); + txBytes = parcel.createLongArray(); + txPackets = parcel.createLongArray(); + } + + public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets, + long txBytes, long txPackets) { + return addValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets)); } /** - * Add new stats entry with given values. + * Add new stats entry, copying from given {@link Entry}. The {@link Entry} + * object can be recycled across multiple calls. */ - public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) { + public NetworkStats addValues(Entry entry) { if (size >= this.iface.length) { - final int newLength = Math.max(this.iface.length, 10) * 3 / 2; - this.iface = Arrays.copyOf(this.iface, newLength); - this.uid = Arrays.copyOf(this.uid, newLength); - this.tag = Arrays.copyOf(this.tag, newLength); - this.rx = Arrays.copyOf(this.rx, newLength); - this.tx = Arrays.copyOf(this.tx, newLength); + final int newLength = Math.max(iface.length, 10) * 3 / 2; + iface = Arrays.copyOf(iface, newLength); + uid = Arrays.copyOf(uid, newLength); + tag = Arrays.copyOf(tag, newLength); + rxBytes = Arrays.copyOf(rxBytes, newLength); + rxPackets = Arrays.copyOf(rxPackets, newLength); + txBytes = Arrays.copyOf(txBytes, newLength); + txPackets = Arrays.copyOf(txPackets, newLength); } - this.iface[size] = iface; - this.uid[size] = uid; - this.tag[size] = tag; - this.rx[size] = rx; - this.tx[size] = tx; + iface[size] = entry.iface; + uid[size] = entry.uid; + tag[size] = entry.tag; + rxBytes[size] = entry.rxBytes; + rxPackets[size] = entry.rxPackets; + txBytes[size] = entry.txBytes; + txPackets[size] = entry.txPackets; size++; return this; } /** + * Return specific stats entry. + */ + public Entry getValues(int i, Entry recycle) { + final Entry entry = recycle != null ? recycle : new Entry(); + entry.iface = iface[i]; + entry.uid = uid[i]; + entry.tag = tag[i]; + entry.rxBytes = rxBytes[i]; + entry.rxPackets = rxPackets[i]; + entry.txBytes = txBytes[i]; + entry.txPackets = txPackets[i]; + return entry; + } + + public long getElapsedRealtime() { + return elapsedRealtime; + } + + public int size() { + return size; + } + + // @VisibleForTesting + public int internalSize() { + return iface.length; + } + + public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, + long txBytes, long txPackets) { + return combineValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets)); + } + + /** * Combine given values with an existing row, or create a new row if * {@link #findIndex(String, int, int)} is unable to find match. Can also be * used to subtract values from existing rows. */ - public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) { - final int i = findIndex(iface, uid, tag); + public NetworkStats combineValues(Entry entry) { + final int i = findIndex(entry.iface, entry.uid, entry.tag); if (i == -1) { // only create new entry when positive contribution - addEntry(iface, uid, tag, rx, tx); + addValues(entry); } else { - this.rx[i] += rx; - this.tx[i] += tx; + rxBytes[i] += entry.rxBytes; + rxPackets[i] += entry.rxPackets; + txBytes[i] += entry.txBytes; + txPackets[i] += entry.txPackets; } return this; } @@ -199,30 +274,41 @@ public class NetworkStats implements Parcelable { } // result will have our rows, and elapsed time between snapshots + final Entry entry = new Entry(); final NetworkStats result = new NetworkStats(deltaRealtime, size); for (int i = 0; i < size; i++) { - final String iface = this.iface[i]; - final int uid = this.uid[i]; - final int tag = this.tag[i]; + entry.iface = iface[i]; + entry.uid = uid[i]; + entry.tag = tag[i]; // find remote row that matches, and subtract - final int j = value.findIndex(iface, uid, tag); + final int j = value.findIndex(entry.iface, entry.uid, entry.tag); if (j == -1) { // newly appearing row, return entire value - result.addEntry(iface, uid, tag, this.rx[i], this.tx[i]); + entry.rxBytes = rxBytes[i]; + entry.rxPackets = rxPackets[i]; + entry.txBytes = txBytes[i]; + entry.txPackets = txPackets[i]; } else { // existing row, subtract remote value - long rx = this.rx[i] - value.rx[j]; - long tx = this.tx[i] - value.tx[j]; - if (enforceMonotonic && (rx < 0 || tx < 0)) { + entry.rxBytes = rxBytes[i] - value.rxBytes[j]; + entry.rxPackets = rxPackets[i] - value.rxPackets[j]; + entry.txBytes = txBytes[i] - value.txBytes[j]; + entry.txPackets = txPackets[i] - value.txPackets[j]; + if (enforceMonotonic + && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 + || entry.txPackets < 0)) { throw new IllegalArgumentException("found non-monotonic values"); } if (clampNegative) { - rx = Math.max(0, rx); - tx = Math.max(0, tx); + entry.rxBytes = Math.max(0, entry.rxBytes); + entry.rxPackets = Math.max(0, entry.rxPackets); + entry.txBytes = Math.max(0, entry.txBytes); + entry.txPackets = Math.max(0, entry.txPackets); } - result.addEntry(iface, uid, tag, rx, tx); } + + result.addValues(entry); } return result; @@ -235,13 +321,15 @@ public class NetworkStats implements Parcelable { public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); - for (int i = 0; i < iface.length; i++) { + for (int i = 0; i < size; i++) { pw.print(prefix); pw.print(" iface="); pw.print(iface[i]); pw.print(" uid="); pw.print(uid[i]); pw.print(" tag="); pw.print(tag[i]); - pw.print(" rx="); pw.print(rx[i]); - pw.print(" tx="); pw.println(tx[i]); + pw.print(" rxBytes="); pw.print(rxBytes[i]); + pw.print(" rxPackets="); pw.print(rxPackets[i]); + pw.print(" txBytes="); pw.print(txBytes[i]); + pw.print(" txPackets="); pw.println(txPackets[i]); } } @@ -264,8 +352,10 @@ public class NetworkStats implements Parcelable { dest.writeStringArray(iface); dest.writeIntArray(uid); dest.writeIntArray(tag); - dest.writeLongArray(rx); - dest.writeLongArray(tx); + dest.writeLongArray(rxBytes); + dest.writeLongArray(rxPackets); + dest.writeLongArray(txBytes); + dest.writeLongArray(txPackets); } public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index dd2945c..8bd1738 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -43,13 +43,20 @@ public class NetworkStatsHistory implements Parcelable { private static final int VERSION_INIT = 1; // TODO: teach about varint encoding to use less disk space - - public final long bucketDuration; - - public int bucketCount; - public long[] bucketStart; - public long[] rx; - public long[] tx; + // TODO: extend to record rxPackets/txPackets + + private final long bucketDuration; + private int bucketCount; + private long[] bucketStart; + private long[] rxBytes; + private long[] txBytes; + + public static class Entry { + public long bucketStart; + public long bucketDuration; + public long rxBytes; + public long txBytes; + } public NetworkStatsHistory(long bucketDuration) { this(bucketDuration, 10); @@ -58,16 +65,16 @@ public class NetworkStatsHistory implements Parcelable { public NetworkStatsHistory(long bucketDuration, int initialSize) { this.bucketDuration = bucketDuration; bucketStart = new long[initialSize]; - rx = new long[initialSize]; - tx = new long[initialSize]; + rxBytes = new long[initialSize]; + txBytes = new long[initialSize]; bucketCount = 0; } public NetworkStatsHistory(Parcel in) { bucketDuration = in.readLong(); bucketStart = readLongArray(in); - rx = in.createLongArray(); - tx = in.createLongArray(); + rxBytes = in.createLongArray(); + txBytes = in.createLongArray(); bucketCount = bucketStart.length; } @@ -75,8 +82,8 @@ public class NetworkStatsHistory implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); - writeLongArray(out, rx, bucketCount); - writeLongArray(out, tx, bucketCount); + writeLongArray(out, rxBytes, bucketCount); + writeLongArray(out, txBytes, bucketCount); } public NetworkStatsHistory(DataInputStream in) throws IOException { @@ -85,8 +92,8 @@ public class NetworkStatsHistory implements Parcelable { case VERSION_INIT: { bucketDuration = in.readLong(); bucketStart = readLongArray(in); - rx = readLongArray(in); - tx = readLongArray(in); + rxBytes = readLongArray(in); + txBytes = readLongArray(in); bucketCount = bucketStart.length; break; } @@ -100,8 +107,8 @@ public class NetworkStatsHistory implements Parcelable { out.writeInt(VERSION_INIT); out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); - writeLongArray(out, rx, bucketCount); - writeLongArray(out, tx, bucketCount); + writeLongArray(out, rxBytes, bucketCount); + writeLongArray(out, txBytes, bucketCount); } /** {@inheritDoc} */ @@ -109,6 +116,42 @@ public class NetworkStatsHistory implements Parcelable { return 0; } + public int size() { + return bucketCount; + } + + public long getBucketDuration() { + return bucketDuration; + } + + public long getStart() { + if (bucketCount > 0) { + return bucketStart[0]; + } else { + return Long.MAX_VALUE; + } + } + + public long getEnd() { + if (bucketCount > 0) { + return bucketStart[bucketCount - 1] + bucketDuration; + } else { + return Long.MIN_VALUE; + } + } + + /** + * Return specific stats entry. + */ + public Entry getValues(int i, Entry recycle) { + final Entry entry = recycle != null ? recycle : new Entry(); + entry.bucketStart = bucketStart[i]; + entry.bucketDuration = bucketDuration; + entry.rxBytes = rxBytes[i]; + entry.txBytes = txBytes[i]; + return entry; + } + /** * Record that data traffic occurred in the given time range. Will * distribute across internal buckets, creating new buckets as needed. @@ -135,8 +178,8 @@ public class NetworkStatsHistory implements Parcelable { final long overlap = Math.min(curEnd, end) - Math.max(curStart, start); if (overlap > 0) { - this.rx[i] += rx * overlap / duration; - this.tx[i] += tx * overlap / duration; + this.rxBytes[i] += rx * overlap / duration; + this.txBytes[i] += tx * overlap / duration; } } } @@ -149,7 +192,7 @@ public class NetworkStatsHistory implements Parcelable { for (int i = 0; i < input.bucketCount; i++) { final long start = input.bucketStart[i]; final long end = start + input.bucketDuration; - recordData(start, end, input.rx[i], input.tx[i]); + recordData(start, end, input.rxBytes[i], input.txBytes[i]); } } @@ -179,8 +222,8 @@ public class NetworkStatsHistory implements Parcelable { if (bucketCount >= bucketStart.length) { final int newLength = Math.max(bucketStart.length, 10) * 3 / 2; bucketStart = Arrays.copyOf(bucketStart, newLength); - rx = Arrays.copyOf(rx, newLength); - tx = Arrays.copyOf(tx, newLength); + rxBytes = Arrays.copyOf(rxBytes, newLength); + txBytes = Arrays.copyOf(txBytes, newLength); } // create gap when inserting bucket in middle @@ -189,13 +232,13 @@ public class NetworkStatsHistory implements Parcelable { final int length = bucketCount - index; System.arraycopy(bucketStart, index, bucketStart, dstPos, length); - System.arraycopy(rx, index, rx, dstPos, length); - System.arraycopy(tx, index, tx, dstPos, length); + System.arraycopy(rxBytes, index, rxBytes, dstPos, length); + System.arraycopy(txBytes, index, txBytes, dstPos, length); } bucketStart[index] = start; - rx[index] = 0; - tx[index] = 0; + rxBytes[index] = 0; + txBytes[index] = 0; bucketCount++; } @@ -216,8 +259,8 @@ public class NetworkStatsHistory implements Parcelable { if (i > 0) { final int length = bucketStart.length; bucketStart = Arrays.copyOfRange(bucketStart, i, length); - rx = Arrays.copyOfRange(rx, i, length); - tx = Arrays.copyOfRange(tx, i, length); + rxBytes = Arrays.copyOfRange(rxBytes, i, length); + txBytes = Arrays.copyOfRange(txBytes, i, length); bucketCount -= i; } } @@ -226,9 +269,20 @@ public class NetworkStatsHistory implements Parcelable { * Return interpolated data usage across the requested range. Interpolates * across buckets, so values may be rounded slightly. */ - public long[] getTotalData(long start, long end, long[] outTotal) { - long rx = 0; - long tx = 0; + public Entry getValues(long start, long end, Entry recycle) { + return getValues(start, end, Long.MAX_VALUE, recycle); + } + + /** + * Return interpolated data usage across the requested range. Interpolates + * across buckets, so values may be rounded slightly. + */ + public Entry getValues(long start, long end, long now, Entry recycle) { + final Entry entry = recycle != null ? recycle : new Entry(); + entry.bucketStart = start; + entry.bucketDuration = end - start; + entry.rxBytes = 0; + entry.txBytes = 0; for (int i = bucketCount - 1; i >= 0; i--) { final long curStart = bucketStart[i]; @@ -239,19 +293,19 @@ public class NetworkStatsHistory implements Parcelable { // bucket is newer than record; keep looking if (curStart > end) continue; + // include full value for active buckets, otherwise only fractional + final boolean activeBucket = curStart < now && curEnd > now; final long overlap = Math.min(curEnd, end) - Math.max(curStart, start); - if (overlap > 0) { - rx += this.rx[i] * overlap / bucketDuration; - tx += this.tx[i] * overlap / bucketDuration; + if (activeBucket || overlap == bucketDuration) { + entry.rxBytes += rxBytes[i]; + entry.txBytes += txBytes[i]; + } else if (overlap > 0) { + entry.rxBytes += rxBytes[i] * overlap / bucketDuration; + entry.txBytes += txBytes[i] * overlap / bucketDuration; } } - if (outTotal == null || outTotal.length != 2) { - outTotal = new long[2]; - } - outTotal[0] = rx; - outTotal[1] = tx; - return outTotal; + return entry; } /** @@ -292,8 +346,8 @@ public class NetworkStatsHistory implements Parcelable { for (int i = start; i < bucketCount; i++) { pw.print(prefix); pw.print(" bucketStart="); pw.print(bucketStart[i]); - pw.print(" rx="); pw.print(rx[i]); - pw.print(" tx="); pw.println(tx[i]); + pw.print(" rxBytes="); pw.print(rxBytes[i]); + pw.print(" txBytes="); pw.println(txBytes[i]); } } diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 8a678d6..76534ef 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -38,8 +38,22 @@ public class NetworkUtils { /** Bring the named network interface down. */ public native static int disableInterface(String interfaceName); - /** Reset any sockets that are connected via the named interface. */ - public native static int resetConnections(String interfaceName); + /** Setting bit 0 indicates reseting of IPv4 addresses required */ + public static final int RESET_IPV4_ADDRESSES = 0x01; + + /** Setting bit 1 indicates reseting of IPv4 addresses required */ + public static final int RESET_IPV6_ADDRESSES = 0x02; + + /** Reset all addresses */ + public static final int RESET_ALL_ADDRESSES = RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES; + + /** + * Reset IPv6 or IPv4 sockets that are connected via the named interface. + * + * @param interfaceName is the interface to reset + * @param mask {@see #RESET_IPV4_ADDRESSES} and {@see #RESET_IPV6_ADDRESSES} + */ + public native static int resetConnections(String interfaceName, int mask); /** * Start the DHCP client daemon, in order to have it request addresses diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java index 641a576..c534e58 100644 --- a/core/java/android/net/http/AndroidHttpClient.java +++ b/core/java/android/net/http/AndroidHttpClient.java @@ -65,7 +65,7 @@ import android.util.Base64; import android.util.Log; /** - * Subclass of the Apache {@link DefaultHttpClient} that is configured with + * Implementation of the Apache {@link DefaultHttpClient} that is configured with * reasonable default settings and registered schemes for Android, and * also lets the user add {@link HttpRequestInterceptor} classes. * Don't create this directly, use the {@link #newInstance} factory method. diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java index 0eb8cd8..b668f30 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/core/java/android/nfc/NdefRecord.java @@ -334,8 +334,6 @@ public final class NdefRecord implements Parcelable { /** * Creates an NDEF record of well known type URI. - * TODO: Make a public API - * @hide */ public static NdefRecord createUri(Uri uri) { return createUri(uri.toString()); @@ -343,8 +341,6 @@ public final class NdefRecord implements Parcelable { /** * Creates an NDEF record of well known type URI. - * TODO: Make a public API - * @hide */ public static NdefRecord createUri(String uriString) { byte prefix = 0x0; diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index 3edd692..c0be664 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -86,9 +86,7 @@ public class Looper { public static final void prepareMainLooper() { prepare(); setMainLooper(myLooper()); - if (Process.supportsProcesses()) { - myLooper().mQueue.mQuitAllowed = false; - } + myLooper().mQueue.mQuitAllowed = false; } private synchronized static void setMainLooper(Looper looper) { diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 673b187..3362575 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -86,6 +86,12 @@ public class Process { public static final int WIFI_UID = 1010; /** + * Defines the UID/GID for the mediaserver process. + * @hide + */ + public static final int MEDIA_UID = 1013; + + /** * Defines the GID for the group that allows write access to the SD card. * @hide */ @@ -266,84 +272,29 @@ public class Process { * @param uid The user-id under which the process will run. * @param gid The group-id under which the process will run. * @param gids Additional group-ids associated with the process. - * @param enableDebugger True if debugging should be enabled for this process. + * @param debugFlags Additional flags. + * @param targetSdkVersion The target SDK version for the app. * @param zygoteArgs Additional arguments to supply to the zygote process. * - * @return int If > 0 the pid of the new process; if 0 the process is - * being emulated by a thread + * @return An object that describes the result of the attempt to start the process. * @throws RuntimeException on fatal start failure * * {@hide} */ - public static final int start(final String processClass, + public static final ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, - int debugFlags, - String[] zygoteArgs) - { - if (supportsProcesses()) { - try { - return startViaZygote(processClass, niceName, uid, gid, gids, - debugFlags, zygoteArgs); - } catch (ZygoteStartFailedEx ex) { - Log.e(LOG_TAG, - "Starting VM process through Zygote failed"); - throw new RuntimeException( - "Starting VM process through Zygote failed", ex); - } - } else { - // Running in single-process mode - - Runnable runnable = new Runnable() { - public void run() { - Process.invokeStaticMain(processClass); - } - }; - - // Thread constructors must not be called with null names (see spec). - if (niceName != null) { - new Thread(runnable, niceName).start(); - } else { - new Thread(runnable).start(); - } - - return 0; - } - } - - /** - * Start a new process. Don't supply a custom nice name. - * {@hide} - */ - public static final int start(String processClass, int uid, int gid, - int[] gids, int debugFlags, String[] zygoteArgs) { - return start(processClass, "", uid, gid, gids, - debugFlags, zygoteArgs); - } - - private static void invokeStaticMain(String className) { - Class cl; - Object args[] = new Object[1]; - - args[0] = new String[0]; //this is argv - + int debugFlags, int targetSdkVersion, + String[] zygoteArgs) { try { - cl = Class.forName(className); - cl.getMethod("main", new Class[] { String[].class }) - .invoke(null, args); - } catch (Exception ex) { - // can be: ClassNotFoundException, - // NoSuchMethodException, SecurityException, - // IllegalAccessException, IllegalArgumentException - // InvocationTargetException - // or uncaught exception from main() - - Log.e(LOG_TAG, "Exception invoking static main on " - + className, ex); - - throw new RuntimeException(ex); + return startViaZygote(processClass, niceName, uid, gid, gids, + debugFlags, targetSdkVersion, zygoteArgs); + } catch (ZygoteStartFailedEx ex) { + Log.e(LOG_TAG, + "Starting VM process through Zygote failed"); + throw new RuntimeException( + "Starting VM process through Zygote failed", ex); } - } /** retry interval for opening a zygote socket */ @@ -430,14 +381,11 @@ public class Process { * and returns the child's pid. Please note: the present implementation * replaces newlines in the argument list with spaces. * @param args argument list - * @return PID of new child process + * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason */ - private static int zygoteSendArgsAndGetPid(ArrayList<String> args) + private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args) throws ZygoteStartFailedEx { - - int pid; - openZygoteSocketIfNeeded(); try { @@ -448,7 +396,8 @@ public class Process { * b) a number of newline-separated argument strings equal to count * * After the zygote process reads these it will write the pid of - * the child or -1 on failure. + * the child or -1 on failure, followed by boolean to + * indicate whether a wrapper process was used. */ sZygoteWriter.write(Integer.toString(args.size())); @@ -468,11 +417,13 @@ public class Process { sZygoteWriter.flush(); // Should there be a timeout on this? - pid = sZygoteInputStream.readInt(); - - if (pid < 0) { + ProcessStartResult result = new ProcessStartResult(); + result.pid = sZygoteInputStream.readInt(); + if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } + result.usingWrapper = sZygoteInputStream.readBoolean(); + return result; } catch (IOException ex) { try { if (sZygoteSocket != null) { @@ -487,8 +438,6 @@ public class Process { throw new ZygoteStartFailedEx(ex); } - - return pid; } /** @@ -500,20 +449,19 @@ public class Process { * @param gid a POSIX gid that the new process shuold setgid() to * @param gids null-ok; a list of supplementary group IDs that the * new process should setgroup() to. - * @param enableDebugger True if debugging should be enabled for this process. + * @param debugFlags Additional flags. + * @param targetSdkVersion The target SDK version for the app. * @param extraArgs Additional arguments to supply to the zygote process. - * @return PID + * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason */ - private static int startViaZygote(final String processClass, + private static ProcessStartResult startViaZygote(final String processClass, final String niceName, final int uid, final int gid, final int[] gids, - int debugFlags, + int debugFlags, int targetSdkVersion, String[] extraArgs) throws ZygoteStartFailedEx { - int pid; - synchronized(Process.class) { ArrayList<String> argsForZygote = new ArrayList<String>(); @@ -537,6 +485,7 @@ public class Process { if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) { argsForZygote.add("--enable-assert"); } + argsForZygote.add("--target-sdk-version=" + targetSdkVersion); //TODO optionally enable debuger //argsForZygote.add("--enable-debugger"); @@ -568,15 +517,9 @@ public class Process { argsForZygote.add(arg); } } - - pid = zygoteSendArgsAndGetPid(argsForZygote); - } - if (pid <= 0) { - throw new ZygoteStartFailedEx("zygote start failed:" + pid); + return zygoteSendArgsAndGetResult(argsForZygote); } - - return pid; } /** @@ -736,8 +679,13 @@ public class Process { * * @return Returns true if the system can run in multiple processes, else * false if everything is running in a single process. + * + * @deprecated This method always returns true. Do not use. */ - public static final native boolean supportsProcesses(); + @Deprecated + public static final boolean supportsProcesses() { + return true; + } /** * Set the out-of-memory badness adjustment for a process. @@ -855,4 +803,21 @@ public class Process { * @hide */ public static final native long getPss(int pid); + + /** + * Specifies the outcome of having started a process. + * @hide + */ + public static final class ProcessStartResult { + /** + * The PID of the newly started process. + * Always >= 0. (If the start failed, an exception will have been thrown instead.) + */ + public int pid; + + /** + * True if the process was started with a wrapper attached. + */ + public boolean usingWrapper; + } } diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java index b721665..1af24f4 100644 --- a/core/java/android/os/ServiceManager.java +++ b/core/java/android/os/ServiceManager.java @@ -114,7 +114,7 @@ public final class ServiceManager { * @hide */ public static void initServiceCache(Map<String, IBinder> cache) { - if (sCache.size() != 0 && Process.supportsProcesses()) { + if (sCache.size() != 0) { throw new IllegalStateException("setServiceCache may only be called once"); } sCache.putAll(cache); diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 792e4c1..2c4b863 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -34,6 +34,8 @@ public class StorageVolume implements Parcelable { private final int mMtpReserveSpace; private final boolean mAllowMassStorage; private int mStorageId; + // maximum file size for the storage, or zero for no limit + private final long mMaxFileSize; // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING, // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED, @@ -41,18 +43,20 @@ public class StorageVolume implements Parcelable { public static final String EXTRA_STORAGE_VOLUME = "storage_volume"; public StorageVolume(String path, String description, boolean removable, - boolean emulated, int mtpReserveSpace, boolean allowMassStorage) { + boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize) { mPath = path; mDescription = description; mRemovable = removable; mEmulated = emulated; mMtpReserveSpace = mtpReserveSpace; mAllowMassStorage = allowMassStorage; + mMaxFileSize = maxFileSize; } // for parcelling only private StorageVolume(String path, String description, boolean removable, - boolean emulated, int mtpReserveSpace, int storageId, boolean allowMassStorage) { + boolean emulated, int mtpReserveSpace, int storageId, + boolean allowMassStorage, long maxFileSize) { mPath = path; mDescription = description; mRemovable = removable; @@ -60,6 +64,7 @@ public class StorageVolume implements Parcelable { mMtpReserveSpace = mtpReserveSpace; mAllowMassStorage = allowMassStorage; mStorageId = storageId; + mMaxFileSize = maxFileSize; } /** @@ -142,6 +147,15 @@ public class StorageVolume implements Parcelable { return mAllowMassStorage; } + /** + * Returns maximum file size for the volume, or zero if it is unbounded. + * + * @return maximum file size + */ + public long getMaxFileSize() { + return mMaxFileSize; + } + @Override public boolean equals(Object obj) { if (obj instanceof StorageVolume && mPath != null) { @@ -158,7 +172,10 @@ public class StorageVolume implements Parcelable { @Override public String toString() { - return mPath; + return "StorageVolume [mAllowMassStorage=" + mAllowMassStorage + ", mDescription=" + + mDescription + ", mEmulated=" + mEmulated + ", mMaxFileSize=" + mMaxFileSize + + ", mMtpReserveSpace=" + mMtpReserveSpace + ", mPath=" + mPath + ", mRemovable=" + + mRemovable + ", mStorageId=" + mStorageId + "]"; } public static final Parcelable.Creator<StorageVolume> CREATOR = @@ -171,9 +188,10 @@ public class StorageVolume implements Parcelable { int storageId = in.readInt(); int mtpReserveSpace = in.readInt(); int allowMassStorage = in.readInt(); + long maxFileSize = in.readLong(); return new StorageVolume(path, description, - removable == 1, emulated == 1, - mtpReserveSpace, storageId, allowMassStorage == 1); + removable == 1, emulated == 1, mtpReserveSpace, + storageId, allowMassStorage == 1, maxFileSize); } public StorageVolume[] newArray(int size) { @@ -193,5 +211,6 @@ public class StorageVolume implements Parcelable { parcel.writeInt(mStorageId); parcel.writeInt(mMtpReserveSpace); parcel.writeInt(mAllowMassStorage ? 1 : 0); + parcel.writeLong(mMaxFileSize); } } diff --git a/core/java/android/pim/EventRecurrence.java b/core/java/android/pim/EventRecurrence.java deleted file mode 100644 index 128b697..0000000 --- a/core/java/android/pim/EventRecurrence.java +++ /dev/null @@ -1,892 +0,0 @@ -/* - * Copyright (C) 2006 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.pim; - -import android.text.TextUtils; -import android.text.format.Time; -import android.util.Log; -import android.util.TimeFormatException; - -import java.util.Calendar; -import java.util.HashMap; - -/** - * Event recurrence utility functions. - */ -public class EventRecurrence { - private static String TAG = "EventRecur"; - - public static final int SECONDLY = 1; - public static final int MINUTELY = 2; - public static final int HOURLY = 3; - public static final int DAILY = 4; - public static final int WEEKLY = 5; - public static final int MONTHLY = 6; - public static final int YEARLY = 7; - - public static final int SU = 0x00010000; - public static final int MO = 0x00020000; - public static final int TU = 0x00040000; - public static final int WE = 0x00080000; - public static final int TH = 0x00100000; - public static final int FR = 0x00200000; - public static final int SA = 0x00400000; - - public Time startDate; // set by setStartDate(), not parse() - - public int freq; // SECONDLY, MINUTELY, etc. - public String until; - public int count; - public int interval; - public int wkst; // SU, MO, TU, etc. - - /* lists with zero entries may be null references */ - public int[] bysecond; - public int bysecondCount; - public int[] byminute; - public int byminuteCount; - public int[] byhour; - public int byhourCount; - public int[] byday; - public int[] bydayNum; - public int bydayCount; - public int[] bymonthday; - public int bymonthdayCount; - public int[] byyearday; - public int byyeardayCount; - public int[] byweekno; - public int byweeknoCount; - public int[] bymonth; - public int bymonthCount; - public int[] bysetpos; - public int bysetposCount; - - /** maps a part string to a parser object */ - private static HashMap<String,PartParser> sParsePartMap; - static { - sParsePartMap = new HashMap<String,PartParser>(); - sParsePartMap.put("FREQ", new ParseFreq()); - sParsePartMap.put("UNTIL", new ParseUntil()); - sParsePartMap.put("COUNT", new ParseCount()); - sParsePartMap.put("INTERVAL", new ParseInterval()); - sParsePartMap.put("BYSECOND", new ParseBySecond()); - sParsePartMap.put("BYMINUTE", new ParseByMinute()); - sParsePartMap.put("BYHOUR", new ParseByHour()); - sParsePartMap.put("BYDAY", new ParseByDay()); - sParsePartMap.put("BYMONTHDAY", new ParseByMonthDay()); - sParsePartMap.put("BYYEARDAY", new ParseByYearDay()); - sParsePartMap.put("BYWEEKNO", new ParseByWeekNo()); - sParsePartMap.put("BYMONTH", new ParseByMonth()); - sParsePartMap.put("BYSETPOS", new ParseBySetPos()); - sParsePartMap.put("WKST", new ParseWkst()); - } - - /* values for bit vector that keeps track of what we have already seen */ - private static final int PARSED_FREQ = 1 << 0; - private static final int PARSED_UNTIL = 1 << 1; - private static final int PARSED_COUNT = 1 << 2; - private static final int PARSED_INTERVAL = 1 << 3; - private static final int PARSED_BYSECOND = 1 << 4; - private static final int PARSED_BYMINUTE = 1 << 5; - private static final int PARSED_BYHOUR = 1 << 6; - private static final int PARSED_BYDAY = 1 << 7; - private static final int PARSED_BYMONTHDAY = 1 << 8; - private static final int PARSED_BYYEARDAY = 1 << 9; - private static final int PARSED_BYWEEKNO = 1 << 10; - private static final int PARSED_BYMONTH = 1 << 11; - private static final int PARSED_BYSETPOS = 1 << 12; - private static final int PARSED_WKST = 1 << 13; - - /** maps a FREQ value to an integer constant */ - private static final HashMap<String,Integer> sParseFreqMap = new HashMap<String,Integer>(); - static { - sParseFreqMap.put("SECONDLY", SECONDLY); - sParseFreqMap.put("MINUTELY", MINUTELY); - sParseFreqMap.put("HOURLY", HOURLY); - sParseFreqMap.put("DAILY", DAILY); - sParseFreqMap.put("WEEKLY", WEEKLY); - sParseFreqMap.put("MONTHLY", MONTHLY); - sParseFreqMap.put("YEARLY", YEARLY); - } - - /** maps a two-character weekday string to an integer constant */ - private static final HashMap<String,Integer> sParseWeekdayMap = new HashMap<String,Integer>(); - static { - sParseWeekdayMap.put("SU", SU); - sParseWeekdayMap.put("MO", MO); - sParseWeekdayMap.put("TU", TU); - sParseWeekdayMap.put("WE", WE); - sParseWeekdayMap.put("TH", TH); - sParseWeekdayMap.put("FR", FR); - sParseWeekdayMap.put("SA", SA); - } - - /** If set, allow lower-case recurrence rule strings. Minor performance impact. */ - private static final boolean ALLOW_LOWER_CASE = false; - - /** If set, validate the value of UNTIL parts. Minor performance impact. */ - private static final boolean VALIDATE_UNTIL = false; - - /** If set, require that only one of {UNTIL,COUNT} is present. Breaks compat w/ old parser. */ - private static final boolean ONLY_ONE_UNTIL_COUNT = false; - - - /** - * Thrown when a recurrence string provided can not be parsed according - * to RFC2445. - */ - public static class InvalidFormatException extends RuntimeException { - InvalidFormatException(String s) { - super(s); - } - } - - - public void setStartDate(Time date) { - startDate = date; - } - - /** - * Converts one of the Calendar.SUNDAY constants to the SU, MO, etc. - * constants. btw, I think we should switch to those here too, to - * get rid of this function, if possible. - */ - public static int calendarDay2Day(int day) - { - switch (day) - { - case Calendar.SUNDAY: - return SU; - case Calendar.MONDAY: - return MO; - case Calendar.TUESDAY: - return TU; - case Calendar.WEDNESDAY: - return WE; - case Calendar.THURSDAY: - return TH; - case Calendar.FRIDAY: - return FR; - case Calendar.SATURDAY: - return SA; - default: - throw new RuntimeException("bad day of week: " + day); - } - } - - public static int timeDay2Day(int day) - { - switch (day) - { - case Time.SUNDAY: - return SU; - case Time.MONDAY: - return MO; - case Time.TUESDAY: - return TU; - case Time.WEDNESDAY: - return WE; - case Time.THURSDAY: - return TH; - case Time.FRIDAY: - return FR; - case Time.SATURDAY: - return SA; - default: - throw new RuntimeException("bad day of week: " + day); - } - } - public static int day2TimeDay(int day) - { - switch (day) - { - case SU: - return Time.SUNDAY; - case MO: - return Time.MONDAY; - case TU: - return Time.TUESDAY; - case WE: - return Time.WEDNESDAY; - case TH: - return Time.THURSDAY; - case FR: - return Time.FRIDAY; - case SA: - return Time.SATURDAY; - default: - throw new RuntimeException("bad day of week: " + day); - } - } - - /** - * Converts one of the SU, MO, etc. constants to the Calendar.SUNDAY - * constants. btw, I think we should switch to those here too, to - * get rid of this function, if possible. - */ - public static int day2CalendarDay(int day) - { - switch (day) - { - case SU: - return Calendar.SUNDAY; - case MO: - return Calendar.MONDAY; - case TU: - return Calendar.TUESDAY; - case WE: - return Calendar.WEDNESDAY; - case TH: - return Calendar.THURSDAY; - case FR: - return Calendar.FRIDAY; - case SA: - return Calendar.SATURDAY; - default: - throw new RuntimeException("bad day of week: " + day); - } - } - - /** - * Converts one of the internal day constants (SU, MO, etc.) to the - * two-letter string representing that constant. - * - * @param day one the internal constants SU, MO, etc. - * @return the two-letter string for the day ("SU", "MO", etc.) - * - * @throws IllegalArgumentException Thrown if the day argument is not one of - * the defined day constants. - */ - private static String day2String(int day) { - switch (day) { - case SU: - return "SU"; - case MO: - return "MO"; - case TU: - return "TU"; - case WE: - return "WE"; - case TH: - return "TH"; - case FR: - return "FR"; - case SA: - return "SA"; - default: - throw new IllegalArgumentException("bad day argument: " + day); - } - } - - private static void appendNumbers(StringBuilder s, String label, - int count, int[] values) - { - if (count > 0) { - s.append(label); - count--; - for (int i=0; i<count; i++) { - s.append(values[i]); - s.append(","); - } - s.append(values[count]); - } - } - - private void appendByDay(StringBuilder s, int i) - { - int n = this.bydayNum[i]; - if (n != 0) { - s.append(n); - } - - String str = day2String(this.byday[i]); - s.append(str); - } - - @Override - public String toString() - { - StringBuilder s = new StringBuilder(); - - s.append("FREQ="); - switch (this.freq) - { - case SECONDLY: - s.append("SECONDLY"); - break; - case MINUTELY: - s.append("MINUTELY"); - break; - case HOURLY: - s.append("HOURLY"); - break; - case DAILY: - s.append("DAILY"); - break; - case WEEKLY: - s.append("WEEKLY"); - break; - case MONTHLY: - s.append("MONTHLY"); - break; - case YEARLY: - s.append("YEARLY"); - break; - } - - if (!TextUtils.isEmpty(this.until)) { - s.append(";UNTIL="); - s.append(until); - } - - if (this.count != 0) { - s.append(";COUNT="); - s.append(this.count); - } - - if (this.interval != 0) { - s.append(";INTERVAL="); - s.append(this.interval); - } - - if (this.wkst != 0) { - s.append(";WKST="); - s.append(day2String(this.wkst)); - } - - appendNumbers(s, ";BYSECOND=", this.bysecondCount, this.bysecond); - appendNumbers(s, ";BYMINUTE=", this.byminuteCount, this.byminute); - appendNumbers(s, ";BYSECOND=", this.byhourCount, this.byhour); - - // day - int count = this.bydayCount; - if (count > 0) { - s.append(";BYDAY="); - count--; - for (int i=0; i<count; i++) { - appendByDay(s, i); - s.append(","); - } - appendByDay(s, count); - } - - appendNumbers(s, ";BYMONTHDAY=", this.bymonthdayCount, this.bymonthday); - appendNumbers(s, ";BYYEARDAY=", this.byyeardayCount, this.byyearday); - appendNumbers(s, ";BYWEEKNO=", this.byweeknoCount, this.byweekno); - appendNumbers(s, ";BYMONTH=", this.bymonthCount, this.bymonth); - appendNumbers(s, ";BYSETPOS=", this.bysetposCount, this.bysetpos); - - return s.toString(); - } - - public boolean repeatsOnEveryWeekDay() { - if (this.freq != WEEKLY) { - return false; - } - - int count = this.bydayCount; - if (count != 5) { - return false; - } - - for (int i = 0 ; i < count ; i++) { - int day = byday[i]; - if (day == SU || day == SA) { - return false; - } - } - - return true; - } - - /** - * Determines whether this rule specifies a simple monthly rule by weekday, such as - * "FREQ=MONTHLY;BYDAY=3TU" (the 3rd Tuesday of every month). - * <p> - * Negative days, e.g. "FREQ=MONTHLY;BYDAY=-1TU" (the last Tuesday of every month), - * will cause "false" to be returned. - * <p> - * Rules that fire every week, such as "FREQ=MONTHLY;BYDAY=TU" (every Tuesday of every - * month) will cause "false" to be returned. (Note these are usually expressed as - * WEEKLY rules, and hence are uncommon.) - * - * @return true if this rule is of the appropriate form - */ - public boolean repeatsMonthlyOnDayCount() { - if (this.freq != MONTHLY) { - return false; - } - - if (bydayCount != 1 || bymonthdayCount != 0) { - return false; - } - - if (bydayNum[0] <= 0) { - return false; - } - - return true; - } - - /** - * Determines whether two integer arrays contain identical elements. - * <p> - * The native implementation over-allocated the arrays (and may have stuff left over from - * a previous run), so we can't just check the arrays -- the separately-maintained count - * field also matters. We assume that a null array will have a count of zero, and that the - * array can hold as many elements as the associated count indicates. - * <p> - * TODO: replace this with Arrays.equals() when the old parser goes away. - */ - private static boolean arraysEqual(int[] array1, int count1, int[] array2, int count2) { - if (count1 != count2) { - return false; - } - - for (int i = 0; i < count1; i++) { - if (array1[i] != array2[i]) - return false; - } - - return true; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof EventRecurrence)) { - return false; - } - - EventRecurrence er = (EventRecurrence) obj; - return (startDate == null ? - er.startDate == null : Time.compare(startDate, er.startDate) == 0) && - freq == er.freq && - (until == null ? er.until == null : until.equals(er.until)) && - count == er.count && - interval == er.interval && - wkst == er.wkst && - arraysEqual(bysecond, bysecondCount, er.bysecond, er.bysecondCount) && - arraysEqual(byminute, byminuteCount, er.byminute, er.byminuteCount) && - arraysEqual(byhour, byhourCount, er.byhour, er.byhourCount) && - arraysEqual(byday, bydayCount, er.byday, er.bydayCount) && - arraysEqual(bydayNum, bydayCount, er.bydayNum, er.bydayCount) && - arraysEqual(bymonthday, bymonthdayCount, er.bymonthday, er.bymonthdayCount) && - arraysEqual(byyearday, byyeardayCount, er.byyearday, er.byyeardayCount) && - arraysEqual(byweekno, byweeknoCount, er.byweekno, er.byweeknoCount) && - arraysEqual(bymonth, bymonthCount, er.bymonth, er.bymonthCount) && - arraysEqual(bysetpos, bysetposCount, er.bysetpos, er.bysetposCount); - } - - @Override public int hashCode() { - // We overrode equals, so we must override hashCode(). Nobody seems to need this though. - throw new UnsupportedOperationException(); - } - - /** - * Resets parser-modified fields to their initial state. Does not alter startDate. - * <p> - * The original parser always set all of the "count" fields, "wkst", and "until", - * essentially allowing the same object to be used multiple times by calling parse(). - * It's unclear whether this behavior was intentional. For now, be paranoid and - * preserve the existing behavior by resetting the fields. - * <p> - * We don't need to touch the integer arrays; they will either be ignored or - * overwritten. The "startDate" field is not set by the parser, so we ignore it here. - */ - private void resetFields() { - until = null; - freq = count = interval = bysecondCount = byminuteCount = byhourCount = - bydayCount = bymonthdayCount = byyeardayCount = byweeknoCount = bymonthCount = - bysetposCount = 0; - } - - /** - * Parses an rfc2445 recurrence rule string into its component pieces. Attempting to parse - * malformed input will result in an EventRecurrence.InvalidFormatException. - * - * @param recur The recurrence rule to parse (in un-folded form). - */ - public void parse(String recur) { - /* - * From RFC 2445 section 4.3.10: - * - * recur = "FREQ"=freq *( - * ; either UNTIL or COUNT may appear in a 'recur', - * ; but UNTIL and COUNT MUST NOT occur in the same 'recur' - * - * ( ";" "UNTIL" "=" enddate ) / - * ( ";" "COUNT" "=" 1*DIGIT ) / - * - * ; the rest of these keywords are optional, - * ; but MUST NOT occur more than once - * - * ( ";" "INTERVAL" "=" 1*DIGIT ) / - * ( ";" "BYSECOND" "=" byseclist ) / - * ( ";" "BYMINUTE" "=" byminlist ) / - * ( ";" "BYHOUR" "=" byhrlist ) / - * ( ";" "BYDAY" "=" bywdaylist ) / - * ( ";" "BYMONTHDAY" "=" bymodaylist ) / - * ( ";" "BYYEARDAY" "=" byyrdaylist ) / - * ( ";" "BYWEEKNO" "=" bywknolist ) / - * ( ";" "BYMONTH" "=" bymolist ) / - * ( ";" "BYSETPOS" "=" bysplist ) / - * ( ";" "WKST" "=" weekday ) / - * ( ";" x-name "=" text ) - * ) - * - * Examples: - * FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU - * FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8 - * - * Strategy: - * (1) Split the string at ';' boundaries to get an array of rule "parts". - * (2) For each part, find substrings for left/right sides of '=' (name/value). - * (3) Call a <name>-specific parsing function to parse the <value> into an - * output field. - * - * By keeping track of which names we've seen in a bit vector, we can verify the - * constraints indicated above (FREQ appears first, none of them appear more than once -- - * though x-[name] would require special treatment), and we have either UNTIL or COUNT - * but not both. - * - * In general, RFC 2445 property names (e.g. "FREQ") and enumerations ("TU") must - * be handled in a case-insensitive fashion, but case may be significant for other - * properties. We don't have any case-sensitive values in RRULE, except possibly - * for the custom "X-" properties, but we ignore those anyway. Thus, we can trivially - * convert the entire string to upper case and then use simple comparisons. - * - * Differences from previous version: - * - allows lower-case property and enumeration values [optional] - * - enforces that FREQ appears first - * - enforces that only one of UNTIL and COUNT may be specified - * - allows (but ignores) X-* parts - * - improved validation on various values (e.g. UNTIL timestamps) - * - error messages are more specific - */ - - /* TODO: replace with "if (freq != 0) throw" if nothing requires this */ - resetFields(); - - int parseFlags = 0; - String[] parts; - if (ALLOW_LOWER_CASE) { - parts = recur.toUpperCase().split(";"); - } else { - parts = recur.split(";"); - } - for (String part : parts) { - int equalIndex = part.indexOf('='); - if (equalIndex <= 0) { - /* no '=' or no LHS */ - throw new InvalidFormatException("Missing LHS in " + part); - } - - String lhs = part.substring(0, equalIndex); - String rhs = part.substring(equalIndex + 1); - if (rhs.length() == 0) { - throw new InvalidFormatException("Missing RHS in " + part); - } - - /* - * In lieu of a "switch" statement that allows string arguments, we use a - * map from strings to parsing functions. - */ - PartParser parser = sParsePartMap.get(lhs); - if (parser == null) { - if (lhs.startsWith("X-")) { - //Log.d(TAG, "Ignoring custom part " + lhs); - continue; - } - throw new InvalidFormatException("Couldn't find parser for " + lhs); - } else { - int flag = parser.parsePart(rhs, this); - if ((parseFlags & flag) != 0) { - throw new InvalidFormatException("Part " + lhs + " was specified twice"); - } - if (parseFlags == 0 && flag != PARSED_FREQ) { - throw new InvalidFormatException("FREQ must be specified first"); - } - parseFlags |= flag; - } - } - - // If not specified, week starts on Monday. - if ((parseFlags & PARSED_WKST) == 0) { - wkst = MO; - } - - // FREQ is mandatory. - if ((parseFlags & PARSED_FREQ) == 0) { - throw new InvalidFormatException("Must specify a FREQ value"); - } - - // Can't have both UNTIL and COUNT. - if ((parseFlags & (PARSED_UNTIL | PARSED_COUNT)) == (PARSED_UNTIL | PARSED_COUNT)) { - if (ONLY_ONE_UNTIL_COUNT) { - throw new InvalidFormatException("Must not specify both UNTIL and COUNT: " + recur); - } else { - Log.w(TAG, "Warning: rrule has both UNTIL and COUNT: " + recur); - } - } - } - - /** - * Base class for the RRULE part parsers. - */ - abstract static class PartParser { - /** - * Parses a single part. - * - * @param value The right-hand-side of the part. - * @param er The EventRecurrence into which the result is stored. - * @return A bit value indicating which part was parsed. - */ - public abstract int parsePart(String value, EventRecurrence er); - - /** - * Parses an integer, with range-checking. - * - * @param str The string to parse. - * @param minVal Minimum allowed value. - * @param maxVal Maximum allowed value. - * @param allowZero Is 0 allowed? - * @return The parsed value. - */ - public static int parseIntRange(String str, int minVal, int maxVal, boolean allowZero) { - try { - if (str.charAt(0) == '+') { - // Integer.parseInt does not allow a leading '+', so skip it manually. - str = str.substring(1); - } - int val = Integer.parseInt(str); - if (val < minVal || val > maxVal || (val == 0 && !allowZero)) { - throw new InvalidFormatException("Integer value out of range: " + str); - } - return val; - } catch (NumberFormatException nfe) { - throw new InvalidFormatException("Invalid integer value: " + str); - } - } - - /** - * Parses a comma-separated list of integers, with range-checking. - * - * @param listStr The string to parse. - * @param minVal Minimum allowed value. - * @param maxVal Maximum allowed value. - * @param allowZero Is 0 allowed? - * @return A new array with values, sized to hold the exact number of elements. - */ - public static int[] parseNumberList(String listStr, int minVal, int maxVal, - boolean allowZero) { - int[] values; - - if (listStr.indexOf(",") < 0) { - // Common case: only one entry, skip split() overhead. - values = new int[1]; - values[0] = parseIntRange(listStr, minVal, maxVal, allowZero); - } else { - String[] valueStrs = listStr.split(","); - int len = valueStrs.length; - values = new int[len]; - for (int i = 0; i < len; i++) { - values[i] = parseIntRange(valueStrs[i], minVal, maxVal, allowZero); - } - } - return values; - } - } - - /** parses FREQ={SECONDLY,MINUTELY,...} */ - private static class ParseFreq extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - Integer freq = sParseFreqMap.get(value); - if (freq == null) { - throw new InvalidFormatException("Invalid FREQ value: " + value); - } - er.freq = freq; - return PARSED_FREQ; - } - } - /** parses UNTIL=enddate, e.g. "19970829T021400" */ - private static class ParseUntil extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - if (VALIDATE_UNTIL) { - try { - // Parse the time to validate it. The result isn't retained. - Time until = new Time(); - until.parse(value); - } catch (TimeFormatException tfe) { - throw new InvalidFormatException("Invalid UNTIL value: " + value); - } - } - er.until = value; - return PARSED_UNTIL; - } - } - /** parses COUNT=[non-negative-integer] */ - private static class ParseCount extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - er.count = parseIntRange(value, 0, Integer.MAX_VALUE, true); - return PARSED_COUNT; - } - } - /** parses INTERVAL=[non-negative-integer] */ - private static class ParseInterval extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - er.interval = parseIntRange(value, 1, Integer.MAX_VALUE, false); - return PARSED_INTERVAL; - } - } - /** parses BYSECOND=byseclist */ - private static class ParseBySecond extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] bysecond = parseNumberList(value, 0, 59, true); - er.bysecond = bysecond; - er.bysecondCount = bysecond.length; - return PARSED_BYSECOND; - } - } - /** parses BYMINUTE=byminlist */ - private static class ParseByMinute extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byminute = parseNumberList(value, 0, 59, true); - er.byminute = byminute; - er.byminuteCount = byminute.length; - return PARSED_BYMINUTE; - } - } - /** parses BYHOUR=byhrlist */ - private static class ParseByHour extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byhour = parseNumberList(value, 0, 23, true); - er.byhour = byhour; - er.byhourCount = byhour.length; - return PARSED_BYHOUR; - } - } - /** parses BYDAY=bywdaylist, e.g. "1SU,-1SU" */ - private static class ParseByDay extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byday; - int[] bydayNum; - int bydayCount; - - if (value.indexOf(",") < 0) { - /* only one entry, skip split() overhead */ - bydayCount = 1; - byday = new int[1]; - bydayNum = new int[1]; - parseWday(value, byday, bydayNum, 0); - } else { - String[] wdays = value.split(","); - int len = wdays.length; - bydayCount = len; - byday = new int[len]; - bydayNum = new int[len]; - for (int i = 0; i < len; i++) { - parseWday(wdays[i], byday, bydayNum, i); - } - } - er.byday = byday; - er.bydayNum = bydayNum; - er.bydayCount = bydayCount; - return PARSED_BYDAY; - } - - /** parses [int]weekday, putting the pieces into parallel array entries */ - private static void parseWday(String str, int[] byday, int[] bydayNum, int index) { - int wdayStrStart = str.length() - 2; - String wdayStr; - - if (wdayStrStart > 0) { - /* number is included; parse it out and advance to weekday */ - String numPart = str.substring(0, wdayStrStart); - int num = parseIntRange(numPart, -53, 53, false); - bydayNum[index] = num; - wdayStr = str.substring(wdayStrStart); - } else { - /* just the weekday string */ - wdayStr = str; - } - Integer wday = sParseWeekdayMap.get(wdayStr); - if (wday == null) { - throw new InvalidFormatException("Invalid BYDAY value: " + str); - } - byday[index] = wday; - } - } - /** parses BYMONTHDAY=bymodaylist */ - private static class ParseByMonthDay extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] bymonthday = parseNumberList(value, -31, 31, false); - er.bymonthday = bymonthday; - er.bymonthdayCount = bymonthday.length; - return PARSED_BYMONTHDAY; - } - } - /** parses BYYEARDAY=byyrdaylist */ - private static class ParseByYearDay extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byyearday = parseNumberList(value, -366, 366, false); - er.byyearday = byyearday; - er.byyeardayCount = byyearday.length; - return PARSED_BYYEARDAY; - } - } - /** parses BYWEEKNO=bywknolist */ - private static class ParseByWeekNo extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byweekno = parseNumberList(value, -53, 53, false); - er.byweekno = byweekno; - er.byweeknoCount = byweekno.length; - return PARSED_BYWEEKNO; - } - } - /** parses BYMONTH=bymolist */ - private static class ParseByMonth extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] bymonth = parseNumberList(value, 1, 12, false); - er.bymonth = bymonth; - er.bymonthCount = bymonth.length; - return PARSED_BYMONTH; - } - } - /** parses BYSETPOS=bysplist */ - private static class ParseBySetPos extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] bysetpos = parseNumberList(value, Integer.MIN_VALUE, Integer.MAX_VALUE, true); - er.bysetpos = bysetpos; - er.bysetposCount = bysetpos.length; - return PARSED_BYSETPOS; - } - } - /** parses WKST={SU,MO,...} */ - private static class ParseWkst extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - Integer wkst = sParseWeekdayMap.get(value); - if (wkst == null) { - throw new InvalidFormatException("Invalid WKST value: " + value); - } - er.wkst = wkst; - return PARSED_WKST; - } - } -} diff --git a/core/java/android/pim/ICalendar.java b/core/java/android/pim/ICalendar.java deleted file mode 100644 index 58c5c63..0000000 --- a/core/java/android/pim/ICalendar.java +++ /dev/null @@ -1,660 +0,0 @@ -/* - * 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 android.pim; - -import android.util.Log; - -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.ArrayList; - -/** - * Parses RFC 2445 iCalendar objects. - */ -public class ICalendar { - - private static final String TAG = "Sync"; - - // TODO: keep track of VEVENT, VTODO, VJOURNAL, VFREEBUSY, VTIMEZONE, VALARM - // components, by type field or by subclass? subclass would allow us to - // enforce grammars. - - /** - * Exception thrown when an iCalendar object has invalid syntax. - */ - public static class FormatException extends Exception { - public FormatException() { - super(); - } - - public FormatException(String msg) { - super(msg); - } - - public FormatException(String msg, Throwable cause) { - super(msg, cause); - } - } - - /** - * A component within an iCalendar (VEVENT, VTODO, VJOURNAL, VFEEBUSY, - * VTIMEZONE, VALARM). - */ - public static class Component { - - // components - private static final String BEGIN = "BEGIN"; - private static final String END = "END"; - private static final String NEWLINE = "\n"; - public static final String VCALENDAR = "VCALENDAR"; - public static final String VEVENT = "VEVENT"; - public static final String VTODO = "VTODO"; - public static final String VJOURNAL = "VJOURNAL"; - public static final String VFREEBUSY = "VFREEBUSY"; - public static final String VTIMEZONE = "VTIMEZONE"; - public static final String VALARM = "VALARM"; - - private final String mName; - private final Component mParent; // see if we can get rid of this - private LinkedList<Component> mChildren = null; - private final LinkedHashMap<String, ArrayList<Property>> mPropsMap = - new LinkedHashMap<String, ArrayList<Property>>(); - - /** - * Creates a new component with the provided name. - * @param name The name of the component. - */ - public Component(String name, Component parent) { - mName = name; - mParent = parent; - } - - /** - * Returns the name of the component. - * @return The name of the component. - */ - public String getName() { - return mName; - } - - /** - * Returns the parent of this component. - * @return The parent of this component. - */ - public Component getParent() { - return mParent; - } - - /** - * Helper that lazily gets/creates the list of children. - * @return The list of children. - */ - protected LinkedList<Component> getOrCreateChildren() { - if (mChildren == null) { - mChildren = new LinkedList<Component>(); - } - return mChildren; - } - - /** - * Adds a child component to this component. - * @param child The child component. - */ - public void addChild(Component child) { - getOrCreateChildren().add(child); - } - - /** - * Returns a list of the Component children of this component. May be - * null, if there are no children. - * - * @return A list of the children. - */ - public List<Component> getComponents() { - return mChildren; - } - - /** - * Adds a Property to this component. - * @param prop - */ - public void addProperty(Property prop) { - String name= prop.getName(); - ArrayList<Property> props = mPropsMap.get(name); - if (props == null) { - props = new ArrayList<Property>(); - mPropsMap.put(name, props); - } - props.add(prop); - } - - /** - * Returns a set of the property names within this component. - * @return A set of property names within this component. - */ - public Set<String> getPropertyNames() { - return mPropsMap.keySet(); - } - - /** - * Returns a list of properties with the specified name. Returns null - * if there are no such properties. - * @param name The name of the property that should be returned. - * @return A list of properties with the requested name. - */ - public List<Property> getProperties(String name) { - return mPropsMap.get(name); - } - - /** - * Returns the first property with the specified name. Returns null - * if there is no such property. - * @param name The name of the property that should be returned. - * @return The first property with the specified name. - */ - public Property getFirstProperty(String name) { - List<Property> props = mPropsMap.get(name); - if (props == null || props.size() == 0) { - return null; - } - return props.get(0); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - sb.append(NEWLINE); - return sb.toString(); - } - - /** - * Helper method that appends this component to a StringBuilder. The - * caller is responsible for appending a newline at the end of the - * component. - */ - public void toString(StringBuilder sb) { - sb.append(BEGIN); - sb.append(":"); - sb.append(mName); - sb.append(NEWLINE); - - // append the properties - for (String propertyName : getPropertyNames()) { - for (Property property : getProperties(propertyName)) { - property.toString(sb); - sb.append(NEWLINE); - } - } - - // append the sub-components - if (mChildren != null) { - for (Component component : mChildren) { - component.toString(sb); - sb.append(NEWLINE); - } - } - - sb.append(END); - sb.append(":"); - sb.append(mName); - } - } - - /** - * A property within an iCalendar component (e.g., DTSTART, DTEND, etc., - * within a VEVENT). - */ - public static class Property { - // properties - // TODO: do we want to list these here? the complete list is long. - public static final String DTSTART = "DTSTART"; - public static final String DTEND = "DTEND"; - public static final String DURATION = "DURATION"; - public static final String RRULE = "RRULE"; - public static final String RDATE = "RDATE"; - public static final String EXRULE = "EXRULE"; - public static final String EXDATE = "EXDATE"; - // ... need to add more. - - private final String mName; - private LinkedHashMap<String, ArrayList<Parameter>> mParamsMap = - new LinkedHashMap<String, ArrayList<Parameter>>(); - private String mValue; // TODO: make this final? - - /** - * Creates a new property with the provided name. - * @param name The name of the property. - */ - public Property(String name) { - mName = name; - } - - /** - * Creates a new property with the provided name and value. - * @param name The name of the property. - * @param value The value of the property. - */ - public Property(String name, String value) { - mName = name; - mValue = value; - } - - /** - * Returns the name of the property. - * @return The name of the property. - */ - public String getName() { - return mName; - } - - /** - * Returns the value of this property. - * @return The value of this property. - */ - public String getValue() { - return mValue; - } - - /** - * Sets the value of this property. - * @param value The desired value for this property. - */ - public void setValue(String value) { - mValue = value; - } - - /** - * Adds a {@link Parameter} to this property. - * @param param The parameter that should be added. - */ - public void addParameter(Parameter param) { - ArrayList<Parameter> params = mParamsMap.get(param.name); - if (params == null) { - params = new ArrayList<Parameter>(); - mParamsMap.put(param.name, params); - } - params.add(param); - } - - /** - * Returns the set of parameter names for this property. - * @return The set of parameter names for this property. - */ - public Set<String> getParameterNames() { - return mParamsMap.keySet(); - } - - /** - * Returns the list of parameters with the specified name. May return - * null if there are no such parameters. - * @param name The name of the parameters that should be returned. - * @return The list of parameters with the specified name. - */ - public List<Parameter> getParameters(String name) { - return mParamsMap.get(name); - } - - /** - * Returns the first parameter with the specified name. May return - * nll if there is no such parameter. - * @param name The name of the parameter that should be returned. - * @return The first parameter with the specified name. - */ - public Parameter getFirstParameter(String name) { - ArrayList<Parameter> params = mParamsMap.get(name); - if (params == null || params.size() == 0) { - return null; - } - return params.get(0); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - - /** - * Helper method that appends this property to a StringBuilder. The - * caller is responsible for appending a newline after this property. - */ - public void toString(StringBuilder sb) { - sb.append(mName); - Set<String> parameterNames = getParameterNames(); - for (String parameterName : parameterNames) { - for (Parameter param : getParameters(parameterName)) { - sb.append(";"); - param.toString(sb); - } - } - sb.append(":"); - sb.append(mValue); - } - } - - /** - * A parameter defined for an iCalendar property. - */ - // TODO: make this a proper class rather than a struct? - public static class Parameter { - public String name; - public String value; - - /** - * Creates a new empty parameter. - */ - public Parameter() { - } - - /** - * Creates a new parameter with the specified name and value. - * @param name The name of the parameter. - * @param value The value of the parameter. - */ - public Parameter(String name, String value) { - this.name = name; - this.value = value; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - - /** - * Helper method that appends this parameter to a StringBuilder. - */ - public void toString(StringBuilder sb) { - sb.append(name); - sb.append("="); - sb.append(value); - } - } - - private static final class ParserState { - // public int lineNumber = 0; - public String line; // TODO: just point to original text - public int index; - } - - // use factory method - private ICalendar() { - } - - // TODO: get rid of this -- handle all of the parsing in one pass through - // the text. - private static String normalizeText(String text) { - // it's supposed to be \r\n, but not everyone does that - text = text.replaceAll("\r\n", "\n"); - text = text.replaceAll("\r", "\n"); - - // we deal with line folding, by replacing all "\n " strings - // with nothing. The RFC specifies "\r\n " to be folded, but - // we handle "\n " and "\r " too because we can get those. - text = text.replaceAll("\n ", ""); - - return text; - } - - /** - * Parses text into an iCalendar component. Parses into the provided - * component, if not null, or parses into a new component. In the latter - * case, expects a BEGIN as the first line. Returns the provided or newly - * created top-level component. - */ - // TODO: use an index into the text, so we can make this a recursive - // function? - private static Component parseComponentImpl(Component component, - String text) - throws FormatException { - Component current = component; - ParserState state = new ParserState(); - state.index = 0; - - // split into lines - String[] lines = text.split("\n"); - - // each line is of the format: - // name *(";" param) ":" value - for (String line : lines) { - try { - current = parseLine(line, state, current); - // if the provided component was null, we will return the root - // NOTE: in this case, if the first line is not a BEGIN, a - // FormatException will get thrown. - if (component == null) { - component = current; - } - } catch (FormatException fe) { - if (false) { - Log.v(TAG, "Cannot parse " + line, fe); - } - // for now, we ignore the parse error. Google Calendar seems - // to be emitting some misformatted iCalendar objects. - } - continue; - } - return component; - } - - /** - * Parses a line into the provided component. Creates a new component if - * the line is a BEGIN, adding the newly created component to the provided - * parent. Returns whatever component is the current one (to which new - * properties will be added) in the parse. - */ - private static Component parseLine(String line, ParserState state, - Component component) - throws FormatException { - state.line = line; - int len = state.line.length(); - - // grab the name - char c = 0; - for (state.index = 0; state.index < len; ++state.index) { - c = line.charAt(state.index); - if (c == ';' || c == ':') { - break; - } - } - String name = line.substring(0, state.index); - - if (component == null) { - if (!Component.BEGIN.equals(name)) { - throw new FormatException("Expected BEGIN"); - } - } - - Property property; - if (Component.BEGIN.equals(name)) { - // start a new component - String componentName = extractValue(state); - Component child = new Component(componentName, component); - if (component != null) { - component.addChild(child); - } - return child; - } else if (Component.END.equals(name)) { - // finish the current component - String componentName = extractValue(state); - if (component == null || - !componentName.equals(component.getName())) { - throw new FormatException("Unexpected END " + componentName); - } - return component.getParent(); - } else { - property = new Property(name); - } - - if (c == ';') { - Parameter parameter = null; - while ((parameter = extractParameter(state)) != null) { - property.addParameter(parameter); - } - } - String value = extractValue(state); - property.setValue(value); - component.addProperty(property); - return component; - } - - /** - * Extracts the value ":..." on the current line. The first character must - * be a ':'. - */ - private static String extractValue(ParserState state) - throws FormatException { - String line = state.line; - if (state.index >= line.length() || line.charAt(state.index) != ':') { - throw new FormatException("Expected ':' before end of line in " - + line); - } - String value = line.substring(state.index + 1); - state.index = line.length() - 1; - return value; - } - - /** - * Extracts the next parameter from the line, if any. If there are no more - * parameters, returns null. - */ - private static Parameter extractParameter(ParserState state) - throws FormatException { - String text = state.line; - int len = text.length(); - Parameter parameter = null; - int startIndex = -1; - int equalIndex = -1; - while (state.index < len) { - char c = text.charAt(state.index); - if (c == ':') { - if (parameter != null) { - if (equalIndex == -1) { - throw new FormatException("Expected '=' within " - + "parameter in " + text); - } - parameter.value = text.substring(equalIndex + 1, - state.index); - } - return parameter; // may be null - } else if (c == ';') { - if (parameter != null) { - if (equalIndex == -1) { - throw new FormatException("Expected '=' within " - + "parameter in " + text); - } - parameter.value = text.substring(equalIndex + 1, - state.index); - return parameter; - } else { - parameter = new Parameter(); - startIndex = state.index; - } - } else if (c == '=') { - equalIndex = state.index; - if ((parameter == null) || (startIndex == -1)) { - throw new FormatException("Expected ';' before '=' in " - + text); - } - parameter.name = text.substring(startIndex + 1, equalIndex); - } else if (c == '"') { - if (parameter == null) { - throw new FormatException("Expected parameter before '\"' in " + text); - } - if (equalIndex == -1) { - throw new FormatException("Expected '=' within parameter in " + text); - } - if (state.index > equalIndex + 1) { - throw new FormatException("Parameter value cannot contain a '\"' in " + text); - } - final int endQuote = text.indexOf('"', state.index + 1); - if (endQuote < 0) { - throw new FormatException("Expected closing '\"' in " + text); - } - parameter.value = text.substring(state.index + 1, endQuote); - state.index = endQuote + 1; - return parameter; - } - ++state.index; - } - throw new FormatException("Expected ':' before end of line in " + text); - } - - /** - * Parses the provided text into an iCalendar object. The top-level - * component must be of type VCALENDAR. - * @param text The text to be parsed. - * @return The top-level VCALENDAR component. - * @throws FormatException Thrown if the text could not be parsed into an - * iCalendar VCALENDAR object. - */ - public static Component parseCalendar(String text) throws FormatException { - Component calendar = parseComponent(null, text); - if (calendar == null || !Component.VCALENDAR.equals(calendar.getName())) { - throw new FormatException("Expected " + Component.VCALENDAR); - } - return calendar; - } - - /** - * Parses the provided text into an iCalendar event. The top-level - * component must be of type VEVENT. - * @param text The text to be parsed. - * @return The top-level VEVENT component. - * @throws FormatException Thrown if the text could not be parsed into an - * iCalendar VEVENT. - */ - public static Component parseEvent(String text) throws FormatException { - Component event = parseComponent(null, text); - if (event == null || !Component.VEVENT.equals(event.getName())) { - throw new FormatException("Expected " + Component.VEVENT); - } - return event; - } - - /** - * Parses the provided text into an iCalendar component. - * @param text The text to be parsed. - * @return The top-level component. - * @throws FormatException Thrown if the text could not be parsed into an - * iCalendar component. - */ - public static Component parseComponent(String text) throws FormatException { - return parseComponent(null, text); - } - - /** - * Parses the provided text, adding to the provided component. - * @param component The component to which the parsed iCalendar data should - * be added. - * @param text The text to be parsed. - * @return The top-level component. - * @throws FormatException Thrown if the text could not be parsed as an - * iCalendar object. - */ - public static Component parseComponent(Component component, String text) - throws FormatException { - text = normalizeText(text); - return parseComponentImpl(component, text); - } -} diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java deleted file mode 100644 index b7fb320..0000000 --- a/core/java/android/pim/RecurrenceSet.java +++ /dev/null @@ -1,511 +0,0 @@ -/* - * 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 android.pim; - -import android.content.ContentValues; -import android.database.Cursor; -import android.provider.CalendarContract; -import android.text.TextUtils; -import android.text.format.Time; -import android.util.Log; - -import java.util.List; -import java.util.regex.Pattern; - -/** - * Basic information about a recurrence, following RFC 2445 Section 4.8.5. - * Contains the RRULEs, RDATE, EXRULEs, and EXDATE properties. - */ -public class RecurrenceSet { - - private final static String TAG = "CalendarProvider"; - - private final static String RULE_SEPARATOR = "\n"; - private final static String FOLDING_SEPARATOR = "\n "; - - // TODO: make these final? - public EventRecurrence[] rrules = null; - public long[] rdates = null; - public EventRecurrence[] exrules = null; - public long[] exdates = null; - - /** - * Creates a new RecurrenceSet from information stored in the - * events table in the CalendarProvider. - * @param values The values retrieved from the Events table. - */ - public RecurrenceSet(ContentValues values) - throws EventRecurrence.InvalidFormatException { - String rruleStr = values.getAsString(CalendarContract.Events.RRULE); - String rdateStr = values.getAsString(CalendarContract.Events.RDATE); - String exruleStr = values.getAsString(CalendarContract.Events.EXRULE); - String exdateStr = values.getAsString(CalendarContract.Events.EXDATE); - init(rruleStr, rdateStr, exruleStr, exdateStr); - } - - /** - * Creates a new RecurrenceSet from information stored in a database - * {@link Cursor} pointing to the events table in the - * CalendarProvider. The cursor must contain the RRULE, RDATE, EXRULE, - * and EXDATE columns. - * - * @param cursor The cursor containing the RRULE, RDATE, EXRULE, and EXDATE - * columns. - */ - public RecurrenceSet(Cursor cursor) - throws EventRecurrence.InvalidFormatException { - int rruleColumn = cursor.getColumnIndex(CalendarContract.Events.RRULE); - int rdateColumn = cursor.getColumnIndex(CalendarContract.Events.RDATE); - int exruleColumn = cursor.getColumnIndex(CalendarContract.Events.EXRULE); - int exdateColumn = cursor.getColumnIndex(CalendarContract.Events.EXDATE); - String rruleStr = cursor.getString(rruleColumn); - String rdateStr = cursor.getString(rdateColumn); - String exruleStr = cursor.getString(exruleColumn); - String exdateStr = cursor.getString(exdateColumn); - init(rruleStr, rdateStr, exruleStr, exdateStr); - } - - public RecurrenceSet(String rruleStr, String rdateStr, - String exruleStr, String exdateStr) - throws EventRecurrence.InvalidFormatException { - init(rruleStr, rdateStr, exruleStr, exdateStr); - } - - private void init(String rruleStr, String rdateStr, - String exruleStr, String exdateStr) - throws EventRecurrence.InvalidFormatException { - if (!TextUtils.isEmpty(rruleStr) || !TextUtils.isEmpty(rdateStr)) { - - if (!TextUtils.isEmpty(rruleStr)) { - String[] rruleStrs = rruleStr.split(RULE_SEPARATOR); - rrules = new EventRecurrence[rruleStrs.length]; - for (int i = 0; i < rruleStrs.length; ++i) { - EventRecurrence rrule = new EventRecurrence(); - rrule.parse(rruleStrs[i]); - rrules[i] = rrule; - } - } - - if (!TextUtils.isEmpty(rdateStr)) { - rdates = parseRecurrenceDates(rdateStr); - } - - if (!TextUtils.isEmpty(exruleStr)) { - String[] exruleStrs = exruleStr.split(RULE_SEPARATOR); - exrules = new EventRecurrence[exruleStrs.length]; - for (int i = 0; i < exruleStrs.length; ++i) { - EventRecurrence exrule = new EventRecurrence(); - exrule.parse(exruleStr); - exrules[i] = exrule; - } - } - - if (!TextUtils.isEmpty(exdateStr)) { - exdates = parseRecurrenceDates(exdateStr); - } - } - } - - /** - * Returns whether or not a recurrence is defined in this RecurrenceSet. - * @return Whether or not a recurrence is defined in this RecurrenceSet. - */ - public boolean hasRecurrence() { - return (rrules != null || rdates != null); - } - - /** - * Parses the provided RDATE or EXDATE string into an array of longs - * representing each date/time in the recurrence. - * @param recurrence The recurrence to be parsed. - * @return The list of date/times. - */ - public static long[] parseRecurrenceDates(String recurrence) { - // TODO: use "local" time as the default. will need to handle times - // that end in "z" (UTC time) explicitly at that point. - String tz = Time.TIMEZONE_UTC; - int tzidx = recurrence.indexOf(";"); - if (tzidx != -1) { - tz = recurrence.substring(0, tzidx); - recurrence = recurrence.substring(tzidx + 1); - } - Time time = new Time(tz); - String[] rawDates = recurrence.split(","); - int n = rawDates.length; - long[] dates = new long[n]; - for (int i = 0; i<n; ++i) { - // The timezone is updated to UTC if the time string specified 'Z'. - time.parse(rawDates[i]); - dates[i] = time.toMillis(false /* use isDst */); - time.timezone = tz; - } - return dates; - } - - /** - * Populates the database map of values with the appropriate RRULE, RDATE, - * EXRULE, and EXDATE values extracted from the parsed iCalendar component. - * @param component The iCalendar component containing the desired - * recurrence specification. - * @param values The db values that should be updated. - * @return true if the component contained the necessary information - * to specify a recurrence. The required fields are DTSTART, - * one of DTEND/DURATION, and one of RRULE/RDATE. Returns false if - * there was an error, including if the date is out of range. - */ - public static boolean populateContentValues(ICalendar.Component component, - ContentValues values) { - ICalendar.Property dtstartProperty = - component.getFirstProperty("DTSTART"); - String dtstart = dtstartProperty.getValue(); - ICalendar.Parameter tzidParam = - dtstartProperty.getFirstParameter("TZID"); - // NOTE: the timezone may be null, if this is a floating time. - String tzid = tzidParam == null ? null : tzidParam.value; - Time start = new Time(tzidParam == null ? Time.TIMEZONE_UTC : tzid); - boolean inUtc = start.parse(dtstart); - boolean allDay = start.allDay; - - // We force TimeZone to UTC for "all day recurring events" as the server is sending no - // TimeZone in DTSTART for them - if (inUtc || allDay) { - tzid = Time.TIMEZONE_UTC; - } - - String duration = computeDuration(start, component); - String rrule = flattenProperties(component, "RRULE"); - String rdate = extractDates(component.getFirstProperty("RDATE")); - String exrule = flattenProperties(component, "EXRULE"); - String exdate = extractDates(component.getFirstProperty("EXDATE")); - - if ((TextUtils.isEmpty(dtstart))|| - (TextUtils.isEmpty(duration))|| - ((TextUtils.isEmpty(rrule))&& - (TextUtils.isEmpty(rdate)))) { - if (false) { - Log.d(TAG, "Recurrence missing DTSTART, DTEND/DURATION, " - + "or RRULE/RDATE: " - + component.toString()); - } - return false; - } - - if (allDay) { - start.timezone = Time.TIMEZONE_UTC; - } - long millis = start.toMillis(false /* use isDst */); - values.put(CalendarContract.Events.DTSTART, millis); - if (millis == -1) { - if (false) { - Log.d(TAG, "DTSTART is out of range: " + component.toString()); - } - return false; - } - - values.put(CalendarContract.Events.RRULE, rrule); - values.put(CalendarContract.Events.RDATE, rdate); - values.put(CalendarContract.Events.EXRULE, exrule); - values.put(CalendarContract.Events.EXDATE, exdate); - values.put(CalendarContract.Events.EVENT_TIMEZONE, tzid); - values.put(CalendarContract.Events.DURATION, duration); - values.put(CalendarContract.Events.ALL_DAY, allDay ? 1 : 0); - return true; - } - - // This can be removed when the old CalendarSyncAdapter is removed. - public static boolean populateComponent(Cursor cursor, - ICalendar.Component component) { - - int dtstartColumn = cursor.getColumnIndex(CalendarContract.Events.DTSTART); - int durationColumn = cursor.getColumnIndex(CalendarContract.Events.DURATION); - int tzidColumn = cursor.getColumnIndex(CalendarContract.Events.EVENT_TIMEZONE); - int rruleColumn = cursor.getColumnIndex(CalendarContract.Events.RRULE); - int rdateColumn = cursor.getColumnIndex(CalendarContract.Events.RDATE); - int exruleColumn = cursor.getColumnIndex(CalendarContract.Events.EXRULE); - int exdateColumn = cursor.getColumnIndex(CalendarContract.Events.EXDATE); - int allDayColumn = cursor.getColumnIndex(CalendarContract.Events.ALL_DAY); - - - long dtstart = -1; - if (!cursor.isNull(dtstartColumn)) { - dtstart = cursor.getLong(dtstartColumn); - } - String duration = cursor.getString(durationColumn); - String tzid = cursor.getString(tzidColumn); - String rruleStr = cursor.getString(rruleColumn); - String rdateStr = cursor.getString(rdateColumn); - String exruleStr = cursor.getString(exruleColumn); - String exdateStr = cursor.getString(exdateColumn); - boolean allDay = cursor.getInt(allDayColumn) == 1; - - if ((dtstart == -1) || - (TextUtils.isEmpty(duration))|| - ((TextUtils.isEmpty(rruleStr))&& - (TextUtils.isEmpty(rdateStr)))) { - // no recurrence. - return false; - } - - ICalendar.Property dtstartProp = new ICalendar.Property("DTSTART"); - Time dtstartTime = null; - if (!TextUtils.isEmpty(tzid)) { - if (!allDay) { - dtstartProp.addParameter(new ICalendar.Parameter("TZID", tzid)); - } - dtstartTime = new Time(tzid); - } else { - // use the "floating" timezone - dtstartTime = new Time(Time.TIMEZONE_UTC); - } - - dtstartTime.set(dtstart); - // make sure the time is printed just as a date, if all day. - // TODO: android.pim.Time really should take care of this for us. - if (allDay) { - dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE")); - dtstartTime.allDay = true; - dtstartTime.hour = 0; - dtstartTime.minute = 0; - dtstartTime.second = 0; - } - - dtstartProp.setValue(dtstartTime.format2445()); - component.addProperty(dtstartProp); - ICalendar.Property durationProp = new ICalendar.Property("DURATION"); - durationProp.setValue(duration); - component.addProperty(durationProp); - - addPropertiesForRuleStr(component, "RRULE", rruleStr); - addPropertyForDateStr(component, "RDATE", rdateStr); - addPropertiesForRuleStr(component, "EXRULE", exruleStr); - addPropertyForDateStr(component, "EXDATE", exdateStr); - return true; - } - -public static boolean populateComponent(ContentValues values, - ICalendar.Component component) { - long dtstart = -1; - if (values.containsKey(CalendarContract.Events.DTSTART)) { - dtstart = values.getAsLong(CalendarContract.Events.DTSTART); - } - String duration = values.getAsString(CalendarContract.Events.DURATION); - String tzid = values.getAsString(CalendarContract.Events.EVENT_TIMEZONE); - String rruleStr = values.getAsString(CalendarContract.Events.RRULE); - String rdateStr = values.getAsString(CalendarContract.Events.RDATE); - String exruleStr = values.getAsString(CalendarContract.Events.EXRULE); - String exdateStr = values.getAsString(CalendarContract.Events.EXDATE); - Integer allDayInteger = values.getAsInteger(CalendarContract.Events.ALL_DAY); - boolean allDay = (null != allDayInteger) ? (allDayInteger == 1) : false; - - if ((dtstart == -1) || - (TextUtils.isEmpty(duration))|| - ((TextUtils.isEmpty(rruleStr))&& - (TextUtils.isEmpty(rdateStr)))) { - // no recurrence. - return false; - } - - ICalendar.Property dtstartProp = new ICalendar.Property("DTSTART"); - Time dtstartTime = null; - if (!TextUtils.isEmpty(tzid)) { - if (!allDay) { - dtstartProp.addParameter(new ICalendar.Parameter("TZID", tzid)); - } - dtstartTime = new Time(tzid); - } else { - // use the "floating" timezone - dtstartTime = new Time(Time.TIMEZONE_UTC); - } - - dtstartTime.set(dtstart); - // make sure the time is printed just as a date, if all day. - // TODO: android.pim.Time really should take care of this for us. - if (allDay) { - dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE")); - dtstartTime.allDay = true; - dtstartTime.hour = 0; - dtstartTime.minute = 0; - dtstartTime.second = 0; - } - - dtstartProp.setValue(dtstartTime.format2445()); - component.addProperty(dtstartProp); - ICalendar.Property durationProp = new ICalendar.Property("DURATION"); - durationProp.setValue(duration); - component.addProperty(durationProp); - - addPropertiesForRuleStr(component, "RRULE", rruleStr); - addPropertyForDateStr(component, "RDATE", rdateStr); - addPropertiesForRuleStr(component, "EXRULE", exruleStr); - addPropertyForDateStr(component, "EXDATE", exdateStr); - return true; - } - - private static void addPropertiesForRuleStr(ICalendar.Component component, - String propertyName, - String ruleStr) { - if (TextUtils.isEmpty(ruleStr)) { - return; - } - String[] rrules = getRuleStrings(ruleStr); - for (String rrule : rrules) { - ICalendar.Property prop = new ICalendar.Property(propertyName); - prop.setValue(rrule); - component.addProperty(prop); - } - } - - private static String[] getRuleStrings(String ruleStr) { - if (null == ruleStr) { - return new String[0]; - } - String unfoldedRuleStr = unfold(ruleStr); - String[] split = unfoldedRuleStr.split(RULE_SEPARATOR); - int count = split.length; - for (int n = 0; n < count; n++) { - split[n] = fold(split[n]); - } - return split; - } - - - private static final Pattern IGNORABLE_ICAL_WHITESPACE_RE = - Pattern.compile("(?:\\r\\n?|\\n)[ \t]"); - - private static final Pattern FOLD_RE = Pattern.compile(".{75}"); - - /** - * fold and unfolds ical content lines as per RFC 2445 section 4.1. - * - * <h3>4.1 Content Lines</h3> - * - * <p>The iCalendar object is organized into individual lines of text, called - * content lines. Content lines are delimited by a line break, which is a CRLF - * sequence (US-ASCII decimal 13, followed by US-ASCII decimal 10). - * - * <p>Lines of text SHOULD NOT be longer than 75 octets, excluding the line - * break. Long content lines SHOULD be split into a multiple line - * representations using a line "folding" technique. That is, a long line can - * be split between any two characters by inserting a CRLF immediately - * followed by a single linear white space character (i.e., SPACE, US-ASCII - * decimal 32 or HTAB, US-ASCII decimal 9). Any sequence of CRLF followed - * immediately by a single linear white space character is ignored (i.e., - * removed) when processing the content type. - */ - public static String fold(String unfoldedIcalContent) { - return FOLD_RE.matcher(unfoldedIcalContent).replaceAll("$0\r\n "); - } - - public static String unfold(String foldedIcalContent) { - return IGNORABLE_ICAL_WHITESPACE_RE.matcher( - foldedIcalContent).replaceAll(""); - } - - private static void addPropertyForDateStr(ICalendar.Component component, - String propertyName, - String dateStr) { - if (TextUtils.isEmpty(dateStr)) { - return; - } - - ICalendar.Property prop = new ICalendar.Property(propertyName); - String tz = null; - int tzidx = dateStr.indexOf(";"); - if (tzidx != -1) { - tz = dateStr.substring(0, tzidx); - dateStr = dateStr.substring(tzidx + 1); - } - if (!TextUtils.isEmpty(tz)) { - prop.addParameter(new ICalendar.Parameter("TZID", tz)); - } - prop.setValue(dateStr); - component.addProperty(prop); - } - - private static String computeDuration(Time start, - ICalendar.Component component) { - // see if a duration is defined - ICalendar.Property durationProperty = - component.getFirstProperty("DURATION"); - if (durationProperty != null) { - // just return the duration - return durationProperty.getValue(); - } - - // must compute a duration from the DTEND - ICalendar.Property dtendProperty = - component.getFirstProperty("DTEND"); - if (dtendProperty == null) { - // no DURATION, no DTEND: 0 second duration - return "+P0S"; - } - ICalendar.Parameter endTzidParameter = - dtendProperty.getFirstParameter("TZID"); - String endTzid = (endTzidParameter == null) - ? start.timezone : endTzidParameter.value; - - Time end = new Time(endTzid); - end.parse(dtendProperty.getValue()); - long durationMillis = end.toMillis(false /* use isDst */) - - start.toMillis(false /* use isDst */); - long durationSeconds = (durationMillis / 1000); - if (start.allDay && (durationSeconds % 86400) == 0) { - return "P" + (durationSeconds / 86400) + "D"; // Server wants this instead of P86400S - } else { - return "P" + durationSeconds + "S"; - } - } - - private static String flattenProperties(ICalendar.Component component, - String name) { - List<ICalendar.Property> properties = component.getProperties(name); - if (properties == null || properties.isEmpty()) { - return null; - } - - if (properties.size() == 1) { - return properties.get(0).getValue(); - } - - StringBuilder sb = new StringBuilder(); - - boolean first = true; - for (ICalendar.Property property : component.getProperties(name)) { - if (first) { - first = false; - } else { - // TODO: use commas. our RECUR parsing should handle that - // anyway. - sb.append(RULE_SEPARATOR); - } - sb.append(property.getValue()); - } - return sb.toString(); - } - - private static String extractDates(ICalendar.Property recurrence) { - if (recurrence == null) { - return null; - } - ICalendar.Parameter tzidParam = - recurrence.getFirstParameter("TZID"); - if (tzidParam != null) { - return tzidParam.value + ";" + recurrence.getValue(); - } - return recurrence.getValue(); - } -} diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index b492615..5b29103 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -17,6 +17,8 @@ package android.provider; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.ContentProviderClient; @@ -83,7 +85,6 @@ import android.util.Log; * adapters</li> * </ul> * - * @hide */ public final class CalendarContract { private static final String TAG = "Calendar"; @@ -92,8 +93,8 @@ public final class CalendarContract { * Broadcast Action: This is the intent that gets fired when an alarm * notification needs to be posted for a reminder. * - * @SdkConstant */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER"; /** @@ -146,6 +147,11 @@ public final class CalendarContract { public static final String ACCOUNT_TYPE_LOCAL = "LOCAL"; /** + * This utility class cannot be instantiated + */ + private CalendarContract() {} + + /** * Generic columns for use by sync adapters. The specific functions of these * columns are private to the sync adapter. Other clients of the API should * not attempt to either read or write this column. These columns are @@ -384,7 +390,7 @@ public final class CalendarContract { * Class that represents a Calendar Entity. There is one entry per calendar. * This is a helper class to make batch operations easier. */ - public static class CalendarEntity implements BaseColumns, SyncColumns, CalendarColumns { + public static final class CalendarEntity implements BaseColumns, SyncColumns, CalendarColumns { /** * The default Uri used when creating a new calendar EntityIterator. @@ -394,6 +400,11 @@ public final class CalendarContract { "/calendar_entities"); /** + * This utility class cannot be instantiated + */ + private CalendarEntity() {} + + /** * Creates an entity iterator for the given cursor. It assumes the * cursor contains a calendars query. * @@ -566,7 +577,13 @@ public final class CalendarContract { * <li>{@link #CAL_SYNC10}</li> * </ul> */ - public static class Calendars implements BaseColumns, SyncColumns, CalendarColumns { + public static final class Calendars implements BaseColumns, SyncColumns, CalendarColumns { + + /** + * This utility class cannot be instantiated + */ + private Calendars() {} + /** * The content:// style URL for accessing Calendars */ @@ -687,7 +704,7 @@ public final class CalendarContract { /** * Fields and helpers for interacting with Attendees. Each row of this table * represents a single attendee or guest of an event. Calling - * {@link #query(ContentResolver, long)} will return a list of attendees for + * {@link #query(ContentResolver, long, String[])} will return a list of attendees for * the event with the given eventId. Both apps and sync adapters may write * to this table. There are six writable fields and all of them except * {@link #ATTENDEE_NAME} must be included when inserting a new attendee. @@ -708,12 +725,12 @@ public final class CalendarContract { */ @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees"); + private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?"; + /** - * the projection used by the attendees query + * This utility class cannot be instantiated */ - public static final String[] PROJECTION = new String[] { - _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,}; - private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?"; + private Attendees() {} /** * Queries all attendees associated with the given event. This is a @@ -721,11 +738,12 @@ public final class CalendarContract { * * @param cr The content resolver to use for the query * @param eventId The id of the event to retrieve attendees for + * @param projection the columns to return in the cursor * @return A Cursor containing all attendees for the event */ - public static final Cursor query(ContentResolver cr, long eventId) { + public static final Cursor query(ContentResolver cr, long eventId, String[] projection) { String[] attArgs = {Long.toString(eventId)}; - return cr.query(CONTENT_URI, PROJECTION, ATTENDEES_WHERE, attArgs /* selection args */, + return cr.query(CONTENT_URI, projection, ATTENDEES_WHERE, attArgs /* selection args */, null /* sort order */); } } @@ -1068,6 +1086,11 @@ public final class CalendarContract { "/event_entities"); /** + * This utility class cannot be instantiated + */ + private EventsEntity() {} + + /** * Creates a new iterator for events * * @param cursor An event query @@ -1411,6 +1434,11 @@ public final class CalendarContract { Uri.parse("content://" + AUTHORITY + "/exception"); /** + * This utility class cannot be instantiated + */ + private Events() {} + + /** * The default sort order for this table */ private static final String DEFAULT_SORT_ORDER = ""; @@ -1484,6 +1512,11 @@ public final class CalendarContract { }; /** + * This utility class cannot be instantiated + */ + private Instances() {} + + /** * Performs a query to return all visible instances in the given range. * This is a blocking function and should not be done on the UI thread. * This will cause an expansion of recurring events to fill this time @@ -1636,7 +1669,7 @@ public final class CalendarContract { * time zone for the instances. These settings are stored using a key/value * scheme. A {@link #KEY} must be specified when updating these values. */ - public static class CalendarCache implements CalendarCacheColumns { + public static final class CalendarCache implements CalendarCacheColumns { /** * The URI to use for retrieving the properties from the Calendar db. */ @@ -1644,6 +1677,11 @@ public final class CalendarContract { Uri.parse("content://" + AUTHORITY + "/properties"); /** + * This utility class cannot be instantiated + */ + private CalendarCache() {} + + /** * They key for updating the use of auto/home time zones in Calendar. * Valid values are {@link #TIMEZONE_TYPE_AUTO} or * {@link #TIMEZONE_TYPE_HOME}. @@ -1724,6 +1762,11 @@ public final class CalendarContract { * @hide */ public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns { + + /** + * This utility class cannot be instantiated + */ + private CalendarMetaData() {} } protected interface EventDaysColumns { @@ -1746,14 +1789,12 @@ public final class CalendarContract { public static final class EventDays implements EventDaysColumns { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/instances/groupbyday"); + private static final String SELECTION = "selected=1"; /** - * The projection used by the EventDays query. + * This utility class cannot be instantiated */ - public static final String[] PROJECTION = { - STARTDAY, ENDDAY - }; - private static final String SELECTION = "selected=1"; + private EventDays() {} /** * Retrieves the days with events for the Julian days starting at @@ -1765,10 +1806,12 @@ public final class CalendarContract { * @param cr the ContentResolver * @param startDay the first Julian day in the range * @param numDays the number of days to load (must be at least 1) + * @param projection the columns to return in the cursor * @return a database cursor containing a list of start and end days for * events */ - public static final Cursor query(ContentResolver cr, int startDay, int numDays) { + public static final Cursor query(ContentResolver cr, int startDay, int numDays, + String[] projection) { if (numDays < 1) { return null; } @@ -1776,7 +1819,7 @@ public final class CalendarContract { Uri.Builder builder = CONTENT_URI.buildUpon(); ContentUris.appendId(builder, startDay); ContentUris.appendId(builder, endDay); - return cr.query(builder.build(), PROJECTION, SELECTION, + return cr.query(builder.build(), projection, SELECTION, null /* selection args */, STARTDAY); } } @@ -1821,7 +1864,7 @@ public final class CalendarContract { /** * Fields and helpers for accessing reminders for an event. Each row of this * table represents a single reminder for an event. Calling - * {@link #query(ContentResolver, long)} will return a list of reminders for + * {@link #query(ContentResolver, long, String[])} will return a list of reminders for * the event with the given eventId. Both apps and sync adapters may write * to this table. There are three writable fields and all of them must be * included when inserting a new reminder. They are: @@ -1833,25 +1876,26 @@ public final class CalendarContract { */ public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns { private static final String REMINDERS_WHERE = CalendarContract.Reminders.EVENT_ID + "=?"; - /** - * The projection used by the reminders query. - */ - public static final String[] PROJECTION = new String[] { - _ID, MINUTES, METHOD,}; @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders"); /** + * This utility class cannot be instantiated + */ + private Reminders() {} + + /** * Queries all reminders associated with the given event. This is a * blocking call and should not be done on the UI thread. * * @param cr The content resolver to use for the query * @param eventId The id of the event to retrieve reminders for + * @param projection the columns to return in the cursor * @return A Cursor containing all reminders for the event */ - public static final Cursor query(ContentResolver cr, long eventId) { + public static final Cursor query(ContentResolver cr, long eventId, String[] projection) { String[] remArgs = {Long.toString(eventId)}; - return cr.query(CONTENT_URI, PROJECTION, REMINDERS_WHERE, remArgs /* selection args */, + return cr.query(CONTENT_URI, projection, REMINDERS_WHERE, remArgs /*selection args*/, null /* sort order */); } } @@ -1964,6 +2008,11 @@ public final class CalendarContract { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendar_alerts"); + /** + * This utility class cannot be instantiated + */ + private CalendarAlerts() {} + private static final String WHERE_ALARM_EXISTS = EVENT_ID + "=?" + " AND " + BEGIN + "=?" + " AND " + ALARM_TIME + "=?"; @@ -2134,7 +2183,7 @@ public final class CalendarContract { * given event id, begin time and alarm time. If one is found then this * alarm already exists and this method returns true. TODO Move to * provider - * + * * @param cr the ContentResolver * @param eventId the event id to match * @param begin the start time of the event in UTC millis @@ -2203,6 +2252,11 @@ public final class CalendarContract { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/extendedproperties"); + /** + * This utility class cannot be instantiated + */ + private ExtendedProperties() {} + // TODO: fill out this class when we actually start utilizing extendedproperties // in the calendar application. } @@ -2271,5 +2325,10 @@ public final class CalendarContract { * @hide */ public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns { + + /** + * This utility class cannot be instantiated + */ + private EventsRawTimes() {} } } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 61deea4..0dd9a4d 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -17,6 +17,7 @@ package android.provider; import android.accounts.Account; +import android.app.Activity; import android.content.ContentProviderClient; import android.content.ContentProviderOperation; import android.content.ContentResolver; @@ -135,14 +136,6 @@ public final class ContactsContract { public static final String ALLOW_PROFILE = "allow_profile"; /** - * A query parameter key used to specify the package that is requesting a query. - * This is used for restricting data based on package name. - * - * @hide - */ - public static final String REQUESTING_PACKAGE_PARAM_KEY = "requesting_package"; - - /** * Query parameter that should be used by the client to access a specific * {@link Directory}. The parameter value should be the _ID of the corresponding * directory, e.g. @@ -271,8 +264,6 @@ public final class ContactsContract { * <li>The URI authority is replaced with the corresponding {@link #DIRECTORY_AUTHORITY}.</li> * <li>The {@code accountName=} and {@code accountType=} parameters are added or * replaced using the corresponding {@link #ACCOUNT_TYPE} and {@link #ACCOUNT_NAME} values.</li> - * <li>If the URI is missing a ContactsContract.REQUESTING_PACKAGE_PARAM_KEY - * parameter, this parameter is added.</li> * </ul> * </p> * <p> @@ -759,11 +750,25 @@ public final class ContactsContract { public static final String PHOTO_ID = "photo_id"; /** + * Photo file ID of the full-size photo. If present, this will be used to populate + * {@link #PHOTO_URI}. The ID can also be used with + * {@link ContactsContract.DisplayPhoto#CONTENT_URI} to create a URI to the photo. + * If this is present, {@link #PHOTO_ID} is also guaranteed to be populated. + * + * <P>Type: INTEGER</P> + */ + public static final String PHOTO_FILE_ID = "photo_file_id"; + + /** * A URI that can be used to retrieve the contact's full-size photo. + * If PHOTO_FILE_ID is not null, this will be populated with a URI based off + * {@link ContactsContract.DisplayPhoto#CONTENT_URI}. Otherwise, this will + * be populated with the same value as {@link #PHOTO_THUMBNAIL_URI}. * A photo can be referred to either by a URI (this field) or by ID - * (see {@link #PHOTO_ID}). If PHOTO_ID is not null, PHOTO_URI and - * PHOTO_THUMBNAIL_URI shall not be null (but not necessarily vice versa). - * Thus using PHOTO_URI is a more robust method of retrieving contact photos. + * (see {@link #PHOTO_ID}). If either PHOTO_FILE_ID or PHOTO_ID is not null, + * PHOTO_URI and PHOTO_THUMBNAIL_URI shall not be null (but not necessarily + * vice versa). Thus using PHOTO_URI is a more robust method of retrieving + * contact photos. * * <P>Type: TEXT</P> */ @@ -776,7 +781,7 @@ public final class ContactsContract { * PHOTO_THUMBNAIL_URI shall not be null (but not necessarily vice versa). * If the content provider does not differentiate between full-size photos * and thumbnail photos, PHOTO_THUMBNAIL_URI and {@link #PHOTO_URI} can contain - * the same value, but either both shell be null or both not null. + * the same value, but either both shall be null or both not null. * * <P>Type: TEXT</P> */ @@ -1700,10 +1705,15 @@ public final class ContactsContract { /** * A <i>read-only</i> sub-directory of a single contact that contains - * the contact's primary photo. + * the contact's primary photo. The photo may be stored in up to two ways - + * the default "photo" is a thumbnail-sized image stored directly in the data + * row, while the "display photo", if present, is a larger version stored as + * a file. * <p> * Usage example: - * + * <dl> + * <dt>Retrieving the thumbnail-sized photo</dt> + * <dd> * <pre> * public InputStream openPhoto(long contactId) { * Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); @@ -1726,10 +1736,29 @@ public final class ContactsContract { * return null; * } * </pre> + * </dd> + * <dt>Retrieving the larger photo version</dt> + * <dd> + * <pre> + * public InputStream openDisplayPhoto(long contactId) { + * Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); + * Uri displayPhotoUri = Uri.withAppendedPath(contactUri, Contacts.Photo.DISPLAY_PHOTO); + * try { + * AssetFileDescriptor fd = + * getContentResolver().openAssetFile(displayPhotoUri, "r"); + * return fd.createInputStream(); + * } catch (FileNotFoundException e) { + * return null; + * } + * } + * </pre> + * </dd> + * </dl> * * </p> - * <p>You should also consider using the convenience method + * <p>You may also consider using the convenience method * {@link ContactsContract.Contacts#openContactPhotoInputStream(ContentResolver, Uri)} + * to retrieve the raw photo contents of the thumbnail-sized photo. * </p> * <p> * This directory can be used either with a {@link #CONTENT_URI} or @@ -1748,6 +1777,19 @@ public final class ContactsContract { public static final String CONTENT_DIRECTORY = "photo"; /** + * The directory twig for retrieving the full-size display photo. + */ + public static final String DISPLAY_PHOTO = "display_photo"; + + /** + * Full-size photo file ID of the raw contact. + * See {@link ContactsContract.DisplayPhoto}. + * <p> + * Type: NUMBER + */ + public static final String PHOTO_FILE_ID = DATA14; + + /** * Thumbnail photo of the raw contact. This is the raw bytes of an image * that could be inflated using {@link android.graphics.BitmapFactory}. * <p> @@ -1881,13 +1923,16 @@ public final class ContactsContract { public static final String CONTACT_ID = "contact_id"; /** - * Flag indicating that this {@link RawContacts} entry and its children have - * been restricted to specific platform apps. - * <P>Type: INTEGER (boolean)</P> + * The data set within the account that this row belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' data. * - * @hide until finalized in future platform release + * This is empty by default, and is completely optional. It only needs to + * be populated if multiple sync adapters are entering distinct data for + * the same account type and account name. + * <P>Type: TEXT</P> */ - public static final String IS_RESTRICTED = "is_restricted"; + public static final String DATA_SET = "data_set"; /** * The aggregation mode for this contact. @@ -2211,8 +2256,8 @@ public final class ContactsContract { * <td>The name of the account instance to which this row belongs, which when paired with * {@link #ACCOUNT_TYPE} identifies a specific account. * For example, this will be the Gmail address if it is a Google account. - * It should be set at the time - * the raw contact is inserted and never changed afterwards.</td> + * It should be set at the time the raw contact is inserted and never + * changed afterwards.</td> * </tr> * <tr> * <td>String</td> @@ -2222,8 +2267,8 @@ public final class ContactsContract { * <p> * The type of account to which this row belongs, which when paired with * {@link #ACCOUNT_NAME} identifies a specific account. - * It should be set at the time - * the raw contact is inserted and never changed afterwards. + * It should be set at the time the raw contact is inserted and never + * changed afterwards. * </p> * <p> * To ensure uniqueness, new account types should be chosen according to the @@ -2233,15 +2278,38 @@ public final class ContactsContract { * </tr> * <tr> * <td>String</td> + * <td>{@link #DATA_SET}</td> + * <td>read/write-once</td> + * <td> + * <p> + * The data set within the account that this row belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' data. The combination of {@link #ACCOUNT_TYPE}, + * {@link #ACCOUNT_NAME}, and {@link #DATA_SET} identifies a set of data + * that is associated with a single sync adapter. + * </p> + * <p> + * This is empty by default, and is completely optional. It only needs to + * be populated if multiple sync adapters are entering distinct data for + * the same account type and account name. + * </p> + * <p> + * It should be set at the time the raw contact is inserted and never + * changed afterwards. + * </p> + * </td> + * </tr> + * <tr> + * <td>String</td> * <td>{@link #SOURCE_ID}</td> * <td>read/write</td> * <td>String that uniquely identifies this row to its source account. * Typically it is set at the time the raw contact is inserted and never * changed afterwards. The one notable exception is a new raw contact: it - * will have an account name and type, but no source id. This - * indicates to the sync adapter that a new contact needs to be created - * server-side and its ID stored in the corresponding SOURCE_ID field on - * the phone. + * will have an account name and type (and possibly a data set), but no + * source id. This indicates to the sync adapter that a new contact needs + * to be created server-side and its ID stored in the corresponding + * SOURCE_ID field on the phone. * </td> * </tr> * <tr> @@ -2482,6 +2550,56 @@ public final class ContactsContract { } /** + * <p> + * A sub-directory of a single raw contact that represents its primary + * display photo. To access this directory append + * {@link RawContacts.DisplayPhoto#CONTENT_DIRECTORY} to the raw contact URI. + * The resulting URI represents an image file, and should be interacted with + * using ContentProvider.openAssetFile. + * <p> + * <p> + * Note that this sub-directory also supports opening the photo as an asset file + * in write mode. Callers can create or replace the primary photo associated + * with this raw contact by opening the asset file and writing the full-size + * photo contents into it. When the file is closed, the image will be parsed, + * sized down if necessary for the full-size display photo and thumbnail + * dimensions, and stored. + * </p> + * <p> + * Usage example: + * <pre> + * public void writeDisplayPhoto(long rawContactId, byte[] photo) { + * Uri rawContactPhotoUri = Uri.withAppendedPath( + * ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), + * RawContacts.DisplayPhoto.CONTENT_DIRECTORY); + * try { + * AssetFileDescriptor fd = + * getContentResolver().openAssetFile(rawContactPhotoUri, "rw"); + * OutputStream os = fd.createOutputStream(); + * os.write(photo); + * os.close(); + * fd.close(); + * } catch (IOException e) { + * // Handle error cases. + * } + * } + * </pre> + * </p> + */ + public static final class DisplayPhoto { + /** + * No public constructor since this is a utility class + */ + private DisplayPhoto() { + } + + /** + * The directory twig for this sub-table + */ + public static final String CONTENT_DIRECTORY = "display_photo"; + } + + /** * TODO: javadoc * @param cursor * @return @@ -2537,7 +2655,6 @@ public final class ContactsContract { DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DELETED); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, CONTACT_ID); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, STARRED); - DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, IS_RESTRICTED); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, NAME_VERIFIED); android.content.Entity contact = new android.content.Entity(cv); @@ -3177,6 +3294,50 @@ public final class ContactsContract { } /** + * <p> + * Constants for the photo files table, which tracks metadata for hi-res photos + * stored in the file system. + * </p> + * + * @hide + */ + public static final class PhotoFiles implements BaseColumns, PhotoFilesColumns { + /** + * No public constructor since this is a utility class + */ + private PhotoFiles() { + } + } + + /** + * Columns in the PhotoFiles table. + * + * @see ContactsContract.PhotoFiles + * + * @hide + */ + protected interface PhotoFilesColumns { + + /** + * The height, in pixels, of the photo this entry is associated with. + * <P>Type: NUMBER</P> + */ + public static final String HEIGHT = "height"; + + /** + * The width, in pixels, of the photo this entry is associated with. + * <P>Type: NUMBER</P> + */ + public static final String WIDTH = "width"; + + /** + * The size, in bytes, of the photo stored on disk. + * <P>Type: NUMBER</P> + */ + public static final String FILESIZE = "filesize"; + } + + /** * Columns in the Data table. * * @see ContactsContract.Data @@ -3814,27 +3975,6 @@ public final class ContactsContract { /** * <p> - * If {@link #FOR_EXPORT_ONLY} is explicitly set to "1", returned Cursor toward - * Data.CONTENT_URI contains only exportable data. - * </p> - * <p> - * This flag is useful (currently) only for vCard exporter in Contacts app, which - * needs to exclude "un-exportable" data from available data to export, while - * Contacts app itself has priviledge to access all data including "un-exportable" - * ones and providers return all of them regardless of the callers' intention. - * </p> - * <p> - * Type: INTEGER - * </p> - * - * @hide Maybe available only in Eclair and not really ready for public use. - * TODO: remove, or implement this feature completely. As of now (Eclair), - * we only use this flag in queryEntities(), not query(). - */ - public static final String FOR_EXPORT_ONLY = "for_export_only"; - - /** - * <p> * Build a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} * style {@link Uri} for the parent {@link android.provider.ContactsContract.Contacts} * entry of the given {@link ContactsContract.Data} entry. @@ -5897,7 +6037,7 @@ public final class ContactsContract { /** * <p> - * A data kind representing an photo for the contact. + * A data kind representing a photo for the contact. * </p> * <p> * Some sync adapters will choose to download photos in a separate @@ -5917,10 +6057,17 @@ public final class ContactsContract { * <th>Alias</th><th colspan='2'>Data column</th> * </tr> * <tr> + * <td>NUMBER</td> + * <td>{@link #PHOTO_FILE_ID}</td> + * <td>{@link #DATA14}</td> + * <td>ID of the hi-res photo file.</td> + * </tr> + * <tr> * <td>BLOB</td> * <td>{@link #PHOTO}</td> * <td>{@link #DATA15}</td> - * <td>By convention, binary data is stored in DATA15.</td> + * <td>By convention, binary data is stored in DATA15. The thumbnail of the + * photo is stored in this column.</td> * </tr> * </table> */ @@ -5934,6 +6081,14 @@ public final class ContactsContract { public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo"; /** + * Photo file ID for the display photo of the raw contact. + * See {@link ContactsContract.DisplayPhoto}. + * <p> + * Type: NUMBER + */ + public static final String PHOTO_FILE_ID = DATA14; + + /** * Thumbnail photo of the raw contact. This is the raw bytes of an image * that could be inflated using {@link android.graphics.BitmapFactory}. * <p> @@ -6213,6 +6368,18 @@ public final class ContactsContract { */ protected interface GroupsColumns { /** + * The data set within the account that this group belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' group data. + * + * This is empty by default, and is completely optional. It only needs to + * be populated if multiple sync adapters are entering distinct group data + * for the same account type and account name. + * <P>Type: TEXT</P> + */ + public static final String DATA_SET = "data_set"; + + /** * The display title of this group. * <p> * Type: TEXT @@ -6245,6 +6412,28 @@ public final class ContactsContract { public static final String NOTES = "notes"; /** + * The Activity action to open the group in the source app (e.g. + * {@link Intent#ACTION_VIEW}). Can be NULL if the group does not have a dedicated viewer. + * This is used in conjunction with {@link #ACTION_URI}: In order to show an "Open in + * (sourceapp)"-button, both of these fields must be set + * <p> + * Type: TEXT + */ + public static final String ACTION = "action"; + + + /** + * Uri to open the group in the source app. + * Can be NULL if the group does not have a dedicated viewer. + * This is used in conjunction with {@link #ACTION}: In order to show an "Open in + * (sourceapp)"-button, both of these fields must be set + * <p> + * Type: TEXT + */ + public static final String ACTION_URI = "action_uri"; + + + /** * The ID of this group if it is a System Group, i.e. a group that has a special meaning * to the sync adapter, null otherwise. * <P>Type: TEXT</P> @@ -6338,6 +6527,29 @@ public final class ContactsContract { * In other words, it would be a really bad idea to delete and reinsert a * group. A sync adapter should always do an update instead.</td> * </tr> + # <tr> + * <td>String</td> + * <td>{@link #DATA_SET}</td> + * <td>read/write-once</td> + * <td> + * <p> + * The data set within the account that this group belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' group data. The combination of {@link #ACCOUNT_TYPE}, + * {@link #ACCOUNT_NAME}, and {@link #DATA_SET} identifies a set of data + * that is associated with a single sync adapter. + * </p> + * <p> + * This is empty by default, and is completely optional. It only needs to + * be populated if multiple sync adapters are entering distinct data for + * the same account type and account name. + * </p> + * <p> + * It should be set at the time the group is inserted and never changed + * afterwards. + * </p> + * </td> + * </tr> * <tr> * <td>String</td> * <td>{@link #TITLE}</td> @@ -7018,6 +7230,66 @@ public final class ContactsContract { } /** + * Helper class for accessing full-size photos by photo file ID. + * <p> + * Usage example: + * <dl> + * <dt>Retrieving a full-size photo by photo file ID (see + * {@link ContactsContract.ContactsColumns#PHOTO_FILE_ID}) + * </dt> + * <dd> + * <pre> + * public InputStream openDisplayPhoto(long photoFileId) { + * Uri displayPhotoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoKey); + * try { + * AssetFileDescriptor fd = getContentResolver().openAssetFile(displayPhotoUri, "r"); + * return fd.createInputStream(); + * } catch (FileNotFoundException e) { + * return null; + * } + * } + * </pre> + * </dd> + * </dl> + * </p> + */ + public static final class DisplayPhoto { + /** + * no public constructor since this is a utility class + */ + private DisplayPhoto() {} + + /** + * The content:// style URI for this class, which allows access to full-size photos, + * given a key. + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "display_photo"); + + /** + * This URI allows the caller to query for the maximum dimensions of a display photo + * or thumbnail. Requests to this URI can be performed on the UI thread because + * they are always unblocking. + */ + public static final Uri CONTENT_MAX_DIMENSIONS_URI = + Uri.withAppendedPath(AUTHORITY_URI, "photo_dimensions"); + + /** + * Queries to {@link ContactsContract.DisplayPhoto#CONTENT_MAX_DIMENSIONS_URI} will + * contain this column, populated with the maximum height and width (in pixels) + * that will be stored for a display photo. Larger photos will be down-sized to + * fit within a square of this many pixels. + */ + public static final String DISPLAY_MAX_DIM = "display_max_dim"; + + /** + * Queries to {@link ContactsContract.DisplayPhoto#CONTENT_MAX_DIMENSIONS_URI} will + * contain this column, populated with the height and width (in pixels) for photo + * thumbnails. + */ + public static final String THUMBNAIL_MAX_DIM = "thumbnail_max_dim"; + } + + /** * Contains helper classes used to create or manage {@link android.content.Intent Intents} * that involve contacts. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 65babc2..23b53ae 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3833,6 +3833,11 @@ public final class Settings { /** {@hide} */ public static final String NETSTATS_TAG_MAX_HISTORY = "netstats_tag_max_history"; + /** Preferred NTP server. {@hide} */ + public static final String NTP_SERVER = "ntp_server"; + /** Timeout in milliseconds to wait for NTP server. {@hide} */ + public static final String NTP_TIMEOUT = "ntp_timeout"; + /** * @hide */ diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 63c420a..f345a6a 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -660,7 +660,6 @@ class BluetoothEventLoop { case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: if (mBluetoothService.attemptAutoPair(address)) return; } diff --git a/core/java/android/server/BluetoothHealthProfileHandler.java b/core/java/android/server/BluetoothHealthProfileHandler.java index 7f862e0..105ff33 100644 --- a/core/java/android/server/BluetoothHealthProfileHandler.java +++ b/core/java/android/server/BluetoothHealthProfileHandler.java @@ -20,15 +20,12 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHealth; import android.bluetooth.BluetoothHealthAppConfiguration; -import android.bluetooth.BluetoothHealth; -import android.bluetooth.BluetoothInputDevice; +import android.bluetooth.IBluetoothHealthCallback; import android.content.Context; -import android.content.Intent; import android.os.Handler; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import android.provider.Settings; import android.util.Log; import java.util.ArrayList; @@ -36,10 +33,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map.Entry; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; - /** * This handles all the operations on the Bluetooth Health profile. * All functions are called by BluetoothService, as Bluetooth Service @@ -58,6 +51,7 @@ final class BluetoothHealthProfileHandler { private ArrayList<HealthChannel> mHealthChannels; private HashMap <BluetoothHealthAppConfiguration, String> mHealthAppConfigs; private HashMap <BluetoothDevice, Integer> mHealthDevices; + private HashMap <BluetoothHealthAppConfiguration, IBluetoothHealthCallback> mCallbacks; private static final int MESSAGE_REGISTER_APPLICATION = 0; private static final int MESSAGE_UNREGISTER_APPLICATION = 1; @@ -103,6 +97,7 @@ final class BluetoothHealthProfileHandler { } if (path == null) { + mCallbacks.remove(registerApp); callHealthApplicationStatusCallback(registerApp, BluetoothHealth.APPLICATION_REGISTRATION_FAILURE); } else { @@ -118,6 +113,7 @@ final class BluetoothHealthProfileHandler { boolean result = mBluetoothService.unregisterHealthApplicationNative( mHealthAppConfigs.get(unregisterApp)); if (result) { + mCallbacks.remove(unregisterApp); callHealthApplicationStatusCallback(unregisterApp, BluetoothHealth.APPLICATION_UNREGISTRATION_SUCCESS); } else { @@ -149,6 +145,7 @@ final class BluetoothHealthProfileHandler { mHealthAppConfigs = new HashMap<BluetoothHealthAppConfiguration, String>(); mHealthChannels = new ArrayList<HealthChannel>(); mHealthDevices = new HashMap<BluetoothDevice, Integer>(); + mCallbacks = new HashMap<BluetoothHealthAppConfiguration, IBluetoothHealthCallback>(); } static synchronized BluetoothHealthProfileHandler getInstance(Context context, @@ -157,10 +154,12 @@ final class BluetoothHealthProfileHandler { return sInstance; } - boolean registerAppConfiguration(BluetoothHealthAppConfiguration config) { + boolean registerAppConfiguration(BluetoothHealthAppConfiguration config, + IBluetoothHealthCallback callback) { Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_APPLICATION); msg.obj = config; mHandler.sendMessage(msg); + mCallbacks.put(config, callback); return true; } @@ -442,11 +441,11 @@ final class BluetoothHealthProfileHandler { debugLog("Health Device Callback: " + device + " State Change: " + prevState + "->" + state); - try { - config.getCallback().onHealthChannelStateChange(config, device, prevState, - state, fd); - } catch (RemoteException e) { - errorLog("Error while making health channel state change callback: " + e); + IBluetoothHealthCallback callback = mCallbacks.get(config); + if (callback != null) { + try { + callback.onHealthChannelStateChange(config, device, prevState, state, fd); + } catch (RemoteException e) {} } } @@ -454,10 +453,11 @@ final class BluetoothHealthProfileHandler { BluetoothHealthAppConfiguration config, int status) { debugLog("Health Device Application: " + config + " State Change: status:" + status); - try { - config.getCallback().onHealthAppConfigurationStatusChange(config, status); - } catch (RemoteException e) { - errorLog("Error while making health app registration state change callback: " + e); + IBluetoothHealthCallback callback = mCallbacks.get(config); + if (callback != null) { + try { + callback.onHealthAppConfigurationStatusChange(config, status); + } catch (RemoteException e) {} } } @@ -526,19 +526,19 @@ final class BluetoothHealthProfileHandler { List<HealthChannel> chan; switch (currDeviceState) { case BluetoothHealth.STATE_DISCONNECTED: - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); break; case BluetoothHealth.STATE_CONNECTING: // Channel got connected. if (newDeviceState == BluetoothHealth.STATE_CONNECTED) { - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); } else { // Channel got disconnected chan = findChannelByStates(device, new int [] { BluetoothHealth.STATE_CHANNEL_CONNECTING, BluetoothHealth.STATE_CHANNEL_DISCONNECTING}); if (chan.isEmpty()) { - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); } } break; @@ -548,22 +548,23 @@ final class BluetoothHealthProfileHandler { BluetoothHealth.STATE_CHANNEL_CONNECTING, BluetoothHealth.STATE_CHANNEL_CONNECTED}); if (chan.isEmpty()) { - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); } + break; case BluetoothHealth.STATE_DISCONNECTING: // Channel got disconnected. chan = findChannelByStates(device, new int [] { BluetoothHealth.STATE_CHANNEL_CONNECTING, BluetoothHealth.STATE_CHANNEL_DISCONNECTING}); if (chan.isEmpty()) { - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); } break; } } } - private void updateAndsendIntent(BluetoothDevice device, int prevDeviceState, + private void updateAndSendIntent(BluetoothDevice device, int prevDeviceState, int newDeviceState) { mHealthDevices.put(device, newDeviceState); mBluetoothService.sendConnectionStateChange(device, prevDeviceState, newDeviceState); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 9839f76..ff16c18 100644..100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -24,8 +24,6 @@ package android.server; -import com.android.internal.app.IBatteryStats; - import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -40,6 +38,7 @@ import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothHealthCallback; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -58,6 +57,8 @@ import android.provider.Settings; import android.util.Log; import android.util.Pair; +import com.android.internal.app.IBatteryStats; + import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -2111,11 +2112,12 @@ public class BluetoothService extends IBluetooth.Stub { /**** Handlers for Health Device Profile ****/ // TODO: All these need to be converted to a state machine. - public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config) { + public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config, + IBluetoothHealthCallback callback) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); synchronized (mBluetoothHealthProfileHandler) { - return mBluetoothHealthProfileHandler.registerAppConfiguration(config); + return mBluetoothHealthProfileHandler.registerAppConfiguration(config, callback); } } @@ -2401,6 +2403,7 @@ public class BluetoothService extends IBluetooth.Stub { convertToAdapterState(state)); intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, convertToAdapterState(prevState)); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, BLUETOOTH_PERM); Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": " + prevState + " -> " + state); diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index c7603ee..1210941 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -384,21 +384,28 @@ class AudioPlaybackHandler { } count += written; } + param.mBytesWritten += count; + param.mLogger.onPlaybackStart(); } private void handleSynthesisDone(MessageParams msg) { final SynthesisMessageParams params = (SynthesisMessageParams) msg; handleSynthesisDone(params); + // This call is delayed more than it should be, but we are + // certain at this point that we have all the data we want. + params.mLogger.onWriteData(); } - // Flush all remaining data to the audio track, stop it and release - // all it's resources. + // Wait for the audio track to stop playing, and then release it's resources. private void handleSynthesisDone(SynthesisMessageParams params) { if (DBG) Log.d(TAG, "handleSynthesisDone()"); final AudioTrack audioTrack = params.getAudioTrack(); try { if (audioTrack != null) { + if (DBG) Log.d(TAG, "Waiting for audio track to complete : " + + audioTrack.hashCode()); + blockUntilDone(params); if (DBG) Log.d(TAG, "Releasing audio track [" + audioTrack.hashCode() + "]"); // The last call to AudioTrack.write( ) will return only after // all data from the audioTrack has been sent to the mixer, so @@ -412,10 +419,24 @@ class AudioPlaybackHandler { } } + private static void blockUntilDone(SynthesisMessageParams params) { + if (params.mAudioTrack == null || params.mBytesWritten <= 0) { + return; + } + + final AudioTrack track = params.mAudioTrack; + final int bytesPerFrame = getBytesPerFrame(params.mAudioFormat); + final int lengthInBytes = params.mBytesWritten; + + blockUntilDone(track, bytesPerFrame, lengthInBytes); + } + private void handleSynthesisCompleteDataAvailable(MessageParams msg) { final SynthesisMessageParams params = (SynthesisMessageParams) msg; if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")"); + params.mLogger.onPlaybackStart(); + // Channel config and bytes per frame are checked before // this message is sent. int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount); @@ -448,13 +469,18 @@ class AudioPlaybackHandler { } - private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) { - int lengthInFrames = length / bytesPerFrame; + private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, + int lengthInBytes) { + int lengthInFrames = lengthInBytes / bytesPerFrame; int currentPosition = 0; while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) { + if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) { + break; + } + long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) / audioTrack.getSampleRate(); - audioTrack.getPlayState(); + if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," + " Playback position : " + currentPosition); try { diff --git a/core/java/android/speech/tts/EventLogTags.logtags b/core/java/android/speech/tts/EventLogTags.logtags new file mode 100644 index 0000000..1a9f5fe --- /dev/null +++ b/core/java/android/speech/tts/EventLogTags.logtags @@ -0,0 +1,6 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +option java_package android.speech.tts; + +76001 tts_speak_success (engine|3),(caller|3),(length|1),(locale|3),(rate|1),(pitch|1),(engine_latency|2|3),(engine_total|2|3),(audio_latency|2|3) +76002 tts_speak_failure (engine|3),(caller|3),(length|1),(locale|3),(rate|1),(pitch|1) diff --git a/core/java/android/speech/tts/EventLogger.java b/core/java/android/speech/tts/EventLogger.java new file mode 100644 index 0000000..63b954b --- /dev/null +++ b/core/java/android/speech/tts/EventLogger.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2011 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.speech.tts; + +import android.os.SystemClock; +import android.text.TextUtils; + +/** + * Writes data about a given speech synthesis request to the event logs. + * The data that is logged includes the calling app, length of the utterance, + * speech rate / pitch and the latency and overall time taken. + * + * Note that {@link EventLogger#onStopped()} and {@link EventLogger#onError()} + * might be called from any thread, but on {@link EventLogger#onPlaybackStart()} and + * {@link EventLogger#onComplete()} must be called from a single thread + * (usually the audio playback thread} + */ +class EventLogger { + private final SynthesisRequest mRequest; + private final String mCallingApp; + private final String mServiceApp; + private final long mReceivedTime; + private long mPlaybackStartTime = -1; + private volatile long mRequestProcessingStartTime = -1; + private volatile long mEngineStartTime = -1; + private volatile long mEngineCompleteTime = -1; + + private volatile boolean mError = false; + private volatile boolean mStopped = false; + private boolean mLogWritten = false; + + EventLogger(SynthesisRequest request, String callingApp, + String serviceApp) { + mRequest = request; + mCallingApp = callingApp; + mServiceApp = serviceApp; + mReceivedTime = SystemClock.elapsedRealtime(); + } + + /** + * Notifies the logger that this request has been selected from + * the processing queue for processing. Engine latency / total time + * is measured from this baseline. + */ + public void onRequestProcessingStart() { + mRequestProcessingStartTime = SystemClock.elapsedRealtime(); + } + + /** + * Notifies the logger that a chunk of data has been received from + * the engine. Might be called multiple times. + */ + public void onEngineDataReceived() { + if (mEngineStartTime == -1) { + mEngineStartTime = SystemClock.elapsedRealtime(); + } + } + + /** + * Notifies the logger that the engine has finished processing data. + * Will be called exactly once. + */ + public void onEngineComplete() { + mEngineCompleteTime = SystemClock.elapsedRealtime(); + } + + /** + * Notifies the logger that audio playback has started for some section + * of the synthesis. This is normally some amount of time after the engine + * has synthesized data and varides depending on utterances and + * other audio currently in the queue. + */ + public void onPlaybackStart() { + // For now, keep track of only the first chunk of audio + // that was played. + if (mPlaybackStartTime == -1) { + mPlaybackStartTime = SystemClock.elapsedRealtime(); + } + } + + /** + * Notifies the logger that the current synthesis was stopped. + * Latency numbers are not reported for stopped syntheses. + */ + public void onStopped() { + mStopped = false; + } + + /** + * Notifies the logger that the current synthesis resulted in + * an error. This is logged using {@link EventLogTags#writeTtsSpeakFailure}. + */ + public void onError() { + mError = true; + } + + /** + * Notifies the logger that the current synthesis has completed. + * All available data is not logged. + */ + public void onWriteData() { + if (mLogWritten) { + return; + } else { + mLogWritten = true; + } + + long completionTime = SystemClock.elapsedRealtime(); + // onPlaybackStart() should normally always be called if an + // error does not occur. + if (mError || mPlaybackStartTime == -1 || mEngineCompleteTime == -1) { + EventLogTags.writeTtsSpeakFailure(mServiceApp, mCallingApp, + getUtteranceLength(), getLocaleString(), + mRequest.getSpeechRate(), mRequest.getPitch()); + return; + } + + // We don't report stopped syntheses because their overall + // total time spent will be innacurate (will not correlate with + // the length of the utterance). + if (mStopped) { + return; + } + + final long audioLatency = mPlaybackStartTime - mReceivedTime; + final long engineLatency = mEngineStartTime - mRequestProcessingStartTime; + final long engineTotal = mEngineCompleteTime - mRequestProcessingStartTime; + EventLogTags.writeTtsSpeakSuccess(mServiceApp, mCallingApp, + getUtteranceLength(), getLocaleString(), + mRequest.getSpeechRate(), mRequest.getPitch(), + engineLatency, engineTotal, audioLatency); + } + + /** + * @return the length of the utterance for the given synthesis, 0 + * if the utterance was {@code null}. + */ + private int getUtteranceLength() { + final String utterance = mRequest.getText(); + return utterance == null ? 0 : utterance.length(); + } + + /** + * Returns a formatted locale string from the synthesis params of the + * form lang-country-variant. + */ + private String getLocaleString() { + StringBuilder sb = new StringBuilder(mRequest.getLanguage()); + if (!TextUtils.isEmpty(mRequest.getCountry())) { + sb.append('-'); + sb.append(mRequest.getCountry()); + + if (!TextUtils.isEmpty(mRequest.getVariant())) { + sb.append('-'); + sb.append(mRequest.getVariant()); + } + } + + return sb.toString(); + } + +} diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index bdaa1b8..38030a6 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -65,29 +65,42 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { private final UtteranceCompletedDispatcher mDispatcher; private final String mCallingApp; + private final EventLogger mLogger; PlaybackSynthesisCallback(int streamType, float volume, float pan, AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher, - String callingApp) { + String callingApp, EventLogger logger) { mStreamType = streamType; mVolume = volume; mPan = pan; mAudioTrackHandler = audioTrackHandler; mDispatcher = dispatcher; mCallingApp = callingApp; + mLogger = logger; } @Override void stop() { if (DBG) Log.d(TAG, "stop()"); + // Note that mLogger.mError might be true too at this point. + mLogger.onStopped(); + synchronized (mStateLock) { - if (mToken == null || mStopped) { - Log.w(TAG, "stop() called twice, before start(), or after done()"); + if (mStopped) { + Log.w(TAG, "stop() called twice"); return; } - mAudioTrackHandler.stop(mToken); - mToken = null; + // mToken will be null if the engine encounters + // an error before it called start(). + if (mToken != null) { + mAudioTrackHandler.stop(mToken); + mToken = null; + } else { + // In all other cases, mAudioTrackHandler.stop() will + // result in onComplete being called. + mLogger.onWriteData(); + } mStopped = true; } } @@ -124,7 +137,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } SynthesisMessageParams params = new SynthesisMessageParams( mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, - mDispatcher, mCallingApp); + mDispatcher, mCallingApp, mLogger); mAudioTrackHandler.enqueueSynthesisStart(params); mToken = params; @@ -157,6 +170,8 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken); } + mLogger.onEngineDataReceived(); + return TextToSpeech.SUCCESS; } @@ -177,6 +192,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } mAudioTrackHandler.enqueueSynthesisDone(mToken); + mLogger.onEngineComplete(); } return TextToSpeech.SUCCESS; } @@ -184,6 +200,9 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { @Override public void error() { if (DBG) Log.d(TAG, "error() [will call stop]"); + // Currently, this call will not be logged if error( ) is called + // before start. + mLogger.onError(); stop(); } @@ -208,7 +227,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } SynthesisMessageParams params = new SynthesisMessageParams( mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, - mDispatcher, mCallingApp); + mDispatcher, mCallingApp, mLogger); params.addBuffer(buffer, offset, length); mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params); diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java index 51f3d2e..ffe70e2 100644 --- a/core/java/android/speech/tts/SynthesisMessageParams.java +++ b/core/java/android/speech/tts/SynthesisMessageParams.java @@ -30,15 +30,18 @@ final class SynthesisMessageParams extends MessageParams { final int mChannelCount; final float mVolume; final float mPan; + final EventLogger mLogger; - public volatile AudioTrack mAudioTrack; + volatile AudioTrack mAudioTrack; + // Not volatile, accessed only from the synthesis thread. + int mBytesWritten; private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>(); SynthesisMessageParams(int streamType, int sampleRate, int audioFormat, int channelCount, float volume, float pan, UtteranceCompletedDispatcher dispatcher, - String callingApp) { + String callingApp, EventLogger logger) { super(dispatcher, callingApp); mStreamType = streamType; @@ -47,9 +50,11 @@ final class SynthesisMessageParams extends MessageParams { mChannelCount = channelCount; mVolume = volume; mPan = pan; + mLogger = logger; // initially null. mAudioTrack = null; + mBytesWritten = 0; } @Override diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 8e4725f..32ca226 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -181,7 +181,7 @@ public class TextToSpeech { * * @hide * @deprecated No longer in use, the default engine is determined by - * the sort order defined in {@link EngineInfoComparator}. Note that + * the sort order defined in {@link TtsEngines}. Note that * this doesn't "break" anything because there is no guarantee that * the engine specified below is installed on a given build, let * alone be the default. @@ -504,36 +504,39 @@ public class TextToSpeech { } private int initTts() { - String defaultEngine = getDefaultEngine(); - String engine = defaultEngine; - if (mEnginesHelper.isEngineInstalled(mRequestedEngine)) { - engine = mRequestedEngine; - } - - // Try requested engine - if (connectToEngine(engine)) { - mCurrentEngine = engine; - return SUCCESS; + // Step 1: Try connecting to the engine that was requested. + if (mRequestedEngine != null && mEnginesHelper.isEngineInstalled(mRequestedEngine)) { + if (connectToEngine(mRequestedEngine)) { + mCurrentEngine = mRequestedEngine; + return SUCCESS; + } } - // Fall back to user's default engine if different from the already tested one - if (!engine.equals(defaultEngine)) { + // Step 2: Try connecting to the user's default engine. + final String defaultEngine = getDefaultEngine(); + if (defaultEngine != null && !defaultEngine.equals(mRequestedEngine)) { if (connectToEngine(defaultEngine)) { - mCurrentEngine = engine; + mCurrentEngine = defaultEngine; return SUCCESS; } } + // Step 3: Try connecting to the highest ranked engine in the + // system. final String highestRanked = mEnginesHelper.getHighestRankedEngineName(); - // Fall back to the hardcoded default if different from the two above - if (!defaultEngine.equals(highestRanked) - && !engine.equals(highestRanked)) { + if (highestRanked != null && !highestRanked.equals(mRequestedEngine) && + !highestRanked.equals(defaultEngine)) { if (connectToEngine(highestRanked)) { - mCurrentEngine = engine; + mCurrentEngine = highestRanked; return SUCCESS; } } + // NOTE: The API currently does not allow the caller to query whether + // they are actually connected to any engine. This might fail for various + // reasons like if the user disables all her TTS engines. + + mCurrentEngine = null; dispatchOnInit(ERROR); return ERROR; } @@ -963,7 +966,7 @@ public class TextToSpeech { /** * Synthesizes the given text to a file using the specified parameters. * - * @param text Thetext that should be synthesized + * @param text The text that should be synthesized * @param params Parameters for the request. Can be null. * Supported parameter names: * {@link Engine#KEY_PARAM_UTTERANCE_ID}. @@ -1073,7 +1076,9 @@ public class TextToSpeech { * * @deprecated This doesn't inform callers when the TTS engine has been * initialized. {@link #TextToSpeech(Context, OnInitListener, String)} - * can be used with the appropriate engine name. + * can be used with the appropriate engine name. Also, there is no + * guarantee that the engine specified will be loaded. If it isn't + * installed or disabled, the user / system wide defaults will apply. * * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico") * diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 7ea9373..010c155 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -82,8 +82,7 @@ public abstract class TextToSpeechService extends Service { private AudioPlaybackHandler mAudioPlaybackHandler; private CallbackMap mCallbacks; - - private int mDefaultAvailability = TextToSpeech.LANG_NOT_SUPPORTED; + private String mPackageName; @Override public void onCreate() { @@ -99,9 +98,10 @@ public abstract class TextToSpeechService extends Service { mCallbacks = new CallbackMap(); + mPackageName = getApplicationInfo().packageName; + // Load default language - mDefaultAvailability = onLoadLanguage(getDefaultLanguage(), - getDefaultCountry(), getDefaultVariant()); + onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant()); } @Override @@ -457,12 +457,14 @@ public abstract class TextToSpeechService extends Service { // Non null after synthesis has started, and all accesses // guarded by 'this'. private AbstractSynthesisCallback mSynthesisCallback; + private final EventLogger mEventLogger; public SynthesisSpeechItem(String callingApp, Bundle params, String text) { super(callingApp, params); mText = text; mSynthesisRequest = new SynthesisRequest(mText, mParams); setRequestParams(mSynthesisRequest); + mEventLogger = new EventLogger(mSynthesisRequest, getCallingApp(), mPackageName); } public String getText() { @@ -485,6 +487,7 @@ public abstract class TextToSpeechService extends Service { @Override protected int playImpl() { AbstractSynthesisCallback synthesisCallback; + mEventLogger.onRequestProcessingStart(); synchronized (this) { mSynthesisCallback = createSynthesisCallback(); synthesisCallback = mSynthesisCallback; @@ -495,7 +498,7 @@ public abstract class TextToSpeechService extends Service { protected AbstractSynthesisCallback createSynthesisCallback() { return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(), - mAudioPlaybackHandler, this, getCallingApp()); + mAudioPlaybackHandler, this, getCallingApp(), mEventLogger); } private void setRequestParams(SynthesisRequest request) { diff --git a/core/java/android/text/method/AllCapsTransformationMethod.java b/core/java/android/text/method/AllCapsTransformationMethod.java new file mode 100644 index 0000000..f9920dd --- /dev/null +++ b/core/java/android/text/method/AllCapsTransformationMethod.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 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.text.method; + +import android.content.Context; +import android.graphics.Rect; +import android.util.Log; +import android.view.View; + +import java.util.Locale; + +/** + * Transforms source text into an ALL CAPS string, locale-aware. + * + * @hide + */ +public class AllCapsTransformationMethod implements TransformationMethod2 { + private static final String TAG = "AllCapsTransformationMethod"; + + private boolean mEnabled; + private Locale mLocale; + + public AllCapsTransformationMethod(Context context) { + mLocale = context.getResources().getConfiguration().locale; + } + + @Override + public CharSequence getTransformation(CharSequence source, View view) { + if (mEnabled) { + return source != null ? source.toString().toUpperCase(mLocale) : null; + } + Log.w(TAG, "Caller did not enable length changes; not transforming text"); + return source; + } + + @Override + public void onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, + Rect previouslyFocusedRect) { + } + + @Override + public void setLengthChangesAllowed(boolean allowLengthChanges) { + mEnabled = allowLengthChanges; + } + +} diff --git a/core/java/android/text/method/TransformationMethod2.java b/core/java/android/text/method/TransformationMethod2.java new file mode 100644 index 0000000..ef00ecd --- /dev/null +++ b/core/java/android/text/method/TransformationMethod2.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2011 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.text.method; + +/** + * TransformationMethod2 extends the TransformationMethod interface + * and adds the ability to relax restrictions of TransformationMethod. + * + * @hide + */ +public interface TransformationMethod2 extends TransformationMethod { + /** + * Relax the contract of TransformationMethod to allow length changes, + * or revert to the length-restricted behavior. + * + * @param allowLengthChanges true to allow the transformation to change the length + * of the input string. + */ + public void setLengthChangesAllowed(boolean allowLengthChanges); +} diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index 240ad9b..555aac5 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -22,12 +22,22 @@ import android.os.Parcelable; import android.os.SystemClock; import android.text.ParcelableSpan; import android.text.TextUtils; +import android.widget.TextView; import java.util.Arrays; import java.util.Locale; /** - * Holds suggestion candidates of words under this span. + * Holds suggestion candidates for the text enclosed in this span. + * + * When such a span is edited in an EditText, double tapping on the text enclosed in this span will + * display a popup dialog listing suggestion replacement for that text. The user can then replace + * the original text by one of the suggestions. + * + * These spans should typically be created by the input method to privide correction and alternates + * for the text. + * + * @see TextView#setSuggestionsEnabled(boolean) */ public class SuggestionSpan implements ParcelableSpan { /** @@ -115,14 +125,14 @@ public class SuggestionSpan implements ParcelableSpan { } /** - * @return suggestions + * @return an array of suggestion texts for this span */ public String[] getSuggestions() { return mSuggestions; } /** - * @return locale of suggestions + * @return the locale of the suggestions */ public String getLocale() { return mLocaleString; diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 5b19ecd..2179ff3 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -16,41 +16,71 @@ package android.util; +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; import android.net.SntpClient; import android.os.SystemClock; +import android.provider.Settings; /** - * {@link TrustedTime} that connects with a remote NTP server as its remote - * trusted time source. + * {@link TrustedTime} that connects with a remote NTP server as its trusted + * time source. * * @hide */ public class NtpTrustedTime implements TrustedTime { - private String mNtpServer; - private long mNtpTimeout; + private static final String TAG = "NtpTrustedTime"; + private static final boolean LOGD = false; + + private static NtpTrustedTime sSingleton; + + private final String mServer; + private final long mTimeout; private boolean mHasCache; private long mCachedNtpTime; private long mCachedNtpElapsedRealtime; private long mCachedNtpCertainty; - public NtpTrustedTime() { + private NtpTrustedTime(String server, long timeout) { + if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server); + mServer = server; + mTimeout = timeout; } - public void setNtpServer(String server, long timeout) { - mNtpServer = server; - mNtpTimeout = timeout; + public static synchronized NtpTrustedTime getInstance(Context context) { + if (sSingleton == null) { + final Resources res = context.getResources(); + final ContentResolver resolver = context.getContentResolver(); + + final String defaultServer = res.getString( + com.android.internal.R.string.config_ntpServer); + final long defaultTimeout = res.getInteger( + com.android.internal.R.integer.config_ntpTimeout); + + final String secureServer = Settings.Secure.getString( + resolver, Settings.Secure.NTP_SERVER); + final long timeout = Settings.Secure.getLong( + resolver, Settings.Secure.NTP_TIMEOUT, defaultTimeout); + + final String server = secureServer != null ? secureServer : defaultServer; + sSingleton = new NtpTrustedTime(server, timeout); + } + + return sSingleton; } /** {@inheritDoc} */ public boolean forceRefresh() { - if (mNtpServer == null) { + if (mServer == null) { // missing server, so no trusted time available return false; } + if (LOGD) Log.d(TAG, "forceRefresh() from cache miss"); final SntpClient client = new SntpClient(); - if (client.requestTime(mNtpServer, (int) mNtpTimeout)) { + if (client.requestTime(mServer, (int) mTimeout)) { mHasCache = true; mCachedNtpTime = client.getNtpTime(); mCachedNtpElapsedRealtime = client.getNtpTimeReference(); @@ -89,9 +119,19 @@ public class NtpTrustedTime implements TrustedTime { if (!mHasCache) { throw new IllegalStateException("Missing authoritative time source"); } + if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit"); // current time is age after the last ntp cache; callers who // want fresh values will hit makeAuthoritative() first. return mCachedNtpTime + getCacheAge(); } + + public long getCachedNtpTime() { + if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit"); + return mCachedNtpTime; + } + + public long getCachedNtpTimeReference() { + return mCachedNtpElapsedRealtime; + } } diff --git a/core/java/android/view/ActionProvider.java b/core/java/android/view/ActionProvider.java index 6491da0..5601dc5 100644 --- a/core/java/android/view/ActionProvider.java +++ b/core/java/android/view/ActionProvider.java @@ -28,7 +28,9 @@ import android.content.Context; * {@link android.app.ActionBar} as a substitute for the menu item when the item is * displayed as an action item. Also the provider is responsible for performing a * default action if a menu item placed on the overflow menu of the ActionBar is - * selected and none of the menu item callbacks has handled the selection. + * selected and none of the menu item callbacks has handled the selection. For this + * case the provider can also optionally provide a sub-menu for accomplishing the + * task at hand. * </p> * <p> * There are two ways for using an action provider for creating and handling of action views: @@ -76,7 +78,7 @@ public abstract class ActionProvider { * Performs an optional default action. * <p> * For the case of an action provider placed in a menu item not shown as an action this - * method is invoked if none of the callbacks for processing menu selection has handled + * method is invoked if previous callbacks for processing menu selection has handled * the event. * </p> * <p> @@ -104,11 +106,36 @@ public abstract class ActionProvider { * </ul> * </p> * <p> - * The default implementation does not perform any action. + * The default implementation does not perform any action and returns false. * </p> + */ + public boolean onPerformDefaultAction() { + return false; + } + + /** + * Determines if this ActionProvider has a submenu associated with it. + * + * <p>Associated submenus will be shown when an action view is not. This + * provider instance will receive a call to {@link #onPrepareSubMenu(SubMenu)} + * after the call to {@link #onPerformDefaultAction()} and before a submenu is + * displayed to the user. + * + * @return true if the item backed by this provider should have an associated submenu + */ + public boolean hasSubMenu() { + return false; + } + + /** + * Called to prepare an associated submenu for the menu item backed by this ActionProvider. + * + * <p>if {@link #hasSubMenu()} returns true, this method will be called when the + * menu item is selected to prepare the submenu for presentation to the user. Apps + * may use this to create or alter submenu content right before display. * - * @param actionView A view created by {@link #onCreateActionView()}. + * @param subMenu Submenu that will be displayed */ - public void onPerformDefaultAction(View actionView) { + public void onPrepareSubMenu(SubMenu subMenu) { } } diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index b6c5522..4987e2f 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -246,8 +246,19 @@ class GLES20Canvas extends HardwareCanvas { return nIsBackBufferPreserved(); } - private static native boolean nIsBackBufferPreserved(); - + private static native boolean nIsBackBufferPreserved(); + + /** + * Disables v-sync. For performance testing only. + * + * @hide + */ + public static void disableVsync() { + nDisableVsync(); + } + + private static native void nDisableVsync(); + @Override void onPreDraw(Rect dirty) { if (dirty != null) { @@ -265,7 +276,7 @@ class GLES20Canvas extends HardwareCanvas { void onPostDraw() { nFinish(mRenderer); } - + private static native void nFinish(int renderer); @Override diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index bbfb4c1..011e44c 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -57,6 +57,16 @@ public abstract class HardwareRenderer { * "false", to disable partial invalidates */ static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions"; + + /** + * System property used to enable or disable vsync. + * The default value of this property is assumed to be false. + * + * Possible values: + * "true", to disable vsync + * "false", to enable vsync + */ + static final String DISABLE_VSYNC_PROPERTY = "hwui.disable_vsync"; /** * Turn on to draw dirty regions every other frame. @@ -118,8 +128,23 @@ public abstract class HardwareRenderer { abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException; /** - * Setup the hardware renderer for drawing. This is called for every - * frame to draw. + * This method should be invoked whenever the current hardware renderer + * context should be reset. + */ + abstract void invalidate(); + + /** + * This method should be invoked to ensure the hardware renderer is in + * valid state (for instance, to ensure the correct EGL context is bound + * to the current thread.) + * + * @return true if the renderer is now valid, false otherwise + */ + abstract boolean validate(); + + /** + * Setup the hardware renderer for drawing. This is called whenever the + * size of the target surface changes or when the surface is first created. * * @param width Width of the drawing surface. * @param height Height of the drawing surface. @@ -279,9 +304,9 @@ public abstract class HardwareRenderer { static abstract class GlRenderer extends HardwareRenderer { // These values are not exposed in our EGL APIs static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - static final int EGL_SURFACE_TYPE = 0x3033; - static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400; static final int EGL_OPENGL_ES2_BIT = 4; + static final int EGL_SURFACE_TYPE = 0x3033; + static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400; private static final int SURFACE_STATE_ERROR = 0; private static final int SURFACE_STATE_SUCCESS = 1; @@ -301,8 +326,17 @@ public abstract class HardwareRenderer { int mFrameCount; Paint mDebugPaint; - boolean mDirtyRegions; - final boolean mDirtyRegionsRequested; + static boolean sDirtyRegions; + static final boolean sDirtyRegionsRequested; + static { + String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); + //noinspection PointlessBooleanExpression,ConstantConditions + sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); + sDirtyRegionsRequested = sDirtyRegions; + } + + boolean mDirtyRegionsEnabled; + final boolean mVsyncDisabled; final int mGlVersion; final boolean mTranslucent; @@ -314,17 +348,19 @@ public abstract class HardwareRenderer { GlRenderer(int glVersion, boolean translucent) { mGlVersion = glVersion; mTranslucent = translucent; - final String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); - //noinspection PointlessBooleanExpression,ConstantConditions - mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); - mDirtyRegionsRequested = mDirtyRegions; + + final String vsyncProperty = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false"); + mVsyncDisabled = "true".equalsIgnoreCase(vsyncProperty); + if (mVsyncDisabled) { + Log.d(LOG_TAG, "Disabling v-sync"); + } } /** * Indicates whether this renderer instance can track and update dirty regions. */ boolean hasDirtyRegions() { - return mDirtyRegions; + return mDirtyRegionsEnabled; } /** @@ -461,8 +497,8 @@ public abstract class HardwareRenderer { sEglConfig = chooseEglConfig(); if (sEglConfig == null) { // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without - if (mDirtyRegions) { - mDirtyRegions = false; + if (sDirtyRegions) { + sDirtyRegions = false; sEglConfig = chooseEglConfig(); if (sEglConfig == null) { throw new RuntimeException("eglConfig not initialized"); @@ -482,7 +518,7 @@ public abstract class HardwareRenderer { private EGLConfig chooseEglConfig() { int[] configsCount = new int[1]; EGLConfig[] configs = new EGLConfig[1]; - int[] configSpec = getConfig(mDirtyRegions); + int[] configSpec = getConfig(sDirtyRegions); if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) { throw new IllegalArgumentException("eglChooseConfig failed " + getEGLErrorString(sEgl.eglGetError())); @@ -548,18 +584,18 @@ public abstract class HardwareRenderer { // If mDirtyRegions is set, this means we have an EGL configuration // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set - if (mDirtyRegions) { - if (!GLES20Canvas.preserveBackBuffer()) { + if (sDirtyRegions) { + if (!(mDirtyRegionsEnabled = GLES20Canvas.preserveBackBuffer())) { Log.w(LOG_TAG, "Backbuffer cannot be preserved"); } - } else if (mDirtyRegionsRequested) { + } else if (sDirtyRegionsRequested) { // If mDirtyRegions is not set, our EGL configuration does not // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default // swap behavior might be EGL_BUFFER_PRESERVED, which means we // want to set mDirtyRegions. We try to do this only if dirty // regions were initially requested as part of the device // configuration (see RENDER_DIRTY_REGIONS) - mDirtyRegions = GLES20Canvas.isBackBufferPreserved(); + mDirtyRegionsEnabled = GLES20Canvas.isBackBufferPreserved(); } return sEglContext.getGL(); @@ -602,6 +638,19 @@ public abstract class HardwareRenderer { } @Override + void invalidate() { + // Cancels any existing buffer to ensure we'll get a buffer + // of the right size before we call eglSwapBuffers + sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + } + + @Override + boolean validate() { + return checkCurrent() != SURFACE_STATE_ERROR; + } + + @Override void setup(int width, int height) { mCanvas.setViewport(width, height); } @@ -627,7 +676,7 @@ public abstract class HardwareRenderer { attachInfo.mDrawingTime = SystemClock.uptimeMillis(); view.mPrivateFlags |= View.DRAWN; - + final int surfaceState = checkCurrent(); if (surfaceState != SURFACE_STATE_ERROR) { // We had to change the current surface and/or context, redraw everything @@ -688,10 +737,21 @@ public abstract class HardwareRenderer { } } } - + + /** + * Ensures the currnet EGL context is the one we expect. + * + * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current, + * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or + * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one + */ private int checkCurrent() { - // TODO: Don't check the current context when we have one per UI thread - // TODO: Use a threadlocal flag to know whether the surface has changed + if (sEglThread != Thread.currentThread()) { + throw new IllegalStateException("Hardware acceleration can only be used with a " + + "single UI thread.\nOriginal thread: " + sEglThread + "\n" + + "Current thread: " + Thread.currentThread()); + } + if (!sEglContext.equals(sEgl.eglGetCurrentContext()) || !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) { if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) { @@ -765,6 +825,14 @@ public abstract class HardwareRenderer { } @Override + void setup(int width, int height) { + super.setup(width, height); + if (mVsyncDisabled) { + GLES20Canvas.disableVsync(); + } + } + + @Override DisplayList createDisplayList(View v) { return new GLES20DisplayList(v); } diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java index a7f0cba..c46e43a 100644 --- a/core/java/android/view/MenuInflater.java +++ b/core/java/android/view/MenuInflater.java @@ -166,7 +166,12 @@ public class MenuInflater { // Add the item if it hasn't been added (if the item was // a submenu, it would have been added already) if (!menuState.hasAddedItem()) { - menuState.addItem(); + if (menuState.itemActionProvider != null && + menuState.itemActionProvider.hasSubMenu()) { + menuState.addSubMenuItem(); + } else { + menuState.addItem(); + } } } else if (tagName.equals(XML_MENU)) { reachedEndOfMenu = true; @@ -270,6 +275,8 @@ public class MenuInflater { private String itemListenerMethodName; + private ActionProvider itemActionProvider; + private static final int defaultGroupId = NO_ID; private static final int defaultItemId = NO_ID; private static final int defaultItemCategory = 0; @@ -347,6 +354,19 @@ public class MenuInflater { itemActionViewClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionViewClass); itemActionProviderClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionProviderClass); + final boolean hasActionProvider = itemActionProviderClassName != null; + if (hasActionProvider && itemActionViewLayout == 0 && itemActionViewClassName == null) { + itemActionProvider = newInstance(itemActionProviderClassName, + ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE, + mActionProviderConstructorArguments); + } else { + if (hasActionProvider) { + Log.w(LOG_TAG, "Ignoring attribute 'actionProviderClass'." + + " Action view already specified."); + } + itemActionProvider = null; + } + a.recycle(); itemAdded = false; @@ -406,16 +426,8 @@ public class MenuInflater { + " Action view already specified."); } } - if (itemActionProviderClassName != null) { - if (!actionViewSpecified) { - ActionProvider actionProvider = newInstance(itemActionProviderClassName, - ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE, - mActionProviderConstructorArguments); - item.setActionProvider(actionProvider); - } else { - Log.w(LOG_TAG, "Ignoring attribute 'itemActionProviderClass'." - + " Action view already specified."); - } + if (itemActionProvider != null) { + item.setActionProvider(itemActionProvider); } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1245898..74dc100 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4474,7 +4474,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit @RemotableViewMethod public void setLayoutDirection(int layoutDirection) { if (getLayoutDirection() != layoutDirection) { - resetLayoutDirectionResolution(); + resetResolvedLayoutDirection(); // Setting the flag will also request a layout. setFlags(layoutDirection, LAYOUT_DIRECTION_MASK); } @@ -9043,10 +9043,8 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH; } jumpDrawablesToCurrentState(); - resetLayoutDirectionResolution(); resolveLayoutDirectionIfNeeded(); resolvePadding(); - resetResolvedTextDirection(); resolveTextDirection(); if (isFocused()) { InputMethodManager imm = InputMethodManager.peekInstance(); @@ -9143,7 +9141,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * * @hide */ - protected void resetLayoutDirectionResolution() { + protected void resetResolvedLayoutDirection() { // Reset the current View resolution mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED; } @@ -9190,6 +9188,9 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } mCurrentAnimation = null; + + resetResolvedLayoutDirection(); + resetResolvedTextDirection(); } /** diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java index d70c798..1dcbc26 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewAncestor.java @@ -901,6 +901,7 @@ public final class ViewAncestor extends Handler implements ViewParent, !mAttachInfo.mTurnOffWindowResizeAnim && mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled() && + mAttachInfo.mHardwareRenderer.validate() && lp != null && !PixelFormat.formatHasAlpha(lp.format)) { disposeResizeBuffer(); @@ -1315,6 +1316,9 @@ public final class ViewAncestor extends Handler implements ViewParent, mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled())) { mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight); + if (!hwInitialized) { + mAttachInfo.mHardwareRenderer.invalidate(); + } } if (!mStopped) { @@ -3845,10 +3849,6 @@ public final class ViewAncestor extends Handler implements ViewParent, } private static int checkCallingPermission(String permission) { - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } - try { return ActivityManagerNative.getDefault().checkPermission( permission, Binder.getCallingPid(), Binder.getCallingUid()); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 41412de..752fd5a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -46,7 +46,6 @@ import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.HashSet; -import java.util.Locale; /** * <p> @@ -5000,15 +4999,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - protected void resetLayoutDirectionResolution() { - super.resetLayoutDirectionResolution(); + protected void resetResolvedLayoutDirection() { + super.resetResolvedLayoutDirection(); // Take care of resetting the children resolution too final int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getLayoutDirection() == LAYOUT_DIRECTION_INHERIT) { - child.resetLayoutDirectionResolution(); + child.resetResolvedLayoutDirection(); } } } @@ -5019,7 +5018,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override protected void resolveTextDirection() { - int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED; + int resolvedTextDirection; switch(mTextDirection) { default: case TEXT_DIRECTION_INHERIT: diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index 0294e3f..88583df 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -120,6 +120,7 @@ class CallbackProxy extends Handler { private static final int AUTO_LOGIN = 140; private static final int CLIENT_CERT_REQUEST = 141; private static final int SEARCHBOX_IS_SUPPORTED_CALLBACK = 142; + private static final int SEARCHBOX_DISPATCH_COMPLETE_CALLBACK= 143; // Message triggered by the client to resume execution private static final int NOTIFY = 200; @@ -821,6 +822,13 @@ class CallbackProxy extends Handler { searchBox.handleIsSupportedCallback(supported); break; } + case SEARCHBOX_DISPATCH_COMPLETE_CALLBACK: { + SearchBoxImpl searchBox = (SearchBoxImpl) mWebView.getSearchBox(); + Boolean success = (Boolean) msg.obj; + searchBox.handleDispatchCompleteCallback(msg.getData().getString("function"), + msg.getData().getInt("id"), success); + break; + } } } @@ -1641,4 +1649,13 @@ class CallbackProxy extends Handler { msg.obj = new Boolean(isSupported); sendMessage(msg); } + + void onSearchboxDispatchCompleteCallback(String function, int id, boolean success) { + Message msg = obtainMessage(SEARCHBOX_DISPATCH_COMPLETE_CALLBACK); + msg.obj = Boolean.valueOf(success); + msg.getData().putString("function", function); + msg.getData().putInt("id", id); + + sendMessage(msg); + } } diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java index 4c42cde..a1c6a53 100644 --- a/core/java/android/webkit/L10nUtils.java +++ b/core/java/android/webkit/L10nUtils.java @@ -74,7 +74,19 @@ public class L10nUtils { com.android.internal.R.string.autofill_country_code_re, // IDS_AUTOFILL_COUNTRY_CODE_RE com.android.internal.R.string.autofill_area_code_notext_re, // IDS_AUTOFILL_AREA_CODE_NOTEXT_RE com.android.internal.R.string.autofill_phone_prefix_separator_re, // IDS_AUTOFILL_PHONE_PREFIX_SEPARATOR_RE - com.android.internal.R.string.autofill_phone_suffix_separator_re // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE + com.android.internal.R.string.autofill_phone_suffix_separator_re, // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE + com.android.internal.R.string.autofill_province, // IDS_AUTOFILL_DIALOG_PROVINCE + com.android.internal.R.string.autofill_postal_code, // IDS_AUTOFILL_DIALOG_POSTAL_CODE + com.android.internal.R.string.autofill_state, // IDS_AUTOFILL_DIALOG_STATE + com.android.internal.R.string.autofill_zip_code, // IDS_AUTOFILL_DIALOG_ZIP_CODE + com.android.internal.R.string.autofill_county, // IDS_AUTOFILL_DIALOG_COUNTY + com.android.internal.R.string.autofill_island, // IDS_AUTOFILL_DIALOG_ISLAND + com.android.internal.R.string.autofill_district, // IDS_AUTOFILL_DIALOG_DISTRICT + com.android.internal.R.string.autofill_department, // IDS_AUTOFILL_DIALOG_DEPARTMENT + com.android.internal.R.string.autofill_prefecture, // IDS_AUTOFILL_DIALOG_PREFECTURE + com.android.internal.R.string.autofill_parish, // IDS_AUTOFILL_DIALOG_PARISH + com.android.internal.R.string.autofill_area, // IDS_AUTOFILL_DIALOG_AREA + com.android.internal.R.string.autofill_emirate // IDS_AUTOFILL_DIALOG_EMIRATE }; private static Context mApplicationContext; diff --git a/core/java/android/webkit/SearchBox.java b/core/java/android/webkit/SearchBox.java index 5075302..6512c4b 100644 --- a/core/java/android/webkit/SearchBox.java +++ b/core/java/android/webkit/SearchBox.java @@ -68,11 +68,15 @@ public interface SearchBox { * Notify the search page of any changes to the searchbox. Such as * a change in the typed query (onchange), the user commiting a given query * (onsubmit), or a change in size of a suggestions dropdown (onresize). + * + * @param listener an optional listener to notify of the success of the operation, + * indicating if the javascript function existed and could be called or not. + * It will be called on the UI thread. */ - void onchange(); - void onsubmit(); - void onresize(); - void oncancel(); + void onchange(SearchBoxListener listener); + void onsubmit(SearchBoxListener listener); + void onresize(SearchBoxListener listener); + void oncancel(SearchBoxListener listener); /** * Add and remove listeners to the given Searchbox. Listeners are notified @@ -91,8 +95,12 @@ public interface SearchBox { * Listeners (if any) will be called on the thread that created the * webview. */ - interface SearchBoxListener { - void onSuggestionsReceived(String query, List<String> suggestions); + public abstract class SearchBoxListener { + public void onSuggestionsReceived(String query, List<String> suggestions) {} + public void onChangeComplete(boolean called) {} + public void onSubmitComplete(boolean called) {} + public void onResizeComplete(boolean called) {} + public void onCancelComplete(boolean called) {} } interface IsSupportedCallback { diff --git a/core/java/android/webkit/SearchBoxImpl.java b/core/java/android/webkit/SearchBoxImpl.java index 61fb2ce..9942d25 100644 --- a/core/java/android/webkit/SearchBoxImpl.java +++ b/core/java/android/webkit/SearchBoxImpl.java @@ -16,10 +16,12 @@ package android.webkit; +import android.text.TextUtils; import android.util.Log; import android.webkit.WebViewCore.EventHub; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import org.json.JSONArray; @@ -69,7 +71,7 @@ final class SearchBoxImpl implements SearchBox { private static final String SET_VERBATIM_SCRIPT = "if (window.chrome && window.chrome.searchBox) {" - + " window.chrome.searchBox.verbatim = %s;" + + " window.chrome.searchBox.verbatim = %1$s;" + "}"; private static final String SET_SELECTION_SCRIPT @@ -89,13 +91,21 @@ final class SearchBoxImpl implements SearchBox { + "}"; private static final String DISPATCH_EVENT_SCRIPT - = "if (window.chrome && window.chrome.searchBox &&" - + " window.chrome.searchBox.on%1$s) { window.chrome.searchBox.on%1$s(); }"; + = "if (window.chrome && window.chrome.searchBox && window.chrome.searchBox.on%1$s) {" + + " window.chrome.searchBox.on%1$s();" + + " window.searchBoxJavaBridge_.dispatchCompleteCallback('%1$s', %2$d, true);" + + "} else {" + + " window.searchBoxJavaBridge_.dispatchCompleteCallback('%1$s', %2$d, false);" + + "}"; + + private static final String EVENT_CHANGE = "change"; + private static final String EVENT_SUBMIT = "submit"; + private static final String EVENT_RESIZE = "resize"; + private static final String EVENT_CANCEL = "cancel"; private static final String IS_SUPPORTED_SCRIPT = "if (window.searchBoxJavaBridge_) {" - + " if (window.chrome && window.chrome.searchBox && " - + " window.chrome.searchBox.onsubmit) {" + + " if (window.chrome && window.chrome.sv) {" + " window.searchBoxJavaBridge_.isSupportedCallback(true);" + " } else {" + " window.searchBoxJavaBridge_.isSupportedCallback(false);" @@ -105,11 +115,14 @@ final class SearchBoxImpl implements SearchBox { private final WebViewCore mWebViewCore; private final CallbackProxy mCallbackProxy; private IsSupportedCallback mSupportedCallback; + private int mNextEventId = 1; + private final HashMap<Integer, SearchBoxListener> mEventCallbacks; SearchBoxImpl(WebViewCore webViewCore, CallbackProxy callbackProxy) { mListeners = new ArrayList<SearchBoxListener>(); mWebViewCore = webViewCore; mCallbackProxy = callbackProxy; + mEventCallbacks = new HashMap<Integer, SearchBoxListener>(); } @Override @@ -141,27 +154,36 @@ final class SearchBoxImpl implements SearchBox { } @Override - public void onchange() { - dispatchEvent("change"); + public void onchange(SearchBoxListener callback) { + dispatchEvent(EVENT_CHANGE, callback); } @Override - public void onsubmit() { - dispatchEvent("submit"); + public void onsubmit(SearchBoxListener callback) { + dispatchEvent(EVENT_SUBMIT, callback); } @Override - public void onresize() { - dispatchEvent("resize"); + public void onresize(SearchBoxListener callback) { + dispatchEvent(EVENT_RESIZE, callback); } @Override - public void oncancel() { - dispatchEvent("cancel"); + public void oncancel(SearchBoxListener callback) { + dispatchEvent(EVENT_CANCEL, callback); } - private void dispatchEvent(String eventName) { - final String js = String.format(DISPATCH_EVENT_SCRIPT, eventName); + private void dispatchEvent(String eventName, SearchBoxListener callback) { + int eventId; + if (callback != null) { + synchronized(this) { + eventId = mNextEventId++; + mEventCallbacks.put(eventId, callback); + } + } else { + eventId = 0; + } + final String js = String.format(DISPATCH_EVENT_SCRIPT, eventName, eventId); dispatchJs(js); } @@ -202,9 +224,35 @@ final class SearchBoxImpl implements SearchBox { } } + // Called by Javascript through the Java bridge. + public void dispatchCompleteCallback(String function, int id, boolean successful) { + mCallbackProxy.onSearchboxDispatchCompleteCallback(function, id, successful); + } + + public void handleDispatchCompleteCallback(String function, int id, boolean successful) { + if (id != 0) { + SearchBoxListener listener; + synchronized(this) { + listener = mEventCallbacks.get(id); + mEventCallbacks.remove(id); + } + if (listener != null) { + if (TextUtils.equals(EVENT_CHANGE, function)) { + listener.onChangeComplete(successful); + } else if (TextUtils.equals(EVENT_SUBMIT, function)) { + listener.onSubmitComplete(successful); + } else if (TextUtils.equals(EVENT_RESIZE, function)) { + listener.onResizeComplete(successful); + } else if (TextUtils.equals(EVENT_CANCEL, function)) { + listener.onCancelComplete(successful); + } + } + } + } + // This is used as a hackish alternative to javascript escaping. // There appears to be no such functionality in the core framework. - private String jsonSerialize(String query) { + private static String jsonSerialize(String query) { JSONStringer stringer = new JSONStringer(); try { stringer.array().value(query).endArray(); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 7ba86a5..3ae10fe 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -9089,6 +9089,52 @@ public class WebView extends AbsoluteLayout } } + /** + * Begin collecting per-tile profiling data + * + * @hide only used by profiling tests + */ + public void tileProfilingStart() { + nativeTileProfilingStart(); + } + /** + * Return per-tile profiling data + * + * @hide only used by profiling tests + */ + public float tileProfilingStop() { + return nativeTileProfilingStop(); + } + + /** @hide only used by profiling tests */ + public void tileProfilingClear() { + nativeTileProfilingClear(); + } + /** @hide only used by profiling tests */ + public int tileProfilingNumFrames() { + return nativeTileProfilingNumFrames(); + } + /** @hide only used by profiling tests */ + public int tileProfilingNumTilesInFrame(int frame) { + return nativeTileProfilingNumTilesInFrame(frame); + } + /** @hide only used by profiling tests */ + public int tileProfilingGetX(int frame, int tile) { + return nativeTileProfilingGetX(frame, tile); + } + /** @hide only used by profiling tests */ + public int tileProfilingGetY(int frame, int tile) { + return nativeTileProfilingGetY(frame, tile); + } + /** @hide only used by profiling tests */ + public boolean tileProfilingGetReady(int frame, int tile) { + return nativeTileProfilingGetReady(frame, tile); + } + /** @hide only used by profiling tests */ + public int tileProfilingGetLevel(int frame, int tile) { + return nativeTileProfilingGetLevel(frame, tile); + } + private native int nativeCacheHitFramePointer(); private native boolean nativeCacheHitIsPlugin(); private native Rect nativeCacheHitNodeBounds(); @@ -9211,6 +9257,15 @@ public class WebView extends AbsoluteLayout private native void nativeStopGL(); private native Rect nativeSubtractLayers(Rect content); private native int nativeTextGeneration(); + private native void nativeTileProfilingStart(); + private native float nativeTileProfilingStop(); + private native void nativeTileProfilingClear(); + private native int nativeTileProfilingNumFrames(); + private native int nativeTileProfilingNumTilesInFrame(int frame); + private native int nativeTileProfilingGetX(int frame, int tile); + private native int nativeTileProfilingGetY(int frame, int tile); + private native boolean nativeTileProfilingGetReady(int frame, int tile); + private native int nativeTileProfilingGetLevel(int frame, int tile); // Never call this version except by updateCachedTextfield(String) - // we always want to pass in our generation number. private native void nativeUpdateCachedTextfield(String updatedText, diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 2145edd..5414b79 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -248,7 +248,7 @@ public final class WebViewCore { /* Get the BrowserFrame component. This is used for subwindow creation and * is called only from BrowserFrame in the WebCore thread. */ - /* package */ BrowserFrame getBrowserFrame() { + /* package */ synchronized BrowserFrame getBrowserFrame() { return mBrowserFrame; } @@ -2240,6 +2240,10 @@ public final class WebViewCore { } private void setupViewport(boolean updateViewState) { + if (mWebView == null || mSettings == null) { + // We've been destroyed or are being destroyed, return early + return; + } // set the viewport settings from WebKit setViewportSettingsFromNative(); diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 252fc8f..49ea944 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -300,7 +300,7 @@ class ZoomManager { } public final float getDefaultScale() { - return mDefaultScale; + return mInitialScale > 0 ? mInitialScale : mDefaultScale; } public final float getReadingLevelScale() { @@ -344,6 +344,8 @@ class ZoomManager { public final void setInitialScaleInPercent(int scaleInPercent) { mInitialScale = scaleInPercent * 0.01f; + mActualScale = mInitialScale > 0 ? mInitialScale : mDefaultScale; + mInvActualScale = 1 / mActualScale; } public final float computeScaleWithLimits(float scale) { @@ -1087,6 +1089,7 @@ class ZoomManager { float scale; if (mInitialScale > 0) { scale = mInitialScale; + mTextWrapScale = scale; } else if (viewState.mViewScale > 0) { mTextWrapScale = viewState.mTextWrapScale; scale = viewState.mViewScale; @@ -1105,7 +1108,7 @@ class ZoomManager { } boolean reflowText = false; if (!viewState.mIsRestored) { - if (settings.getUseFixedViewport()) { + if (settings.getUseFixedViewport() && mInitialScale == 0) { // Override the scale only in case of fixed viewport. scale = Math.max(scale, overviewScale); mTextWrapScale = Math.max(mTextWrapScale, overviewScale); diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index 83f80ff..32c44d8 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -126,7 +126,7 @@ public class ActivityChooserModel extends DataSetObservable { */ // This cannot be done by a simple comparator since an Activity weight // is computed from history. Note that Activity implements Comparable. - public void sort(Intent intent, List<Activity> activities, + public void sort(Intent intent, List<ActivityResolveInfo> activities, List<HistoricalRecord> historicalRecords); } @@ -215,7 +215,7 @@ public class ActivityChooserModel extends DataSetObservable { /** * List of activities that can handle the current intent. */ - private final List<Activity> mActivitys = new ArrayList<Activity>(); + private final List<ActivityResolveInfo> mActivites = new ArrayList<ActivityResolveInfo>(); /** * List with historical choice records. @@ -311,9 +311,6 @@ public class ActivityChooserModel extends DataSetObservable { * @return The model. */ public static ActivityChooserModel get(Context context, String historyFileName) { - if (historyFileName == null) { - return new ActivityChooserModel(context, historyFileName); - } synchronized (sRegistryLock) { ActivityChooserModel dataModel = sDataModelRegistry.get(historyFileName); if (dataModel == null) { @@ -380,7 +377,7 @@ public class ActivityChooserModel extends DataSetObservable { */ public int getActivityCount() { synchronized (mInstanceLock) { - return mActivitys.size(); + return mActivites.size(); } } @@ -389,12 +386,12 @@ public class ActivityChooserModel extends DataSetObservable { * * @return The activity. * - * @see Activity + * @see ActivityResolveInfo * @see #setIntent(Intent) */ public ResolveInfo getActivity(int index) { synchronized (mInstanceLock) { - return mActivitys.get(index).resolveInfo; + return mActivites.get(index).resolveInfo; } } @@ -406,10 +403,10 @@ public class ActivityChooserModel extends DataSetObservable { * @return The index if found, -1 otherwise. */ public int getActivityIndex(ResolveInfo activity) { - List<Activity> activities = mActivitys; + List<ActivityResolveInfo> activities = mActivites; final int activityCount = activities.size(); for (int i = 0; i < activityCount; i++) { - Activity currentActivity = activities.get(i); + ActivityResolveInfo currentActivity = activities.get(i); if (currentActivity.resolveInfo == activity) { return i; } @@ -433,8 +430,8 @@ public class ActivityChooserModel extends DataSetObservable { * @see HistoricalRecord */ public Intent chooseActivity(int index) { - Activity chosenActivity = mActivitys.get(index); - Activity defaultActivity = mActivitys.get(0); + ActivityResolveInfo chosenActivity = mActivites.get(index); + ActivityResolveInfo defaultActivity = mActivites.get(0); ComponentName chosenName = new ComponentName( chosenActivity.resolveInfo.activityInfo.packageName, @@ -460,8 +457,8 @@ public class ActivityChooserModel extends DataSetObservable { */ public ResolveInfo getDefaultActivity() { synchronized (mInstanceLock) { - if (!mActivitys.isEmpty()) { - return mActivitys.get(0).resolveInfo; + if (!mActivites.isEmpty()) { + return mActivites.get(0).resolveInfo; } } return null; @@ -478,8 +475,8 @@ public class ActivityChooserModel extends DataSetObservable { * @param index The index of the activity to set as default. */ public void setDefaultActivity(int index) { - Activity newDefaultActivity = mActivitys.get(index); - Activity oldDefaultActivity = mActivitys.get(0); + ActivityResolveInfo newDefaultActivity = mActivites.get(index); + ActivityResolveInfo oldDefaultActivity = mActivites.get(0); final float weight; if (oldDefaultActivity != null) { @@ -572,8 +569,8 @@ public class ActivityChooserModel extends DataSetObservable { */ private void sortActivities() { synchronized (mInstanceLock) { - if (mActivitySorter != null && !mActivitys.isEmpty()) { - mActivitySorter.sort(mIntent, mActivitys, + if (mActivitySorter != null && !mActivites.isEmpty()) { + mActivitySorter.sort(mIntent, mActivites, Collections.unmodifiableList(mHistoricalRecords)); notifyChanged(); } @@ -661,14 +658,14 @@ public class ActivityChooserModel extends DataSetObservable { * Loads the activities. */ private void loadActivitiesLocked() { - mActivitys.clear(); + mActivites.clear(); if (mIntent != null) { List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentActivities(mIntent, 0); final int resolveInfoCount = resolveInfos.size(); for (int i = 0; i < resolveInfoCount; i++) { ResolveInfo resolveInfo = resolveInfos.get(i); - mActivitys.add(new Activity(resolveInfo)); + mActivites.add(new ActivityResolveInfo(resolveInfo)); } sortActivities(); } else { @@ -797,7 +794,7 @@ public class ActivityChooserModel extends DataSetObservable { /** * Represents an activity. */ - public final class Activity implements Comparable<Activity> { + public final class ActivityResolveInfo implements Comparable<ActivityResolveInfo> { /** * The {@link ResolveInfo} of the activity. @@ -814,7 +811,7 @@ public class ActivityChooserModel extends DataSetObservable { * * @param resolveInfo activity {@link ResolveInfo}. */ - public Activity(ResolveInfo resolveInfo) { + public ActivityResolveInfo(ResolveInfo resolveInfo) { this.resolveInfo = resolveInfo; } @@ -834,14 +831,14 @@ public class ActivityChooserModel extends DataSetObservable { if (getClass() != obj.getClass()) { return false; } - Activity other = (Activity) obj; + ActivityResolveInfo other = (ActivityResolveInfo) obj; if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) { return false; } return true; } - public int compareTo(Activity another) { + public int compareTo(ActivityResolveInfo another) { return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight); } @@ -862,18 +859,18 @@ public class ActivityChooserModel extends DataSetObservable { private final class DefaultSorter implements ActivitySorter { private static final float WEIGHT_DECAY_COEFFICIENT = 0.95f; - private final Map<String, Activity> mPackageNameToActivityMap = - new HashMap<String, Activity>(); + private final Map<String, ActivityResolveInfo> mPackageNameToActivityMap = + new HashMap<String, ActivityResolveInfo>(); - public void sort(Intent intent, List<Activity> activities, + public void sort(Intent intent, List<ActivityResolveInfo> activities, List<HistoricalRecord> historicalRecords) { - Map<String, Activity> packageNameToActivityMap = + Map<String, ActivityResolveInfo> packageNameToActivityMap = mPackageNameToActivityMap; packageNameToActivityMap.clear(); final int activityCount = activities.size(); for (int i = 0; i < activityCount; i++) { - Activity activity = activities.get(i); + ActivityResolveInfo activity = activities.get(i); activity.weight = 0.0f; String packageName = activity.resolveInfo.activityInfo.packageName; packageNameToActivityMap.put(packageName, activity); @@ -884,9 +881,11 @@ public class ActivityChooserModel extends DataSetObservable { for (int i = lastShareIndex; i >= 0; i--) { HistoricalRecord historicalRecord = historicalRecords.get(i); String packageName = historicalRecord.activity.getPackageName(); - Activity activity = packageNameToActivityMap.get(packageName); - activity.weight += historicalRecord.weight * nextRecordWeight; - nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT; + ActivityResolveInfo activity = packageNameToActivityMap.get(packageName); + if (activity != null) { + activity.weight += historicalRecord.weight * nextRecordWeight; + nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT; + } } Collections.sort(activities); diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index 2fe8162..f500b39 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -16,10 +16,7 @@ package android.widget; -import android.app.AlertDialog; -import android.app.AlertDialog.Builder; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -27,12 +24,20 @@ import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Canvas; import android.graphics.drawable.Drawable; -import android.os.Debug; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ActivityChooserModel; import android.widget.ActivityChooserModel.ActivityChooserModelClient; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListPopupWindow; +import android.widget.PopupWindow; +import android.widget.TextView; import com.android.internal.R; @@ -56,11 +61,6 @@ import com.android.internal.R; * </li> * </ul> * </p> - * </p> - * This view is backed by a {@link ActivityChooserModel}. Calling {@link #showPopup()} - * while this view is attached to the view hierarchy will show a popup with - * activities while if the view is not attached it will show a dialog. - * </p> * * @hide */ @@ -92,29 +92,21 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod private final ImageButton mDefaultActionButton; /** - * The header for handlers list. + * Observer for the model data. */ - private final View mListHeaderView; + private final DataSetObserver mModelDataSetOberver = new DataSetObserver() { - /** - * The footer for handlers list. - */ - private final View mListFooterView; - - /** - * The title of the header view. - */ - private TextView mListHeaderViewTitle; - - /** - * The title for expanding the activities list. - */ - private final String mListHeaderViewTitleSelectDefault; - - /** - * The title if no activity exist. - */ - private final String mListHeaderViewTitleNoActivities; + @Override + public void onChanged() { + super.onChanged(); + mAdapter.notifyDataSetChanged(); + } + @Override + public void onInvalidated() { + super.onInvalidated(); + mAdapter.notifyDataSetInvalidated(); + } + }; /** * Popup window for showing the activity overflow list. @@ -122,11 +114,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod private ListPopupWindow mListPopupWindow; /** - * Alert dialog for showing the activity overflow list. - */ - private AlertDialog mAlertDialog; - - /** * Listener for the dismissal of the popup/alert. */ private PopupWindow.OnDismissListener mOnDismissListener; @@ -147,16 +134,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod private boolean mIsAttachedToWindow; /** - * Flag whether this view is showing an alert dialog. - */ - private boolean mIsShowingAlertDialog; - - /** - * Flag whether this view is showing a popup window. - */ - private boolean mIsShowingPopuWindow; - - /** * Create a new instance. * * @param context The application environment. @@ -195,8 +172,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod Drawable expandActivityOverflowButtonDrawable = attributesArray.getDrawable( R.styleable.ActivityChooserView_expandActivityOverflowButtonDrawable); - LayoutInflater inflater = (LayoutInflater) context.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); + LayoutInflater inflater = LayoutInflater.from(mContext); inflater.inflate(R.layout.activity_chooser_view, this, true); mCallbacks = new Callbacks(); @@ -211,15 +187,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod mExpandActivityOverflowButton.setOnClickListener(mCallbacks); mExpandActivityOverflowButton.setBackgroundDrawable(expandActivityOverflowButtonDrawable); - mListHeaderView = inflater.inflate(R.layout.activity_chooser_list_header, null); - mListFooterView = inflater.inflate(R.layout.activity_chooser_list_footer, null); - - mListHeaderViewTitle = (TextView) mListHeaderView.findViewById(R.id.title); - mListHeaderViewTitleSelectDefault = context.getString( - R.string.activity_chooser_view_select_default); - mListHeaderViewTitleNoActivities = context.getString( - R.string.activity_chooser_view_no_activities); - mAdapter = new ActivityChooserViewAdapter(); mAdapter.registerDataSetObserver(new DataSetObserver() { @Override @@ -262,7 +229,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod * @return True if the popup was shown, false if already showing. */ public boolean showPopup() { - if (isShowingPopup()) { + if (isShowingPopup() || !mIsAttachedToWindow) { return false; } mIsSelectingDefaultActivity = false; @@ -276,38 +243,29 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod * @param maxActivityCount The max number of activities to display. */ private void showPopupUnchecked(int maxActivityCount) { - mAdapter.setMaxActivityCount(maxActivityCount); - if (mIsSelectingDefaultActivity) { - if (mAdapter.getActivityCount() > 0) { - mListHeaderViewTitle.setText(mListHeaderViewTitleSelectDefault); - } else { - mListHeaderViewTitle.setText(mListHeaderViewTitleNoActivities); - } - mAdapter.setHeaderView(mListHeaderView); - } else { - mAdapter.setHeaderView(null); + if (mAdapter.getDataModel() == null) { + throw new IllegalStateException("No data model. Did you call #setDataModel?"); } - if (mAdapter.getActivityCount() > maxActivityCount + 1) { - mAdapter.setFooterView(mListFooterView); + mAdapter.setMaxActivityCount(maxActivityCount); + + final int activityCount = mAdapter.getActivityCount(); + if (maxActivityCount != ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED + && activityCount > maxActivityCount + 1) { + mAdapter.setShowFooterView(true); } else { - mAdapter.setFooterView(null); + mAdapter.setShowFooterView(false); } - if (!mIsAttachedToWindow || mIsShowingAlertDialog) { - AlertDialog alertDialog = getAlertDilalog(); - if (!alertDialog.isShowing()) { - alertDialog.setCustomTitle(this); - alertDialog.show(); - mIsShowingAlertDialog = true; - } - } else { - ListPopupWindow popupWindow = getListPopupWindow(); - if (!popupWindow.isShowing()) { - popupWindow.setContentWidth(mAdapter.measureContentWidth()); - popupWindow.show(); - mIsShowingPopuWindow = true; + ListPopupWindow popupWindow = getListPopupWindow(); + if (!popupWindow.isShowing()) { + if (mIsSelectingDefaultActivity) { + mAdapter.setShowDefaultActivity(true); + } else { + mAdapter.setShowDefaultActivity(false); } + popupWindow.setContentWidth(mAdapter.measureContentWidth()); + popupWindow.show(); } } @@ -317,12 +275,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod * @return True if dismissed, false if already dismissed. */ public boolean dismissPopup() { - if (!isShowingPopup()) { - return false; - } - if (mIsShowingAlertDialog) { - getAlertDilalog().dismiss(); - } else if (mIsShowingPopuWindow) { + if (isShowingPopup()) { getListPopupWindow().dismiss(); } return true; @@ -334,12 +287,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod * @return True if the popup is shown. */ public boolean isShowingPopup() { - if (mIsShowingAlertDialog) { - return getAlertDilalog().isShowing(); - } else if (mIsShowingPopuWindow) { - return getListPopupWindow().isShowing(); - } - return false; + return getListPopupWindow().isShowing(); } @Override @@ -347,6 +295,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod super.onAttachedToWindow(); ActivityChooserModel dataModel = mAdapter.getDataModel(); if (dataModel != null) { + dataModel.registerObserver(mModelDataSetOberver); dataModel.readHistoricalData(); } mIsAttachedToWindow = true; @@ -357,6 +306,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod super.onDetachedFromWindow(); ActivityChooserModel dataModel = mAdapter.getDataModel(); if (dataModel != null) { + dataModel.unregisterObserver(mModelDataSetOberver); dataModel.persistHistoricalData(); } mIsAttachedToWindow = false; @@ -371,13 +321,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - mActivityChooserContent.layout(left, top, right, bottom); - if (mIsShowingPopuWindow) { - if (isShown()) { - showPopupUnchecked(mAdapter.getMaxActivityCount()); - } else { - dismissPopup(); - } + mActivityChooserContent.layout(0, 0, right - left, bottom - top); + if (getListPopupWindow().isShowing()) { + showPopupUnchecked(mAdapter.getMaxActivityCount()); + } else { + dismissPopup(); } } @@ -429,22 +377,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } /** - * Gets the alert dialog which is lazily initialized. - * - * @return The popup. - */ - private AlertDialog getAlertDilalog() { - if (mAlertDialog == null) { - Builder builder = new Builder(getContext()); - builder.setAdapter(mAdapter, null); - mAlertDialog = builder.create(); - mAlertDialog.getListView().setOnItemClickListener(mCallbacks); - mAlertDialog.setOnDismissListener(mCallbacks); - } - return mAlertDialog; - } - - /** * Updates the buttons state. */ private void updateButtons() { @@ -469,24 +401,23 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod * Interface implementation to avoid publishing them in the APIs. */ private class Callbacks implements AdapterView.OnItemClickListener, - View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener, - DialogInterface.OnDismissListener { + View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener { // AdapterView#OnItemClickListener public void onItemClick(AdapterView<?> parent, View view, int position, long id) { ActivityChooserViewAdapter adapter = (ActivityChooserViewAdapter) parent.getAdapter(); final int itemViewType = adapter.getItemViewType(position); switch (itemViewType) { - case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_HEADER: { - /* do nothing */ - } break; case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_FOOTER: { showPopupUnchecked(ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED); } break; case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_ACTIVITY: { dismissPopup(); if (mIsSelectingDefaultActivity) { - mAdapter.getDataModel().setDefaultActivity(position); + // The item at position zero is the default already. + if (position > 0) { + mAdapter.getDataModel().setDefaultActivity(position); + } } else { // The first item in the model is default action => adjust index Intent launchIntent = mAdapter.getDataModel().chooseActivity(position + 1); @@ -530,16 +461,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod // PopUpWindow.OnDismissListener#onDismiss public void onDismiss() { - mIsShowingPopuWindow = false; - notifyOnDismissListener(); - } - - // DialogInterface.OnDismissListener#onDismiss - @Override - public void onDismiss(DialogInterface dialog) { - mIsShowingAlertDialog = false; - AlertDialog alertDialog = (AlertDialog) dialog; - alertDialog.setCustomTitle(null); notifyOnDismissListener(); } @@ -559,59 +480,35 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod public static final int MAX_ACTIVITY_COUNT_DEFAULT = 4; - private static final int ITEM_VIEW_TYPE_HEADER = 0; + private static final int ITEM_VIEW_TYPE_ACTIVITY = 0; - private static final int ITEM_VIEW_TYPE_ACTIVITY = 1; - - private static final int ITEM_VIEW_TYPE_FOOTER = 2; + private static final int ITEM_VIEW_TYPE_FOOTER = 1; private static final int ITEM_VIEW_TYPE_COUNT = 3; - private final DataSetObserver mDataSetOberver = new DataSetObserver() { - - @Override - public void onChanged() { - super.onChanged(); - notifyDataSetChanged(); - } - @Override - public void onInvalidated() { - super.onInvalidated(); - notifyDataSetInvalidated(); - } - }; - private ActivityChooserModel mDataModel; private int mMaxActivityCount = MAX_ACTIVITY_COUNT_DEFAULT; - private ResolveInfo mDefaultActivity; + private boolean mShowDefaultActivity; - private View mHeaderView; - - private View mFooterView; + private boolean mShowFooterView; public void setDataModel(ActivityChooserModel dataModel) { + ActivityChooserModel oldDataModel = mAdapter.getDataModel(); + if (oldDataModel != null && isShown()) { + oldDataModel.unregisterObserver(mModelDataSetOberver); + } mDataModel = dataModel; - mDataModel.registerObserver(mDataSetOberver); - notifyDataSetChanged(); - } - - @Override - public void notifyDataSetChanged() { - if (mDataModel.getActivityCount() > 0) { - mDefaultActivity = mDataModel.getDefaultActivity(); - } else { - mDefaultActivity = null; + if (dataModel != null && isShown()) { + dataModel.registerObserver(mModelDataSetOberver); } - super.notifyDataSetChanged(); + notifyDataSetChanged(); } @Override public int getItemViewType(int position) { - if (mHeaderView != null && position == 0) { - return ITEM_VIEW_TYPE_HEADER; - } else if (mFooterView != null && position == getCount() - 1) { + if (mShowFooterView && position == getCount() - 1) { return ITEM_VIEW_TYPE_FOOTER; } else { return ITEM_VIEW_TYPE_ACTIVITY; @@ -626,14 +523,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod public int getCount() { int count = 0; int activityCount = mDataModel.getActivityCount(); - if (activityCount > 0) { + if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) { activityCount--; } count = Math.min(activityCount, mMaxActivityCount); - if (mHeaderView != null) { - count++; - } - if (mFooterView != null) { + if (mShowFooterView) { count++; } return count; @@ -642,16 +536,13 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod public Object getItem(int position) { final int itemViewType = getItemViewType(position); switch (itemViewType) { - case ITEM_VIEW_TYPE_HEADER: - return mHeaderView; case ITEM_VIEW_TYPE_FOOTER: - return mFooterView; + return null; case ITEM_VIEW_TYPE_ACTIVITY: - int targetIndex = (mHeaderView == null) ? position : position - 1; - if (mDefaultActivity != null) { - targetIndex++; + if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) { + position++; } - return mDataModel.getActivity(targetIndex); + return mDataModel.getActivity(position); default: throw new IllegalArgumentException(); } @@ -661,27 +552,19 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod return position; } - @Override - public boolean isEnabled(int position) { - final int itemViewType = getItemViewType(position); - switch (itemViewType) { - case ITEM_VIEW_TYPE_HEADER: - return false; - case ITEM_VIEW_TYPE_FOOTER: - case ITEM_VIEW_TYPE_ACTIVITY: - return true; - default: - throw new IllegalArgumentException(); - } - } - public View getView(int position, View convertView, ViewGroup parent) { final int itemViewType = getItemViewType(position); switch (itemViewType) { - case ITEM_VIEW_TYPE_HEADER: - return mHeaderView; case ITEM_VIEW_TYPE_FOOTER: - return mFooterView; + if (convertView == null || convertView.getId() != ITEM_VIEW_TYPE_FOOTER) { + convertView = LayoutInflater.from(getContext()).inflate( + R.layout.activity_chooser_view_list_item, parent, false); + convertView.setId(ITEM_VIEW_TYPE_FOOTER); + TextView titleView = (TextView) convertView.findViewById(R.id.title); + titleView.setText(mContext.getString( + R.string.activity_chooser_view_see_all)); + } + return convertView; case ITEM_VIEW_TYPE_ACTIVITY: if (convertView == null || convertView.getId() != R.id.list_item) { convertView = LayoutInflater.from(getContext()).inflate( @@ -695,6 +578,12 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod // Set the title. TextView titleView = (TextView) convertView.findViewById(R.id.title); titleView.setText(activity.loadLabel(packageManager)); + // Highlight the default. + if (mShowDefaultActivity && position == 0) { + convertView.setActivated(true); + } else { + convertView.setActivated(false); + } return convertView; default: throw new IllegalArgumentException(); @@ -702,7 +591,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } public int measureContentWidth() { - // The user may have specified some of the target not to be show but we + // The user may have specified some of the target not to be shown but we // want to measure all of them since after expansion they should fit. final int oldMaxActivityCount = mMaxActivityCount; mMaxActivityCount = MAX_ACTIVITY_COUNT_UNLIMITED; @@ -733,19 +622,12 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } public ResolveInfo getDefaultActivity() { - return mDefaultActivity; - } - - public void setHeaderView(View headerView) { - if (mHeaderView != headerView) { - mHeaderView = headerView; - notifyDataSetChanged(); - } + return mDataModel.getDefaultActivity(); } - public void setFooterView(View footerView) { - if (mFooterView != footerView) { - mFooterView = footerView; + public void setShowFooterView(boolean showFooterView) { + if (mShowFooterView != showFooterView) { + mShowFooterView = showFooterView; notifyDataSetChanged(); } } @@ -761,5 +643,12 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod public ActivityChooserModel getDataModel() { return mDataModel; } + + public void setShowDefaultActivity(boolean showDefaultActivity) { + if (mShowDefaultActivity != showDefaultActivity) { + mShowDefaultActivity = showDefaultActivity; + notifyDataSetChanged(); + } + } } } diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java index f8c76f2..1b713c3 100644 --- a/core/java/android/widget/CalendarView.java +++ b/core/java/android/widget/CalendarView.java @@ -21,6 +21,7 @@ import com.android.internal.R; import android.annotation.Widget; import android.app.Service; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Canvas; @@ -239,21 +240,11 @@ public class CalendarView extends FrameLayout { private String[] mDayLabels; /** - * Temporary instance to avoid multiple instantiations. - */ - private Calendar mTempDate = Calendar.getInstance(); - - /** * The first day of the week. */ private int mFirstDayOfWeek; /** - * The first day of the focused month. - */ - private Calendar mFirstDayOfMonth = Calendar.getInstance(); - - /** * Which month should be displayed/highlighted [0-11]. */ private int mCurrentMonthDisplayed; @@ -289,14 +280,24 @@ public class CalendarView extends FrameLayout { private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(); /** + * Temporary instance to avoid multiple instantiations. + */ + private Calendar mTempDate; + + /** + * The first day of the focused month. + */ + private Calendar mFirstDayOfMonth; + + /** * The start date of the range supported by this picker. */ - private Calendar mMinDate = Calendar.getInstance(); + private Calendar mMinDate; /** * The end date of the range supported by this picker. */ - private Calendar mMaxDate = Calendar.getInstance(); + private Calendar mMaxDate; /** * Date format for parsing dates. @@ -304,6 +305,11 @@ public class CalendarView extends FrameLayout { private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT); /** + * The current locale. + */ + private Locale mCurrentLocale; + + /** * The callback used to indicate the user changes the date. */ public interface OnDateChangeListener { @@ -330,6 +336,9 @@ public class CalendarView extends FrameLayout { public CalendarView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, 0); + // initialization based on locale + setCurrentLocale(Locale.getDefault()); + TypedValue calendarViewStyle = new TypedValue(); context.getTheme().resolveAttribute(R.attr.calendarViewStyle, calendarViewStyle, true); TypedArray attributesArray = context.obtainStyledAttributes(calendarViewStyle.resourceId, @@ -366,6 +375,7 @@ public class CalendarView extends FrameLayout { com.android.internal.R.styleable.TextAppearance); mDateTextSize = dateTextAppearance.getDimensionPixelSize( R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE); + dateTextAppearance.recycle(); int weekDayTextAppearanceResId = attributesArray.getResourceId( R.styleable.CalendarView_weekDayTextAppearance, @@ -413,6 +423,12 @@ public class CalendarView extends FrameLayout { return mListView.isEnabled(); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + setCurrentLocale(newConfig.locale); + } + /** * Gets the minimal date supported by this {@link CalendarView} in milliseconds * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time @@ -624,6 +640,41 @@ public class CalendarView extends FrameLayout { } /** + * Sets the current locale. + * + * @param locale The current locale. + */ + private void setCurrentLocale(Locale locale) { + if (locale.equals(mCurrentLocale)) { + return; + } + + mCurrentLocale = locale; + + mTempDate = getCalendarForLocale(mTempDate, locale); + mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale); + mMinDate = getCalendarForLocale(mMinDate, locale); + mMaxDate = getCalendarForLocale(mMaxDate, locale); + } + + /** + * Gets a calendar for locale bootstrapped with the value of a given calendar. + * + * @param oldCalendar The old calendar. + * @param locale The locale. + */ + private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) { + if (oldCalendar == null) { + return Calendar.getInstance(locale); + } else { + final long currentTimeMillis = oldCalendar.getTimeInMillis(); + Calendar newCalendar = Calendar.getInstance(locale); + newCalendar.setTimeInMillis(currentTimeMillis); + return newCalendar; + } + } + + /** * @return True if the <code>firstDate</code> is the same as the <code> * secondDate</code>. */ diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 30fb927..4812283 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -16,10 +16,9 @@ package android.widget; -import com.android.internal.R; - import android.annotation.Widget; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; @@ -33,6 +32,8 @@ import android.view.LayoutInflater; import android.view.accessibility.AccessibilityEvent; import android.widget.NumberPicker.OnValueChangeListener; +import com.android.internal.R; + import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -89,23 +90,23 @@ public class DatePicker extends FrameLayout { private final CalendarView mCalendarView; - private OnDateChangedListener mOnDateChangedListener; + private Locale mCurrentLocale; - private Locale mMonthLocale; + private OnDateChangedListener mOnDateChangedListener; - private final Calendar mTempDate = Calendar.getInstance(); + private String[] mShortMonths; - private final int mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1; + private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT); - private final String[] mShortMonths = new String[mNumberOfMonths]; + private int mNumberOfMonths; - private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT); + private Calendar mTempDate; - private final Calendar mMinDate = Calendar.getInstance(); + private Calendar mMinDate; - private final Calendar mMaxDate = Calendar.getInstance(); + private Calendar mMaxDate; - private final Calendar mCurrentDate = Calendar.getInstance(); + private Calendar mCurrentDate; private boolean mIsEnabled = DEFAULT_ENABLED_STATE; @@ -137,6 +138,9 @@ public class DatePicker extends FrameLayout { public DatePicker(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + // initialization based on locale + setCurrentLocale(Locale.getDefault()); + TypedArray attributesArray = context.obtainStyledAttributes(attrs, R.styleable.DatePicker, defStyle, 0); boolean spinnersShown = attributesArray.getBoolean(R.styleable.DatePicker_spinnersShown, @@ -213,7 +217,7 @@ public class DatePicker extends FrameLayout { mMonthSpinner = (NumberPicker) findViewById(R.id.month); mMonthSpinner.setMinValue(0); mMonthSpinner.setMaxValue(mNumberOfMonths - 1); - mMonthSpinner.setDisplayedValues(getShortMonths()); + mMonthSpinner.setDisplayedValues(mShortMonths); mMonthSpinner.setOnLongPressUpdateInterval(200); mMonthSpinner.setOnValueChangedListener(onChangeListener); @@ -363,6 +367,12 @@ public class DatePicker extends FrameLayout { event.getText().add(selectedDateUtterance); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + setCurrentLocale(newConfig.locale); + } + /** * Gets whether the {@link CalendarView} is shown. * @@ -411,6 +421,48 @@ public class DatePicker extends FrameLayout { } /** + * Sets the current locale. + * + * @param locale The current locale. + */ + private void setCurrentLocale(Locale locale) { + if (locale.equals(mCurrentLocale)) { + return; + } + + mCurrentLocale = locale; + + mTempDate = getCalendarForLocale(mTempDate, locale); + mMinDate = getCalendarForLocale(mMinDate, locale); + mMaxDate = getCalendarForLocale(mMaxDate, locale); + mCurrentDate = getCalendarForLocale(mCurrentDate, locale); + + mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1; + mShortMonths = new String[mNumberOfMonths]; + for (int i = 0; i < mNumberOfMonths; i++) { + mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i, + DateUtils.LENGTH_MEDIUM); + } + } + + /** + * Gets a calendar for locale bootstrapped with the value of a given calendar. + * + * @param oldCalendar The old calendar. + * @param locale The locale. + */ + private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) { + if (oldCalendar == null) { + return Calendar.getInstance(locale); + } else { + final long currentTimeMillis = oldCalendar.getTimeInMillis(); + Calendar newCalendar = Calendar.getInstance(locale); + newCalendar.setTimeInMillis(currentTimeMillis); + return newCalendar; + } + } + + /** * Reorders the spinners according to the date format that is * explicitly set by the user and if no such is set fall back * to the current locale's default format. @@ -507,23 +559,6 @@ public class DatePicker extends FrameLayout { } } - /** - * @return The short month abbreviations. - */ - private String[] getShortMonths() { - final Locale currentLocale = Locale.getDefault(); - if (currentLocale.equals(mMonthLocale)) { - return mShortMonths; - } else { - for (int i = 0; i < mNumberOfMonths; i++) { - mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i, - DateUtils.LENGTH_MEDIUM); - } - mMonthLocale = currentLocale; - return mShortMonths; - } - } - private boolean isNewDate(int year, int month, int dayOfMonth) { return (mCurrentDate.get(Calendar.YEAR) != year || mCurrentDate.get(Calendar.MONTH) != dayOfMonth @@ -569,7 +604,7 @@ public class DatePicker extends FrameLayout { // make sure the month names are a zero based array // with the months in the month spinner - String[] displayedValues = Arrays.copyOfRange(getShortMonths(), + String[] displayedValues = Arrays.copyOfRange(mShortMonths, mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1); mMonthSpinner.setDisplayedValues(displayedValues); diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index c2759e5..b9eb5ff 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -53,12 +53,12 @@ import static java.lang.Math.min; * container and grid index {@code N} is fixed to its trailing edge * (after padding is taken into account). * - * <h4>Row and Column Groups</h4> + * <h4>Row and Column Specs</h4> * * Children occupy one or more contiguous cells, as defined - * by their {@link GridLayout.LayoutParams#rowGroup rowGroup} and - * {@link GridLayout.LayoutParams#columnGroup columnGroup} layout parameters. - * Each group specifies the set of rows or columns that are to be + * by their {@link GridLayout.LayoutParams#rowSpec rowSpec} and + * {@link GridLayout.LayoutParams#columnSpec columnSpec} layout parameters. + * Each spec defines the set of rows or columns that are to be * occupied; and how children should be aligned within the resulting group of cells. * Although cells do not normally overlap in a GridLayout, GridLayout does * not prevent children being defined to occupy the same cell or group of cells. @@ -92,7 +92,7 @@ import static java.lang.Math.min; * * <h4>Excess Space Distribution</h4> * - * A child's ability to stretch is controlled using the {@link Group#flexibility flexibility} + * A child's ability to stretch is controlled using the flexibility * properties of its row and column groups. * <p> * <p> @@ -167,8 +167,7 @@ public class GridLayout extends ViewGroup { // Misc constants private static final String TAG = GridLayout.class.getName(); - private static final boolean DEBUG = false; - private static final double GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2; + static final boolean DEBUG = false; private static final int PRF = 1; // Defaults @@ -178,8 +177,9 @@ public class GridLayout extends ViewGroup { private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false; private static final boolean DEFAULT_ORDER_PRESERVED = false; private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS; - // todo remove this - private static final int DEFAULT_CONTAINER_MARGIN = 20; + private static final int DEFAULT_CONTAINER_MARGIN = 0; + private static final int DEFAULT_MARGIN = 8; + private static final int DEFAULT_CONTAINER_PADDING = 16; private static final int MAX_SIZE = 100000; // TypedArray indices @@ -278,12 +278,12 @@ public class GridLayout extends ViewGroup { /** * Returns the current number of rows. This is either the last value that was set * with {@link #setRowCount(int)} or, if no such value was set, the maximum - * value of each the upper bounds defined in {@link LayoutParams#rowGroup}. + * value of each the upper bounds defined in {@link LayoutParams#rowSpec}. * * @return the current number of rows * * @see #setRowCount(int) - * @see LayoutParams#rowGroup + * @see LayoutParams#rowSpec * * @attr ref android.R.styleable#GridLayout_rowCount */ @@ -299,7 +299,7 @@ public class GridLayout extends ViewGroup { * @param rowCount the number of rows * * @see #getRowCount() - * @see LayoutParams#rowGroup + * @see LayoutParams#rowSpec * * @attr ref android.R.styleable#GridLayout_rowCount */ @@ -310,12 +310,12 @@ public class GridLayout extends ViewGroup { /** * Returns the current number of columns. This is either the last value that was set * with {@link #setColumnCount(int)} or, if no such value was set, the maximum - * value of each the upper bounds defined in {@link LayoutParams#columnGroup}. + * value of each the upper bounds defined in {@link LayoutParams#columnSpec}. * * @return the current number of columns * * @see #setColumnCount(int) - * @see LayoutParams#columnGroup + * @see LayoutParams#columnSpec * * @attr ref android.R.styleable#GridLayout_columnCount */ @@ -331,7 +331,7 @@ public class GridLayout extends ViewGroup { * @param columnCount the number of columns. * * @see #getColumnCount() - * @see LayoutParams#columnGroup + * @see LayoutParams#columnSpec * * @attr ref android.R.styleable#GridLayout_columnCount */ @@ -381,6 +381,10 @@ public class GridLayout extends ViewGroup { */ public void setUseDefaultMargins(boolean useDefaultMargins) { mUseDefaultMargins = useDefaultMargins; + if (useDefaultMargins) { + int padding = DEFAULT_CONTAINER_PADDING; + setPadding(padding, padding, padding, padding); + } requestLayout(); } @@ -518,6 +522,14 @@ public class GridLayout extends ViewGroup { return result; } + private static int sum(int[] a) { + int result = 0; + for (int i = 0, N = a.length; i < N; i++) { + result += a[i]; + } + return result; + } + private static <T> T[] append(T[] a, T[] b) { T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length); System.arraycopy(a, 0, result, 0, a.length); @@ -526,16 +538,10 @@ public class GridLayout extends ViewGroup { } private int getDefaultMargin(View c, boolean horizontal, boolean leading) { - // In the absence of any other information, calculate a default gap such - // that, in a grid of identical components, the heights and the vertical - // gaps are in the proportion of the golden ratio. - // To effect this with equal margins at each edge, set each of the - // four margin values to half this amount. - return (int) (c.getMeasuredHeight() / GOLDEN_RATIO / 2); + return DEFAULT_MARGIN; } private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) { - // todo remove DEFAULT_CONTAINER_MARGIN. Use padding? Seek advice on Themes/Styles, etc. return isAtEdge ? DEFAULT_CONTAINER_MARGIN : getDefaultMargin(c, horizontal, leading); } @@ -543,9 +549,9 @@ public class GridLayout extends ViewGroup { if (!mUseDefaultMargins) { return 0; } - Group group = horizontal ? p.columnGroup : p.rowGroup; + Spec spec = horizontal ? p.columnSpec : p.rowSpec; Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis; - Interval span = group.span; + Interval span = spec.span; boolean isAtEdge = leading ? (span.min == 0) : (span.max == axis.getCount()); return getDefaultMargin(c, isAtEdge, horizontal, leading); @@ -593,12 +599,12 @@ public class GridLayout extends ViewGroup { if (isGone(c)) continue; LayoutParams lp = getLayoutParams1(c); - final Group colGroup = lp.columnGroup; - final Interval cols = colGroup.span; + final Spec colSpec = lp.columnSpec; + final Interval cols = colSpec.span; final int colSpan = cols.size(); - final Group rowGroup = lp.rowGroup; - final Interval rows = rowGroup.span; + final Spec rowSpec = lp.rowSpec; + final Interval rows = rowSpec.span; final int rowSpan = rows.size(); if (horizontal) { @@ -623,8 +629,8 @@ public class GridLayout extends ViewGroup { maxSize = max(maxSize, colSpan); } - lp.setColumnGroupSpan(new Interval(col, col + colSpan)); - lp.setRowGroupSpan(new Interval(row, row + rowSpan)); + lp.setColumnSpecSpan(new Interval(col, col + colSpan)); + lp.setRowSpecSpan(new Interval(row, row + rowSpan)); if (horizontal) { col = col + colSpan; @@ -737,7 +743,7 @@ public class GridLayout extends ViewGroup { } // Draw margins - paint.setColor(Color.YELLOW); + paint.setColor(Color.MAGENTA); for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); drawRectangle(canvas, @@ -872,11 +878,11 @@ public class GridLayout extends ViewGroup { View c = getChildAt(i); if (isGone(c)) continue; LayoutParams lp = getLayoutParams(c); - Group columnGroup = lp.columnGroup; - Group rowGroup = lp.rowGroup; + Spec columnSpec = lp.columnSpec; + Spec rowSpec = lp.rowSpec; - Interval colSpan = columnGroup.span; - Interval rowSpan = rowGroup.span; + Interval colSpan = columnSpec.span; + Interval rowSpan = rowSpec.span; int x1 = mHorizontalAxis.getLocationIncludingMargin(true, colSpan.min); int y1 = mVerticalAxis.getLocationIncludingMargin(true, rowSpan.min); @@ -890,8 +896,8 @@ public class GridLayout extends ViewGroup { int pWidth = getMeasurement(c, true); int pHeight = getMeasurement(c, false); - Alignment hAlign = columnGroup.alignment; - Alignment vAlign = rowGroup.alignment; + Alignment hAlign = columnSpec.alignment; + Alignment vAlign = rowSpec.alignment; int dx, dy; @@ -961,7 +967,7 @@ public class GridLayout extends ViewGroup { public boolean countValid = false; public boolean countWasExplicitySet = false; - PackedMap<Group, Bounds> groupBounds; + PackedMap<Spec, Bounds> groupBounds; public boolean groupBoundsValid = false; PackedMap<Interval, MutableInt> forwardLinks; @@ -998,9 +1004,9 @@ public class GridLayout extends ViewGroup { View c = getChildAt(i); if (isGone(c)) continue; LayoutParams params = getLayoutParams(c); - Group g = horizontal ? params.columnGroup : params.rowGroup; - count = max(count, g.span.min); - count = max(count, g.span.max); + Spec spec = horizontal ? params.columnSpec : params.rowSpec; + count = max(count, spec.span.min); + count = max(count, spec.span.max); } return count == -1 ? UNDEFINED : count; } @@ -1027,17 +1033,17 @@ public class GridLayout extends ViewGroup { invalidateStructure(); } - private PackedMap<Group, Bounds> createGroupBounds() { - Assoc<Group, Bounds> assoc = Assoc.of(Group.class, Bounds.class); + private PackedMap<Spec, Bounds> createGroupBounds() { + Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class); for (int i = 0, N = getChildCount(); i < N; i++) { View c = getChildAt(i); if (isGone(c)) { - assoc.put(Group.GONE, Bounds.GONE); + assoc.put(Spec.GONE, Bounds.GONE); } else { LayoutParams lp = getLayoutParams(c); - Group group = horizontal ? lp.columnGroup : lp.rowGroup; - Bounds bounds = group.alignment.getBounds(); - assoc.put(group, bounds); + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + Bounds bounds = spec.alignment.getBounds(); + assoc.put(spec, bounds); } } return assoc.pack(); @@ -1052,12 +1058,12 @@ public class GridLayout extends ViewGroup { View c = getChildAt(i); if (isGone(c)) continue; LayoutParams lp = getLayoutParams(c); - Group g = horizontal ? lp.columnGroup : lp.rowGroup; - groupBounds.getValue(i).include(c, g, GridLayout.this, this); + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + groupBounds.getValue(i).include(c, spec, GridLayout.this, this); } } - private PackedMap<Group, Bounds> getGroupBounds() { + private PackedMap<Spec, Bounds> getGroupBounds() { if (groupBounds == null) { groupBounds = createGroupBounds(); } @@ -1071,7 +1077,7 @@ public class GridLayout extends ViewGroup { // Add values computed by alignment - taking the max of all alignments in each span private PackedMap<Interval, MutableInt> createLinks(boolean min) { Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class); - Group[] keys = getGroupBounds().keys; + Spec[] keys = getGroupBounds().keys; for (int i = 0, N = keys.length; i < N; i++) { Interval span = min ? keys[i].span : keys[i].span.inverse(); result.put(span, new MutableInt()); @@ -1092,8 +1098,7 @@ public class GridLayout extends ViewGroup { MutableInt valueHolder = links.getValue(i); if (min) { valueHolder.value = max(valueHolder.value, size); - } - else { + } else { valueHolder.value = -max(-valueHolder.value, size); } } @@ -1236,8 +1241,8 @@ public class GridLayout extends ViewGroup { View c = getChildAt(i); if (isGone(c)) continue; LayoutParams lp = getLayoutParams(c); - Group g = horizontal ? lp.columnGroup : lp.rowGroup; - Interval span = g.span; + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + Interval span = spec.span; leadingEdgeCount[span.min]++; trailingEdgeCount[span.max]++; } @@ -1436,8 +1441,8 @@ public class GridLayout extends ViewGroup { View c = getChildAt(i); if (isGone(c)) continue; LayoutParams lp = getLayoutParams(c); - Group g = horizontal ? lp.columnGroup : lp.rowGroup; - Interval span = g.span; + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + Interval span = spec.span; int index = leading ? span.min : span.max; margins[index] = max(margins[index], getMargin(c, horizontal, leading)); } @@ -1514,6 +1519,12 @@ public class GridLayout extends ViewGroup { } private void setParentConstraints(int min, int max) { + if (mAlignmentMode != ALIGN_MARGINS) { + int margins = sum(getLeadingMargins()) + sum(getTrailingMargins()); + min -= margins; + max -= margins; + } + parentMin.value = min; parentMax.value = -max; locationsValid = false; @@ -1529,7 +1540,7 @@ public class GridLayout extends ViewGroup { int size = MeasureSpec.getSize(measureSpec); switch (mode) { case MeasureSpec.UNSPECIFIED: { - return getMeasure(0, MAX_SIZE); + return getMeasure(0, MAX_SIZE); } case MeasureSpec.EXACTLY: { return getMeasure(size, size); @@ -1584,14 +1595,14 @@ public class GridLayout extends ViewGroup { * GridLayout supports both row and column spanning and arbitrary forms of alignment within * each cell group. The fundamental parameters associated with each cell group are * gathered into their vertical and horizontal components and stored - * in the {@link #rowGroup} and {@link #columnGroup} layout parameters. - * {@link Group Groups} are immutable structures and may be shared between the layout + * in the {@link #rowSpec} and {@link #columnSpec} layout parameters. + * {@link android.widget.GridLayout.Spec Specs} are immutable structures and may be shared between the layout * parameters of different children. * <p> - * The row and column groups contain the leading and trailing indices along each axis + * The row and column specs contain the leading and trailing indices along each axis * and together specify the four grid indices that delimit the cells of this cell group. * <p> - * The {@link Group#alignment alignment} fields of the row and column groups together specify + * The alignment properties of the row and column specs together specify * both aspects of alignment within the cell group. It is also possible to specify a child's * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)} * method. @@ -1620,10 +1631,10 @@ public class GridLayout extends ViewGroup { * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is * {@code false}; otherwise {@link #UNDEFINED}, to * indicate that a default value should be computed on demand. </li> - * <li>{@link #rowGroup}{@code .span} = {@code [0, 1]} </li> - * <li>{@link #rowGroup}{@code .alignment} = {@link #BASELINE} </li> - * <li>{@link #columnGroup}{@code .span} = {@code [0, 1]} </li> - * <li>{@link #columnGroup}{@code .alignment} = {@link #LEFT} </li> + * <li>{@link #rowSpec}{@code .span} = {@code [0, 1]} </li> + * <li>{@link #rowSpec}{@code .alignment} = {@link #BASELINE} </li> + * <li>{@link #columnSpec}{@code .span} = {@code [0, 1]} </li> + * <li>{@link #columnSpec}{@code .alignment} = {@link #LEFT} </li> * </ul> * * @attr ref android.R.styleable#GridLayout_Layout_layout_row @@ -1678,48 +1689,48 @@ public class GridLayout extends ViewGroup { // Instance variables /** - * The group that specifies the vertical characteristics of the cell group + * The spec that specifies the vertical characteristics of the cell group * described by these layout parameters. */ - public Group rowGroup; + public Spec rowSpec; /** - * The group that specifies the horizontal characteristics of the cell group + * The spec that specifies the horizontal characteristics of the cell group * described by these layout parameters. */ - public Group columnGroup; + public Spec columnSpec; // Constructors private LayoutParams( int width, int height, int left, int top, int right, int bottom, - Group rowGroup, Group columnGroup) { + Spec rowSpec, Spec columnSpec) { super(width, height); setMargins(left, top, right, bottom); - this.rowGroup = rowGroup; - this.columnGroup = columnGroup; + this.rowSpec = rowSpec; + this.columnSpec = columnSpec; } /** - * Constructs a new LayoutParams instance for this <code>rowGroup</code> - * and <code>columnGroup</code>. All other fields are initialized with + * Constructs a new LayoutParams instance for this <code>rowSpec</code> + * and <code>columnSpec</code>. All other fields are initialized with * default values as defined in {@link LayoutParams}. * - * @param rowGroup the rowGroup - * @param columnGroup the columnGroup + * @param rowSpec the rowSpec + * @param columnSpec the columnSpec */ - public LayoutParams(Group rowGroup, Group columnGroup) { + public LayoutParams(Spec rowSpec, Spec columnSpec) { this(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, - rowGroup, columnGroup); + rowSpec, columnSpec); } /** * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}. */ public LayoutParams() { - this(new Group(DEFAULT_SPAN, DEFAULT_ROW_ALIGNMENT), - new Group(DEFAULT_SPAN, DEFAULT_COLUMN_ALIGNMENT)); + this(new Spec(DEFAULT_SPAN, DEFAULT_ROW_ALIGNMENT, Spec.DEFAULT_FLEXIBILITY), + new Spec(DEFAULT_SPAN, DEFAULT_COLUMN_ALIGNMENT, Spec.DEFAULT_FLEXIBILITY)); } // Copying constructors @@ -1743,8 +1754,8 @@ public class GridLayout extends ViewGroup { */ public LayoutParams(LayoutParams that) { super(that); - this.columnGroup = new Group(that.columnGroup); - this.rowGroup = new Group(that.rowGroup); + this.columnSpec = new Spec(that.columnSpec); + this.rowSpec = new Spec(that.rowSpec); } // AttributeSet constructors @@ -1836,14 +1847,14 @@ public class GridLayout extends ViewGroup { int column = a.getInt(COLUMN, DEFAULT_COLUMN); int columnSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE); Interval hSpan = new Interval(column, column + columnSpan); - int hFlexibility = a.getInt(COLUMN_FLEXIBILITY, Group.DEFAULT_FLEXIBILITY); - this.columnGroup = new Group(hSpan, getColAlignment(gravity, width), hFlexibility); + int hFlexibility = a.getInt(COLUMN_FLEXIBILITY, Spec.DEFAULT_FLEXIBILITY); + this.columnSpec = new Spec(hSpan, getColAlignment(gravity, width), hFlexibility); int row = a.getInt(ROW, DEFAULT_ROW); int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE); Interval vSpan = new Interval(row, row + rowSpan); - int vFlexibility = a.getInt(ROW_FLEXIBILITY, Group.DEFAULT_FLEXIBILITY); - this.rowGroup = new Group(vSpan, getRowAlignment(gravity, height), vFlexibility); + int vFlexibility = a.getInt(ROW_FLEXIBILITY, Spec.DEFAULT_FLEXIBILITY); + this.rowSpec = new Spec(vSpan, getRowAlignment(gravity, height), vFlexibility); } finally { a.recycle(); } @@ -1858,8 +1869,8 @@ public class GridLayout extends ViewGroup { * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity */ public void setGravity(int gravity) { - columnGroup = columnGroup.copyWriteAlignment(getColAlignment(gravity, width)); - rowGroup = rowGroup.copyWriteAlignment(getRowAlignment(gravity, height)); + columnSpec = columnSpec.copyWriteAlignment(getColAlignment(gravity, width)); + rowSpec = rowSpec.copyWriteAlignment(getRowAlignment(gravity, height)); } @Override @@ -1868,12 +1879,12 @@ public class GridLayout extends ViewGroup { this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT); } - private void setRowGroupSpan(Interval span) { - rowGroup = rowGroup.copyWriteSpan(span); + private void setRowSpecSpan(Interval span) { + rowSpec = rowSpec.copyWriteSpan(span); } - private void setColumnGroupSpan(Interval span) { - columnGroup = columnGroup.copyWriteSpan(span); + private void setColumnSpecSpan(Interval span) { + columnSpec = columnSpec.copyWriteSpan(span); } } @@ -2019,14 +2030,14 @@ public class GridLayout extends ViewGroup { } /* - For each Group (with a given alignment) we need to store the amount of space required + For each group (with a given alignment) we need to store the amount of space required before the alignment point and the amount of space required after it. One side of this calculation is always 0 for LEADING and TRAILING alignments but we don't make use of this. For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no simple optimisations are possible. The general algorithm therefore is to create a Map (actually a PackedMap) from - Group to Bounds and to loop through all Views in the group taking the maximum + group to Bounds and to loop through all Views in the group taking the maximum of the values for each View. */ private static class Bounds { @@ -2067,11 +2078,11 @@ public class GridLayout extends ViewGroup { return before - alignment.getAlignmentValue(c, size); } - protected void include(View c, Group group, GridLayout gridLayout, Axis axis) { - this.flexibility &= group.flexibility; + protected void include(View c, Spec spec, GridLayout gridLayout, Axis axis) { + this.flexibility &= spec.flexibility; int size = gridLayout.getMeasurementIncludingMargin(c, axis.horizontal); // todo test this works correctly when the returned value is UNDEFINED - int before = group.alignment.getAlignmentValue(c, size); + int before = spec.alignment.getAlignmentValue(c, size); include(before, size - before); } @@ -2176,16 +2187,13 @@ public class GridLayout extends ViewGroup { } /** - * A group specifies either the horizontal or vertical characteristics of a group of + * A spec defines either the horizontal or vertical characteristics of a group of * cells. - * <p> - * Groups are immutable and so may be shared between views with the same - * {@code span} and {@code alignment}. */ - public static class Group { + public static class Spec { private static final int DEFAULT_FLEXIBILITY = UNDEFINED_FLEXIBILITY; - private static final Group GONE = new Group(Interval.GONE, Alignment.GONE); + private static final Spec GONE = new Spec(Interval.GONE, Alignment.GONE); /** * The grid indices of the leading and trailing edges of this cell group for the @@ -2200,7 +2208,7 @@ public class GridLayout extends ViewGroup { * For row groups, this specifies the vertical alignment. * For column groups, this specifies the horizontal alignment. */ - public final Alignment alignment; + final Alignment alignment; /** * The flexibility field tells GridLayout how to derive minimum and maximum size @@ -2212,82 +2220,48 @@ public class GridLayout extends ViewGroup { * * @see GridLayout#CAN_STRETCH */ - public int flexibility = DEFAULT_FLEXIBILITY; + final int flexibility; - /** - * Construct a new Group, {@code group}, where: - * <ul> - * <li> {@code group.span = span} </li> - * <li> {@code group.alignment = alignment} </li> - * </ul> - * - * @param span the span - * @param alignment the alignment - */ - private Group(Interval span, Alignment alignment) { + private Spec(Interval span, Alignment alignment, int flexibility) { this.span = span; this.alignment = alignment; + this.flexibility = flexibility; } - private Group(Interval span, Alignment alignment, int flexibility) { - this.span = span; - this.alignment = alignment; - this.flexibility = flexibility; + private Spec(Interval span, Alignment alignment) { + this(span, alignment, DEFAULT_FLEXIBILITY); } /* Copying constructor */ - private Group(Group that) { - this.span = that.span; - this.alignment = that.alignment; - this.flexibility = that.flexibility; + private Spec(Spec that) { + this(that.span, that.alignment, that.flexibility); } - /** - * Construct a new Group, {@code group}, where: - * <ul> - * <li> {@code group.span = [start, start + size]} </li> - * <li> {@code group.alignment = alignment} </li> - * </ul> - * - * @param start the start - * @param size the size - * @param alignment the alignment - */ - public Group(int start, int size, Alignment alignment) { - this(new Interval(start, start + size), alignment); + Spec(int start, int size, Alignment alignment, int flexibility) { + this(new Interval(start, start + size), alignment, flexibility); } - /** - * Construct a new Group, {@code group}, where: - * <ul> - * <li> {@code group.span = [start, start + 1]} </li> - * <li> {@code group.alignment = alignment} </li> - * </ul> - * - * @param start the start index - * @param alignment the alignment - */ - public Group(int start, Alignment alignment) { - this(start, 1, alignment); + private Spec copyWriteSpan(Interval span) { + return new Spec(span, alignment, flexibility); } - private Group copyWriteSpan(Interval span) { - return new Group(span, alignment, flexibility); + private Spec copyWriteAlignment(Alignment alignment) { + return new Spec(span, alignment, flexibility); } - private Group copyWriteAlignment(Alignment alignment) { - return new Group(span, alignment, flexibility); + private Spec copyWriteFlexibility(int flexibility) { + return new Spec(span, alignment, flexibility); } /** - * Returns {@code true} if the {@link #getClass class}, {@link #alignment} and {@code span} - * properties of this Group and the supplied parameter are pairwise equal, + * Returns {@code true} if the {@code class}, {@code alignment} and {@code span} + * properties of this Spec and the supplied parameter are pairwise equal, * {@code false} otherwise. * - * @param that the object to compare this group with + * @param that the object to compare this spec with * * @return {@code true} if the specified object is equal to this - * {@code Group}; {@code false} otherwise + * {@code Spec}; {@code false} otherwise */ @Override public boolean equals(Object that) { @@ -2298,12 +2272,12 @@ public class GridLayout extends ViewGroup { return false; } - Group group = (Group) that; + Spec spec = (Spec) that; - if (!alignment.equals(group.alignment)) { + if (!alignment.equals(spec.alignment)) { return false; } - if (!span.equals(group.span)) { + if (!span.equals(spec.span)) { return false; } @@ -2319,12 +2293,93 @@ public class GridLayout extends ViewGroup { } /** + * Temporary backward compatibility class for Launcher - to avoid + * dependent multi-project commit. This class will be deleted after + * AppsCustomizePagedView is updated to new API. + * + * @hide + */ + @Deprecated + public static class Group extends Spec { + /** + * @deprecated Please replace with {@link #spec(int, int, Alignment)} + * @hide + */ + @Deprecated + public Group(int start, int size, Alignment alignment) { + super(start, size, alignment, UNDEFINED_FLEXIBILITY); + } + } + + /** + * Return a Spec, {@code spec}, where: + * <ul> + * <li> {@code spec.span = [start, start + size]} </li> + * <li> {@code spec.alignment = alignment} </li> + * <li> {@code spec.flexibility = flexibility} </li> + * </ul> + * + * @param start the start + * @param size the size + * @param alignment the alignment + * @param flexibility the flexibility + */ + public static Spec spec(int start, int size, Alignment alignment, int flexibility) { + return new Spec(start, size, alignment, flexibility); + } + + /** + * Return a Spec, {@code spec}, where: + * <ul> + * <li> {@code spec.span = [start, start + 1]} </li> + * <li> {@code spec.alignment = alignment} </li> + * <li> {@code spec.flexibility = flexibility} </li> + * </ul> + * + * @param start the start + * @param alignment the alignment + * @param flexibility the flexibility + */ + public static Spec spec(int start, Alignment alignment, int flexibility) { + return spec(start, 1, alignment, flexibility); + } + + /** + * Return a Spec, {@code spec}, where: + * <ul> + * <li> {@code spec.span = [start, start + size]} </li> + * <li> {@code spec.alignment = alignment} </li> + * </ul> + * + * @param start the start + * @param size the size + * @param alignment the alignment + */ + public static Spec spec(int start, int size, Alignment alignment) { + return spec(start, size, alignment, Spec.DEFAULT_FLEXIBILITY); + } + + /** + * Return a Spec, {@code spec}, where: + * <ul> + * <li> {@code spec.span = [start, start + 1]} </li> + * <li> {@code spec.alignment = alignment} </li> + * </ul> + * + * @param start the start index + * @param alignment the alignment + */ + public static Spec spec(int start, Alignment alignment) { + return spec(start, 1, alignment); + } + + /** * Alignments specify where a view should be placed within a cell group and * what size it should be. * <p> - * The {@link LayoutParams} class contains a {@link LayoutParams#rowGroup rowGroup} - * and a {@link LayoutParams#columnGroup columnGroup} each of which contains an - * {@link Group#alignment alignment}. Overall placement of the view in the cell + * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec} + * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an + * {@code alignment}. Overall placement of the view in the cell * group is specified by the two alignments which act along each axis independently. * <p> * The GridLayout class defines the most common alignments used in general layout: @@ -2425,8 +2480,8 @@ public class GridLayout extends ViewGroup { /** * Indicates that a view should be <em>centered</em> with the other views in its cell group. - * This constant may be used in both {@link LayoutParams#rowGroup rowGroups} and {@link - * LayoutParams#columnGroup columnGroups}. + * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link + * LayoutParams#columnSpec columnSpecs}. */ public static final Alignment CENTER = new Alignment() { public int getAlignmentValue(View view, int viewSize) { @@ -2437,7 +2492,7 @@ public class GridLayout extends ViewGroup { /** * Indicates that a view should be aligned with the <em>baselines</em> * of the other views in its cell group. - * This constant may only be used as an alignment in {@link LayoutParams#rowGroup rowGroups}. + * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}. * * @see View#getBaseline() */ @@ -2488,8 +2543,8 @@ public class GridLayout extends ViewGroup { /** * Indicates that a view should expanded to fit the boundaries of its cell group. - * This constant may be used in both {@link LayoutParams#rowGroup rowGroups} and - * {@link LayoutParams#columnGroup columnGroups}. + * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and + * {@link LayoutParams#columnSpec columnSpecs}. */ public static final Alignment FILL = new Alignment() { public int getAlignmentValue(View view, int viewSize) { @@ -2513,42 +2568,30 @@ public class GridLayout extends ViewGroup { /** * Indicates that a view requests precisely the size specified by its layout parameters. * - * @see Group#flexibility - * - * @hide + * @see Spec#flexibility */ - public static final int FIXED = 0; + private static final int NONE = 0; /** * Indicates that a view's size should lie between its minimum and the size specified by * its layout parameters. * - * @see Group#flexibility - * - * @hide + * @see Spec#flexibility */ - public static final int CAN_SHRINK = 1; + private static final int CAN_SHRINK = 1; /** * Indicates that a view's size should be greater than or equal to the size specified by * its layout parameters. * - * @see Group#flexibility + * @see Spec#flexibility */ public static final int CAN_STRETCH = 2; /** - * Indicates that a view will ignore its measurement, and can take any size that is greater - * than its minimum. - * - * @see Group#flexibility - */ - private static final int CAN_SHRINK_OR_STRETCH = CAN_SHRINK | CAN_STRETCH; - - /** * A default value for flexibility. * - * @see Group#flexibility + * @see Spec#flexibility */ private static final int UNDEFINED_FLEXIBILITY = UNDEFINED | CAN_SHRINK | CAN_STRETCH; diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index d6e426f..2e0cc62 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -18,18 +18,23 @@ package android.widget; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.util.TypedValue; import android.view.ActionProvider; +import android.view.Menu; import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.SubMenu; import android.view.View; import com.android.internal.R; /** * This is a provider for a share action. It is responsible for creating views - * that enable data sharing and also to perform a default action for showing - * a share dialog. + * that enable data sharing and also to show a sub menu with sharing activities + * if the hosting item is placed on the overflow menu. * <p> * Here is how to use the action provider with custom backing file in a {@link MenuItem}: * </p> @@ -47,15 +52,13 @@ import com.android.internal.R; * // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this * // line if using the default share history file is desired. * mShareActionProvider.setShareHistoryFileName("custom_share_history.xml"); - * // Get the action view and hold onto it to set the share intent. - * mActionView = menuItem.getActionView(); * . . . * } * * // Somewhere in the application. * public void doShare(Intent shareIntent) { * // When you want to share set the share intent. - * mShareActionProvider.setShareIntent(mActionView, shareIntent); + * mShareActionProvider.setShareIntent(shareIntent); * } * </pre> * </code> @@ -70,11 +73,34 @@ import com.android.internal.R; public class ShareActionProvider extends ActionProvider { /** + * The default for the maximal number of activities shown in the sub-menu. + */ + private static final int DEFAULT_INITIAL_ACTIVITY_COUNT = 4; + + /** + * The the maximum number activities shown in the sub-menu. + */ + private int mMaxShownActivityCount = DEFAULT_INITIAL_ACTIVITY_COUNT; + + /** + * Listener for handling menu item clicks. + */ + private final ShareMenuItemOnMenuItemClickListener mOnMenuItemClickListener = + new ShareMenuItemOnMenuItemClickListener(); + + /** * The default name for storing share history. */ public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml"; + /** + * Context for accessing resources. + */ private final Context mContext; + + /** + * The name of the file with share history data. + */ private String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME; /** @@ -92,13 +118,17 @@ public class ShareActionProvider extends ActionProvider { */ @Override public View onCreateActionView() { + // Create the view and set its data model. ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); ActivityChooserView activityChooserView = new ActivityChooserView(mContext); activityChooserView.setActivityChooserModel(dataModel); + + // Lookup and set the expand action icon. TypedValue outTypedValue = new TypedValue(); mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true); Drawable drawable = mContext.getResources().getDrawable(outTypedValue.resourceId); activityChooserView.setExpandActivityOverflowButtonDrawable(drawable); + return activityChooserView; } @@ -106,12 +136,40 @@ public class ShareActionProvider extends ActionProvider { * {@inheritDoc} */ @Override - public void onPerformDefaultAction(View actionView) { - if (actionView instanceof ActivityChooserView) { - ActivityChooserView activityChooserView = (ActivityChooserView) actionView; - activityChooserView.showPopup(); - } else { - throw new IllegalArgumentException("actionView not instance of ActivityChooserView"); + public boolean hasSubMenu() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void onPrepareSubMenu(SubMenu subMenu) { + // Clear since the order of items may change. + subMenu.clear(); + + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); + PackageManager packageManager = mContext.getPackageManager(); + + final int expandedActivityCount = dataModel.getActivityCount(); + final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount); + + // Populate the sub-menu with a sub set of the activities. + for (int i = 0; i < collapsedActivityCount; i++) { + ResolveInfo activity = dataModel.getActivity(i); + subMenu.add(0, i, i, activity.loadLabel(packageManager)) + .setIcon(activity.loadIcon(packageManager)) + .setOnMenuItemClickListener(mOnMenuItemClickListener); + } + + // Add a sub-menu for showing all activities as a list item. + SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount, + collapsedActivityCount, mContext.getString(R.string.activity_chooser_view_see_all)); + for (int i = 0; i < expandedActivityCount; i++) { + ResolveInfo activity = dataModel.getActivity(i); + expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager)) + .setIcon(activity.loadIcon(packageManager)) + .setOnMenuItemClickListener(mOnMenuItemClickListener); } } @@ -147,18 +205,29 @@ public class ShareActionProvider extends ActionProvider { * </code> * </p> * - * @param actionView An action view created by {@link #onCreateActionView()}. * @param shareIntent The share intent. * * @see Intent#ACTION_SEND * @see Intent#ACTION_SEND_MULTIPLE */ - public void setShareIntent(View actionView, Intent shareIntent) { - if (actionView instanceof ActivityChooserView) { - ActivityChooserView activityChooserView = (ActivityChooserView) actionView; - activityChooserView.getDataModel().setIntent(shareIntent); - } else { - throw new IllegalArgumentException("actionView not instance of ActivityChooserView"); + public void setShareIntent(Intent shareIntent) { + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, + mShareHistoryFileName); + dataModel.setIntent(shareIntent); + } + + /** + * Reusable listener for handling share item clicks. + */ + private class ShareMenuItemOnMenuItemClickListener implements OnMenuItemClickListener { + @Override + public boolean onMenuItemClick(MenuItem item) { + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, + mShareHistoryFileName); + final int itemId = item.getItemId(); + Intent launchIntent = dataModel.chooseActivity(itemId); + mContext.startActivity(launchIntent); + return true; } } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 772e8e9..6b4e454 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -62,6 +62,7 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.method.AllCapsTransformationMethod; import android.text.method.ArrowKeyMovementMethod; import android.text.method.DateKeyListener; import android.text.method.DateTimeKeyListener; @@ -76,6 +77,7 @@ import android.text.method.SingleLineTransformationMethod; import android.text.method.TextKeyListener; import android.text.method.TimeKeyListener; import android.text.method.TransformationMethod; +import android.text.method.TransformationMethod2; import android.text.method.WordIterator; import android.text.style.ClickableSpan; import android.text.style.ParagraphStyle; @@ -258,9 +260,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener class Drawables { final Rect mCompoundRect = new Rect(); - Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight; - int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight; - int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight; + Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight, + mDrawableStart, mDrawableEnd; + int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight, + mDrawableSizeStart, mDrawableSizeEnd; + int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight, + mDrawableHeightStart, mDrawableHeightEnd; int mDrawablePadding; } private Drawables mDrawables; @@ -350,6 +355,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END; } + private boolean bResolvedDrawables = false; + /* * Kick-start the font cache for the zygote process (to pay the cost of * initializing freetype for our default font only once). @@ -424,6 +431,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int textSize = 15; int typefaceIndex = -1; int styleIndex = -1; + boolean allCaps = false; /* * Look the appearance up without checking first if it exists because @@ -471,6 +479,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextAppearance_textStyle: styleIndex = appearance.getInt(attr, -1); break; + + case com.android.internal.R.styleable.TextAppearance_textAllCaps: + allCaps = appearance.getBoolean(attr, false); + break; } } @@ -487,7 +499,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int buffertype = 0; boolean selectallonfocus = false; Drawable drawableLeft = null, drawableTop = null, drawableRight = null, - drawableBottom = null; + drawableBottom = null, drawableStart = null, drawableEnd = null; int drawablePadding = 0; int ellipsize = -1; boolean singleLine = false; @@ -564,6 +576,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener drawableBottom = a.getDrawable(attr); break; + case com.android.internal.R.styleable.TextView_drawableStart: + drawableStart = a.getDrawable(attr); + break; + + case com.android.internal.R.styleable.TextView_drawableEnd: + drawableEnd = a.getDrawable(attr); + break; + case com.android.internal.R.styleable.TextView_drawablePadding: drawablePadding = a.getDimensionPixelSize(attr, drawablePadding); break; @@ -822,6 +842,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_suggestionsEnabled: mSuggestionsEnabled = a.getBoolean(attr, true); break; + + case com.android.internal.R.styleable.TextView_textAllCaps: + allCaps = a.getBoolean(attr, false); + break; } } a.recycle(); @@ -969,6 +993,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setCompoundDrawablesWithIntrinsicBounds( drawableLeft, drawableTop, drawableRight, drawableBottom); + setRelativeDrawablesIfNeeded(drawableStart, drawableEnd); setCompoundDrawablePadding(drawablePadding); // Same as setSingleLine(), but make sure the transformation method and the maximum number @@ -1004,6 +1029,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } setRawTextSize(textSize); + if (allCaps) { + setTransformationMethod(new AllCapsTransformationMethod(getContext())); + } + if (password || passwordInputType || webPasswordInputType || numberPasswordInputType) { setTransformationMethod(PasswordTransformationMethod.getInstance()); typefaceIndex = MONOSPACE; @@ -1090,6 +1119,42 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setTypeface(tf, styleIndex); } + private void setRelativeDrawablesIfNeeded(Drawable start, Drawable end) { + boolean hasRelativeDrawables = (start != null) || (end != null); + if (hasRelativeDrawables) { + Drawables dr = mDrawables; + if (dr == null) { + mDrawables = dr = new Drawables(); + } + final Rect compoundRect = dr.mCompoundRect; + int[] state = getDrawableState(); + if (start != null) { + start.setBounds(0, 0, start.getIntrinsicWidth(), start.getIntrinsicHeight()); + start.setState(state); + start.copyBounds(compoundRect); + start.setCallback(this); + + dr.mDrawableStart = start; + dr.mDrawableSizeStart = compoundRect.width(); + dr.mDrawableHeightStart = compoundRect.height(); + } else { + dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0; + } + if (end != null) { + end.setBounds(0, 0, end.getIntrinsicWidth(), end.getIntrinsicHeight()); + end.setState(state); + end.copyBounds(compoundRect); + end.setCallback(this); + + dr.mDrawableEnd = end; + dr.mDrawableSizeEnd = compoundRect.width(); + dr.mDrawableHeightEnd = compoundRect.height(); + } else { + dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0; + } + } + } + @Override public void setEnabled(boolean enabled) { if (enabled == isEnabled()) { @@ -1104,6 +1169,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } super.setEnabled(enabled); + prepareCursorControllers(); } /** @@ -1331,6 +1397,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTransformation = method; + if (method instanceof TransformationMethod2) { + TransformationMethod2 method2 = (TransformationMethod2) method; + mAllowTransformationLengthChange = !mTextIsSelectable && !(mText instanceof Editable); + method2.setLengthChangesAllowed(mAllowTransformationLengthChange); + } else { + mAllowTransformationLengthChange = false; + } + setText(mText); } @@ -1387,6 +1461,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Returns the start padding of the view, plus space for the start + * Drawable if any. + * + * @hide + */ + public int getCompoundPaddingStart() { + resolveDrawables(); + switch(getResolvedLayoutDirection()) { + default: + case LAYOUT_DIRECTION_LTR: + return getCompoundPaddingLeft(); + case LAYOUT_DIRECTION_RTL: + return getCompoundPaddingRight(); + } + } + + /** + * Returns the end padding of the view, plus space for the end + * Drawable if any. + * + * @hide + */ + public int getCompoundPaddingEnd() { + resolveDrawables(); + switch(getResolvedLayoutDirection()) { + default: + case LAYOUT_DIRECTION_LTR: + return getCompoundPaddingRight(); + case LAYOUT_DIRECTION_RTL: + return getCompoundPaddingLeft(); + } + } + + /** * Returns the extended top padding of the view, including both the * top Drawable if any and any extra space to keep more than maxLines * of text from showing. It is only valid to call this after measuring. @@ -1469,6 +1577,26 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Returns the total start padding of the view, including the start + * Drawable if any. + * + * @hide + */ + public int getTotalPaddingStart() { + return getCompoundPaddingStart(); + } + + /** + * Returns the total end padding of the view, including the end + * Drawable if any. + * + * @hide + */ + public int getTotalPaddingEnd() { + return getCompoundPaddingEnd(); + } + + /** * Returns the total top padding of the view, including the top * Drawable if any, the extra space to keep more than maxLines * from showing, and the vertical offset for gravity, if any. @@ -1655,6 +1783,185 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Sets the Drawables (if any) to appear to the start of, above, + * to the end of, and below the text. Use null if you do not + * want a Drawable there. The Drawables must already have had + * {@link Drawable#setBounds} called. + * + * @attr ref android.R.styleable#TextView_drawableStart + * @attr ref android.R.styleable#TextView_drawableTop + * @attr ref android.R.styleable#TextView_drawableEnd + * @attr ref android.R.styleable#TextView_drawableBottom + * + * @hide + */ + public void setCompoundDrawablesRelative(Drawable start, Drawable top, + Drawable end, Drawable bottom) { + Drawables dr = mDrawables; + + final boolean drawables = start != null || top != null + || end != null || bottom != null; + + if (!drawables) { + // Clearing drawables... can we free the data structure? + if (dr != null) { + if (dr.mDrawablePadding == 0) { + mDrawables = null; + } else { + // We need to retain the last set padding, so just clear + // out all of the fields in the existing structure. + if (dr.mDrawableStart != null) dr.mDrawableStart.setCallback(null); + dr.mDrawableStart = null; + if (dr.mDrawableTop != null) dr.mDrawableTop.setCallback(null); + dr.mDrawableTop = null; + if (dr.mDrawableEnd != null) dr.mDrawableEnd.setCallback(null); + dr.mDrawableEnd = null; + if (dr.mDrawableBottom != null) dr.mDrawableBottom.setCallback(null); + dr.mDrawableBottom = null; + dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0; + dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0; + dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0; + dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0; + } + } + } else { + if (dr == null) { + mDrawables = dr = new Drawables(); + } + + if (dr.mDrawableStart != start && dr.mDrawableStart != null) { + dr.mDrawableStart.setCallback(null); + } + dr.mDrawableStart = start; + + if (dr.mDrawableTop != top && dr.mDrawableTop != null) { + dr.mDrawableTop.setCallback(null); + } + dr.mDrawableTop = top; + + if (dr.mDrawableEnd != end && dr.mDrawableEnd != null) { + dr.mDrawableEnd.setCallback(null); + } + dr.mDrawableEnd = end; + + if (dr.mDrawableBottom != bottom && dr.mDrawableBottom != null) { + dr.mDrawableBottom.setCallback(null); + } + dr.mDrawableBottom = bottom; + + final Rect compoundRect = dr.mCompoundRect; + int[] state; + + state = getDrawableState(); + + if (start != null) { + start.setState(state); + start.copyBounds(compoundRect); + start.setCallback(this); + dr.mDrawableSizeStart = compoundRect.width(); + dr.mDrawableHeightStart = compoundRect.height(); + } else { + dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0; + } + + if (end != null) { + end.setState(state); + end.copyBounds(compoundRect); + end.setCallback(this); + dr.mDrawableSizeEnd = compoundRect.width(); + dr.mDrawableHeightEnd = compoundRect.height(); + } else { + dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0; + } + + if (top != null) { + top.setState(state); + top.copyBounds(compoundRect); + top.setCallback(this); + dr.mDrawableSizeTop = compoundRect.height(); + dr.mDrawableWidthTop = compoundRect.width(); + } else { + dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0; + } + + if (bottom != null) { + bottom.setState(state); + bottom.copyBounds(compoundRect); + bottom.setCallback(this); + dr.mDrawableSizeBottom = compoundRect.height(); + dr.mDrawableWidthBottom = compoundRect.width(); + } else { + dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0; + } + } + + resolveDrawables(); + invalidate(); + requestLayout(); + } + + /** + * Sets the Drawables (if any) to appear to the start of, above, + * to the end of, and below the text. Use 0 if you do not + * want a Drawable there. The Drawables' bounds will be set to + * their intrinsic bounds. + * + * @param start Resource identifier of the start Drawable. + * @param top Resource identifier of the top Drawable. + * @param end Resource identifier of the end Drawable. + * @param bottom Resource identifier of the bottom Drawable. + * + * @attr ref android.R.styleable#TextView_drawableStart + * @attr ref android.R.styleable#TextView_drawableTop + * @attr ref android.R.styleable#TextView_drawableEnd + * @attr ref android.R.styleable#TextView_drawableBottom + * + * @hide + */ + public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end, + int bottom) { + resetResolvedDrawables(); + final Resources resources = getContext().getResources(); + setCompoundDrawablesRelativeWithIntrinsicBounds( + start != 0 ? resources.getDrawable(start) : null, + top != 0 ? resources.getDrawable(top) : null, + end != 0 ? resources.getDrawable(end) : null, + bottom != 0 ? resources.getDrawable(bottom) : null); + } + + /** + * Sets the Drawables (if any) to appear to the start of, above, + * to the end of, and below the text. Use null if you do not + * want a Drawable there. The Drawables' bounds will be set to + * their intrinsic bounds. + * + * @attr ref android.R.styleable#TextView_drawableStart + * @attr ref android.R.styleable#TextView_drawableTop + * @attr ref android.R.styleable#TextView_drawableEnd + * @attr ref android.R.styleable#TextView_drawableBottom + * + * @hide + */ + public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top, + Drawable end, Drawable bottom) { + + resetResolvedDrawables(); + if (start != null) { + start.setBounds(0, 0, start.getIntrinsicWidth(), start.getIntrinsicHeight()); + } + if (end != null) { + end.setBounds(0, 0, end.getIntrinsicWidth(), end.getIntrinsicHeight()); + } + if (top != null) { + top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight()); + } + if (bottom != null) { + bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight()); + } + setCompoundDrawablesRelative(start, top, end, bottom); + } + + /** * Returns drawables for the left, top, right, and bottom borders. */ public Drawable[] getCompoundDrawables() { @@ -1669,6 +1976,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Returns drawables for the start, top, end, and bottom borders. + * + * @hide + */ + public Drawable[] getCompoundDrawablesRelative() { + final Drawables dr = mDrawables; + if (dr != null) { + return new Drawable[] { + dr.mDrawableStart, dr.mDrawableTop, dr.mDrawableEnd, dr.mDrawableBottom + }; + } else { + return new Drawable[] { null, null, null, null }; + } + } + + /** * Sets the size of the padding between the compound drawables and * the text. * @@ -1775,6 +2098,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setTypefaceByIndex(typefaceIndex, styleIndex); + if (appearance.getBoolean(com.android.internal.R.styleable.TextAppearance_textAllCaps, + false)) { + setTransformationMethod(new AllCapsTransformationMethod(getContext())); + } + appearance.recycle(); } @@ -2466,6 +2794,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (dr.mDrawableRight != null && dr.mDrawableRight.isStateful()) { dr.mDrawableRight.setState(state); } + if (dr.mDrawableStart != null && dr.mDrawableStart.isStateful()) { + dr.mDrawableStart.setState(state); + } + if (dr.mDrawableEnd != null && dr.mDrawableEnd.isStateful()) { + dr.mDrawableEnd.setState(state); + } } } @@ -2813,8 +3147,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Do not change the movement method for text that support text selection as it // would prevent an arbitrary cursor displacement. - final boolean hasTextSelection = this instanceof EditText || mTextIsSelectable; - if (mLinksClickable && !hasTextSelection) { + if (mLinksClickable && !textCanBeSelected()) { setMovementMethod(LinkMovementMethod.getInstance()); } } @@ -2823,14 +3156,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mBufferType = type; mText = text; - if (mTransformation == null) + if (mTransformation == null) { mTransformed = text; - else + } else { mTransformed = mTransformation.getTransformation(text, this); + } final int textLength = text.length(); - if (text instanceof Spannable) { + if (text instanceof Spannable && !mAllowTransformationLengthChange) { Spannable sp = (Spannable) text; // Remove any ChangeWatchers that might have come @@ -2852,7 +3186,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mTransformation != null) { sp.setSpan(mTransformation, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } if (mMovement != null) { @@ -3518,7 +3851,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mErrorWasChanged = true; final Drawables dr = mDrawables; if (dr != null) { - setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon, dr.mDrawableBottom); + switch (getResolvedLayoutDirection()) { + default: + case LAYOUT_DIRECTION_LTR: + setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon, + dr.mDrawableBottom); + break; + case LAYOUT_DIRECTION_RTL: + setCompoundDrawables(icon, dr.mDrawableTop, dr.mDrawableRight, + dr.mDrawableBottom); + break; + } } else { setCompoundDrawables(null, null, icon, null); } @@ -4020,6 +4363,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mSelectionModifierCursorController != null) { observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); } + + // Resolve drawables as the layout direction has been resolved + resolveDrawables(); } @Override @@ -4049,6 +4395,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } hideControllers(); + + resetResolvedDrawables(); } @Override @@ -4083,7 +4431,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final boolean verified = super.verifyDrawable(who); if (!verified && mDrawables != null) { return who == mDrawables.mDrawableLeft || who == mDrawables.mDrawableTop || - who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom; + who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom || + who == mDrawables.mDrawableStart || who == mDrawables.mDrawableEnd; } return verified; } @@ -4104,6 +4453,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mDrawables.mDrawableBottom != null) { mDrawables.mDrawableBottom.jumpToCurrentState(); } + if (mDrawables.mDrawableStart != null) { + mDrawables.mDrawableStart.jumpToCurrentState(); + } + if (mDrawables.mDrawableEnd != null) { + mDrawables.mDrawableEnd.jumpToCurrentState(); + } } } @@ -4164,7 +4519,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mDrawables != null) { final Drawables drawables = mDrawables; if (who == drawables.mDrawableLeft || who == drawables.mDrawableRight || - who == drawables.mDrawableTop || who == drawables.mDrawableBottom) { + who == drawables.mDrawableTop || who == drawables.mDrawableBottom || + who == drawables.mDrawableStart || who == drawables.mDrawableEnd) { return getResolvedLayoutDirection(); } } @@ -4183,6 +4539,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (dr.mDrawableTop != null) dr.mDrawableTop.mutate().setAlpha(alpha); if (dr.mDrawableRight != null) dr.mDrawableRight.mutate().setAlpha(alpha); if (dr.mDrawableBottom != null) dr.mDrawableBottom.mutate().setAlpha(alpha); + if (dr.mDrawableStart != null) dr.mDrawableStart.mutate().setAlpha(alpha); + if (dr.mDrawableEnd != null) dr.mDrawableEnd.mutate().setAlpha(alpha); } return true; } @@ -4200,7 +4558,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * {@link android.R.styleable#TextView_textIsSelectable} XML attribute to make this TextView * selectable (text is not selectable by default). * - * Note that the content of an EditText is always selectable. + * Note that this method simply returns the state of this flag. Although this flag has to be set + * in order to select text in non-editable TextView, the content of an {@link EditText} can + * always be selected, independently of the value of this flag. * * @return True if the text displayed in this TextView can be selected by the user. * @@ -4437,12 +4797,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener selStart = getSelectionStart(); selEnd = getSelectionEnd(); - if ((isCursorVisible() || mTextIsSelectable) && selStart >= 0 && isEnabled()) { + if (selStart >= 0) { if (mHighlightPath == null) mHighlightPath = new Path(); if (selStart == selEnd) { - if (!mTextIsSelectable && + if (isCursorVisible() && (SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) { if (mHighlightPathBogus) { mHighlightPath.reset(); @@ -4461,7 +4821,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener highlight = mHighlightPath; drawCursor = mCursorCount > 0; } - } else { + } else if (textCanBeSelected()) { if (mHighlightPathBogus) { mHighlightPath.reset(); mLayout.getSelectionPath(selStart, selEnd, mHighlightPath); @@ -5543,8 +5903,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - protected void resetLayoutDirectionResolution() { - super.resetLayoutDirectionResolution(); + protected void resetResolvedLayoutDirection() { + super.resetResolvedLayoutDirection(); if (mLayoutAlignment != null && (mTextAlign == TextAlign.VIEW_START || @@ -6184,6 +6544,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int scrollx, scrolly; + // Convert to left, center, or right alignment. + if (a == Layout.Alignment.ALIGN_NORMAL) { + a = dir == Layout.DIR_LEFT_TO_RIGHT ? Layout.Alignment.ALIGN_LEFT : + Layout.Alignment.ALIGN_RIGHT; + } else if (a == Layout.Alignment.ALIGN_OPPOSITE){ + a = dir == Layout.DIR_LEFT_TO_RIGHT ? Layout.Alignment.ALIGN_RIGHT : + Layout.Alignment.ALIGN_LEFT; + } + if (a == Layout.Alignment.ALIGN_CENTER) { /* * Keep centered if possible, or, if it is too wide to fit, @@ -6202,28 +6571,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener scrollx = left; } } - } else if (a == Layout.Alignment.ALIGN_NORMAL) { - /* - * Keep leading edge in view. - */ - - if (dir < 0) { - int right = (int) FloatMath.ceil(mLayout.getLineRight(line)); - scrollx = right - hspace; - } else { - scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line)); - } - } else /* a == Layout.Alignment.ALIGN_OPPOSITE */ { - /* - * Keep trailing edge in view. - */ - - if (dir < 0) { - scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line)); - } else { - int right = (int) FloatMath.ceil(mLayout.getLineRight(line)); - scrollx = right - hspace; - } + } else if (a == Layout.Alignment.ALIGN_LEFT) { + scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line)); + } else { // a == Layout.Alignment.ALIGN_RIGHT + int right = (int) FloatMath.ceil(mLayout.getLineRight(line)); + scrollx = right - hspace; } if (ht < vspace) { @@ -6265,20 +6617,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int grav; switch (mLayout.getParagraphAlignment(line)) { - case ALIGN_NORMAL: + case ALIGN_LEFT: grav = 1; break; - - case ALIGN_OPPOSITE: + case ALIGN_RIGHT: grav = -1; break; - + case ALIGN_NORMAL: + grav = mLayout.getParagraphDirection(line); + break; + case ALIGN_OPPOSITE: + grav = -mLayout.getParagraphDirection(line); + break; + case ALIGN_CENTER: default: grav = 0; + break; } - grav *= mLayout.getParagraphDirection(line); - int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(); int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom(); @@ -6570,6 +6926,26 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Sets the properties of this field to transform input to ALL CAPS + * display. This may use a "small caps" formatting if available. + * This setting will be ignored if this field is editable or selectable. + * + * This call replaces the current transformation method. Disabling this + * will not necessarily restore the previous behavior from before this + * was enabled. + * + * @see #setTransformationMethod(TransformationMethod) + * @attr ref android.R.styleable#TextView_textAllCaps + */ + public void setAllCaps(boolean allCaps) { + if (allCaps) { + setTransformationMethod(new AllCapsTransformationMethod(getContext())); + } else { + setTransformationMethod(null); + } + } + + /** * If true, sets the properties of this field (number of lines, horizontally scrolling, * transformation method) to be for a single-line input; if false, restores these to the default * conditions. @@ -7406,9 +7782,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); - if (hasInsertionController()) { - getInsertionController().onTouchEvent(event); - } if (hasSelectionController()) { getSelectionController().onTouchEvent(event); } @@ -7836,7 +8209,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // prepareCursorController() relies on this method. // If you change this condition, make sure prepareCursorController is called anywhere // the value of this condition might be changed. - return mText instanceof Spannable && mMovement != null && mMovement.canSelectArbitrarily(); + if (mMovement == null || !mMovement.canSelectArbitrarily()) return false; + return isTextEditable() || (mTextIsSelectable && mText instanceof Spannable && isEnabled()); } private boolean canCut() { @@ -7924,6 +8298,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int minOffset = extractRangeStartFromLong(lastTouchOffsets); final int maxOffset = extractRangeEndFromLong(lastTouchOffsets); + // Safety check in case standard touch event handling has been bypassed + if (minOffset < 0 || minOffset >= mText.length()) return false; + if (maxOffset < 0 || maxOffset >= mText.length()) return false; + int selectionStart, selectionEnd; // If a URLSpan (web address, email, phone...) is found at that position, select it. @@ -9679,13 +10057,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void hide(); /** - * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller - * a chance to become active and/or visible. - * @param event The touch event - */ - public boolean onTouchEvent(MotionEvent event); - - /** * Called when the view is detached from window. Perform house keeping task, such as * stopping Runnable thread that would otherwise keep a reference on the context, thus * preventing the activity from being recycled. @@ -9712,10 +10083,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - public boolean onTouchEvent(MotionEvent ev) { - return false; - } - public void onTouchModeChanged(boolean isInTouchMode) { if (!isInTouchMode) { hide(); @@ -9774,52 +10141,49 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mEndHandle != null) mEndHandle.hide(); } - public boolean onTouchEvent(MotionEvent event) { + public void onTouchEvent(MotionEvent event) { // This is done even when the View does not have focus, so that long presses can start // selection and tap can move cursor from this tap position. - if (isTextEditable() || mTextIsSelectable) { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - final float x = event.getX(); - final float y = event.getY(); - - // Remember finger down position, to be able to start selection from there - mMinTouchOffset = mMaxTouchOffset = getOffsetForPosition(x, y); - - // Double tap detection - long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; - if (duration <= ViewConfiguration.getDoubleTapTimeout() && - isPositionOnText(x, y)) { - final float deltaX = x - mPreviousTapPositionX; - final float deltaY = y - mPreviousTapPositionY; - final float distanceSquared = deltaX * deltaX + deltaY * deltaY; - if (distanceSquared < mSquaredTouchSlopDistance) { - showSuggestions(); - mDiscardNextActionUp = true; - } + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + final float x = event.getX(); + final float y = event.getY(); + + // Remember finger down position, to be able to start selection from there + mMinTouchOffset = mMaxTouchOffset = getOffsetForPosition(x, y); + + // Double tap detection + long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; + if (duration <= ViewConfiguration.getDoubleTapTimeout() && + isPositionOnText(x, y)) { + final float deltaX = x - mPreviousTapPositionX; + final float deltaY = y - mPreviousTapPositionY; + final float distanceSquared = deltaX * deltaX + deltaY * deltaY; + if (distanceSquared < mSquaredTouchSlopDistance) { + showSuggestions(); + mDiscardNextActionUp = true; } + } - mPreviousTapPositionX = x; - mPreviousTapPositionY = y; + mPreviousTapPositionX = x; + mPreviousTapPositionY = y; - break; + break; - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_POINTER_UP: - // Handle multi-point gestures. Keep min and max offset positions. - // Only activated for devices that correctly handle multi-touch. - if (mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)) { - updateMinAndMaxOffsets(event); - } - break; + case MotionEvent.ACTION_POINTER_DOWN: + case MotionEvent.ACTION_POINTER_UP: + // Handle multi-point gestures. Keep min and max offset positions. + // Only activated for devices that correctly handle multi-touch. + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)) { + updateMinAndMaxOffsets(event); + } + break; - case MotionEvent.ACTION_UP: - mPreviousTapUpTime = SystemClock.uptimeMillis(); - break; - } + case MotionEvent.ACTION_UP: + mPreviousTapUpTime = SystemClock.uptimeMillis(); + break; } - return false; } /** @@ -10241,6 +10605,68 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT); } + /** + * Subclasses will need to override this method to implement their own way of resolving + * drawables depending on the layout direction. + * + * A call to the super method will be required from the subclasses implementation. + * + */ + protected void resolveDrawables() { + // No need to resolve twice + if (bResolvedDrawables) { + return; + } + // No drawable to resolve + if (mDrawables == null) { + return; + } + // No relative drawable to resolve + if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) { + bResolvedDrawables = true; + return; + } + + Drawables dr = mDrawables; + switch(getResolvedLayoutDirection()) { + case LAYOUT_DIRECTION_RTL: + if (dr.mDrawableStart != null) { + dr.mDrawableRight = dr.mDrawableStart; + + dr.mDrawableSizeRight = dr.mDrawableSizeStart; + dr.mDrawableHeightRight = dr.mDrawableHeightStart; + } + if (dr.mDrawableEnd != null) { + dr.mDrawableLeft = dr.mDrawableEnd; + + dr.mDrawableSizeLeft = dr.mDrawableSizeEnd; + dr.mDrawableHeightLeft = dr.mDrawableHeightEnd; + } + break; + + case LAYOUT_DIRECTION_LTR: + default: + if (dr.mDrawableStart != null) { + dr.mDrawableLeft = dr.mDrawableStart; + + dr.mDrawableSizeLeft = dr.mDrawableSizeStart; + dr.mDrawableHeightLeft = dr.mDrawableHeightStart; + } + if (dr.mDrawableEnd != null) { + dr.mDrawableRight = dr.mDrawableEnd; + + dr.mDrawableSizeRight = dr.mDrawableSizeEnd; + dr.mDrawableHeightRight = dr.mDrawableHeightEnd; + } + break; + } + bResolvedDrawables = true; + } + + protected void resetResolvedDrawables() { + bResolvedDrawables = false; + } + @ViewDebug.ExportedProperty(category = "text") private CharSequence mText; private CharSequence mTransformed; @@ -10254,6 +10680,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private MovementMethod mMovement; private TransformationMethod mTransformation; + private boolean mAllowTransformationLengthChange; private ChangeWatcher mChangeWatcher; private ArrayList<TextWatcher> mListeners = null; diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index 423e735..0547438 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -20,6 +20,7 @@ import com.android.internal.R; import android.annotation.Widget; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; @@ -32,6 +33,7 @@ import android.widget.NumberPicker.OnValueChangeListener; import java.text.DateFormatSymbols; import java.util.Calendar; +import java.util.Locale; /** * A view for selecting the time of day, in either 24 hour or AM/PM mode. The @@ -92,6 +94,8 @@ public class TimePicker extends FrameLayout { private Calendar mTempCalendar; + private Locale mCurrentLocale; + /** * The callback interface used to indicate the time has been adjusted. */ @@ -116,6 +120,9 @@ public class TimePicker extends FrameLayout { public TimePicker(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + // initialization based on locale + setCurrentLocale(Locale.getDefault()); + // process style attributes TypedArray attributesArray = context.obtainStyledAttributes( attrs, R.styleable.TimePicker, defStyle, 0); @@ -211,8 +218,6 @@ public class TimePicker extends FrameLayout { updateHourControl(); updateAmPmControl(); - // initialize to current time - mTempCalendar = Calendar.getInstance(); setOnTimeChangedListener(NO_OP_CHANGE_LISTENER); // set to current time @@ -248,6 +253,25 @@ public class TimePicker extends FrameLayout { return mIsEnabled; } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + setCurrentLocale(newConfig.locale); + } + + /** + * Sets the current locale. + * + * @param locale The current locale. + */ + private void setCurrentLocale(Locale locale) { + if (locale.equals(mCurrentLocale)) { + return; + } + mCurrentLocale = locale; + mTempCalendar = Calendar.getInstance(locale); + } + /** * Used to save / restore state of time picker */ diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 519acf5..243c605 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -36,6 +36,7 @@ import android.app.FragmentTransaction; import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Handler; import android.view.ActionMode; import android.view.LayoutInflater; @@ -155,6 +156,13 @@ public class ActionBarImpl extends ActionBar { CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; mContentHeight = mActionView.getContentHeight(); + + // Older apps get the home button interaction enabled by default. + // Newer apps need to enable it explicitly. + if (mContext.getApplicationInfo().targetSdkVersion < + Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + setHomeButtonEnabled(true); + } } public void onConfigurationChanged(Configuration newConfig) { @@ -266,8 +274,8 @@ public class ActionBarImpl extends ActionBar { } @Override - public void setDisplayDisableHomeEnabled(boolean disableHome) { - setDisplayOptions(disableHome ? DISPLAY_DISABLE_HOME : 0, DISPLAY_DISABLE_HOME); + public void setHomeButtonEnabled(boolean enable) { + mActionView.setHomeButtonEnabled(enable); } @Override diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java index 1ac191b..8c2c405 100755 --- a/core/java/com/android/internal/os/PkgUsageStats.java +++ b/core/java/com/android/internal/os/PkgUsageStats.java @@ -19,6 +19,9 @@ package com.android.internal.os; import android.os.Parcel; import android.os.Parcelable; +import java.util.HashMap; +import java.util.Map; + /** * implementation of PkgUsageStats associated with an * application package. @@ -28,6 +31,7 @@ public class PkgUsageStats implements Parcelable { public String packageName; public int launchCount; public long usageTime; + public Map<String, Long> componentResumeTimes; public static final Parcelable.Creator<PkgUsageStats> CREATOR = new Parcelable.Creator<PkgUsageStats>() { @@ -46,31 +50,45 @@ public class PkgUsageStats implements Parcelable { + " " + packageName + "}"; } - public PkgUsageStats(String pkgName, int count, long time) { + public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) { packageName = pkgName; launchCount = count; usageTime = time; + componentResumeTimes = new HashMap<String, Long>(lastResumeTimes); } public PkgUsageStats(Parcel source) { packageName = source.readString(); launchCount = source.readInt(); usageTime = source.readLong(); + final int N = source.readInt(); + componentResumeTimes = new HashMap<String, Long>(N); + for (int i = 0; i < N; i++) { + String component = source.readString(); + long lastResumeTime = source.readLong(); + componentResumeTimes.put(component, lastResumeTime); + } } public PkgUsageStats(PkgUsageStats pStats) { packageName = pStats.packageName; launchCount = pStats.launchCount; usageTime = pStats.usageTime; + componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes); } public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int parcelableFlags){ + public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeString(packageName); dest.writeInt(launchCount); dest.writeLong(usageTime); + dest.writeInt(componentResumeTimes.size()); + for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) { + dest.writeString(ent.getKey()); + dest.writeLong(ent.getValue()); + } } } diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index f13e770..53516c0 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -252,9 +252,10 @@ public class RuntimeInit { * <li> <code> [--] <start class name> <args> * </ul> * + * @param targetSdkVersion target SDK version * @param argv arg strings */ - public static final void zygoteInit(String[] argv) + public static final void zygoteInit(int targetSdkVersion, String[] argv) throws ZygoteInit.MethodAndArgsCaller { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); @@ -263,7 +264,7 @@ public class RuntimeInit { commonInit(); zygoteInitNative(); - applicationInit(argv); + applicationInit(targetSdkVersion, argv); } /** @@ -274,20 +275,22 @@ public class RuntimeInit { * which calls {@link WrapperInit#main} which then calls this method. * So we don't need to call commonInit() here. * + * @param targetSdkVersion target SDK version * @param argv arg strings */ - public static void wrapperInit(String[] argv) + public static void wrapperInit(int targetSdkVersion, String[] argv) throws ZygoteInit.MethodAndArgsCaller { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper"); - applicationInit(argv); + applicationInit(targetSdkVersion, argv); } - private static void applicationInit(String[] argv) + private static void applicationInit(int targetSdkVersion, String[] argv) throws ZygoteInit.MethodAndArgsCaller { // We want to be fairly aggressive about heap utilization, to avoid // holding on to a lot of memory that isn't needed. VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); + VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); final Arguments args; try { diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index 860a08c..c6b3e7c 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -47,16 +47,22 @@ public class WrapperInit { * wrapper process instead of by forking Zygote. * * The first argument specifies the file descriptor for a pipe that should receive - * the pid of this process, or 0 if none. The remaining arguments are passed to - * the runtime. + * the pid of this process, or 0 if none. + * + * The second argument is the target SDK version for the app. + * + * The remaining arguments are passed to the runtime. * * @param args The command-line arguments. */ public static void main(String[] args) { try { + // Parse our mandatory arguments. + int fdNum = Integer.parseInt(args[0], 10); + int targetSdkVersion = Integer.parseInt(args[1], 10); + // Tell the Zygote what our actual PID is (since it only knows about the // wrapper that it directly forked). - int fdNum = Integer.parseInt(args[0], 10); if (fdNum != 0) { try { FileDescriptor fd = ZygoteInit.createFileDescriptor(fdNum); @@ -73,9 +79,9 @@ public class WrapperInit { ZygoteInit.preload(); // Launch the application. - String[] runtimeArgs = new String[args.length - 1]; - System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length); - RuntimeInit.wrapperInit(runtimeArgs); + String[] runtimeArgs = new String[args.length - 2]; + System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length); + RuntimeInit.wrapperInit(targetSdkVersion, runtimeArgs); } catch (ZygoteInit.MethodAndArgsCaller caller) { caller.run(); } @@ -87,11 +93,12 @@ public class WrapperInit { * * @param invokeWith The wrapper command. * @param niceName The nice name for the application, or null if none. + * @param targetSdkVersion The target SDK version for the app. * @param pipeFd The pipe to which the application's pid should be written, or null if none. * @param args Arguments for {@link RuntimeInit.main}. */ public static void execApplication(String invokeWith, String niceName, - FileDescriptor pipeFd, String[] args) { + int targetSdkVersion, FileDescriptor pipeFd, String[] args) { StringBuilder command = new StringBuilder(invokeWith); command.append(" /system/bin/app_process /system/bin --application"); if (niceName != null) { @@ -99,6 +106,8 @@ public class WrapperInit { } command.append(" com.android.internal.os.WrapperInit "); command.append(pipeFd != null ? pipeFd.getInt$() : 0); + command.append(' '); + command.append(targetSdkVersion); Zygote.appendQuotedShellArgs(command, args); Zygote.execShell(command.toString()); } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 7cb002c..9af7e96 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -18,6 +18,7 @@ package com.android.internal.os; import android.net.Credentials; import android.net.LocalSocket; +import android.os.Build; import android.os.Process; import android.os.SystemProperties; import android.util.Log; @@ -333,6 +334,10 @@ class ZygoteConnection { */ int debugFlags; + /** from --target-sdk-version. */ + int targetSdkVersion; + boolean targetSdkVersionSpecified; + /** from --classpath */ String classpath; @@ -402,6 +407,14 @@ class ZygoteConnection { gidSpecified = true; gid = Integer.parseInt( arg.substring(arg.indexOf('=') + 1)); + } else if (arg.startsWith("--target-sdk-version=")) { + if (targetSdkVersionSpecified) { + throw new IllegalArgumentException( + "Duplicate target-sdk-version specified"); + } + targetSdkVersionSpecified = true; + targetSdkVersion = Integer.parseInt( + arg.substring(arg.indexOf('=') + 1)); } else if (arg.equals("--enable-debugger")) { debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; } else if (arg.equals("--enable-safemode")) { @@ -821,9 +834,11 @@ class ZygoteConnection { if (parsedArgs.runtimeInit) { if (parsedArgs.invokeWith != null) { WrapperInit.execApplication(parsedArgs.invokeWith, - parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs); + parsedArgs.niceName, parsedArgs.targetSdkVersion, + pipeFd, parsedArgs.remainingArgs); } else { - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, + parsedArgs.remainingArgs); } } else { String className; @@ -885,6 +900,7 @@ class ZygoteConnection { } } + boolean usingWrapper = false; if (pipeFd != null && pid > 0) { DataInputStream is = new DataInputStream(new FileInputStream(pipeFd)); int innerPid = -1; @@ -909,6 +925,7 @@ class ZygoteConnection { if (parentPid > 0) { Log.i(TAG, "Wrapped process has pid " + innerPid); pid = innerPid; + usingWrapper = true; } else { Log.w(TAG, "Wrapped process reported a pid that is not a child of " + "the process that we forked: childPid=" + pid @@ -919,6 +936,7 @@ class ZygoteConnection { try { mSocketOutStream.writeInt(pid); + mSocketOutStream.writeBoolean(usingWrapper); } catch (IOException ex) { Log.e(TAG, "Error reading from command socket", ex); return true; diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index b4a7e52..6ec186d 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -48,7 +48,7 @@ import java.util.ArrayList; * Startup class for the zygote process. * * Pre-initializes some classes, and then waits for commands on a UNIX domain - * socket. Based on these commands, forks of child processes that inherit + * socket. Based on these commands, forks off child processes that inherit * the initial state of the VM. * * Please see {@link ZygoteConnection.Arguments} for documentation on the @@ -453,12 +453,13 @@ public class ZygoteInit { if (parsedArgs.invokeWith != null) { WrapperInit.execApplication(parsedArgs.invokeWith, - parsedArgs.niceName, null, parsedArgs.remainingArgs); + parsedArgs.niceName, parsedArgs.targetSdkVersion, + null, parsedArgs.remainingArgs); } else { /* * Pass the remaining arguments to SystemServer. */ - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs); } /* should never reach here */ @@ -491,7 +492,9 @@ public class ZygoteInit { /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, - parsedArgs.gids, parsedArgs.debugFlags, null, + parsedArgs.gids, + parsedArgs.debugFlags, + null, parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IllegalArgumentException ex) { diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index 2e7ec58..b754d94 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -26,6 +26,9 @@ package com.android.internal.util; * codes with Message.what starting at Protocol.WIFI + 1 and less than or equal to Protocol.WIFI + * Protocol.MAX_MESSAGE * + * NOTE: After a value is created and source released a value shouldn't be changed to + * maintain backwards compatibility. + * * {@hide} */ public class Protocol { @@ -40,7 +43,7 @@ public class Protocol { public static final int BASE_DHCP = 0x00030000; public static final int BASE_DATA_CONNECTION = 0x00040000; public static final int BASE_DATA_CONNECTION_AC = 0x00041000; - public static final int BASE_DATA_CONNECTION_TRACKER = 0x00050000; + public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000; //TODO: define all used protocols } diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index 3b497e4..09bebae 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -154,12 +154,7 @@ public class ActionMenuItemView extends LinearLayout // populate accessibility description with title setContentDescription(title); - if (mShowTextAllCaps && title != null) { - mTextButton.setText(title.toString().toUpperCase( - getContext().getResources().getConfiguration().locale)); - } else { - mTextButton.setText(mTitle); - } + mTextButton.setText(mTitle); updateTextButtonVisibility(); } diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index b86eb13..80f68ac 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -20,7 +20,8 @@ import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView; import android.content.Context; import android.content.res.Resources; -import android.util.Log; +import android.os.Parcel; +import android.os.Parcelable; import android.util.SparseBooleanArray; import android.view.MenuItem; import android.view.SoundEffectConstants; @@ -60,6 +61,9 @@ public class ActionMenuPresenter extends BaseMenuPresenter { private OpenOverflowRunnable mPostedOpenRunnable; + final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback(); + int mOpenSubMenuId; + public ActionMenuPresenter() { super(com.android.internal.R.layout.action_menu_layout, com.android.internal.R.layout.action_menu_item_layout); @@ -196,8 +200,12 @@ public class ActionMenuPresenter extends BaseMenuPresenter { topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu(); } View anchor = findViewForItem(topSubMenu.getItem()); - if (anchor == null) return false; + if (anchor == null) { + if (mOverflowButton == null) return false; + anchor = mOverflowButton; + } + mOpenSubMenuId = subMenu.getItem().getItemId(); mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu); mActionButtonPopup.setAnchorView(anchor); mActionButtonPopup.show(); @@ -426,6 +434,57 @@ public class ActionMenuPresenter extends BaseMenuPresenter { super.onCloseMenu(menu, allMenusAreClosing); } + @Override + public Parcelable onSaveInstanceState() { + SavedState state = new SavedState(); + state.openSubMenuId = mOpenSubMenuId; + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState saved = (SavedState) state; + if (saved.openSubMenuId > 0) { + MenuItem item = mMenu.findItem(saved.openSubMenuId); + if (item != null) { + SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); + onSubMenuSelected(subMenu); + } + } + } + + private static class SavedState implements Parcelable { + public int openSubMenuId; + + SavedState() { + } + + SavedState(Parcel in) { + openSubMenuId = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(openSubMenuId); + } + + public static final Parcelable.Creator<SavedState> CREATOR + = new Parcelable.Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + private class OverflowMenuButton extends ImageButton implements ActionMenuChildView { public OverflowMenuButton(Context context) { super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle); @@ -460,6 +519,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter { public OverflowPopup(Context context, MenuBuilder menu, View anchorView, boolean overflowOnly) { super(context, menu, anchorView, overflowOnly); + setCallback(mPopupPresenterCallback); } @Override @@ -482,6 +542,19 @@ public class ActionMenuPresenter extends BaseMenuPresenter { // Give a reasonable anchor to nested submenus. setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton); } + + setCallback(mPopupPresenterCallback); + + boolean preserveIconSpacing = false; + final int count = subMenu.size(); + for (int i = 0; i < count; i++) { + MenuItem childItem = subMenu.getItem(i); + if (childItem.isVisible() && childItem.getIcon() != null) { + preserveIconSpacing = true; + break; + } + } + setForceShowIcon(preserveIconSpacing); } @Override @@ -489,6 +562,20 @@ public class ActionMenuPresenter extends BaseMenuPresenter { super.onDismiss(); mSubMenu.close(); mActionButtonPopup = null; + mOpenSubMenuId = 0; + } + } + + private class PopupPresenterCallback implements MenuPresenter.Callback { + + @Override + public boolean onOpenSubMenu(MenuBuilder subMenu) { + mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId(); + return false; + } + + @Override + public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { } } diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java index ddbb08c..ed9d34a 100644 --- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java @@ -39,6 +39,8 @@ public abstract class BaseMenuPresenter implements MenuPresenter { protected MenuView mMenuView; + private int mId; + /** * Construct a new BaseMenuPresenter. * @@ -200,4 +202,12 @@ public abstract class BaseMenuPresenter implements MenuPresenter { public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { return false; } + + public int getId() { + return mId; + } + + public void setId(int id) { + mId = id; + } } diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java index f717904..d1b1dae 100644 --- a/core/java/com/android/internal/view/menu/IconMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import android.util.SparseArray; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -35,7 +36,12 @@ public class IconMenuPresenter extends BaseMenuPresenter { private IconMenuItemView mMoreView; private int mMaxItems = -1; + int mOpenSubMenuId; + SubMenuPresenterCallback mSubMenuPresenterCallback = new SubMenuPresenterCallback(); + MenuDialogHelper mOpenSubMenu; + private static final String VIEWS_TAG = "android:menu:icon"; + private static final String OPEN_SUBMENU_KEY = "android:menu:icon:submenu"; public IconMenuPresenter() { super(com.android.internal.R.layout.icon_menu_layout, @@ -86,7 +92,11 @@ public class IconMenuPresenter extends BaseMenuPresenter { if (!subMenu.hasVisibleItems()) return false; // The window manager will give us a token. - new MenuDialogHelper(subMenu).show(null); + MenuDialogHelper helper = new MenuDialogHelper(subMenu); + helper.setPresenterCallback(mSubMenuPresenterCallback); + helper.show(null); + mOpenSubMenu = helper; + mOpenSubMenuId = subMenu.getItem().getItemId(); super.onSubMenuSelected(subMenu); return true; } @@ -137,5 +147,49 @@ public class IconMenuPresenter extends BaseMenuPresenter { if (viewStates != null) { ((View) mMenuView).restoreHierarchyState(viewStates); } + int subMenuId = inState.getInt(OPEN_SUBMENU_KEY, 0); + if (subMenuId > 0 && mMenu != null) { + MenuItem item = mMenu.findItem(subMenuId); + if (item != null) { + onSubMenuSelected((SubMenuBuilder) item.getSubMenu()); + } + } + } + + @Override + public Parcelable onSaveInstanceState() { + if (mMenuView == null) { + return null; + } + + Bundle state = new Bundle(); + saveHierarchyState(state); + if (mOpenSubMenuId > 0) { + state.putInt(OPEN_SUBMENU_KEY, mOpenSubMenuId); + } + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + restoreHierarchyState((Bundle) state); + } + + class SubMenuPresenterCallback implements MenuPresenter.Callback { + @Override + public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { + mOpenSubMenuId = 0; + if (mOpenSubMenu != null) { + mOpenSubMenu.dismiss(); + mOpenSubMenu = null; + } + } + + @Override + public boolean onOpenSubMenu(MenuBuilder subMenu) { + mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId(); + return false; + } + } } diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index 0c3c605..a1e16d4 100644 --- a/core/java/com/android/internal/view/menu/ListMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java @@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.ImageView; @@ -50,6 +51,8 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView private LayoutInflater mInflater; + private boolean mForceShowIcon; + public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs); @@ -99,6 +102,10 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView setEnabled(itemData.isEnabled()); } + public void setForceShowIcon(boolean forceShow) { + mPreserveIconSpacing = mForceShowIcon = forceShow; + } + public void setTitle(CharSequence title) { if (title != null) { mTitleView.setText(title); @@ -189,12 +196,12 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView } public void setIcon(Drawable icon) { - final boolean showIcon = mItemData.shouldShowIcon(); + final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon; if (!showIcon && !mPreserveIconSpacing) { return; } - if (mIconView == null && icon == null) { + if (mIconView == null && icon == null && !mPreserveIconSpacing) { return; } @@ -213,6 +220,19 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView } } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mIconView != null && mPreserveIconSpacing) { + // Enforce minimum icon spacing + ViewGroup.LayoutParams lp = getLayoutParams(); + LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); + if (lp.height > 0 && iconLp.width <= 0) { + iconLp.width = lp.height; + } + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + private void insertIconView() { LayoutInflater inflater = getInflater(); mIconView = (ImageView) inflater.inflate(com.android.internal.R.layout.list_menu_item_icon, @@ -241,7 +261,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView } public boolean showsIcon() { - return false; + return mForceShowIcon; } private LayoutInflater getInflater() { diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java index 27e4191..146c7ac 100644 --- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java @@ -47,6 +47,8 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick private Callback mCallback; private MenuAdapter mAdapter; + private int mId; + public static final String VIEWS_TAG = "android:menu:list"; /** @@ -182,6 +184,31 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick } } + public void setId(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + + @Override + public Parcelable onSaveInstanceState() { + if (mMenuView == null) { + return null; + } + + Bundle state = new Bundle(); + saveHierarchyState(state); + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + restoreHierarchyState((Bundle) state); + } + private class MenuAdapter extends BaseAdapter { public int getCount() { ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index fdfa954..164d581 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -25,9 +25,10 @@ import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.os.Parcelable; -import android.util.Log; import android.util.SparseArray; +import android.view.ActionProvider; import android.view.ContextMenu.ContextMenuInfo; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -38,7 +39,6 @@ import android.view.View; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -49,6 +49,8 @@ import java.util.concurrent.CopyOnWriteArrayList; public class MenuBuilder implements Menu { private static final String LOGTAG = "MenuBuilder"; + private static final String PRESENTER_KEY = "android:menu:presenters"; + private static final int[] sCategoryToOrder = new int[] { 1, /* No category */ 4, /* CONTAINER */ @@ -254,6 +256,58 @@ public class MenuBuilder implements Menu { return result; } + private void dispatchSaveInstanceState(Bundle outState) { + if (mPresenters.isEmpty()) return; + + SparseArray<Parcelable> presenterStates = new SparseArray<Parcelable>(); + + for (WeakReference<MenuPresenter> ref : mPresenters) { + final MenuPresenter presenter = ref.get(); + if (presenter == null) { + mPresenters.remove(ref); + } else { + final int id = presenter.getId(); + if (id > 0) { + final Parcelable state = presenter.onSaveInstanceState(); + if (state != null) { + presenterStates.put(id, state); + } + } + } + } + + outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates); + } + + private void dispatchRestoreInstanceState(Bundle state) { + SparseArray<Parcelable> presenterStates = state.getSparseParcelableArray(PRESENTER_KEY); + + if (presenterStates == null || mPresenters.isEmpty()) return; + + for (WeakReference<MenuPresenter> ref : mPresenters) { + final MenuPresenter presenter = ref.get(); + if (presenter == null) { + mPresenters.remove(ref); + } else { + final int id = presenter.getId(); + if (id > 0) { + Parcelable parcel = presenterStates.get(id); + if (parcel != null) { + presenter.onRestoreInstanceState(parcel); + } + } + } + } + } + + public void savePresenterStates(Bundle outState) { + dispatchSaveInstanceState(outState); + } + + public void restorePresenterStates(Bundle state) { + dispatchRestoreInstanceState(state); + } + public void setCallback(Callback cb) { mCallback = cb; } @@ -745,7 +799,7 @@ public class MenuBuilder implements Menu { if (itemImpl == null || !itemImpl.isEnabled()) { return false; } - + boolean invoked = itemImpl.invoke(); if (itemImpl.hasCollapsibleActionView()) { @@ -754,7 +808,12 @@ public class MenuBuilder implements Menu { } else if (item.hasSubMenu()) { close(false); - invoked |= dispatchSubMenuSelected((SubMenuBuilder) item.getSubMenu()); + final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); + final ActionProvider provider = item.getActionProvider(); + if (provider != null && provider.hasSubMenu()) { + provider.onPrepareSubMenu(subMenu); + } + invoked |= dispatchSubMenuSelected(subMenu); if (!invoked) close(true); } else { if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) { diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java index 20b7d80..541d101 100644 --- a/core/java/com/android/internal/view/menu/MenuItemImpl.java +++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java @@ -164,10 +164,7 @@ public final class MenuItemImpl implements MenuItem { } } - if (mActionProvider != null) { - // The action view is created by the provider in this case. - View actionView = getActionView(); - mActionProvider.onPerformDefaultAction(actionView); + if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) { return true; } @@ -323,11 +320,6 @@ public final class MenuItemImpl implements MenuItem { } void setSubMenu(SubMenuBuilder subMenu) { - if ((mMenu != null) && (mMenu instanceof SubMenu)) { - throw new UnsupportedOperationException( - "Attempt to add a sub-menu to a sub-menu."); - } - mSubMenu = subMenu; subMenu.setHeaderTitle(getTitle()); diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index cffbb4e..6265618 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -18,15 +18,17 @@ package com.android.internal.view.menu; import android.content.Context; import android.content.res.Resources; -import android.util.DisplayMetrics; +import android.os.Parcelable; import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.AdapterView; import android.widget.BaseAdapter; +import android.widget.FrameLayout; import android.widget.ListAdapter; import android.widget.ListPopupWindow; import android.widget.PopupWindow; @@ -57,6 +59,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On private Callback mPresenterCallback; + boolean mForceShowIcon; + + private ViewGroup mMeasureParent; + public MenuPopupHelper(Context context, MenuBuilder menu) { this(context, menu, null, false); } @@ -85,6 +91,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On mAnchorView = anchor; } + public void setForceShowIcon(boolean forceShow) { + mForceShowIcon = forceShow; + } + public void show() { if (!tryShow()) { throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); @@ -169,7 +179,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On itemType = positionType; itemView = null; } - itemView = adapter.getView(i, itemView, null); + if (mMeasureParent == null) { + mMeasureParent = new FrameLayout(mContext); + } + itemView = adapter.getView(i, itemView, mMeasureParent); itemView.measure(widthMeasureSpec, heightMeasureSpec); width = Math.max(width, itemView.getMeasuredWidth()); } @@ -227,6 +240,18 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On if (subMenu.hasVisibleItems()) { MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView, false); subPopup.setCallback(mPresenterCallback); + + boolean preserveIconSpacing = false; + final int count = subMenu.size(); + for (int i = 0; i < count; i++) { + MenuItem childItem = subMenu.getItem(i); + if (childItem.isVisible() && childItem.getIcon() != null) { + preserveIconSpacing = true; + break; + } + } + subPopup.setForceShowIcon(preserveIconSpacing); + if (subPopup.tryShow()) { if (mPresenterCallback != null) { mPresenterCallback.onOpenSubMenu(subMenu); @@ -292,8 +317,25 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On } MenuView.ItemView itemView = (MenuView.ItemView) convertView; + if (mForceShowIcon) { + ((ListMenuItemView) convertView).setForceShowIcon(true); + } itemView.initialize(getItem(position), 0); return convertView; } } + + @Override + public int getId() { + return 0; + } + + @Override + public Parcelable onSaveInstanceState() { + return null; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + } } diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java index bd66448..d913a39 100644 --- a/core/java/com/android/internal/view/menu/MenuPresenter.java +++ b/core/java/com/android/internal/view/menu/MenuPresenter.java @@ -17,6 +17,7 @@ package com.android.internal.view.menu; import android.content.Context; +import android.os.Parcelable; import android.view.Menu; import android.view.ViewGroup; @@ -125,4 +126,24 @@ public interface MenuPresenter { * @return true if this presenter collapsed the action view, false otherwise. */ public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item); + + /** + * Returns an ID for determining how to save/restore instance state. + * @return a valid ID value. + */ + public int getId(); + + /** + * Returns a Parcelable describing the current state of the presenter. + * It will be passed to the {@link #onRestoreInstanceState(Parcelable)} + * method of the presenter sharing the same ID later. + * @return The saved instance state + */ + public Parcelable onSaveInstanceState(); + + /** + * Supplies the previously saved instance state to be restored. + * @param state The previously saved instance state + */ + public void onRestoreInstanceState(Parcelable state); } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 09bc1fc..58043c9 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -337,6 +337,7 @@ public class ActionBarView extends AbsActionBarView { if (mActionMenuPresenter == null) { mActionMenuPresenter = new ActionMenuPresenter(); mActionMenuPresenter.setCallback(cb); + mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter); mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); } @@ -433,22 +434,40 @@ public class ActionBarView extends AbsActionBarView { } } + public void setHomeButtonEnabled(boolean enable) { + mHomeLayout.setEnabled(enable); + // Make sure the home button has an accurate content description for accessibility. + if (!enable) { + mHomeLayout.setContentDescription(null); + } else if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) { + mHomeLayout.setContentDescription(mContext.getResources().getText( + R.string.action_bar_up_description)); + } else { + mHomeLayout.setContentDescription(mContext.getResources().getText( + R.string.action_bar_home_description)); + } + } + public void setDisplayOptions(int options) { final int flagsChanged = options ^ mDisplayOptions; mDisplayOptions = options; - if ((flagsChanged & ActionBar.DISPLAY_DISABLE_HOME) != 0) { - final boolean disableHome = (options & ActionBar.DISPLAY_DISABLE_HOME) != 0; - mHomeLayout.setEnabled(!disableHome); - } - if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0; final int vis = showHome ? VISIBLE : GONE; mHomeLayout.setVisibility(vis); if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { - mHomeLayout.setUp((options & ActionBar.DISPLAY_HOME_AS_UP) != 0); + final boolean setUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0; + mHomeLayout.setUp(setUp); + + // Showing home as up implicitly enables interaction with it. + // In honeycomb it was always enabled, so make this transition + // a bit easier for developers in the common case. + // (It would be silly to show it as up without responding to it.) + if (setUp) { + setHomeButtonEnabled(true); + } } if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) { @@ -486,7 +505,7 @@ public class ActionBarView extends AbsActionBarView { } // Make sure the home button has an accurate content description for accessibility. - if ((options & ActionBar.DISPLAY_DISABLE_HOME) != 0) { + if (!mHomeLayout.isEnabled()) { mHomeLayout.setContentDescription(null); } else if ((options & ActionBar.DISPLAY_HOME_AS_UP) != 0) { mHomeLayout.setContentDescription(mContext.getResources().getText( @@ -1301,5 +1320,19 @@ public class ActionBarView extends AbsActionBarView { item.setActionViewExpanded(false); return true; } + + @Override + public int getId() { + return 0; + } + + @Override + public Parcelable onSaveInstanceState() { + return null; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + } } } diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java index 2f7adf0..40e5e8a 100644 --- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -260,7 +260,6 @@ public class ScrollingTabContainerView extends HorizontalScrollView { if (mTextView == null) { TextView textView = new TextView(getContext(), null, com.android.internal.R.attr.actionBarTabTextStyle); - textView.setSingleLine(); textView.setEllipsize(TruncateAt.END); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java index 2871073..c446cfb 100644 --- a/core/java/com/android/server/NetworkManagementSocketTagger.java +++ b/core/java/com/android/server/NetworkManagementSocketTagger.java @@ -16,24 +16,37 @@ package com.android.server; +import android.os.SystemProperties; +import android.util.Log; + import dalvik.system.SocketTagger; + import java.io.FileDescriptor; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.math.BigInteger; import java.net.SocketException; import java.nio.charset.Charsets; +import libcore.io.IoUtils; + /** * Assigns tags to sockets for traffic stats. */ public final class NetworkManagementSocketTagger extends SocketTagger { + private static final String TAG = "NetworkManagementSocketTagger"; + private static final boolean LOGD = false; - private static final boolean LOGI = false; - private static final boolean ENABLE_TAGGING = false; + /** + * {@link SystemProperties} key that indicates if {@code qtaguid} bandwidth + * controls have been enabled. + */ + // TODO: remove when always enabled, or once socket tagging silently fails. + public static final String PROP_QTAGUID_ENABLED = "net.qtaguid_enabled"; private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() { - @Override protected SocketTags initialValue() { + @Override + protected SocketTags initialValue() { return new SocketTags(); } }; @@ -50,11 +63,12 @@ public final class NetworkManagementSocketTagger extends SocketTagger { threadSocketTags.get().statsUid = uid; } - @Override public void tag(FileDescriptor fd) throws SocketException { + @Override + public void tag(FileDescriptor fd) throws SocketException { final SocketTags options = threadSocketTags.get(); - if (LOGI) { - System.logI("tagSocket(" + fd.getInt$() + ") with statsTag=" - + options.statsTag + ", statsUid=" + options.statsUid); + if (LOGD) { + Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=" + options.statsTag + + ", statsUid=" + options.statsUid); } try { // TODO: skip tagging when options would be no-op @@ -82,9 +96,10 @@ public final class NetworkManagementSocketTagger extends SocketTagger { internalModuleCtrl(cmd); } - @Override public void untag(FileDescriptor fd) throws SocketException { - if (LOGI) { - System.logI("untagSocket(" + fd.getInt$() + ")"); + @Override + public void untag(FileDescriptor fd) throws SocketException { + if (LOGD) { + Log.i(TAG, "untagSocket(" + fd.getInt$() + ")"); } try { unTagSocketFd(fd); @@ -125,31 +140,22 @@ public final class NetworkManagementSocketTagger extends SocketTagger { * */ private void internalModuleCtrl(String cmd) throws IOException { - if (!ENABLE_TAGGING) return; + if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return; - final FileOutputStream procOut; - // TODO: Use something like - // android.os.SystemProperties.getInt("persist.bandwidth.enable", 0) - // to see if tagging should happen or not. + // TODO: migrate to native library for tagging commands + FileOutputStream procOut = null; try { procOut = new FileOutputStream("/proc/net/xt_qtaguid/ctrl"); - } catch (FileNotFoundException e) { - if (LOGI) { - System.logI("Can't talk to kernel module:" + e); - } - return; - } - try { procOut.write(cmd.getBytes(Charsets.US_ASCII)); } finally { - procOut.close(); + IoUtils.closeQuietly(procOut); } } /** * Convert {@link Integer} tag to {@code /proc/} format. Assumes unsigned * base-10 format like {@code 2147483647}. Currently strips signed bit to - * avoid using {@link java.math.BigInteger}. + * avoid using {@link BigInteger}. */ public static String tagToKernel(int tag) { // TODO: eventually write in hex, since that's what proc exports diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 63fa504..58e7c8d 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -224,22 +224,14 @@ LOCAL_CFLAGS += -DHAVE_BLUETOOTH LOCAL_SHARED_LIBRARIES += libbluedroid libdbus endif -ifneq ($(TARGET_SIMULATOR),true) LOCAL_SHARED_LIBRARIES += \ libdl - # we need to access the private Bionic header - # <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp - LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private -endif +# we need to access the private Bionic header +# <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp +LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private LOCAL_LDLIBS += -lpthread -ldl -ifeq ($(TARGET_SIMULATOR),true) -ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) -LOCAL_LDLIBS += -lrt -endif -endif - ifeq ($(WITH_MALLOC_LEAK_CHECK),true) LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK endif diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index a61217a..23c6da7 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -623,16 +623,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) } /* enable debugging; set suspend=y to pause during VM init */ -#ifdef HAVE_ANDROID_OS /* use android ADB transport */ opt.optionString = "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y"; -#else - /* use TCP socket; address=0 means start at port 8000 and probe up */ - LOGI("Using TCP socket for JDWP\n"); - opt.optionString = - "-agentlib:jdwp=transport=dt_socket,suspend=n,server=y,address=0"; -#endif mOptions.add(opt); char enableDPBuf[sizeof("-Xdeadlockpredict:") + PROPERTY_VALUE_MAX]; diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 258ffa5..ea35006 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -116,7 +116,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, bool forcePurgeable = false) { int sampleSize = 1; SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; - SkBitmap::Config prefConfig = SkBitmap::kNo_Config; + SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config; bool doDither = true; bool isMutable = false; bool isPurgeable = forcePurgeable || diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index ddae505..d9bd50e 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -26,7 +26,7 @@ extern "C" { int ifc_enable(const char *ifname); int ifc_disable(const char *ifname); -int ifc_reset_connections(const char *ifname); +int ifc_reset_connections(const char *ifname, int reset_mask); int dhcp_do_request(const char *ifname, const char *ipaddr, @@ -90,12 +90,17 @@ static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstri return (jint)result; } -static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstring ifname) +static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, + jstring ifname, jint mask) { int result; const char *nameStr = env->GetStringUTFChars(ifname, NULL); - result = ::ifc_reset_connections(nameStr); + + LOGD("android_net_utils_resetConnections in env=%p clazz=%p iface=%s mask=0x%x\n", + env, clazz, nameStr, mask); + + result = ::ifc_reset_connections(nameStr, mask); env->ReleaseStringUTFChars(ifname, nameStr); return (jint)result; } @@ -206,7 +211,7 @@ static JNINativeMethod gNetworkUtilMethods[] = { { "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface }, { "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface }, - { "resetConnections", "(Ljava/lang/String;)I", (void *)android_net_utils_resetConnections }, + { "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections }, { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcp }, { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcpRenew }, { "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp }, diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp index 203b5ef..c22b071 100644 --- a/core/jni/android_net_TrafficStats.cpp +++ b/core/jni/android_net_TrafficStats.cpp @@ -44,7 +44,6 @@ enum Tcp_Udp { // Returns an ASCII decimal number read from the specified file, -1 on error. static jlong readNumber(char const* filename) { -#ifdef HAVE_ANDROID_OS char buf[80]; int fd = open(filename, O_RDONLY); if (fd < 0) { @@ -62,21 +61,42 @@ static jlong readNumber(char const* filename) { close(fd); buf[len] = '\0'; return atoll(buf); -#else // Simulator - return -1; -#endif } -// Return the number from the first file which exists and contains data -static jlong tryBoth(char const* a, char const* b) { - jlong num = readNumber(a); - return num >= 0 ? num : readNumber(b); +static const char* mobile_iface_list[] = { + "rmnet0", + "rmnet1", + "rmnet2", + "rmnet3", + "ppp0", + 0 +}; + +static jlong getAll(const char** iface_list, const char* what) { + + char filename[80]; + int idx = 0; + bool supported = false; + jlong total = 0; + while (iface_list[idx] != 0) { + + snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", + iface_list[idx], what); + jlong number = readNumber(filename); + if (number >= 0) { + supported = true; + total += number; + } + idx++; + } + if (supported) return total; + + return -1; } // Returns the sum of numbers from the specified path under /sys/class/net/*, // -1 if no such file exists. static jlong readTotal(char const* suffix) { -#ifdef HAVE_ANDROID_OS char filename[PATH_MAX] = "/sys/class/net/"; DIR *dir = opendir(filename); if (dir == NULL) { @@ -98,9 +118,6 @@ static jlong readTotal(char const* suffix) { closedir(dir); return total; -#else // Simulator - return -1; -#endif } // Mobile stats get accessed a lot more often than total stats. @@ -108,27 +125,19 @@ static jlong readTotal(char const* suffix) { // each file every time (rather than caching which ones exist). static jlong getMobileTxPackets(JNIEnv* env, jobject clazz) { - return tryBoth( - "/sys/class/net/rmnet0/statistics/tx_packets", - "/sys/class/net/ppp0/statistics/tx_packets"); + return getAll(mobile_iface_list, "tx_packets"); } static jlong getMobileRxPackets(JNIEnv* env, jobject clazz) { - return tryBoth( - "/sys/class/net/rmnet0/statistics/rx_packets", - "/sys/class/net/ppp0/statistics/rx_packets"); + return getAll(mobile_iface_list, "rx_packets"); } static jlong getMobileTxBytes(JNIEnv* env, jobject clazz) { - return tryBoth( - "/sys/class/net/rmnet0/statistics/tx_bytes", - "/sys/class/net/ppp0/statistics/tx_bytes"); + return getAll(mobile_iface_list, "tx_bytes"); } static jlong getMobileRxBytes(JNIEnv* env, jobject clazz) { - return tryBoth( - "/sys/class/net/rmnet0/statistics/rx_bytes", - "/sys/class/net/ppp0/statistics/rx_bytes"); + return getAll(mobile_iface_list, "rx_bytes"); } static jlong getData(JNIEnv* env, const char* what, jstring javaInterface) { diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 2297834..a4432c3 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -278,7 +278,6 @@ jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz); jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz); -#ifdef HAVE_ANDROID_OS /* pulled out of bionic */ extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); @@ -414,7 +413,6 @@ static void dumpNativeHeap(FILE* fp) fprintf(fp, "END\n"); } -#endif /*HAVE_ANDROID_OS*/ /* * Dump the native heap, writing human-readable output to the specified @@ -449,13 +447,9 @@ static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, return; } -#ifdef HAVE_ANDROID_OS LOGD("Native heap dump starting...\n"); dumpNativeHeap(fp); LOGD("Native heap dump complete.\n"); -#else - fprintf(fp, "Native heap dump not available on this platform\n"); -#endif fclose(fp); } diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp index 89dce89..8d65cbc 100644 --- a/core/jni/android_os_FileUtils.cpp +++ b/core/jni/android_os_FileUtils.cpp @@ -28,11 +28,8 @@ #include <sys/types.h> #include <fcntl.h> #include <signal.h> - -#if HAVE_ANDROID_OS #include <sys/ioctl.h> #include <linux/msdos_fs.h> -#endif namespace android { @@ -53,7 +50,6 @@ jint android_os_FileUtils_setPermissions(JNIEnv* env, jobject clazz, jstring file, jint mode, jint uid, jint gid) { - #if HAVE_ANDROID_OS const jchar* str = env->GetStringCritical(file, 0); String8 file8; if (str) { @@ -70,15 +66,11 @@ jint android_os_FileUtils_setPermissions(JNIEnv* env, jobject clazz, } } return chmod(file8.string(), mode) == 0 ? 0 : errno; - #else - return ENOSYS; - #endif } jint android_os_FileUtils_getPermissions(JNIEnv* env, jobject clazz, jstring file, jintArray outArray) { - #if HAVE_ANDROID_OS const jchar* str = env->GetStringCritical(file, 0); String8 file8; if (str) { @@ -107,9 +99,6 @@ jint android_os_FileUtils_getPermissions(JNIEnv* env, jobject clazz, } env->ReleasePrimitiveArrayCritical(outArray, array, 0); return 0; - #else - return ENOSYS; - #endif } jint android_os_FileUtils_setUMask(JNIEnv* env, jobject clazz, jint mask) @@ -119,7 +108,6 @@ jint android_os_FileUtils_setUMask(JNIEnv* env, jobject clazz, jint mask) jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path) { - #if HAVE_ANDROID_OS if (path == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return -1; @@ -137,9 +125,6 @@ jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring pat env->ReleaseStringUTFChars(path, pathStr); return result; - #else - return -1; - #endif } jboolean android_os_FileUtils_getFileStatus(JNIEnv* env, jobject clazz, jstring path, jobject fileStatus) { diff --git a/core/jni/android_os_Power.cpp b/core/jni/android_os_Power.cpp index 9ae4a63..dc16990 100644 --- a/core/jni/android_os_Power.cpp +++ b/core/jni/android_os_Power.cpp @@ -70,16 +70,11 @@ setScreenState(JNIEnv *env, jobject clazz, jboolean on) static void android_os_Power_shutdown(JNIEnv *env, jobject clazz) { -#ifdef HAVE_ANDROID_OS android_reboot(ANDROID_RB_POWEROFF, 0, 0); -#else - sync(); -#endif } static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason) { -#ifdef HAVE_ANDROID_OS if (reason == NULL) { android_reboot(ANDROID_RB_RESTART, 0, 0); } else { @@ -88,9 +83,6 @@ static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason) env->ReleaseStringUTFChars(reason, chars); // In case it fails. } jniThrowIOException(env, errno); -#else - sync(); -#endif } static JNINativeMethod method_table[] = { diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 2b09442..8f84b81 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -1115,7 +1115,7 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, LOGV("... uuid = %s", uuid); dbus_message_ref(msg); // increment refcount because we pass to java - env->CallBooleanMethod(nat->me, method_onAgentAuthorize, + env->CallVoidMethod(nat->me, method_onAgentAuthorize, env->NewStringUTF(object_path), env->NewStringUTF(uuid), int(msg)); diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index 1166ae4..86e7cc0 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -1396,7 +1396,6 @@ static jstring registerSinkHealthApplicationNative(JNIEnv *env, jobject object, LOG_AND_FREE_DBUS_ERROR(&err); } } else { - LOGE("--_Call made getting the patch..."); if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &c_path, DBUS_TYPE_INVALID)) { @@ -1405,7 +1404,6 @@ static jstring registerSinkHealthApplicationNative(JNIEnv *env, jobject object, } } else { path = env->NewStringUTF(c_path); - LOGE("----Path is %s", c_path); } dbus_message_unref(reply); } @@ -1459,7 +1457,6 @@ static jboolean createChannelNative(JNIEnv *env, jobject object, const char *c_device_path = env->GetStringUTFChars(devicePath, NULL); const char *c_app_path = env->GetStringUTFChars(appPath, NULL); const char *c_config = env->GetStringUTFChars(config, NULL); - LOGE("Params...%s, %s, %s \n", c_device_path, c_app_path, c_config); DBusMessage *reply = dbus_func_args(env, nat->conn, c_device_path, @@ -1531,7 +1528,6 @@ static jstring getMainChannelNative(JNIEnv *env, jobject object, jstring deviceP DBusError err; dbus_error_init(&err); - LOGE("---Args %s", c_device_path); DBusMessage *reply = dbus_func_args(env, nat->conn, c_device_path, DBUS_HEALTH_DEVICE_IFACE, "GetProperties", @@ -1566,8 +1562,6 @@ static jstring getChannelApplicationNative(JNIEnv *env, jobject object, jstring DBusError err; dbus_error_init(&err); - LOGE("---Args %s", c_channel_path); - DBusMessage *reply = dbus_func_args(env, nat->conn, c_channel_path, DBUS_HEALTH_CHANNEL_IFACE, "GetProperties", @@ -1596,7 +1590,6 @@ static jstring getChannelApplicationNative(JNIEnv *env, jobject object, jstring if (!strcmp(c_name, "Application")) { path = (jstring) env->GetObjectArrayElement(str_array, i+1); - LOGE("----Path is %s", env->GetStringUTFChars(path, NULL)); env->ReleaseStringUTFChars(name, c_name); return path; } @@ -1655,13 +1648,11 @@ static jobject getChannelFdNative(JNIEnv *env, jobject object, jstring channelPa fd = dbus_returns_unixfd(env, reply); if (fd == -1) return NULL; - LOGE("---got fd %d\n", fd); // Create FileDescriptor object jobject fileDesc = jniCreateFileDescriptor(env, fd); if (fileDesc == NULL) { // FileDescriptor constructor has thrown an exception releaseChannelFdNative(env, object, channelPath); - LOGE("---File Desc is null"); return NULL; } diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp index 0fbe0e7..2c7bb84 100644 --- a/core/jni/android_util_Log.cpp +++ b/core/jni/android_util_Log.cpp @@ -58,9 +58,6 @@ static int toLevel(const char* value) static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level) { -#ifndef HAVE_ANDROID_OS - return false; -#else /* HAVE_ANDROID_OS */ int len; char key[PROPERTY_KEY_MAX]; char buf[PROPERTY_VALUE_MAX]; @@ -93,7 +90,6 @@ static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring len = property_get(key, buf, ""); int logLevel = toLevel(buf); return (logLevel >= 0 && level >= logLevel) ? true : false; -#endif /* HAVE_ANDROID_OS */ } /* diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 0960b25..47d343a 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -318,17 +318,15 @@ jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz, jint pid, jint adj) { #ifdef HAVE_OOM_ADJ - if (ProcessState::self()->supportsProcesses()) { - char text[64]; - sprintf(text, "/proc/%d/oom_adj", pid); - int fd = open(text, O_WRONLY); - if (fd >= 0) { - sprintf(text, "%d", adj); - write(fd, text, strlen(text)); - close(fd); - } - return true; + char text[64]; + sprintf(text, "/proc/%d/oom_adj", pid); + int fd = open(text, O_WRONLY); + if (fd >= 0) { + sprintf(text, "%d", adj); + write(fd, text, strlen(text)); + close(fd); } + return true; #endif return false; } @@ -354,25 +352,12 @@ void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name) jint android_os_Process_setUid(JNIEnv* env, jobject clazz, jint uid) { - #if HAVE_ANDROID_OS return setuid(uid) == 0 ? 0 : errno; - #else - return ENOSYS; - #endif } jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint uid) { - #if HAVE_ANDROID_OS return setgid(uid) == 0 ? 0 : errno; - #else - return ENOSYS; - #endif -} - -jboolean android_os_Process_supportsProcesses(JNIEnv* env, jobject clazz) -{ - return ProcessState::self()->supportsProcesses(); } static int pid_compare(const void* v1, const void* v2) @@ -878,7 +863,6 @@ static const JNINativeMethod methods[] = { {"setGid", "(I)I", (void*)android_os_Process_setGid}, {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet}, - {"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses}, {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines}, {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 2106eb4..681f43f 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -115,6 +115,18 @@ static jboolean android_view_GLES20Canvas_isBackBufferPreserved(JNIEnv* env, job return error == EGL_SUCCESS && value == EGL_BUFFER_PRESERVED; } +static void android_view_GLES20Canvas_disableVsync(JNIEnv* env, jobject clazz) { + EGLDisplay display = eglGetCurrentDisplay(); + + eglGetError(); + eglSwapInterval(display, 0); + + EGLint error = eglGetError(); + if (error != EGL_SUCCESS) { + RENDERER_LOGD("Could not disable v-sync (%x)", error); + } +} + // ---------------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------------- @@ -621,7 +633,7 @@ static Layer* android_view_GLES20Canvas_createTextureLayer(JNIEnv* env, jobject if (layer) { jint* storage = env->GetIntArrayElements(layerInfo, NULL); - storage[0] = layer->texture; + storage[0] = layer->getTexture(); env->ReleaseIntArrayElements(layerInfo, storage, 0); } @@ -634,8 +646,8 @@ static Layer* android_view_GLES20Canvas_createLayer(JNIEnv* env, jobject clazz, if (layer) { jint* storage = env->GetIntArrayElements(layerInfo, NULL); - storage[0] = layer->width; - storage[1] = layer->height; + storage[0] = layer->getWidth(); + storage[1] = layer->getHeight(); env->ReleaseIntArrayElements(layerInfo, storage, 0); } @@ -647,8 +659,8 @@ static void android_view_GLES20Canvas_resizeLayer(JNIEnv* env, jobject clazz, LayerRenderer::resizeLayer(layer, width, height); jint* storage = env->GetIntArrayElements(layerInfo, NULL); - storage[0] = layer->width; - storage[1] = layer->height; + storage[0] = layer->getWidth(); + storage[1] = layer->getHeight(); env->ReleaseIntArrayElements(layerInfo, storage, 0); } @@ -722,6 +734,7 @@ static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER { "nIsBackBufferPreserved", "()Z", (void*) android_view_GLES20Canvas_isBackBufferPreserved }, { "nPreserveBackBuffer", "()Z", (void*) android_view_GLES20Canvas_preserveBackBuffer }, + { "nDisableVsync", "()V", (void*) android_view_GLES20Canvas_disableVsync }, { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer }, { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer }, diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp index 86fd9cb..7e5dede 100644 --- a/core/jni/com_android_internal_os_ZygoteInit.cpp +++ b/core/jni/com_android_internal_os_ZygoteInit.cpp @@ -27,13 +27,11 @@ #include <JNIHelp.h> #include "android_runtime/AndroidRuntime.h" -#ifdef HAVE_ANDROID_OS #include <linux/capability.h> #include <linux/prctl.h> #include <sys/prctl.h> extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); -#endif namespace android { @@ -168,7 +166,6 @@ static void com_android_internal_os_ZygoteInit_setCloseOnExec (JNIEnv *env, static void com_android_internal_os_ZygoteInit_setCapabilities (JNIEnv *env, jobject clazz, jlong permitted, jlong effective) { -#ifdef HAVE_ANDROID_OS struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata; int err; @@ -190,15 +187,11 @@ static void com_android_internal_os_ZygoteInit_setCapabilities (JNIEnv *env, jniThrowIOException(env, errno); return; } -#endif /* HAVE_ANDROID_OS */ } static jlong com_android_internal_os_ZygoteInit_capgetPermitted (JNIEnv *env, jobject clazz, jint pid) { -#ifndef HAVE_ANDROID_OS - return (jlong)0; -#else struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata; int err; @@ -217,7 +210,6 @@ static jlong com_android_internal_os_ZygoteInit_capgetPermitted (JNIEnv *env, } return (jlong) capdata.permitted; -#endif /* HAVE_ANDROID_OS */ } static jint com_android_internal_os_ZygoteInit_selectReadable ( diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png Binary files differindex 5225a81..acbbb38 100644 --- a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png Binary files differindex 2e7e973..6009528 100644 --- a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png +++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png Binary files differindex 4591627..30727d7 100644 --- a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png Binary files differindex 9cf1826..7cea5e1 100644 --- a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png +++ b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png Binary files differindex a47ef40..ba0d612 100644 --- a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png Binary files differindex 9b50c73..e8646b9 100644 --- a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png +++ b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png Binary files differindex a0d36de..14cb4c9 100644 --- a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png Binary files differindex 805b956..80fd218 100644 --- a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png +++ b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/ic_btn_back.png b/core/res/res/drawable-hdpi/ic_btn_back.png Binary files differdeleted file mode 100644 index f8b3285..0000000 --- a/core/res/res/drawable-hdpi/ic_btn_back.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ic_btn_next.png b/core/res/res/drawable-hdpi/ic_btn_next.png Binary files differdeleted file mode 100644 index b2c6e1b..0000000 --- a/core/res/res/drawable-hdpi/ic_btn_next.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png Binary files differindex b0fba52..1014d8a 100644 --- a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png Binary files differindex 3ef8935..18cd171 100644 --- a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png +++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/toast_frame.9.png b/core/res/res/drawable-hdpi/toast_frame.9.png Binary files differindex 9769bbb..ad2cb5a 100644 --- a/core/res/res/drawable-hdpi/toast_frame.9.png +++ b/core/res/res/drawable-hdpi/toast_frame.9.png diff --git a/core/res/res/drawable-hdpi/toast_frame_holo.9.png b/core/res/res/drawable-hdpi/toast_frame_holo.9.png Binary files differindex 9769bbb..ad2cb5a 100644 --- a/core/res/res/drawable-hdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-hdpi/toast_frame_holo.9.png diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png Binary files differindex a0bd4e3..4836da1 100644 --- a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png Binary files differindex 12abcd2..c299931 100644 --- a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png +++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png Binary files differindex adb8104..86edad7 100644 --- a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png Binary files differindex d7c6bbf..53ee68b 100644 --- a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png +++ b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png Binary files differindex 42cfc52..606adaf 100644 --- a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png Binary files differindex 9a08e15..14d2e5e 100644 --- a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png +++ b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png Binary files differindex 5d86b2a..2646332 100644 --- a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png Binary files differindex ad22f5b..48ec0a4 100644 --- a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png +++ b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/ic_btn_back.png b/core/res/res/drawable-mdpi/ic_btn_back.png Binary files differdeleted file mode 100644 index c9bff4c..0000000 --- a/core/res/res/drawable-mdpi/ic_btn_back.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_btn_next.png b/core/res/res/drawable-mdpi/ic_btn_next.png Binary files differdeleted file mode 100755 index c6cf436..0000000 --- a/core/res/res/drawable-mdpi/ic_btn_next.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png Binary files differindex 923f92d..dd5dd39 100644 --- a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png Binary files differindex afada39..12d65be 100644 --- a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png +++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/toast_frame.9.png b/core/res/res/drawable-mdpi/toast_frame.9.png Binary files differindex 06cfc70..b9105de 100755 --- a/core/res/res/drawable-mdpi/toast_frame.9.png +++ b/core/res/res/drawable-mdpi/toast_frame.9.png diff --git a/core/res/res/drawable-mdpi/toast_frame_holo.9.png b/core/res/res/drawable-mdpi/toast_frame_holo.9.png Binary files differindex 06cfc70..b9105de 100755 --- a/core/res/res/drawable-mdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-mdpi/toast_frame_holo.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png Binary files differindex b33e117..077e4d3 100644 --- a/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png Binary files differindex d84d427..357c17f 100644 --- a/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png Binary files differindex 9c0ff47..5b51072 100644 --- a/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png Binary files differindex 331a4f2..2705a39 100644 --- a/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png Binary files differindex cdc887d..101876f 100644 --- a/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png Binary files differindex bdb6824..0df1503 100644 --- a/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png Binary files differindex 600efb3..344a4e2 100644 --- a/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png Binary files differindex aa8401d..249848f 100644 --- a/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..92acc47 --- /dev/null +++ b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png Binary files differnew file mode 100644 index 0000000..4e54b4b6 --- /dev/null +++ b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/toast_frame.9.png b/core/res/res/drawable-xhdpi/toast_frame.9.png Binary files differindex f7debee..9f39a77 100644 --- a/core/res/res/drawable-xhdpi/toast_frame.9.png +++ b/core/res/res/drawable-xhdpi/toast_frame.9.png diff --git a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png Binary files differindex f7debee..9f39a77 100644 --- a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png diff --git a/core/res/res/layout-sw600dp/preference_list_content.xml b/core/res/res/layout-sw600dp/preference_list_content.xml index 5a345c6..a5320a7 100644 --- a/core/res/res/layout-sw600dp/preference_list_content.xml +++ b/core/res/res/layout-sw600dp/preference_list_content.xml @@ -87,7 +87,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -95,8 +94,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -117,8 +114,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/layout-w600dp/preference_list_content_single.xml b/core/res/res/layout-w600dp/preference_list_content_single.xml index 6725996..bbad296 100644 --- a/core/res/res/layout-w600dp/preference_list_content_single.xml +++ b/core/res/res/layout-w600dp/preference_list_content_single.xml @@ -61,7 +61,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -69,8 +68,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -91,8 +88,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/layout-xlarge/activity_list.xml b/core/res/res/layout-xlarge/activity_list.xml index ad485c1..5093a5e 100644 --- a/core/res/res/layout-xlarge/activity_list.xml +++ b/core/res/res/layout-xlarge/activity_list.xml @@ -54,21 +54,15 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" - android:layout_height="4dip" - android:layout_marginLeft="16dip" - android:layout_marginRight="16dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:layout_height="2dip" + android:background="@android:color/holo_blue_light" /> <!-- If the client uses a customTitle, it will be added here. --> </LinearLayout> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_marginLeft="32dip" - android:layout_marginRight="32dip" android:layout_height="0dip" android:layout_weight="1"> @@ -95,15 +89,12 @@ android:minHeight="54dip" android:orientation="vertical" android:divider="?android:attr/dividerHorizontal" - android:showDividers="beginning" - android:dividerPadding="16dip"> + android:showDividers="beginning"> <LinearLayout style="?android:attr/buttonBarStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:paddingLeft="2dip" - android:paddingRight="2dip" android:measureWithLargestChild="true"> <LinearLayout android:id="@+id/leftSpacer" android:layout_weight="0.25" diff --git a/core/res/res/layout/activity_chooser_list_footer.xml b/core/res/res/layout/activity_chooser_list_footer.xml deleted file mode 100644 index 7603a31..0000000 --- a/core/res/res/layout/activity_chooser_list_footer.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/list_footer" - android:paddingLeft="16dip" - android:paddingRight="16dip" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center" - android:orientation="vertical"> - - <ImageView - android:id="@+id/divider" - android:layout_width="match_parent" - android:layout_height="2dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@drawable/divider_strong_holo" /> - - <TextView - android:id="@+id/title" - android:layout_width="wrap_content" - android:layout_height="48dip" - android:gravity="center" - android:textAppearance="?android:attr/textAppearanceLargePopupMenu" - android:duplicateParentState="true" - android:singleLine="true" - android:text="@string/activity_chooser_view_see_all" /> - -</LinearLayout> diff --git a/core/res/res/layout/activity_chooser_list_header.xml b/core/res/res/layout/activity_chooser_list_header.xml deleted file mode 100644 index 867014b..0000000 --- a/core/res/res/layout/activity_chooser_list_header.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/list_header" - android:paddingLeft="16dip" - android:paddingRight="16dip" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center" - android:orientation="vertical"> - - <TextView - android:id="@+id/title" - android:layout_width="wrap_content" - android:layout_height="?android:attr/dropdownListPreferredItemHeight" - android:gravity="center" - android:textAppearance="?android:attr/textAppearanceLargePopupMenu" - android:duplicateParentState="true" - android:singleLine="true" /> - - <ImageView - android:id="@+id/divider" - android:layout_width="match_parent" - android:layout_height="2dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@drawable/divider_strong_holo" /> - -</LinearLayout> diff --git a/core/res/res/layout/activity_chooser_view.xml b/core/res/res/layout/activity_chooser_view.xml index ccf49fc..902b3c0 100644 --- a/core/res/res/layout/activity_chooser_view.xml +++ b/core/res/res/layout/activity_chooser_view.xml @@ -25,11 +25,11 @@ <ImageButton android:id="@+id/default_activity_button" android:layout_width="32dip" android:layout_height="32dip" - android:layout_marginLeft="16dip" /> + android:layout_marginRight="8dip" /> <ImageButton android:id="@+id/expand_activities_button" android:layout_width="32dip" android:layout_height="32dip" - android:layout_marginLeft="16dip" /> + android:layout_marginLeft="8dip" /> </LinearLayout> diff --git a/core/res/res/layout/activity_chooser_view_list_item.xml b/core/res/res/layout/activity_chooser_view_list_item.xml index 61b7e70..f90044e 100644 --- a/core/res/res/layout/activity_chooser_view_list_item.xml +++ b/core/res/res/layout/activity_chooser_view_list_item.xml @@ -18,26 +18,35 @@ android:id="@+id/list_item" android:layout_width="match_parent" android:layout_height="?android:attr/dropdownListPreferredItemHeight" - android:gravity="center_vertical" android:paddingLeft="16dip" - android:paddingRight="16dip"> - - <ImageView - android:id="@+id/icon" - android:layout_width="32dip" - android:layout_height="32dip" - android:layout_gravity="center_vertical" - android:layout_marginRight="8dip" - android:duplicateParentState="true" /> - - <TextView - android:id="@+id/title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceLargePopupMenu" - android:singleLine="true" - android:duplicateParentState="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" /> + android:paddingRight="16dip" + android:background="?android:attr/activatedBackgroundIndicator" + android:orientation="vertical" > + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:duplicateParentState="true" > + + <ImageView + android:id="@+id/icon" + android:layout_width="32dip" + android:layout_height="32dip" + android:layout_gravity="center_vertical" + android:layout_marginRight="8dip" + android:duplicateParentState="true" /> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:textAppearance="?android:attr/textAppearanceLargePopupMenu" + android:duplicateParentState="true" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" /> + + </LinearLayout> </LinearLayout> diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml index 2185467..2b686bd 100644 --- a/core/res/res/layout/alert_dialog_holo.xml +++ b/core/res/res/layout/alert_dialog_holo.xml @@ -30,13 +30,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> - <ImageView android:id="@+id/titleDividerTop" + <View android:id="@+id/titleDividerTop" android:layout_width="match_parent" - android:layout_height="1dip" + android:layout_height="2dip" android:visibility="gone" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:background="@android:color/holo_blue_light" /> <LinearLayout android:id="@+id/title_template" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -51,20 +49,17 @@ android:paddingRight="8dip" android:src="@null" /> <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle" - style="?android:attr/textAppearanceLarge" - android:textColor="@android:color/holo_blue" + style="?android:attr/windowTitleStyle" android:singleLine="true" android:ellipsize="end" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" - android:layout_height="1dip" + android:layout_height="2dip" android:visibility="gone" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:background="@android:color/holo_blue_light" /> <!-- If the client uses a customTitle, it will be added here. --> </LinearLayout> diff --git a/core/res/res/layout/dialog_custom_title_holo.xml b/core/res/res/layout/dialog_custom_title_holo.xml index 5261553..e2335a7 100644 --- a/core/res/res/layout/dialog_custom_title_holo.xml +++ b/core/res/res/layout/dialog_custom_title_holo.xml @@ -28,12 +28,10 @@ This is an custom layout for a dialog. android:gravity="center_vertical|left" style="?android:attr/windowTitleBackgroundStyle"> </FrameLayout> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" - android:layout_height="1dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:layout_height="2dip" + android:background="@android:color/holo_blue_light" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" diff --git a/core/res/res/layout/dialog_title_holo.xml b/core/res/res/layout/dialog_title_holo.xml index 400ef60..50bb0ba 100644 --- a/core/res/res/layout/dialog_title_holo.xml +++ b/core/res/res/layout/dialog_title_holo.xml @@ -30,12 +30,10 @@ enabled. android:paddingLeft="16dip" android:paddingRight="16dip" android:gravity="center_vertical|left" /> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" - android:layout_height="1dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:layout_height="2dip" + android:background="@android:color/holo_blue_light" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" diff --git a/core/res/res/layout/dialog_title_icons_holo.xml b/core/res/res/layout/dialog_title_icons_holo.xml index f780ab0..7d7959f 100644 --- a/core/res/res/layout/dialog_title_icons_holo.xml +++ b/core/res/res/layout/dialog_title_icons_holo.xml @@ -48,12 +48,10 @@ enabled. android:layout_marginLeft="8dip" /> </LinearLayout> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" android:layout_height="1dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:background="@android:color/holo_blue_light" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/core/res/res/layout/list_menu_item_icon.xml b/core/res/res/layout/list_menu_item_icon.xml index 6ff14dd..a885211 100644 --- a/core/res/res/layout/list_menu_item_icon.xml +++ b/core/res/res/layout/list_menu_item_icon.xml @@ -19,6 +19,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginRight="8dip" + android:layout_marginLeft="8dip" + android:layout_marginRight="-8dip" + android:scaleType="center" android:duplicateParentState="true" /> diff --git a/core/res/res/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml index fef017d..1a12c01 100644 --- a/core/res/res/layout/popup_menu_item_layout.xml +++ b/core/res/res/layout/popup_menu_item_layout.xml @@ -18,7 +18,6 @@ android:layout_width="match_parent" android:layout_height="?android:attr/dropdownListPreferredItemHeight" android:minWidth="196dip" - android:paddingLeft="16dip" android:paddingRight="16dip"> <!-- Icon will be inserted here. --> @@ -29,6 +28,7 @@ android:layout_weight="1" android:layout_height="wrap_content" android:layout_gravity="center_vertical" + android:layout_marginLeft="16dip" android:duplicateParentState="true"> <TextView diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml index 82b3a4c..fb898ee 100644 --- a/core/res/res/layout/preference_list_content.xml +++ b/core/res/res/layout/preference_list_content.xml @@ -85,7 +85,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -93,8 +92,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -115,8 +112,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/layout/preference_list_content_single.xml b/core/res/res/layout/preference_list_content_single.xml index a015761..6902ffd 100644 --- a/core/res/res/layout/preference_list_content_single.xml +++ b/core/res/res/layout/preference_list_content_single.xml @@ -56,7 +56,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -64,8 +63,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -86,8 +83,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml index 986536e..315f708 100644 --- a/core/res/res/layout/preference_list_fragment.xml +++ b/core/res/res/layout/preference_list_fragment.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* +/* ** Copyright 2010, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,7 +41,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -49,8 +48,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -71,8 +68,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 2cc8171..9613712 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -40,6 +40,21 @@ to use accelerated drawing (thus setting state_accelerated), the cache hint is ignored and always assumed to be transparent. --> <attr name="colorBackgroundCacheHint" format="color" /> + + <!-- Default highlight color for items that are pressed. --> + <attr name="colorPressedHighlight" format="color" /> + <!-- Default highlight color for items that are long-pressed. --> + <attr name="colorLongPressedHighlight" format="color" /> + <!-- Default highlight color for items that are + focused. (Focused meaning cursor-based selection.) --> + <attr name="colorFocusedHighlight" format="color" /> + <!-- Default highlight color for items that are + activated. (Activated meaning persistent selection.) --> + <attr name="colorActivatedHighlight" format="color" /> + <!-- Default highlight color for items in multiple selection + mode. --> + <attr name="colorMultiSelectHighlight" format="color" /> + <!-- Default disabled alpha for widgets that set enabled/disabled alpha programmatically. --> <attr name="disabledAlpha" format="float" /> <!-- Default background dim amount when a menu, dialog, or something similar pops up. --> @@ -2810,6 +2825,8 @@ <attr name="textColorHint" /> <!-- Color of the links. --> <attr name="textColorLink" /> + <!-- Present the text in ALL CAPS. This may use a small-caps form when available. --> + <attr name="textAllCaps" format="boolean" /> </declare-styleable> <declare-styleable name="TextSwitcher"> </declare-styleable> @@ -2998,6 +3015,10 @@ <attr name="drawableLeft" format="reference|color" /> <!-- The drawable to be drawn to the right of the text. --> <attr name="drawableRight" format="reference|color" /> + <!-- The drawable to be drawn to the start of the text. --> + <attr name="drawableStart" format="reference|color" /> + <!-- The drawable to be drawn to the end of the text. --> + <attr name="drawableEnd" format="reference|color" /> <!-- The padding between the drawables and the text. --> <attr name="drawablePadding" format="dimension" /> <!-- Extra spacing between lines of text. --> @@ -3074,6 +3095,8 @@ <attr name="textIsSelectable" /> <!-- Suggestions will be displayed when the user double taps on editable text. --> <attr name="suggestionsEnabled" /> + <!-- Present the text in ALL CAPS. This may use a small-caps form when available. --> + <attr name="textAllCaps" /> </declare-styleable> <!-- An <code>input-extras</code> is a container for extra data to supply to an input method. Contains @@ -3326,7 +3349,7 @@ <!-- The row span: the difference between the bottom and top boundaries delimiting the group of cells occupied by this view. The default is one. - See {@link android.widget.GridLayout.Group}. --> + See {@link android.widget.GridLayout.Spec}. --> <attr name="layout_rowSpan" format="integer" min="1" /> <!-- The column boundary delimiting the left of the group of cells occupied by this view. --> @@ -3334,23 +3357,21 @@ <!-- The column span: the difference between the right and left boundaries delimiting the group of cells occupied by this view. The default is one. - See {@link android.widget.GridLayout.Group}. --> + See {@link android.widget.GridLayout.Spec}. --> <attr name="layout_columnSpan" format="integer" min="1" /> <!-- Gravity specifies how a component should be placed in its group of cells. The default is LEFT | BASELINE. See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. --> <attr name="layout_gravity" /> <!-- A value specifying how much deficit or excess width this component can accomodate. - The default is FIXED. - See {@link android.widget.GridLayout.Group#flexibility}.--> + The default is FIXED. --> <attr name="layout_columnFlexibility" > <!-- If possible, width should be greater than or equal to the specified width. See {@link android.widget.GridLayout#CAN_STRETCH}. --> <enum name="canStretch" value="2" /> </attr> <!-- A value specifying how much deficit or excess height this component can accomodate. - The default is FIXED. - See {@link android.widget.GridLayout.Group#flexibility}.--> + The default is FIXED. --> <attr name="layout_rowFlexibility" > <!-- If possible, height should be greater than or equal to the specified height. See {@link android.widget.GridLayout#CAN_STRETCH}. --> @@ -5299,6 +5320,8 @@ <attr name="mtpReserve" format="integer" /> <!-- true if the storage can be shared via USB mass storage --> <attr name="allowMassStorage" format="boolean" /> + <!-- maximum file size for the volume in megabytes, zero or unspecified if it is unbounded --> + <attr name="maxFileSize" format="integer" /> </declare-styleable> <declare-styleable name="SwitchPreference"> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 2a1ebfc..631d8c6 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -143,19 +143,42 @@ <color name="link_text_holo_light">#0000ee</color> <!-- Group buttons --> + <eat-comment /> <color name="group_button_dialog_pressed_holo_dark">#46c5c1ff</color> <color name="group_button_dialog_focused_holo_dark">#2699cc00</color> <color name="group_button_dialog_pressed_holo_light">#ffffffff</color> <color name="group_button_dialog_focused_holo_light">#4699cc00</color> + <!-- Highlight colors for the legacy themes --> + <eat-comment /> + <color name="legacy_pressed_highlight">#fffeaa0c</color> + <color name="legacy_selected_highlight">#fff17a0a</color> + <color name="legacy_long_pressed_highlight">#ffffffff</color> + <!-- General purpose colors for Holo-themed elements --> <eat-comment /> - <!-- A Holo shade of blue --> - <color name="holo_blue">#ff6699ff</color> - <!-- A Holo shade of green --> - <color name="holo_green">#ff99cc00</color> + <!-- A light Holo shade of blue --> + <color name="holo_blue_light">#ff33b5e5</color> + <!-- A light Holo shade of green --> + <color name="holo_green_light">#ff99cc00</color> + <!-- A light Holo shade of red --> + <color name="holo_red_light">#ffff4444</color> + <!-- A dark Holo shade of blue --> + <color name="holo_blue_dark">#ff0099cc</color> + <!-- A dark Holo shade of green --> + <color name="holo_green_dark">#ff669900</color> + <!-- A dark Holo shade of red --> + <color name="holo_red_dark">#ffcc0000</color> + <!-- A Holo shade of purple --> + <color name="holo_purple">#ffaa66cc</color> + <!-- A light Holo shade of orange --> + <color name="holo_orange_light">#ffffbb33</color> + <!-- A dark Holo shade of orange --> + <color name="holo_orange_dark">#ffff8800</color> + <!-- A really bright Holo shade of blue --> + <color name="holo_blue_bright">#ff00ddff</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9f05cbc..1f2b7fb 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -660,4 +660,9 @@ extremely limited. --> <bool name="config_allowActionMenuItemTextWithIcon">false</bool> + <!-- Remote server that can provide NTP responses. --> + <string translatable="false" name="config_ntpServer">pool.ntp.org</string> + <!-- Timeout to wait for NTP server response. --> + <integer name="config_ntpTimeout">20000</integer> + </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index a5e5f70..0f6e5cf 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -133,4 +133,7 @@ <!-- Size of right margin on Unsecure unlock LockScreen --> <dimen name="keyguard_lockscreen_status_line_font_right_margin">45dip</dimen> + <!-- Minimum popup width for selecting an activity in ActivityChooserDialog/ActivityChooserView. --> + <dimen name="activity_chooser_popup_min_width">200dip</dimen> + </resources> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index d05685c..547a192 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -74,4 +74,9 @@ <item type="id" name="rowTypeId" /> <item type="id" name="up" /> <item type="id" name="action_menu_divider" /> + <item type="id" name="icon_menu_presenter" /> + <item type="id" name="list_menu_presenter" /> + <item type="id" name="action_menu_presenter" /> + <item type="id" name="overflow_menu_presenter" /> + <item type="id" name="popup_submenu_presenter" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 2de9cd3..6dedc83 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1779,6 +1779,14 @@ <public type="attr" name="backgroundStacked" /> <public type="attr" name="backgroundSplit" /> + <public type="attr" name="textAllCaps" /> + + <public type="attr" name="colorPressedHighlight" /> + <public type="attr" name="colorLongPressedHighlight" /> + <public type="attr" name="colorFocusedHighlight" /> + <public type="attr" name="colorActivatedHighlight" /> + <public type="attr" name="colorMultiSelectHighlight" /> + <public type="style" name="TextAppearance.SuggestionHighlight" /> <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" /> <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" /> @@ -1807,4 +1815,18 @@ <public type="integer" name="status_bar_notification_info_maxnum" /> <public type="string" name="status_bar_notification_info_overflow" /> + <public type="color" name="holo_blue_light" /> + <public type="color" name="holo_blue_dark" /> + <public type="color" name="holo_green_light" /> + <public type="color" name="holo_green_dark" /> + <public type="color" name="holo_red_light" /> + <public type="color" name="holo_red_dark" /> + <public type="color" name="holo_orange_light" /> + <public type="color" name="holo_orange_dark" /> + <public type="color" name="holo_purple" /> + <public type="color" name="holo_blue_bright" /> + + <public type="attr" name="drawableStart" /> + <public type="attr" name="drawableEnd" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 50f8df7..d0e3f14 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2038,13 +2038,13 @@ <string name="autofill_phone_re">phone<!-- de-DE -->|telefonnummer<!-- es -->|telefono|teléfono<!-- fr-FR -->|telfixe<!-- ja-JP -->|電話<!-- pt-BR, pt-PT -->|telefone|telemovel<!-- ru -->|телефон<!-- zh-CN -->|电话</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_area_code_re">area code</string> + <string name="autofill_area_code_re">area.*code|acode|area</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_phone_prefix_re">^-$|\\)$|prefix<!-- fr-FR -->|preselection<!-- pt-BR, pt-PT -->|ddd</string> + <string name="autofill_phone_prefix_re">prefix<!-- fr-FR -->|preselection<!-- pt-BR, pt-PT -->|ddd</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_phone_suffix_re">^-$|suffix</string> + <string name="autofill_phone_suffix_re">suffix</string> <!-- Do not translate. Regex used by AutoFill. --> <string name="autofill_phone_extension_re">ext<!-- pt-BR, pt-PT -->|ramal</string> @@ -2077,14 +2077,50 @@ <string name="autofill_country_code_re">country.*code|ccode|_cc</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_area_code_notext_re">^\($</string> + <string name="autofill_area_code_notext_re">^\\($</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_phone_prefix_separator_re">^-$|^\)$</string> + <string name="autofill_phone_prefix_separator_re">^-$|^\\)$</string> <!-- Do not translate. Regex used by AutoFill. --> <string name="autofill_phone_suffix_separator_re">^-$</string> + <!-- Label in a web form for "Province" [CHAR-LIMIT=NONE] --> + <string name="autofill_province">Province</string> + + <!-- Label in a web form for "Postal code" [CHAR-LIMIT=NONE] --> + <string name="autofill_postal_code">Postal code</string> + + <!-- Label in a web form for "State" [CHAR-LIMIT=NONE] --> + <string name="autofill_state">State</string> + + <!-- Label in a web form for "ZIP code" [CHAR-LIMIT=NONE] --> + <string name="autofill_zip_code">ZIP code</string> + + <!-- Label in a web form for "County" [CHAR-LIMIT=NONE] --> + <string name="autofill_county">County</string> + + <!-- Label in a web form for "Island" [CHAR-LIMIT=NONE] --> + <string name="autofill_island">Island</string> + + <!-- Label in a web form for "District" [CHAR-LIMIT=NONE] --> + <string name="autofill_district">District</string> + + <!-- Label in a web form for "Department" [CHAR-LIMIT=NONE] --> + <string name="autofill_department">Department</string> + + <!-- Label in a web form for "Prefecture" [CHAR-LIMIT=NONE] --> + <string name="autofill_prefecture">Prefecture</string> + + <!-- Label in a web form for "Parish" [CHAR-LIMIT=NONE] --> + <string name="autofill_parish">Parish</string> + + <!-- Label in a web form for "Area" [CHAR-LIMIT=NONE] --> + <string name="autofill_area">Area</string> + + <!-- Label in a web form for "Emirate" [CHAR-LIMIT=NONE] --> + <string name="autofill_emirate">Emirate</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string> @@ -3000,9 +3036,10 @@ <!-- Title for a button to expand the list of activities in ActivityChooserView [CHAR LIMIT=25] --> <string name="activity_chooser_view_see_all">See all...</string> - <!-- Title for a message that there are no activities in ActivityChooserView [CHAR LIMIT=25] --> - <string name="activity_chooser_view_no_activities">No activities</string> - <!-- Title for a message that prompts selection of a default share handler in ActivityChooserView [CHAR LIMIT=25] --> - <string name="activity_chooser_view_select_default">Select default</string> + <!-- Title default for a dialog showing possible activities in ActivityChooserView [CHAR LIMIT=25] --> + <string name="activity_chooser_view_dialog_title_default">Select activity</string> + + <!-- Title for a dialog showing possible activities for sharing in ShareActionProvider [CHAR LIMIT=25] --> + <string name="share_action_provider_share_with">Share with...</string> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 11016f4..a5cd6e3 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1319,6 +1319,7 @@ <item name="android:textSize">12sp</item> <item name="android:textStyle">bold</item> <item name="android:textColor">?android:attr/actionMenuTextColor</item> + <item name="android:textAllCaps">true</item> </style> <style name="TextAppearance.Holo.Widget.ActionMode"> @@ -1355,7 +1356,7 @@ <style name="TextAppearance.Holo.DialogWindowTitle"> <item name="android:textSize">22sp</item> - <item name="android:textColor">@android:color/holo_blue</item> + <item name="android:textColor">@android:color/holo_blue_light</item> </style> <style name="TextAppearance.Holo.CalendarViewWeekDayView" parent="TextAppearance.Small.CalendarViewWeekDayView"> @@ -1454,7 +1455,7 @@ <style name="TextAppearance.Holo.Light.DialogWindowTitle"> <item name="android:textSize">22sp</item> - <item name="android:textColor">@android:color/holo_blue</item> + <item name="android:textColor">@android:color/holo_blue_light</item> </style> <style name="TextAppearance.Holo.Light.CalendarViewWeekDayView" parent="TextAppearance.Small.CalendarViewWeekDayView"> @@ -1857,6 +1858,7 @@ <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:textSize">12sp</item> <item name="android:textStyle">bold</item> + <item name="android:textAllCaps">true</item> </style> <style name="Widget.Holo.ActionMode" parent="Widget.ActionMode"> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 9a52cff..90f3602 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -31,6 +31,13 @@ <item name="colorForegroundInverse">@android:color/bright_foreground_dark_inverse</item> <item name="colorBackground">@android:color/background_dark</item> <item name="colorBackgroundCacheHint">?android:attr/colorBackground</item> + + <item name="colorPressedHighlight">@color/legacy_pressed_highlight</item> + <item name="colorLongPressedHighlight">@color/legacy_long_pressed_highlight</item> + <item name="colorFocusedHighlight">@color/legacy_selected_highlight</item> + <item name="colorMultiSelectHighlight">@color/legacy_selected_highlight</item> + <item name="colorActivatedHighlight">@color/legacy_selected_highlight</item> + <item name="disabledAlpha">0.5</item> <item name="backgroundDimAmount">0.6</item> @@ -799,6 +806,12 @@ <item name="disabledAlpha">0.5</item> <item name="backgroundDimAmount">0.6</item> + <item name="colorPressedHighlight">@color/holo_blue_light</item> + <item name="colorLongPressedHighlight">@color/holo_blue_bright</item> + <item name="colorFocusedHighlight">@color/holo_blue_dark</item> + <item name="colorMultiSelectHighlight">@color/holo_green_light</item> + <item name="colorActivatedHighlight">@color/holo_blue_dark</item> + <!-- Text styles --> <item name="textAppearance">@android:style/TextAppearance.Holo</item> <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Inverse</item> @@ -1090,6 +1103,11 @@ <item name="disabledAlpha">0.5</item> <item name="backgroundDimAmount">0.6</item> + <item name="colorPressedHighlight">@color/holo_blue_light</item> + <item name="colorLongPressedHighlight">@color/holo_blue_bright</item> + <item name="colorFocusedHighlight">@color/holo_blue_dark</item> + <item name="colorMultiSelectHighlight">@color/holo_green_light</item> + <item name="colorActivatedHighlight">@color/holo_blue_dark</item> <!-- Text styles --> <item name="textAppearance">@android:style/TextAppearance.Holo.Light</item> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 00f47fa..b2ebb08 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -12,7 +12,7 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, EnabledTestApp/src) LOCAL_DX_FLAGS := --core-library -LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver +LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver guava LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksCoreTests diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 8e2d925..146466f 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1051,6 +1051,13 @@ </intent-filter> </activity> + <activity android:name="android.widget.TextViewTestActivity" android:label="TextViewTestActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> + <!-- Activity-level metadata --> @@ -1235,6 +1242,13 @@ </intent-filter> </activity> + <activity android:name="android.animation.BasicAnimatorActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> + </application> <instrumentation android:name="android.test.InstrumentationTestRunner" diff --git a/core/tests/coretests/res/layout/animator_basic.xml b/core/tests/coretests/res/layout/animator_basic.xml new file mode 100644 index 0000000..7b8ef11 --- /dev/null +++ b/core/tests/coretests/res/layout/animator_basic.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/animatingButton"/> +</LinearLayout>
\ No newline at end of file diff --git a/core/tests/coretests/res/layout/textview_test.xml b/core/tests/coretests/res/layout/textview_test.xml new file mode 100644 index 0000000..f0c7b9e --- /dev/null +++ b/core/tests/coretests/res/layout/textview_test.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/textviewtest_layout" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView android:id="@+id/textviewtest_textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/textview_hebrew_text"/> + +</LinearLayout>
\ No newline at end of file diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml index f51b08e..71f3520 100644 --- a/core/tests/coretests/res/values/strings.xml +++ b/core/tests/coretests/res/values/strings.xml @@ -129,4 +129,6 @@ <string name="button8">Button8</string> <string name="button9">Button9</string> + <string name="textview_hebrew_text">םמab?!</string> + </resources> diff --git a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java new file mode 100644 index 0000000..65f2b8e --- /dev/null +++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java @@ -0,0 +1,39 @@ +/* +* Copyright (C) 2011 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.animation; + +import android.widget.Button; +import com.android.frameworks.coretests.R; + +/** + * Listener tests for AnimatorSet. + */ +public class AnimatorSetEventsTest extends EventsTest { + + @Override + public void setUp() throws Exception { + final BasicAnimatorActivity activity = getActivity(); + Button button = (Button) activity.findViewById(R.id.animatingButton); + + ObjectAnimator xAnim = ObjectAnimator.ofFloat(button, "translationX", 0, 100); + ObjectAnimator yAnim = ObjectAnimator.ofFloat(button, "translationX", 0, 100); + mAnimator = new AnimatorSet(); + ((AnimatorSet)mAnimator).playSequentially(xAnim, yAnim); + + super.setUp(); + } + +} diff --git a/services/jni/com_android_server_InputApplication.h b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java index 85fb891..93808d9 100644 --- a/services/jni/com_android_server_InputApplication.h +++ b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java @@ -13,20 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.animation; -#ifndef _ANDROID_SERVER_INPUT_APPLICATION_H -#define _ANDROID_SERVER_INPUT_APPLICATION_H +import com.android.frameworks.coretests.R; -#include <input/InputApplication.h> +import android.app.Activity; +import android.os.Bundle; -#include "JNIHelp.h" -#include "jni.h" - -namespace android { - -extern void android_server_InputApplication_toNative( - JNIEnv* env, jobject inputApplicationObj, InputApplication* outInputApplication); - -} // namespace android - -#endif // _ANDROID_SERVER_INPUT_APPLICATION_H +public class BasicAnimatorActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.animator_basic); + } +} diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java new file mode 100644 index 0000000..f970ffc --- /dev/null +++ b/core/tests/coretests/src/android/animation/EventsTest.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2011 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.animation; + +import android.os.Handler; +import android.test.ActivityInstrumentationTestCase2; +import android.test.UiThreadTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; + +/** + * Tests for the various lifecycle events of Animators. This abstract class is subclassed by + * concrete implementations that provide the actual Animator objects being tested. All of the + * testing mechanisms are in this class; the subclasses are only responsible for providing + * the mAnimator object. + * + * This test is more complicated than a typical synchronous test because much of the functionality + * must happen on the UI thread. Some tests do this by using the UiThreadTest annotation to + * automatically run the whole test on that thread. Other tests must run on the UI thread and also + * wait for some later event to occur before ending. These tests use a combination of an + * AbstractFuture mechanism and a delayed action to release that Future later. + */ +public abstract class EventsTest + extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { + + private static final int ANIM_DURATION = 400; + private static final int ANIM_DELAY = 100; + private static final int ANIM_MID_DURATION = ANIM_DURATION / 2; + private static final int ANIM_MID_DELAY = ANIM_DELAY / 2; + + private boolean mRunning; // tracks whether we've started the animator + private boolean mCanceled; // trackes whether we've canceled the animator + private Animator.AnimatorListener mFutureListener; // mechanism for delaying the end of the test + private FutureWaiter mFuture; // Mechanism for waiting for the UI test to complete + private Animator.AnimatorListener mListener; // Listener that handles/tests the events + + protected Animator mAnimator; // The animator used in the tests. Must be set in subclass + // setup() method prior to calling the superclass setup() + + /** + * Cancels the given animator. Used to delay cancelation until some later time (after the + * animator has started playing). + */ + static class Canceler implements Runnable { + Animator mAnim; + public Canceler(Animator anim) { + mAnim = anim; + } + @Override + public void run() { + mAnim.cancel(); + } + }; + + /** + * Releases the given Future object when the listener's end() event is called. Specifically, + * it releases it after some further delay, to give the test time to do other things right + * after an animation ends. + */ + static class FutureReleaseListener extends AnimatorListenerAdapter { + FutureWaiter mFuture; + + public FutureReleaseListener(FutureWaiter future) { + mFuture = future; + } + @Override + public void onAnimationEnd(Animator animation) { + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + mFuture.release(); + } + }, ANIM_MID_DURATION); + } + }; + + public EventsTest() { + super(BasicAnimatorActivity.class); + } + + + /** + * Sets up the fields used by each test. Subclasses must override this method to create + * the protected mAnimator object used in all tests. Overrides must create that animator + * and then call super.setup(), where further properties are set on that animator. + * @throws Exception + */ + @Override + public void setUp() throws Exception { + super.setUp(); + + // mListener is the main testing mechanism of this file. The asserts of each test + // are embedded in the listener callbacks that it implements. + mListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + // This should only be called on an animation that has been started and not + // yet canceled or ended + assertFalse(mCanceled); + assertTrue(mRunning); + mCanceled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + // This should only be called on an animation that has been started and not + // yet ended + assertTrue(mRunning); + mRunning = false; + super.onAnimationEnd(animation); + } + }; + + mAnimator.addListener(mListener); + mAnimator.setDuration(ANIM_DURATION); + + mFuture = new FutureWaiter(); + + mRunning = false; + mCanceled = false; + } + + /** + * Verify that calling cancel on an unstarted animator does nothing. + */ + @UiThreadTest + @SmallTest + public void testCancel() throws Exception { + mAnimator.cancel(); + } + + /** + * Verify that calling cancel on a started animator does the right thing. + */ + @UiThreadTest + @SmallTest + public void testStartCancel() throws Exception { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + } + + /** + * Same as testStartCancel, but with a startDelayed animator + */ + @UiThreadTest + @SmallTest + public void testStartDelayedCancel() throws Exception { + mAnimator.setStartDelay(ANIM_DELAY); + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + } + + /** + * Verify that canceling an animator that is playing does the right thing. + */ + @MediumTest + public void testPlayingCancel() throws Exception { + mFutureListener = new FutureReleaseListener(mFuture); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Canceler(mAnimator), ANIM_MID_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); + } + } + }); + mFuture.get(); + } + + /** + * Same as testPlayingCancel, but with a startDelayed animator + */ + @MediumTest + public void testPlayingDelayedCancel() throws Exception { + mAnimator.setStartDelay(ANIM_DELAY); + mFutureListener = new FutureReleaseListener(mFuture); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Canceler(mAnimator), ANIM_MID_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); + } + } + }); + mFuture.get(); + } + + /** + * Verifies that canceling a started animation after it has already been canceled + * does nothing. + */ + @MediumTest + public void testStartDoubleCancel() throws Exception { + mFutureListener = new FutureReleaseListener(mFuture); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); + } + } + }); + mFuture.get(); + } + + /** + * Same as testStartDoubleCancel, but with a startDelayed animator + */ + @MediumTest + public void testStartDelayedDoubleCancel() throws Exception { + mAnimator.setStartDelay(ANIM_DELAY); + mFutureListener = new FutureReleaseListener(mFuture); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); + } + } + }); + mFuture.get(); + } + + +} diff --git a/core/tests/coretests/src/android/animation/FutureWaiter.java b/core/tests/coretests/src/android/animation/FutureWaiter.java new file mode 100644 index 0000000..320a1c2 --- /dev/null +++ b/core/tests/coretests/src/android/animation/FutureWaiter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 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.animation; + +import com.google.common.util.concurrent.AbstractFuture; + +/** + * Simple extension of {@link com.google.common.util.concurrent.AbstractFuture} which exposes a new + * release() method which calls the protected + * {@link com.google.common.util.concurrent.AbstractFuture#set(Object)} method internally. It + * also exposes the protected {@link AbstractFuture#setException(Throwable)} method. + */ +public class FutureWaiter extends AbstractFuture<Void> { + + /** + * Release the Future currently waiting on + * {@link com.google.common.util.concurrent.AbstractFuture#get()}. + */ + public void release() { + super.set(null); + } + + @Override + public boolean setException(Throwable throwable) { + return super.setException(throwable); + } +} diff --git a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java new file mode 100644 index 0000000..606a939 --- /dev/null +++ b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 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.animation; + +import android.widget.Button; +import com.android.frameworks.coretests.R; + +/** + * Listener tests for ObjectAnimator. + */ +public class ObjectAnimatorEventsTest extends EventsTest { + + @Override + public void setUp() throws Exception { + final BasicAnimatorActivity activity = getActivity(); + Button button = (Button) activity.findViewById(R.id.animatingButton); + + mAnimator = ObjectAnimator.ofFloat(button, "translationX", 0, 100); + super.setUp(); + } + +} diff --git a/services/java/com/android/server/wm/InputApplication.java b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java index e04fd31..c25d050 100644 --- a/services/java/com/android/server/wm/InputApplication.java +++ b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 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. @@ -13,25 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.server.wm; - +package android.animation; /** - * Describes input-related application properties for use by the input dispatcher. - * @hide + * Listener tests for ValueAnimator. */ -public final class InputApplication { - // Application handle. - public InputApplicationHandle inputApplicationHandle; - - // Application name. - public String name; +public class ValueAnimatorEventsTest extends EventsTest { - // Dispatching timeout. - public long dispatchingTimeoutNanos; - - public void recycle() { - inputApplicationHandle = null; + @Override + public void setUp() throws Exception { + mAnimator = ValueAnimator.ofFloat(0, 1); + super.setUp(); } + } diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java index 0b72c3c..9403d95 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java @@ -54,8 +54,8 @@ public class NetworkStatsHistoryTest extends TestCase { // record data into narrow window to get single bucket stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L); - assertEquals(1, stats.bucketCount); - assertBucket(stats, 0, 1024L, 2048L); + assertEquals(1, stats.size()); + assertValues(stats, 0, 1024L, 2048L); } public void testRecordEqualBuckets() throws Exception { @@ -66,9 +66,9 @@ public class NetworkStatsHistoryTest extends TestCase { final long recordStart = TEST_START + (bucketDuration / 2); stats.recordData(recordStart, recordStart + bucketDuration, 1024L, 128L); - assertEquals(2, stats.bucketCount); - assertBucket(stats, 0, 512L, 64L); - assertBucket(stats, 1, 512L, 64L); + assertEquals(2, stats.size()); + assertValues(stats, 0, 512L, 64L); + assertValues(stats, 1, 512L, 64L); } public void testRecordTouchingBuckets() throws Exception { @@ -81,13 +81,13 @@ public class NetworkStatsHistoryTest extends TestCase { final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4); stats.recordData(recordStart, recordEnd, 1000L, 5000L); - assertEquals(3, stats.bucketCount); + assertEquals(3, stats.size()); // first bucket should have (1/20 of value) - assertBucket(stats, 0, 50L, 250L); + assertValues(stats, 0, 50L, 250L); // second bucket should have (15/20 of value) - assertBucket(stats, 1, 750L, 3750L); + assertValues(stats, 1, 750L, 3750L); // final bucket should have (4/20 of value) - assertBucket(stats, 2, 200L, 1000L); + assertValues(stats, 2, 200L, 1000L); } public void testRecordGapBuckets() throws Exception { @@ -101,9 +101,9 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS, 64L, 512L); // we should have two buckets, far apart from each other - assertEquals(2, stats.bucketCount); - assertBucket(stats, 0, 128L, 256L); - assertBucket(stats, 1, 64L, 512L); + assertEquals(2, stats.size()); + assertValues(stats, 0, 128L, 256L); + assertValues(stats, 1, 64L, 512L); // now record something in middle, spread across two buckets final long middleStart = TEST_START + DAY_IN_MILLIS; @@ -111,11 +111,11 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordData(middleStart, middleEnd, 2048L, 2048L); // now should have four buckets, with new record in middle two buckets - assertEquals(4, stats.bucketCount); - assertBucket(stats, 0, 128L, 256L); - assertBucket(stats, 1, 1024L, 1024L); - assertBucket(stats, 2, 1024L, 1024L); - assertBucket(stats, 3, 64L, 512L); + assertEquals(4, stats.size()); + assertValues(stats, 0, 128L, 256L); + assertValues(stats, 1, 1024L, 1024L); + assertValues(stats, 2, 1024L, 1024L); + assertValues(stats, 3, 64L, 512L); } public void testRecordOverlapBuckets() throws Exception { @@ -128,14 +128,12 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordData(midStart, midStart + HOUR_IN_MILLIS, 1024L, 1024L); // should have two buckets, with some data mixed together - assertEquals(2, stats.bucketCount); - assertBucket(stats, 0, 768L, 768L); - assertBucket(stats, 1, 512L, 512L); + assertEquals(2, stats.size()); + assertValues(stats, 0, 768L, 768L); + assertValues(stats, 1, 512L, 512L); } public void testRecordEntireGapIdentical() throws Exception { - final long[] total = new long[2]; - // first, create two separate histories far apart final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L); @@ -150,19 +148,16 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordEntireHistory(stats2); // first verify that totals match up - stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total); - assertTotalEquals(total, 3000L, 1500L); + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 3000L, 1500L); // now inspect internal buckets - assertBucket(stats, 0, 1000L, 500L); - assertBucket(stats, 1, 1000L, 500L); - assertBucket(stats, 2, 500L, 250L); - assertBucket(stats, 3, 500L, 250L); + assertValues(stats, 0, 1000L, 500L); + assertValues(stats, 1, 1000L, 500L); + assertValues(stats, 2, 500L, 250L); + assertValues(stats, 3, 500L, 250L); } public void testRecordEntireOverlapVaryingBuckets() throws Exception { - final long[] total = new long[2]; - // create history just over hour bucket boundary final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L); @@ -177,17 +172,16 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordEntireHistory(stats2); // first verify that totals match up - stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total); - assertTotalEquals(total, 650L, 650L); + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L); // now inspect internal buckets - assertBucket(stats, 0, 10L, 10L); - assertBucket(stats, 1, 20L, 20L); - assertBucket(stats, 2, 20L, 20L); - assertBucket(stats, 3, 20L, 20L); - assertBucket(stats, 4, 20L, 20L); - assertBucket(stats, 5, 20L, 20L); - assertBucket(stats, 6, 10L, 10L); + assertValues(stats, 0, 10L, 10L); + assertValues(stats, 1, 20L, 20L); + assertValues(stats, 2, 20L, 20L); + assertValues(stats, 3, 20L, 20L); + assertValues(stats, 4, 20L, 20L); + assertValues(stats, 5, 20L, 20L); + assertValues(stats, 6, 10L, 10L); // now combine using 15min buckets stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4); @@ -195,14 +189,13 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordEntireHistory(stats2); // first verify that totals match up - stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total); - assertTotalEquals(total, 650L, 650L); + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L); // and inspect buckets - assertBucket(stats, 0, 200L, 200L); - assertBucket(stats, 1, 150L, 150L); - assertBucket(stats, 2, 150L, 150L); - assertBucket(stats, 3, 150L, 150L); + assertValues(stats, 0, 200L, 200L); + assertValues(stats, 1, 150L, 150L); + assertValues(stats, 2, 150L, 150L); + assertValues(stats, 3, 150L, 150L); } public void testRemove() throws Exception { @@ -210,28 +203,28 @@ public class NetworkStatsHistoryTest extends TestCase { // record some data across 24 buckets stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L); - assertEquals(24, stats.bucketCount); + assertEquals(24, stats.size()); // try removing far before buckets; should be no change stats.removeBucketsBefore(TEST_START - YEAR_IN_MILLIS); - assertEquals(24, stats.bucketCount); + assertEquals(24, stats.size()); // try removing just moments into first bucket; should be no change // since that bucket contains data beyond the cutoff stats.removeBucketsBefore(TEST_START + SECOND_IN_MILLIS); - assertEquals(24, stats.bucketCount); + assertEquals(24, stats.size()); // try removing single bucket stats.removeBucketsBefore(TEST_START + HOUR_IN_MILLIS); - assertEquals(23, stats.bucketCount); + assertEquals(23, stats.size()); // try removing multiple buckets stats.removeBucketsBefore(TEST_START + (4 * HOUR_IN_MILLIS)); - assertEquals(20, stats.bucketCount); + assertEquals(20, stats.size()); // try removing all buckets stats.removeBucketsBefore(TEST_START + YEAR_IN_MILLIS); - assertEquals(0, stats.bucketCount); + assertEquals(0, stats.size()); } public void testTotalData() throws Exception { @@ -241,27 +234,20 @@ public class NetworkStatsHistoryTest extends TestCase { // record uniform data across day stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L); - final long[] total = new long[2]; - // verify that total outside range is 0 - stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, total); - assertTotalEquals(total, 0, 0); + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, 0L, 0L); // verify total in first hour - stats.getTotalData(TEST_START, TEST_START + HOUR_IN_MILLIS, total); - assertTotalEquals(total, 100, 200); + assertValues(stats, TEST_START, TEST_START + HOUR_IN_MILLIS, 100L, 200L); // verify total across 1.5 hours - stats.getTotalData(TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), total); - assertTotalEquals(total, 150, 300); + assertValues(stats, TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), 150L, 300L); // verify total beyond end - stats.getTotalData(TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, total); - assertTotalEquals(total, 100, 200); + assertValues(stats, TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, 100L, 200L); // verify everything total - stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total); - assertTotalEquals(total, 2400, 4800); + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 2400L, 4800L); } @@ -293,19 +279,27 @@ public class NetworkStatsHistoryTest extends TestCase { private static void assertConsistent(NetworkStatsHistory stats) { // verify timestamps are monotonic - for (int i = 1; i < stats.bucketCount; i++) { - assertTrue(stats.bucketStart[i - 1] < stats.bucketStart[i]); + long lastStart = Long.MIN_VALUE; + NetworkStatsHistory.Entry entry = null; + for (int i = 0; i < stats.size(); i++) { + entry = stats.getValues(i, entry); + assertTrue(lastStart < entry.bucketStart); + lastStart = entry.bucketStart; } } - private static void assertTotalEquals(long[] total, long rx, long tx) { - assertEquals("unexpected rx", rx, total[0]); - assertEquals("unexpected tx", tx, total[1]); + private static void assertValues( + NetworkStatsHistory stats, int index, long rxBytes, long txBytes) { + final NetworkStatsHistory.Entry entry = stats.getValues(index, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); } - private static void assertBucket(NetworkStatsHistory stats, int index, long rx, long tx) { - assertEquals("unexpected rx", rx, stats.rx[index]); - assertEquals("unexpected tx", tx, stats.tx[index]); + private static void assertValues( + NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) { + final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); } } diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java index 3cb64c7..2434e9f 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -31,9 +31,9 @@ public class NetworkStatsTest extends TestCase { public void testFindIndex() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 3) - .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) - .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L) - .addEntry(TEST_IFACE, 102, TAG_NONE, 1024L, 1024L); + .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L) + .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L) + .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L); assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE)); assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE)); @@ -44,110 +44,106 @@ public class NetworkStatsTest extends TestCase { public void testAddEntryGrow() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 2); - assertEquals(0, stats.size); - assertEquals(2, stats.iface.length); + assertEquals(0, stats.size()); + assertEquals(2, stats.internalSize()); - stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 2L); - stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L); + stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L); + stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L); - assertEquals(2, stats.size); - assertEquals(2, stats.iface.length); + assertEquals(2, stats.size()); + assertEquals(2, stats.internalSize()); - stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 4L); - stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 4L); - stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 5L); + stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L); + stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L); + stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L); - assertEquals(5, stats.size); - assertTrue(stats.iface.length >= 5); + assertEquals(5, stats.size()); + assertTrue(stats.internalSize() >= 5); - assertEquals(1L, stats.rx[0]); - assertEquals(2L, stats.rx[1]); - assertEquals(3L, stats.rx[2]); - assertEquals(4L, stats.rx[3]); - assertEquals(5L, stats.rx[4]); + assertEntry(stats, 0, TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L); + assertEntry(stats, 1, TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L); + assertEntry(stats, 2, TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L); + assertEntry(stats, 3, TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L); + assertEntry(stats, 4, TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L); } public void testCombineExisting() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 10); - stats.addEntry(TEST_IFACE, 1001, TAG_NONE, 512L, 256L); - stats.addEntry(TEST_IFACE, 1001, 0xff, 128L, 128L); - stats.combineEntry(TEST_IFACE, 1001, TAG_NONE, -128L, -128L); + stats.addValues(TEST_IFACE, 1001, TAG_NONE, 512L, 4L, 256L, 2L); + stats.addValues(TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L); + stats.combineValues(TEST_IFACE, 1001, TAG_NONE, -128L, -1L, -128L, -1L); - assertStatsEntry(stats, 0, TEST_IFACE, 1001, TAG_NONE, 384L, 128L); - assertStatsEntry(stats, 1, TEST_IFACE, 1001, 0xff, 128L, 128L); + assertEntry(stats, 0, TEST_IFACE, 1001, TAG_NONE, 384L, 3L, 128L, 1L); + assertEntry(stats, 1, TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L); // now try combining that should create row - stats.combineEntry(TEST_IFACE, 5005, TAG_NONE, 128L, 128L); - assertStatsEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 128L, 128L); - stats.combineEntry(TEST_IFACE, 5005, TAG_NONE, 128L, 128L); - assertStatsEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 256L, 256L); + stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L); + assertEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L); + stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L); + assertEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 256L, 2L, 256L, 2L); } public void testSubtractIdenticalData() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) - .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L); + .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L) + .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L); final NetworkStats after = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) - .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L); + .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L) + .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L); final NetworkStats result = after.subtract(before); // identical data should result in zero delta - assertEquals(0, result.rx[0]); - assertEquals(0, result.tx[0]); - assertEquals(0, result.rx[1]); - assertEquals(0, result.tx[1]); + assertEntry(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L); + assertEntry(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L); } public void testSubtractIdenticalRows() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) - .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L); + .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L) + .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L); final NetworkStats after = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, TAG_NONE, 1025L, 2L) - .addEntry(TEST_IFACE, 101, TAG_NONE, 3L, 1028L); + .addValues(TEST_IFACE, 100, TAG_NONE, 1025L, 9L, 2L, 1L) + .addValues(TEST_IFACE, 101, TAG_NONE, 3L, 1L, 1028L, 9L); final NetworkStats result = after.subtract(before); // expect delta between measurements - assertEquals(1, result.rx[0]); - assertEquals(2, result.tx[0]); - assertEquals(3, result.rx[1]); - assertEquals(4, result.tx[1]); + assertEntry(result, 0, TEST_IFACE, 100, TAG_NONE, 1L, 1L, 2L, 1L); + assertEntry(result, 1, TEST_IFACE, 101, TAG_NONE, 3L, 1L, 4L, 1L); } public void testSubtractNewRows() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) - .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L); + .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L) + .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L); final NetworkStats after = new NetworkStats(TEST_START, 3) - .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L) - .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L) - .addEntry(TEST_IFACE, 102, TAG_NONE, 1024L, 1024L); + .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L) + .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L) + .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L); final NetworkStats result = after.subtract(before); // its okay to have new rows - assertEquals(0, result.rx[0]); - assertEquals(0, result.tx[0]); - assertEquals(0, result.rx[1]); - assertEquals(0, result.tx[1]); - assertEquals(1024, result.rx[2]); - assertEquals(1024, result.tx[2]); + assertEntry(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L); + assertEntry(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L); + assertEntry(result, 2, TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L); } - private static void assertStatsEntry( - NetworkStats stats, int i, String iface, int uid, int tag, long rx, long tx) { - assertEquals(iface, stats.iface[i]); - assertEquals(uid, stats.uid[i]); - assertEquals(tag, stats.tag[i]); - assertEquals(rx, stats.rx[i]); - assertEquals(tx, stats.tx[i]); + private static void assertEntry(NetworkStats stats, int index, String iface, int uid, int tag, + long rxBytes, long rxPackets, long txBytes, long txPackets) { + final NetworkStats.Entry entry = stats.getValues(index, null); + assertEquals(iface, entry.iface); + assertEquals(uid, entry.uid); + assertEquals(tag, entry.tag); + assertEquals(rxBytes, entry.rxBytes); + assertEquals(rxPackets, entry.rxPackets); + assertEquals(txBytes, entry.txBytes); + assertEquals(txPackets, entry.txPackets); } } diff --git a/core/tests/coretests/src/android/pim/EventRecurrenceTest.java b/core/tests/coretests/src/android/pim/EventRecurrenceTest.java deleted file mode 100644 index 05000f1..0000000 --- a/core/tests/coretests/src/android/pim/EventRecurrenceTest.java +++ /dev/null @@ -1,753 +0,0 @@ -/* - * Copyright (C) 2006 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.pim; - -import android.pim.EventRecurrence.InvalidFormatException; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; - -import junit.framework.TestCase; - -import java.util.Arrays; - -/** - * Test android.pim.EventRecurrence. - * - * adb shell am instrument -w -e class android.pim.EventRecurrenceTest \ - * com.android.frameworks.coretests/android.test.InstrumentationTestRunner - */ -public class EventRecurrenceTest extends TestCase { - - @SmallTest - public void test0() throws Exception { - verifyRecurType("FREQ=SECONDLY", - /* int freq */ EventRecurrence.SECONDLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test1() throws Exception { - verifyRecurType("FREQ=MINUTELY", - /* int freq */ EventRecurrence.MINUTELY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test2() throws Exception { - verifyRecurType("FREQ=HOURLY", - /* int freq */ EventRecurrence.HOURLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test3() throws Exception { - verifyRecurType("FREQ=DAILY", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test4() throws Exception { - verifyRecurType("FREQ=WEEKLY", - /* int freq */ EventRecurrence.WEEKLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test5() throws Exception { - verifyRecurType("FREQ=MONTHLY", - /* int freq */ EventRecurrence.MONTHLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test6() throws Exception { - verifyRecurType("FREQ=YEARLY", - /* int freq */ EventRecurrence.YEARLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test7() throws Exception { - // with an until - verifyRecurType("FREQ=DAILY;UNTIL=112233T223344Z", - /* int freq */ EventRecurrence.DAILY, - /* String until */ "112233T223344Z", - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test8() throws Exception { - // with a count - verifyRecurType("FREQ=DAILY;COUNT=334", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 334, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test9() throws Exception { - // with a count - verifyRecurType("FREQ=DAILY;INTERVAL=5000", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 5000, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test10() throws Exception { - // verifyRecurType all of the BY* ones with one element - verifyRecurType("FREQ=DAILY" - + ";BYSECOND=0" - + ";BYMINUTE=1" - + ";BYHOUR=2" - + ";BYMONTHDAY=30" - + ";BYYEARDAY=300" - + ";BYWEEKNO=53" - + ";BYMONTH=12" - + ";BYSETPOS=-15" - + ";WKST=SU", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ new int[]{0}, - /* int[] byminute */ new int[]{1}, - /* int[] byhour */ new int[]{2}, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ new int[]{30}, - /* int[] byyearday */ new int[]{300}, - /* int[] byweekno */ new int[]{53}, - /* int[] bymonth */ new int[]{12}, - /* int[] bysetpos */ new int[]{-15}, - /* int wkst */ EventRecurrence.SU - ); - } - - @SmallTest - public void test11() throws Exception { - // verifyRecurType all of the BY* ones with one element - verifyRecurType("FREQ=DAILY" - + ";BYSECOND=0,30,59" - + ";BYMINUTE=0,41,59" - + ";BYHOUR=0,4,23" - + ";BYMONTHDAY=-31,-1,1,31" - + ";BYYEARDAY=-366,-1,1,366" - + ";BYWEEKNO=-53,-1,1,53" - + ";BYMONTH=1,12" - + ";BYSETPOS=1,2,3,4,500,10000" - + ";WKST=SU", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ new int[]{0, 30, 59}, - /* int[] byminute */ new int[]{0, 41, 59}, - /* int[] byhour */ new int[]{0, 4, 23}, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ new int[]{-31, -1, 1, 31}, - /* int[] byyearday */ new int[]{-366, -1, 1, 366}, - /* int[] byweekno */ new int[]{-53, -1, 1, 53}, - /* int[] bymonth */ new int[]{1, 12}, - /* int[] bysetpos */ new int[]{1, 2, 3, 4, 500, 10000}, - /* int wkst */ EventRecurrence.SU - ); - } - - private static class Check { - Check(String k, int... v) { - key = k; - values = v; - } - - String key; - int[] values; - } - - // this is a negative verifyRecurType case to verifyRecurType the range of the numbers accepted - @SmallTest - public void test12() throws Exception { - Check[] checks = new Check[]{ - new Check("BYSECOND", -100, -1, 60, 100), - new Check("BYMINUTE", -100, -1, 60, 100), - new Check("BYHOUR", -100, -1, 24, 100), - new Check("BYMONTHDAY", -100, -32, 0, 32, 100), - new Check("BYYEARDAY", -400, -367, 0, 367, 400), - new Check("BYWEEKNO", -100, -54, 0, 54, 100), - new Check("BYMONTH", -100, -5, 0, 13, 100) - }; - - for (Check ck : checks) { - for (int n : ck.values) { - String recur = "FREQ=DAILY;" + ck.key + "=" + n; - try { - EventRecurrence er = new EventRecurrence(); - er.parse(recur); - fail("Negative verifyRecurType failed. " - + " parse failed to throw an exception for '" - + recur + "'"); - } catch (EventRecurrence.InvalidFormatException e) { - // expected - } - } - } - } - - // verifyRecurType BYDAY - @SmallTest - public void test13() throws Exception { - verifyRecurType("FREQ=DAILY;BYDAY=1SU,-2MO,+33TU,WE,TH,FR,SA", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.SU, - EventRecurrence.MO, - EventRecurrence.TU, - EventRecurrence.WE, - EventRecurrence.TH, - EventRecurrence.FR, - EventRecurrence.SA - }, - /* int[] bydayNum */ new int[]{1, -2, 33, 0, 0, 0, 0}, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @Suppress - // Repro bug #2331761 - this should fail because of the last comma into BYDAY - public void test14() throws Exception { - verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;BYDAY=MO,TU,WE,", - /* int freq */ EventRecurrence.WEEKLY, - /* String until */ "20100129T130000Z", - /* int count */ 0, - /* int interval */ 1, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.MO, - EventRecurrence.TU, - EventRecurrence.WE, - }, - /* int[] bydayNum */ new int[]{0, 0, 0}, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // This test should pass - public void test15() throws Exception { - verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;" - + "BYDAY=MO,TU,WE,TH,FR,SA,SU", - /* int freq */ EventRecurrence.WEEKLY, - /* String until */ "20100129T130000Z", - /* int count */ 0, - /* int interval */ 1, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.MO, - EventRecurrence.TU, - EventRecurrence.WE, - EventRecurrence.TH, - EventRecurrence.FR, - EventRecurrence.SA, - EventRecurrence.SU - }, - /* int[] bydayNum */ new int[]{0, 0, 0, 0, 0, 0, 0}, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // Sample coming from RFC2445 - public void test16() throws Exception { - verifyRecurType("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1", - /* int freq */ EventRecurrence.MONTHLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.MO, - EventRecurrence.TU, - EventRecurrence.WE, - EventRecurrence.TH, - EventRecurrence.FR - }, - /* int[] bydayNum */ new int[] {0, 0, 0, 0, 0}, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ new int[] { -1 }, - /* int wkst */ EventRecurrence.MO - ); - } - - // Sample coming from RFC2445 - public void test17() throws Exception { - verifyRecurType("FREQ=DAILY;COUNT=10;INTERVAL=2", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 10, - /* int interval */ 2, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // Sample coming from RFC2445 - public void test18() throws Exception { - verifyRecurType("FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10", - /* int freq */ EventRecurrence.YEARLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.SU - }, - /* int[] bydayNum */ new int[] { -1 }, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ new int[] { 10 }, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // Sample coming from bug #1640517 - public void test19() throws Exception { - verifyRecurType("FREQ=YEARLY;BYMONTH=3;BYDAY=TH", - /* int freq */ EventRecurrence.YEARLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.TH - }, - /* int[] bydayNum */ new int[] { 0 }, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ new int[] { 3 }, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // for your copying pleasure - public void fakeTestXX() throws Exception { - verifyRecurType("FREQ=DAILY;", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - private static void cmp(int vlen, int[] v, int[] correct, String name) { - if ((correct == null && v != null) - || (correct != null && v == null)) { - throw new RuntimeException("One is null, one isn't for " + name - + ": correct=" + Arrays.toString(correct) - + " actual=" + Arrays.toString(v)); - } - if ((correct == null && vlen != 0) - || (vlen != (correct == null ? 0 : correct.length))) { - throw new RuntimeException("Reported length mismatch for " + name - + ": correct=" + ((correct == null) ? "null" : correct.length) - + " actual=" + vlen); - } - if (correct == null) { - return; - } - if (v.length < correct.length) { - throw new RuntimeException("Array length mismatch for " + name - + ": correct=" + Arrays.toString(correct) - + " actual=" + Arrays.toString(v)); - } - for (int i = 0; i < correct.length; i++) { - if (v[i] != correct[i]) { - throw new RuntimeException("Array value mismatch for " + name - + ": correct=" + Arrays.toString(correct) - + " actual=" + Arrays.toString(v)); - } - } - } - - private static boolean eq(String a, String b) { - if ((a == null && b != null) || (a != null && b == null)) { - return false; - } else { - return a == b || a.equals(b); - } - } - - private static void verifyRecurType(String recur, - int freq, String until, int count, int interval, - int[] bysecond, int[] byminute, int[] byhour, - int[] byday, int[] bydayNum, int[] bymonthday, - int[] byyearday, int[] byweekno, int[] bymonth, - int[] bysetpos, int wkst) { - EventRecurrence eventRecurrence = new EventRecurrence(); - eventRecurrence.parse(recur); - if (eventRecurrence.freq != freq - || !eq(eventRecurrence.until, until) - || eventRecurrence.count != count - || eventRecurrence.interval != interval - || eventRecurrence.wkst != wkst) { - System.out.println("Error... got:"); - print(eventRecurrence); - System.out.println("expected:"); - System.out.println("{"); - System.out.println(" freq=" + freq); - System.out.println(" until=" + until); - System.out.println(" count=" + count); - System.out.println(" interval=" + interval); - System.out.println(" wkst=" + wkst); - System.out.println(" bysecond=" + Arrays.toString(bysecond)); - System.out.println(" byminute=" + Arrays.toString(byminute)); - System.out.println(" byhour=" + Arrays.toString(byhour)); - System.out.println(" byday=" + Arrays.toString(byday)); - System.out.println(" bydayNum=" + Arrays.toString(bydayNum)); - System.out.println(" bymonthday=" + Arrays.toString(bymonthday)); - System.out.println(" byyearday=" + Arrays.toString(byyearday)); - System.out.println(" byweekno=" + Arrays.toString(byweekno)); - System.out.println(" bymonth=" + Arrays.toString(bymonth)); - System.out.println(" bysetpos=" + Arrays.toString(bysetpos)); - System.out.println("}"); - throw new RuntimeException("Mismatch in fields"); - } - cmp(eventRecurrence.bysecondCount, eventRecurrence.bysecond, bysecond, "bysecond"); - cmp(eventRecurrence.byminuteCount, eventRecurrence.byminute, byminute, "byminute"); - cmp(eventRecurrence.byhourCount, eventRecurrence.byhour, byhour, "byhour"); - cmp(eventRecurrence.bydayCount, eventRecurrence.byday, byday, "byday"); - cmp(eventRecurrence.bydayCount, eventRecurrence.bydayNum, bydayNum, "bydayNum"); - cmp(eventRecurrence.bymonthdayCount, eventRecurrence.bymonthday, bymonthday, "bymonthday"); - cmp(eventRecurrence.byyeardayCount, eventRecurrence.byyearday, byyearday, "byyearday"); - cmp(eventRecurrence.byweeknoCount, eventRecurrence.byweekno, byweekno, "byweekno"); - cmp(eventRecurrence.bymonthCount, eventRecurrence.bymonth, bymonth, "bymonth"); - cmp(eventRecurrence.bysetposCount, eventRecurrence.bysetpos, bysetpos, "bysetpos"); - } - - private static void print(EventRecurrence er) { - System.out.println("{"); - System.out.println(" freq=" + er.freq); - System.out.println(" until=" + er.until); - System.out.println(" count=" + er.count); - System.out.println(" interval=" + er.interval); - System.out.println(" wkst=" + er.wkst); - System.out.println(" bysecond=" + Arrays.toString(er.bysecond)); - System.out.println(" bysecondCount=" + er.bysecondCount); - System.out.println(" byminute=" + Arrays.toString(er.byminute)); - System.out.println(" byminuteCount=" + er.byminuteCount); - System.out.println(" byhour=" + Arrays.toString(er.byhour)); - System.out.println(" byhourCount=" + er.byhourCount); - System.out.println(" byday=" + Arrays.toString(er.byday)); - System.out.println(" bydayNum=" + Arrays.toString(er.bydayNum)); - System.out.println(" bydayCount=" + er.bydayCount); - System.out.println(" bymonthday=" + Arrays.toString(er.bymonthday)); - System.out.println(" bymonthdayCount=" + er.bymonthdayCount); - System.out.println(" byyearday=" + Arrays.toString(er.byyearday)); - System.out.println(" byyeardayCount=" + er.byyeardayCount); - System.out.println(" byweekno=" + Arrays.toString(er.byweekno)); - System.out.println(" byweeknoCount=" + er.byweeknoCount); - System.out.println(" bymonth=" + Arrays.toString(er.bymonth)); - System.out.println(" bymonthCount=" + er.bymonthCount); - System.out.println(" bysetpos=" + Arrays.toString(er.bysetpos)); - System.out.println(" bysetposCount=" + er.bysetposCount); - System.out.println("}"); - } - - - /** A list of valid rules. The parser must accept these. */ - private static final String[] GOOD_RRULES = { - /* extracted wholesale from from RFC 2445 section 4.8.5.4 */ - "FREQ=DAILY;COUNT=10", - "FREQ=DAILY;UNTIL=19971224T000000Z", - "FREQ=DAILY;INTERVAL=2", - "FREQ=DAILY;INTERVAL=10;COUNT=5", - "FREQ=YEARLY;UNTIL=20000131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA", - "FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1", - "FREQ=WEEKLY;COUNT=10", - "FREQ=WEEKLY;UNTIL=19971224T000000Z", - "FREQ=WEEKLY;INTERVAL=2;WKST=SU", - "FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH", - "FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH", - "FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR", - "FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH", - "FREQ=MONTHLY;COUNT=10;BYDAY=1FR", - "FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR", - "FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU", - "FREQ=MONTHLY;COUNT=6;BYDAY=-2MO", - "FREQ=MONTHLY;BYMONTHDAY=-3", - "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15", - "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1", - "FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15", - "FREQ=MONTHLY;INTERVAL=2;BYDAY=TU", - "FREQ=YEARLY;COUNT=10;BYMONTH=6,7", - "FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3", - "FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200", - "FREQ=YEARLY;BYDAY=20MO", - "FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO", - "FREQ=YEARLY;BYMONTH=3;BYDAY=TH", - "FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8", - "FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13", - "FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13", - "FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8", - "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3", - "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2", - "FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z", - "FREQ=MINUTELY;INTERVAL=15;COUNT=6", - "FREQ=MINUTELY;INTERVAL=90;COUNT=4", - "FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40", - "FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16", - "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO", - "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU", - /* a few more */ - "FREQ=SECONDLY;BYSECOND=0,15,59", - "FREQ=MINUTELY;BYMINUTE=0,15,59", - "FREQ=HOURLY;BYHOUR=+0,+15,+23", - "FREQ=DAILY;X-WHATEVER=blah", // fails on old parser - //"freq=daily;wkst=su", // fails on old parser - }; - - /** The parser must reject these. */ - private static final String[] BAD_RRULES = { - "INTERVAL=4;FREQ=YEARLY", // FREQ must come first - "FREQ=MONTHLY;FREQ=MONTHLY", // can't specify twice - "FREQ=MONTHLY;COUNT=1;COUNT=1", // can't specify twice - "FREQ=SECONDLY;BYSECOND=60", // range - "FREQ=MINUTELY;BYMINUTE=-1", // range - "FREQ=HOURLY;BYHOUR=24", // range - "FREQ=YEARLY;BYMONTHDAY=0", // zero not valid - //"FREQ=YEARLY;COUNT=1;UNTIL=12345", // can't have both COUNT and UNTIL - //"FREQ=DAILY;UNTIL=19970829T021400e", // invalid date - }; - - /** - * Simple test of good/bad rules. - */ - @SmallTest - public void testBasicParse() { - for (String rule : GOOD_RRULES) { - EventRecurrence recur = new EventRecurrence(); - recur.parse(rule); - } - - for (String rule : BAD_RRULES) { - EventRecurrence recur = new EventRecurrence(); - boolean didThrow = false; - - try { - recur.parse(rule); - } catch (InvalidFormatException ife) { - didThrow = true; - } - - assertTrue("Expected throw on " + rule, didThrow); - } - } -} diff --git a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java b/core/tests/coretests/src/android/pim/RecurrenceSetTest.java deleted file mode 100644 index e5ab179..0000000 --- a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2009 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.pim; - -import android.content.ContentValues; -import android.pim.ICalendar; -import android.pim.RecurrenceSet; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; -import android.provider.CalendarContract; -import junit.framework.TestCase; - -/** - * Test some pim.RecurrenceSet functionality. - */ -public class RecurrenceSetTest extends TestCase { - - // Test a recurrence - @SmallTest - public void testRecurrenceSet0() throws Exception { - String recurrence = "DTSTART;TZID=America/New_York:20080221T070000\n" - + "DTEND;TZID=America/New_York:20080221T190000\n" - + "RRULE:FREQ=DAILY;UNTIL=20080222T000000Z\n" - + "EXDATE:20080222T120000Z"; - verifyPopulateContentValues(recurrence, "FREQ=DAILY;UNTIL=20080222T000000Z", null, - null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0); - } - - // Test 1 day all-day event - @SmallTest - public void testRecurrenceSet1() throws Exception { - String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090822\n" - + "RRULE:FREQ=YEARLY;WKST=SU"; - verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null, - null, null, 1250812800000L, "UTC", "P1D", 1); - } - - // Test 2 day all-day event - @SmallTest - public void testRecurrenceSet2() throws Exception { - String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090823\n" - + "RRULE:FREQ=YEARLY;WKST=SU"; - verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null, - null, null, 1250812800000L, "UTC", "P2D", 1); - } - - // run populateContentValues and verify the results - private void verifyPopulateContentValues(String recurrence, String rrule, String rdate, - String exrule, String exdate, long dtstart, String tzid, String duration, int allDay) - throws ICalendar.FormatException { - ICalendar.Component recurrenceComponent = - new ICalendar.Component("DUMMY", null /* parent */); - ICalendar.parseComponent(recurrenceComponent, recurrence); - ContentValues values = new ContentValues(); - RecurrenceSet.populateContentValues(recurrenceComponent, values); - Log.d("KS", "values " + values); - - assertEquals(rrule, values.get(android.provider.CalendarContract.Events.RRULE)); - assertEquals(rdate, values.get(android.provider.CalendarContract.Events.RDATE)); - assertEquals(exrule, values.get(android.provider.CalendarContract.Events.EXRULE)); - assertEquals(exdate, values.get(android.provider.CalendarContract.Events.EXDATE)); - assertEquals(dtstart, (long) values.getAsLong(CalendarContract.Events.DTSTART)); - assertEquals(tzid, values.get(android.provider.CalendarContract.Events.EVENT_TIMEZONE)); - assertEquals(duration, values.get(android.provider.CalendarContract.Events.DURATION)); - assertEquals(allDay, - (int) values.getAsInteger(android.provider.CalendarContract.Events.ALL_DAY)); - } -} diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index 6db67c0..c54e4a1 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -16,23 +16,25 @@ package android.widget; -import com.google.android.collect.Lists; -import com.google.android.collect.Maps; +import com.android.frameworks.coretests.R; -import android.test.AndroidTestCase; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.text.GetChars; import android.view.View; -import android.widget.TextView; /** * TextViewTest tests {@link TextView}. */ -public class TextViewTest extends AndroidTestCase { +public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewTestActivity> { + + public TextViewTest() { + super(TextViewTestActivity.class); + } @SmallTest public void testArray() throws Exception { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; @@ -62,13 +64,13 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testTextDirectionDefault() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getTextDirection()); } @SmallTest public void testSetGetTextDirection() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); @@ -88,7 +90,7 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testGetResolvedTextDirectionLtr() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setText("this is a test"); tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); @@ -109,10 +111,10 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testGetResolvedTextDirectionLtrWithInheritance() { - LinearLayout ll = new LinearLayout(mContext); + LinearLayout ll = new LinearLayout(getActivity()); ll.setTextDirection(View.TEXT_DIRECTION_RTL); - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setText("this is a test"); ll.addView(tv); @@ -134,7 +136,7 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testGetResolvedTextDirectionRtl() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setText("\u05DD\u05DE"); // hebrew tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); @@ -155,10 +157,9 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testGetResolvedTextDirectionRtlWithInheritance() { - LinearLayout ll = new LinearLayout(mContext); - ll.setTextDirection(View.TEXT_DIRECTION_RTL); + LinearLayout ll = new LinearLayout(getActivity()); - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setText("\u05DD\u05DE"); // hebrew ll.addView(tv); @@ -169,6 +170,24 @@ public class TextViewTest extends AndroidTestCase { assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); + assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_LTR); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + // Force to RTL text direction on the layout + ll.setTextDirection(View.TEXT_DIRECTION_RTL); + + tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); tv.setTextDirection(View.TEXT_DIRECTION_LTR); @@ -180,10 +199,10 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testCharCountHeuristic() { - LinearLayout ll = new LinearLayout(mContext); + LinearLayout ll = new LinearLayout(getActivity()); ll.setLayoutDirection(View.LAYOUT_DIRECTION_RTL); - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); ll.addView(tv); tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT); @@ -211,4 +230,23 @@ public class TextViewTest extends AndroidTestCase { tv.setText("ab \u05DD\u05DE"); // latin + hebrew at 50% each assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); } + + @SmallTest + public void testResetTextDirection() { + final TextViewTestActivity activity = getActivity(); + + final LinearLayout ll = (LinearLayout) activity.findViewById(R.id.textviewtest_layout); + final TextView tv = (TextView) activity.findViewById(R.id.textviewtest_textview); + + getActivity().runOnUiThread(new Runnable() { + public void run() { + tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + assertEquals(true, tv.isResolvedTextDirection()); + + ll.removeView(tv); + assertEquals(false, tv.isResolvedTextDirection()); + } + }); + } } diff --git a/services/jni/com_android_server_InputWindow.h b/core/tests/coretests/src/android/widget/TextViewTestActivity.java index eaf7bde..1bb4d24 100644 --- a/services/jni/com_android_server_InputWindow.h +++ b/core/tests/coretests/src/android/widget/TextViewTestActivity.java @@ -13,20 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.widget; -#ifndef _ANDROID_SERVER_INPUT_WINDOW_H -#define _ANDROID_SERVER_INPUT_WINDOW_H +import android.app.Activity; +import android.os.Bundle; -#include <input/InputWindow.h> +import com.android.frameworks.coretests.R; -#include "JNIHelp.h" -#include "jni.h" +public class TextViewTestActivity extends Activity { -namespace android { - -extern void android_server_InputWindow_toNative( - JNIEnv* env, jobject inputWindowObj, InputWindow* outInputWindow); - -} // namespace android - -#endif // _ANDROID_SERVER_INPUT_WINDOW_H + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.textview_test); + } +} diff --git a/docs/html/guide/developing/tools/adb.jd b/docs/html/guide/developing/tools/adb.jd index a109dc8..78d12ef 100644 --- a/docs/html/guide/developing/tools/adb.jd +++ b/docs/html/guide/developing/tools/adb.jd @@ -503,7 +503,7 @@ application and send 500 pseudo-random events to it.</p> <ul> <li><code>V</code> — Verbose (lowest priority)</li> <li><code>D</code> — Debug</li> - <li><code>I</code> — Info</li> + <li><code>I</code> — Info (default priority)</li> <li><code>W</code> — Warning</li> <li><code>E</code> — Error</li> <li><code>F</code> — Fatal</li> @@ -520,7 +520,7 @@ of each message, given as <code><priority>/<tag></code>. </p> <p>To reduce the log output to a manageable level, you can restrict log output using <em>filter expressions</em>. Filter expressions let you indicate to the system the tags-priority combinations that you are interested in — the system suppresses other messages for the specified tags. </p> -<p>A filter expression follows this format <code>tag:priority ...</code>, where <code>tag</code> indicates the tag of interest and <code>priority</code> indicates the <em>minimum</em> level of priority to report for that tag. Messages for that tag at or above the specified priority are written to the log. You can supply any number of <code>tag:priority</code> specifications in a single filter expression. The series of specifications is whitespace-delimited. </p> +<p>A filter expression follows this format <code>tag:priority ...</code>, where <code>tag</code> indicates the tag of interest and <code>priority</code> indicates the <em>minimum</em> level of priority to report for that tag. Messages for that tag at or above the specified priority are written to the log. You can supply any number of <code>tag:priority</code> specifications in a single filter expression. The series of specifications is whitespace-delimited. The default output is to show all log messages with the Info priority (*:I).</p> <p>Here's an example of a filter expression that suppresses all log messages except those with the tag "ActivityManager", at priority "Info" or above, and all log messages with tag "MyApp", with priority "Debug" or above:</p> diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 3ec174e..f7dbe30 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -256,8 +256,7 @@ <li class="toggle-list"> <div><a href="<?cs var:toroot ?>guide/topics/renderscript/index.html"> <span class="en">RenderScript</span> - </a> - <span class="new">new!</span></div> + </a></div> <ul> <li><a href="<?cs var:toroot ?>guide/topics/renderscript/graphics.html"> <span class="en">3D Graphics</span> @@ -304,11 +303,9 @@ <!--<li><a style="color:gray;">Localization</a></li> --> <li><a href="<?cs var:toroot ?>guide/topics/appwidgets/index.html"> <span class="en">App Widgets</span></a> - <span class="new">updated</span> </li> <li><a href="<?cs var:toroot?>guide/topics/wireless/bluetooth.html"> <span class="en">Bluetooth</span></a> - <span class="new">updated</span> </li> <li><a href="<?cs var:toroot?>guide/topics/nfc/index.html"> <span class="en">Near Field Communication</span> @@ -316,7 +313,6 @@ <li class="toggle-list"> <div><a href="<?cs var:toroot?>guide/topics/usb/index.html"> <span class="en">USB</span></a> - <span class="new">new!</span> </div> <ul> <li><a href="<?cs var:toroot ?>guide/topics/usb/accessory.html">Accessory</a></li> @@ -341,7 +337,6 @@ </li> <li><a href="<?cs var:toroot?>guide/topics/admin/device-admin.html"> <span class="en">Device Administration</span></a> - <span class="new">updated</span> </li> <li class="toggle-list"> <div> diff --git a/docs/html/index.jd b/docs/html/index.jd index eeeedd0..dce46f9 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -148,19 +148,18 @@ href="{@docRoot}resources/dashboard/platform-versions.html">Learn more »</ 'tv': { 'layout':"imgLeft", - 'icon':"tv_s.png", + 'icon':"GTV_icon_small.png", 'name':"Google TV", - 'img':"tv_l.png", - 'title':"Announcing Google TV!", - 'desc': "<p><a href='http://www.google.com/tv/'>Google TV</a> is a new platform " - + "for television built on Android. Google " - + "has partnered with Sony and Logitech to integrate " - + "this platform into TVs, blu-ray players, and companion " - + "boxes. </p>" - + "<p><a href='http://www.google.com/tv/'>Learn more about " - + "Google TV »</a></p>" + 'img':"GTV_icon_large.png", + 'title':"Google TV!", + 'desc': "<p>Build something big. By big, we mean <em>worthy-of-the-living-room</em> big.</p>" + + " <p>Use <a href='http://code.google.com/tv'>Google TV</a> to bring the power of Android" + + " and Google Chrome to television." + + " The average American watches five hours of TV per day. Give them the web and apps" + + " to update their status, listen to music, watch web videos, and much more...</p>" }, + 'devphone': { 'layout':"imgLeft", 'icon':"devphone-small.png", diff --git a/docs/html/resources/dashboard/opengl.jd b/docs/html/resources/dashboard/opengl.jd index 3fcfa89..362ee16 100644 --- a/docs/html/resources/dashboard/opengl.jd +++ b/docs/html/resources/dashboard/opengl.jd @@ -74,6 +74,6 @@ src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl= </tr> </table> -<p><em>Data collected during a 7-day period ending on May 6, 2011</em></p> +<p><em>Data collected during a 7-day period ending on July 1, 2011</em></p> </div> diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd index b4f1b27..18f47a6 100644 --- a/docs/html/sdk/eclipse-adt.jd +++ b/docs/html/sdk/eclipse-adt.jd @@ -99,7 +99,7 @@ padding: .25em 1em; <a href="#" onclick="return toggleDiv(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" /> -ADT 12.0.0</a> <em>(June 2011)</em> +ADT 12.0.0</a> <em>(July 2011)</em> <div class="toggleme"> <dl> diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd index e980ca5..97df84f 100644 --- a/docs/html/sdk/ndk/index.jd +++ b/docs/html/sdk/ndk/index.jd @@ -1,16 +1,16 @@ ndk=true -ndk.win_download=android-ndk-r5c-windows.zip -ndk.win_bytes=61627716 -ndk.win_checksum=2c7423842fa0f46871eab118495d4b45 +ndk.win_download=android-ndk-r6-windows.zip +ndk.win_bytes=67642809 +ndk.win_checksum=9c7d5ccc02151a3e5e950c70dc05ac6d -ndk.mac_download=android-ndk-r5c-darwin-x86.tar.bz2 -ndk.mac_bytes=50714712 -ndk.mac_checksum=183bfbbd85cf8e4c0bd7531e8803e75d +ndk.mac_download=android-ndk-r6-darwin-x86.tar.bz2 +ndk.mac_bytes=52682746 +ndk.mac_checksum=a154905e49a6246abd823b75b6eda738 -ndk.linux_download=android-ndk-r5c-linux-x86.tar.bz2 -ndk.linux_bytes=44539890 -ndk.linux_checksum=7659dfdc97026ed1d913e224d0531f61 +ndk.linux_download=android-ndk-r6-linux-x86.tar.bz2 +ndk.linux_bytes=46425290 +ndk.linux_checksum=ff0a43085fe206696d5cdcef3f4f4637 page.title=Android NDK @jd:body @@ -62,6 +62,58 @@ padding: .25em 1em; <div class="toggleable open"> <a href="#" onclick="return toggleDiv(this)"><img src= "{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px"> + Android NDK, Revision 6</a> <em>(July 2011)</em> + + <div class="toggleme"> + <p>This release of the NDK includes support for the x86 ABI and other minor changes. + For detailed information describing the changes in this release, read the + <code>CHANGES.HTML</code> document included in the NDK package. + </p> + <dl> + <dt>General notes:</dt> + <dd> + <ul> + <li>Adds support for the x86 ABI, which allows you to generate machine code + that runs on compatible x86-based Android devices. Major features for x86 + include x86-specific toolchains, system headers, libraries and + debugging support. For all of the details regarding x86 support, + see <code>docs/CPU-X86.html</code> in the NDK package. + + <p>By default, code is generated for ARM-based devices, but you can add x86 to your + <code>APP_ABI</code> definition in your <code>Application.mk</code> file to build + for x86 platforms. For example, the following line instructs <code>ndk-build</code> + to build your code for three distinct ABIs:</p> + + <pre>APP_ABI := armeabi armeabi-v7a x86</pre> + + <p>Unless you rely on ARM-based assembly sources, you shouldn't need to touch + your <code>Android.mk</code> files to build x86 machine code.</p> + + </li> + + <li>You can build a standalone x86 toolchain using the <code>--toolchain=x86-4.4.3</code> + option when calling <code>make-standalone-toolchain.sh</code>. See + <code>docs/STANDALONE-TOOLCHAIN.html</code> for more details. + </li> + <li>The new <code>ndk-stack</code> tool lets you translate stack traces in + <code>logcat</code> that are generated by native code. The tool translates + instruction addresses into a readable format that contains things such + as the function, source file, and line number corresponding to each stack frame. + For more information and a usage example, see <code>docs/NDK-STACK.html</code>. + </li> + </ul> + </dd> + <dt>Other changes:</dt> + <dd><code>arm-eabi-4.4.0</code>, which had been deprecated since NDK r5, has been + removed from the NDK distribution.</dd> + + </dl> + </div> + </div> + +<div class="toggleable closed"> + <a href="#" onclick="return toggleDiv(this)"><img src= + "{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px"> Android NDK, Revision 5c</a> <em>(June 2011)</em> <div class="toggleme"> diff --git a/docs/html/sdk/ndk/overview.jd b/docs/html/sdk/ndk/overview.jd index 2562a25..93c664d 100644 --- a/docs/html/sdk/ndk/overview.jd +++ b/docs/html/sdk/ndk/overview.jd @@ -53,11 +53,7 @@ page.title=What is the NDK? <li>ARMv7-A (including Thumb-2 and VFPv3-D16 instructions, with optional support for NEON/VFPv3-D32 instructions)</li> - </ul> - - <p>Future releases of the NDK will also support:</p> - <ul> <li>x86 instructions (see CPU-ARCH-ABIS.HTML for more information)</li> </ul> diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs index 0607aad..0539adb 100644 --- a/docs/html/sdk/sdk_toc.cs +++ b/docs/html/sdk/sdk_toc.cs @@ -77,7 +77,7 @@ class="new">new!</span></li> <ul> <li class="toggle-list"> <div><a href="<?cs var:toroot ?>sdk/android-3.1.html"> - <span class="en">Android 3.1 Platform</span></a> <span class="new">new!</span></div> + <span class="en">Android 3.1 Platform</span></a></div> <ul> <li><a href="<?cs var:toroot ?>sdk/android-3.1-highlights.html">Platform Highlights</a></li> <li><a href="<?cs var:toroot ?>sdk/api_diff/12/changes.html">API Differences Report »</a></li> @@ -91,7 +91,7 @@ class="new">new!</span></li> <li><a href="<?cs var:toroot ?>sdk/api_diff/11/changes.html">API Differences Report »</a></li> </ul> </li> - <li><a href="<?cs var:toroot ?>sdk/android-2.3.4.html">Android 2.3.4 Platform</span></a> <span class="new">new!</span></li> + <li><a href="<?cs var:toroot ?>sdk/android-2.3.4.html">Android 2.3.4 Platform</span></a></li> <li class="toggle-list"> <div><a href="<?cs var:toroot ?>sdk/android-2.3.3.html"> <span class="en">Android 2.3.3 Platform</span></a></div> @@ -137,8 +137,8 @@ class="new">new!</span></li> <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r12</a> <span class="new">new!</span></li> <li><a href="<?cs var:toroot ?>sdk/win-usb.html">Google USB Driver, r4</a></li> - <li><a href="<?cs var:toroot ?>sdk/compatibility-library.html">Compatibility Library, r2</a> <span -class="new">new!</span></li> + <li><a href="<?cs var:toroot ?>sdk/compatibility-library.html">Compatibility Library, +r2</a></li> </ul> </li> <li> @@ -175,7 +175,7 @@ class="new">new!</span></li> <span style="display:none" class="zh-TW"></span> </h2> <ul> - <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r5c <span + <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r6 <span class="new">new!</span></a> </li> <li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li> diff --git a/docs/html/sdk/tools-notes.jd b/docs/html/sdk/tools-notes.jd index d4ebf8a..8c4d037 100644 --- a/docs/html/sdk/tools-notes.jd +++ b/docs/html/sdk/tools-notes.jd @@ -66,7 +66,7 @@ padding: .25em 1em; <div class="toggleable opened"> <a href="#" onclick="return toggleDiv(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" /> -SDK Tools, Revision 12</a> <em>(June 2011)</em> +SDK Tools, Revision 12</a> <em>(July 2011)</em> <div class="toggleme"> <dl> <dt>Dependencies:</dt> diff --git a/drm/drmserver/Android.mk b/drm/drmserver/Android.mk index e3cd44f..fd417cb 100644 --- a/drm/drmserver/Android.mk +++ b/drm/drmserver/Android.mk @@ -24,13 +24,8 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libmedia \ libutils \ - libbinder - -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -ldl -else - LOCAL_SHARED_LIBRARIES += libdl -endif + libbinder \ + libdl LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk index 69bb48d..4a55fc0 100644 --- a/drm/jni/Android.mk +++ b/drm/jni/Android.mk @@ -26,13 +26,8 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libandroid_runtime \ libnativehelper \ - libbinder - -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -ldl -else - LOCAL_SHARED_LIBRARIES += libdl -endif + libbinder \ + libdl LOCAL_STATIC_LIBRARIES := diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk index f1526a4..c534402 100644 --- a/drm/libdrmframework/Android.mk +++ b/drm/libdrmframework/Android.mk @@ -25,13 +25,8 @@ LOCAL_MODULE:= libdrmframework LOCAL_SHARED_LIBRARIES := \ libutils \ - libbinder - -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -ldl -else - LOCAL_SHARED_LIBRARIES += libdl -endif + libbinder \ + libdl LOCAL_STATIC_LIBRARIES := \ libdrmframeworkcommon diff --git a/drm/libdrmframework/plugins/passthru/Android.mk b/drm/libdrmframework/plugins/passthru/Android.mk index be18b64..d0d1439 100644 --- a/drm/libdrmframework/plugins/passthru/Android.mk +++ b/drm/libdrmframework/plugins/passthru/Android.mk @@ -24,14 +24,8 @@ LOCAL_MODULE := libdrmpassthruplugin LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon LOCAL_SHARED_LIBRARIES := \ - libutils - -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -ldl -else - LOCAL_SHARED_LIBRARIES += libdl -endif - + libutils \ + libdl LOCAL_C_INCLUDES += \ diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java index 1324431..c5d7500 100644 --- a/graphics/java/android/graphics/Path.java +++ b/graphics/java/android/graphics/Path.java @@ -40,6 +40,7 @@ public class Path { */ public Region rects; private boolean mDetectSimplePaths; + private Direction mLastDirection = null; /** * Create an empty path @@ -70,6 +71,7 @@ public class Path { public void reset() { isSimplePath = true; if (mDetectSimplePaths) { + mLastDirection = null; if (rects != null) rects.setEmpty(); } native_reset(mNativePath); @@ -82,6 +84,7 @@ public class Path { public void rewind() { isSimplePath = true; if (mDetectSimplePaths) { + mLastDirection = null; if (rects != null) rects.setEmpty(); } native_rewind(mNativePath); @@ -378,6 +381,20 @@ public class Path { final int nativeInt; } + private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) { + if (mDetectSimplePaths) { + if (mLastDirection == null) { + mLastDirection = dir; + } + if (mLastDirection != dir) { + isSimplePath = false; + } else { + if (rects == null) rects = new Region(); + rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION); + } + } + } + /** * Add a closed rectangle contour to the path * @@ -388,11 +405,7 @@ public class Path { if (rect == null) { throw new NullPointerException("need rect parameter"); } - if (mDetectSimplePaths) { - if (rects == null) rects = new Region(); - rects.op((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom, - Region.Op.UNION); - } + detectSimplePath(rect.left, rect.top, rect.right, rect.bottom, dir); native_addRect(mNativePath, rect, dir.nativeInt); } @@ -406,10 +419,7 @@ public class Path { * @param dir The direction to wind the rectangle's contour */ public void addRect(float left, float top, float right, float bottom, Direction dir) { - if (mDetectSimplePaths) { - if (rects == null) rects = new Region(); - rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION); - } + detectSimplePath(left, top, right, bottom, dir); native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt); } diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java index 6254192..12e5ada 100644 --- a/graphics/java/android/renderscript/Allocation.java +++ b/graphics/java/android/renderscript/Allocation.java @@ -155,6 +155,14 @@ public class Allocation extends BaseObj { } } + + private int getIDSafe() { + if (mAdaptedAllocation != null) { + return mAdaptedAllocation.getID(); + } + return getID(); + } + private void updateCacheInfo(Type t) { mCurrentDimX = t.getX(); mCurrentDimY = t.getY(); @@ -262,7 +270,7 @@ public class Allocation extends BaseObj { throw new RSIllegalArgumentException("Source must be exactly one usage type."); } mRS.validate(); - mRS.nAllocationSyncAll(getID(), srcLocation); + mRS.nAllocationSyncAll(getIDSafe(), srcLocation); } public void copyFrom(BaseObj[] d) { @@ -480,7 +488,7 @@ public class Allocation extends BaseObj { " does not match component size " + eSize + "."); } - mRS.nAllocationElementData1D(getID(), xoff, mSelectedLOD, + mRS.nAllocationElementData1D(getIDSafe(), xoff, mSelectedLOD, component_number, data, data.length); } @@ -527,7 +535,7 @@ public class Allocation extends BaseObj { public void copy1DRangeFromUnchecked(int off, int count, int[] d) { int dataSize = mType.mElement.getSizeBytes() * count; data1DChecks(off, count, d.length * 4, dataSize); - mRS.nAllocationData1D(getID(), off, mSelectedLOD, count, d, dataSize); + mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize); } /** * Copy part of an allocation from an array. This variant is @@ -541,7 +549,7 @@ public class Allocation extends BaseObj { public void copy1DRangeFromUnchecked(int off, int count, short[] d) { int dataSize = mType.mElement.getSizeBytes() * count; data1DChecks(off, count, d.length * 2, dataSize); - mRS.nAllocationData1D(getID(), off, mSelectedLOD, count, d, dataSize); + mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize); } /** * Copy part of an allocation from an array. This variant is @@ -555,7 +563,7 @@ public class Allocation extends BaseObj { public void copy1DRangeFromUnchecked(int off, int count, byte[] d) { int dataSize = mType.mElement.getSizeBytes() * count; data1DChecks(off, count, d.length, dataSize); - mRS.nAllocationData1D(getID(), off, mSelectedLOD, count, d, dataSize); + mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize); } /** * Copy part of an allocation from an array. This variant is @@ -569,7 +577,7 @@ public class Allocation extends BaseObj { public void copy1DRangeFromUnchecked(int off, int count, float[] d) { int dataSize = mType.mElement.getSizeBytes() * count; data1DChecks(off, count, d.length * 4, dataSize); - mRS.nAllocationData1D(getID(), off, mSelectedLOD, count, d, dataSize); + mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize); } /** @@ -638,7 +646,7 @@ public class Allocation extends BaseObj { * be copied. */ public void copy1DRangeFrom(int off, int count, Allocation data, int dataOff) { - mRS.nAllocationData2D(getID(), off, 0, + mRS.nAllocationData2D(getIDSafe(), off, 0, mSelectedLOD, mSelectedFace.mID, count, 1, data.getID(), dataOff, 0, data.mSelectedLOD, data.mSelectedFace.mID); @@ -674,28 +682,28 @@ public class Allocation extends BaseObj { public void copy2DRangeFrom(int xoff, int yoff, int w, int h, byte[] data) { mRS.validate(); validate2DRange(xoff, yoff, w, h); - mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, + mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h, data, data.length); } public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) { mRS.validate(); validate2DRange(xoff, yoff, w, h); - mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, + mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h, data, data.length * 2); } public void copy2DRangeFrom(int xoff, int yoff, int w, int h, int[] data) { mRS.validate(); validate2DRange(xoff, yoff, w, h); - mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, + mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h, data, data.length * 4); } public void copy2DRangeFrom(int xoff, int yoff, int w, int h, float[] data) { mRS.validate(); validate2DRange(xoff, yoff, w, h); - mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, + mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h, data, data.length * 4); } @@ -715,7 +723,7 @@ public class Allocation extends BaseObj { Allocation data, int dataXoff, int dataYoff) { mRS.validate(); validate2DRange(xoff, yoff, w, h); - mRS.nAllocationData2D(getID(), xoff, yoff, + mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h, data.getID(), dataXoff, dataYoff, data.mSelectedLOD, data.mSelectedFace.mID); @@ -734,10 +742,16 @@ public class Allocation extends BaseObj { mRS.validate(); validateBitmapFormat(data); validate2DRange(xoff, yoff, data.getWidth(), data.getHeight()); - mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data); + mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data); } + /** + * Copy from the Allocation into a Bitmap. The bitmap must + * match the dimensions of the Allocation. + * + * @param b The bitmap to be set from the Allocation. + */ public void copyTo(Bitmap b) { mRS.validate(); validateBitmapFormat(b); @@ -745,24 +759,52 @@ public class Allocation extends BaseObj { mRS.nAllocationCopyToBitmap(getID(), b); } + /** + * Copy from the Allocation into a byte array. The array must + * be at least as large as the Allocation. The allocation must + * be of an 8 bit elemental type. + * + * @param d The array to be set from the Allocation. + */ public void copyTo(byte[] d) { validateIsInt8(); mRS.validate(); mRS.nAllocationRead(getID(), d); } + /** + * Copy from the Allocation into a short array. The array must + * be at least as large as the Allocation. The allocation must + * be of an 16 bit elemental type. + * + * @param d The array to be set from the Allocation. + */ public void copyTo(short[] d) { validateIsInt16(); mRS.validate(); mRS.nAllocationRead(getID(), d); } + /** + * Copy from the Allocation into a int array. The array must be + * at least as large as the Allocation. The allocation must be + * of an 32 bit elemental type. + * + * @param d The array to be set from the Allocation. + */ public void copyTo(int[] d) { validateIsInt32(); mRS.validate(); mRS.nAllocationRead(getID(), d); } + /** + * Copy from the Allocation into a float array. The array must + * be at least as large as the Allocation. The allocation must + * be of an 32 bit float elemental type. + * + * @param d The array to be set from the Allocation. + */ public void copyTo(float[] d) { validateIsFloat32(); mRS.validate(); diff --git a/graphics/java/android/renderscript/AllocationAdapter.java b/graphics/java/android/renderscript/AllocationAdapter.java index ca5246a..d38f2df 100644 --- a/graphics/java/android/renderscript/AllocationAdapter.java +++ b/graphics/java/android/renderscript/AllocationAdapter.java @@ -31,7 +31,8 @@ public class AllocationAdapter extends Allocation { } int getID() { - return mAdaptedAllocation.getID(); + throw new RSInvalidStateException( + "This operation is not supported with adapters at this time."); } /** diff --git a/graphics/java/android/renderscript/ScriptC.java b/graphics/java/android/renderscript/ScriptC.java index f865753..90f959f 100644 --- a/graphics/java/android/renderscript/ScriptC.java +++ b/graphics/java/android/renderscript/ScriptC.java @@ -95,7 +95,7 @@ public class ScriptC extends Script { // E.g, /system/apps/Fountain.apk //String packageName = rs.getApplicationContext().getPackageResourcePath(); // For res/raw/fountain.bc, it wil be /com.android.fountain:raw/fountain - String resName = resources.getResourceName(resourceID); + String resName = resources.getResourceEntryName(resourceID); String cacheDir = rs.getApplicationContext().getCacheDir().toString(); Log.v(TAG, "Create script for resource = " + resName); diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk index 084f54a..652189f 100644 --- a/graphics/jni/Android.mk +++ b/graphics/jni/Android.mk @@ -1,10 +1,3 @@ - -# libRS needs libacc, which isn't 64-bit clean, and so can't be built -# for the simulator on gHardy, and therefore libRS needs to be excluded -# from the simulator as well, and so in turn librs_jni needs to be -# excluded. -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -41,5 +34,3 @@ LOCAL_MODULE_TAGS := optional LOCAL_REQUIRED_MODULES := libRS include $(BUILD_SHARED_LIBRARY) - -endif #simulator diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h index feeb3c3..9725822 100644 --- a/include/binder/ProcessState.h +++ b/include/binder/ProcessState.h @@ -39,8 +39,6 @@ class ProcessState : public virtual RefBase public: static sp<ProcessState> self(); - static void setSingleProcess(bool singleProcess); - void setContextObject(const sp<IBinder>& object); sp<IBinder> getContextObject(const sp<IBinder>& caller); @@ -48,8 +46,6 @@ public: const String16& name); sp<IBinder> getContextObject(const String16& name, const sp<IBinder>& caller); - - bool supportsProcesses() const; void startThreadPool(); diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h index 6ce44fc..9db7364 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -65,6 +65,8 @@ private: int dispatchDisconnect(va_list args); int dispatchSetBufferCount(va_list args); int dispatchSetBuffersGeometry(va_list args); + int dispatchSetBuffersDimensions(va_list args); + int dispatchSetBuffersFormat(va_list args); int dispatchSetBuffersTransform(va_list args); int dispatchSetBuffersTimestamp(va_list args); int dispatchSetCrop(va_list args); @@ -73,7 +75,8 @@ private: int connect(int api); int disconnect(int api); int setBufferCount(int bufferCount); - int setBuffersGeometry(int w, int h, int format); + int setBuffersDimensions(int w, int h); + int setBuffersFormat(int format); int setBuffersTransform(int transform); int setBuffersTimestamp(int64_t timestamp); int setCrop(Rect const* rect); diff --git a/include/media/IOMX.h b/include/media/IOMX.h index 3c65147..02ad703 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -184,6 +184,11 @@ public: uint32_t flags = 0); }; +struct CodecProfileLevel { + OMX_U32 mProfile; + OMX_U32 mLevel; +}; + } // namespace android #endif // ANDROID_IOMX_H_ diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h index d310cee..cc63356 100644 --- a/include/media/IStreamSource.h +++ b/include/media/IStreamSource.h @@ -51,6 +51,17 @@ struct IStreamListener : public IInterface { // will be suppressed until media time reaches this timestamp. static const char *const kKeyResumeAtPTS; + // When signalling a discontinuity you can optionally + // signal that this is a "hard" discontinuity, i.e. the format + // or configuration of subsequent stream data differs from that + // currently active. To do so, include a non-zero int32_t value + // under the key "kKeyFormatChange" when issuing the DISCONTINUITY + // command. + // The new logical stream must start with proper codec initialization + // information for playback to continue, i.e. SPS and PPS in the case + // of AVC video etc. + static const char *const kKeyFormatChange; + virtual void issueCommand( Command cmd, bool synchronous, const sp<AMessage> &msg = NULL) = 0; }; diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h index 80b7c1c..8c1c593 100644 --- a/include/media/stagefright/CameraSource.h +++ b/include/media/stagefright/CameraSource.h @@ -197,6 +197,12 @@ private: status_t init(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy, int32_t cameraId, Size videoSize, int32_t frameRate, bool storeMetaDataInVideoBuffers); + + status_t initWithCameraAccess( + const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy, + int32_t cameraId, Size videoSize, int32_t frameRate, + bool storeMetaDataInVideoBuffers); + status_t isCameraAvailable(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy, int32_t cameraId); diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h index 904ce2a..77166ed 100644 --- a/include/media/stagefright/MPEG4Writer.h +++ b/include/media/stagefright/MPEG4Writer.h @@ -71,7 +71,8 @@ private: bool mUse32BitOffset; bool mIsFileSizeLimitExplicitlyRequested; bool mPaused; - bool mStarted; + bool mStarted; // Writer thread + track threads started successfully + bool mWriterThreadStarted; // Only writer thread started successfully off64_t mOffset; off_t mMdatOffset; uint8_t *mMoovBoxBuffer; @@ -182,6 +183,7 @@ private: void writeLatitude(int degreex10000); void writeLongitude(int degreex10000); void sendSessionSummary(); + void release(); MPEG4Writer(const MPEG4Writer &); MPEG4Writer &operator=(const MPEG4Writer &); diff --git a/include/media/stagefright/MetadataBufferType.h b/include/media/stagefright/MetadataBufferType.h index 275c19f..52a3257 100644 --- a/include/media/stagefright/MetadataBufferType.h +++ b/include/media/stagefright/MetadataBufferType.h @@ -17,7 +17,11 @@ #ifndef METADATA_BUFFER_TYPE_H #define METADATA_BUFFER_TYPE_H +#ifdef __cplusplus +extern "C" { namespace android { +#endif + /* * MetadataBufferType defines the type of the metadata buffers that * can be passed to video encoder component for encoding, via Stagefright @@ -72,6 +76,9 @@ typedef enum { } MetadataBufferType; +#ifdef __cplusplus } // namespace android +} +#endif #endif // METADATA_BUFFER_TYPE_H diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 7f3c497..a042ddb 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -336,11 +336,6 @@ private: OMXCodec &operator=(const OMXCodec &); }; -struct CodecProfileLevel { - OMX_U32 mProfile; - OMX_U32 mLevel; -}; - struct CodecCapabilities { String8 mComponentName; Vector<CodecProfileLevel> mProfileLevels; diff --git a/include/media/stagefright/ShoutcastSource.h b/include/media/stagefright/ShoutcastSource.h deleted file mode 100644 index bc67156..0000000 --- a/include/media/stagefright/ShoutcastSource.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2009 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 SHOUTCAST_SOURCE_H_ - -#define SHOUTCAST_SOURCE_H_ - -#include <sys/types.h> - -#include <media/stagefright/MediaSource.h> - -namespace android { - -class HTTPStream; -class MediaBufferGroup; - -class ShoutcastSource : public MediaSource { -public: - // Assumes ownership of "http". - ShoutcastSource(HTTPStream *http); - - virtual status_t start(MetaData *params = NULL); - virtual status_t stop(); - - virtual sp<MetaData> getFormat(); - - virtual status_t read( - MediaBuffer **buffer, const ReadOptions *options = NULL); - -protected: - virtual ~ShoutcastSource(); - -private: - HTTPStream *mHttp; - size_t mMetaDataOffset; - size_t mBytesUntilMetaData; - - MediaBufferGroup *mGroup; - bool mStarted; - - ShoutcastSource(const ShoutcastSource &); - ShoutcastSource &operator= (const ShoutcastSource &); -}; - -} // namespace android - -#endif // SHOUTCAST_SOURCE_H_ - diff --git a/include/private/binder/binder_module.h b/include/private/binder/binder_module.h index fdf327e..a8dd64f 100644 --- a/include/private/binder/binder_module.h +++ b/include/private/binder/binder_module.h @@ -21,126 +21,11 @@ namespace android { #endif -#if defined(HAVE_ANDROID_OS) - /* obtain structures and constants from the kernel header */ #include <sys/ioctl.h> #include <linux/binder.h> -#else - -/* Some parts of the simulator need fake versions of this - * stuff in order to compile. Really this should go away - * entirely... - */ - -#define BINDER_CURRENT_PROTOCOL_VERSION 7 - -#define BINDER_TYPE_BINDER 1 -#define BINDER_TYPE_WEAK_BINDER 2 -#define BINDER_TYPE_HANDLE 3 -#define BINDER_TYPE_WEAK_HANDLE 4 -#define BINDER_TYPE_FD 5 - -struct flat_binder_object { - unsigned long type; - unsigned long flags; - union { - void *binder; - signed long handle; - }; - void *cookie; -}; - -struct binder_write_read { - signed long write_size; - signed long write_consumed; - unsigned long write_buffer; - signed long read_size; - signed long read_consumed; - unsigned long read_buffer; -}; - -struct binder_transaction_data { - union { - size_t handle; - void *ptr; - } target; - void *cookie; - unsigned int code; - - unsigned int flags; - pid_t sender_pid; - uid_t sender_euid; - size_t data_size; - size_t offsets_size; - - union { - struct { - const void *buffer; - const void *offsets; - } ptr; - uint8_t buf[8]; - } data; -}; - -enum transaction_flags { - TF_ONE_WAY = 0x01, - TF_ROOT_OBJECT = 0x04, - TF_STATUS_CODE = 0x08, - TF_ACCEPT_FDS = 0x10, -}; - - -enum { - FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, - FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, -}; - -enum BinderDriverReturnProtocol { - BR_ERROR, - BR_OK, - BR_TRANSACTION, - BR_REPLY, - BR_ACQUIRE_RESULT, - BR_DEAD_REPLY, - BR_TRANSACTION_COMPLETE, - BR_INCREFS, - BR_ACQUIRE, - BR_RELEASE, - BR_DECREFS, - BR_ATTEMPT_ACQUIRE, - BR_NOOP, - BR_SPAWN_LOOPER, - BR_FINISHED, - BR_DEAD_BINDER, - BR_CLEAR_DEATH_NOTIFICATION_DONE, - BR_FAILED_REPLY, -}; - -enum BinderDriverCommandProtocol { - BC_TRANSACTION, - BC_REPLY, - BC_ACQUIRE_RESULT, - BC_FREE_BUFFER, - BC_INCREFS, - BC_ACQUIRE, - BC_RELEASE, - BC_DECREFS, - BC_INCREFS_DONE, - BC_ACQUIRE_DONE, - BC_ATTEMPT_ACQUIRE, - BC_REGISTER_LOOPER, - BC_ENTER_LOOPER, - BC_EXIT_LOOPER, - BC_REQUEST_DEATH_NOTIFICATION, - BC_CLEAR_DEATH_NOTIFICATION, - BC_DEAD_BINDER_DONE, -}; - -#endif - #ifdef __cplusplus } // namespace android #endif diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 2d4e10d..f5288c8 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -43,8 +43,6 @@ #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)) -static bool gSingleProcess = false; - // --------------------------------------------------------------------------- @@ -82,12 +80,6 @@ sp<ProcessState> ProcessState::self() return gProcess; } -void ProcessState::setSingleProcess(bool singleProcess) -{ - gSingleProcess = singleProcess; -} - - void ProcessState::setContextObject(const sp<IBinder>& object) { setContextObject(object, String16("default")); @@ -95,11 +87,7 @@ void ProcessState::setContextObject(const sp<IBinder>& object) sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller) { - if (supportsProcesses()) { - return getStrongProxyForHandle(0); - } else { - return getContextObject(String16("default"), caller); - } + return getStrongProxyForHandle(0); } void ProcessState::setContextObject(const sp<IBinder>& object, const String16& name) @@ -144,11 +132,6 @@ sp<IBinder> ProcessState::getContextObject(const String16& name, const sp<IBinde return object; } -bool ProcessState::supportsProcesses() const -{ - return mDriverFD >= 0; -} - void ProcessState::startThreadPool() { AutoMutex _l(mLock); @@ -169,24 +152,15 @@ bool ProcessState::becomeContextManager(context_check_func checkFunc, void* user AutoMutex _l(mLock); mBinderContextCheckFunc = checkFunc; mBinderContextUserData = userData; - if (mDriverFD >= 0) { - int dummy = 0; -#if defined(HAVE_ANDROID_OS) - status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); -#else - status_t result = INVALID_OPERATION; -#endif - if (result == 0) { - mManagesContexts = true; - } else if (result == -1) { - mBinderContextCheckFunc = NULL; - mBinderContextUserData = NULL; - LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); - } - } else { - // If there is no driver, our only world is the local - // process so we can always become the context manager there. + + int dummy = 0; + status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); + if (result == 0) { mManagesContexts = true; + } else if (result == -1) { + mBinderContextCheckFunc = NULL; + mBinderContextUserData = NULL; + LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); } } return mManagesContexts; @@ -322,20 +296,11 @@ void ProcessState::spawnPooledThread(bool isMain) static int open_driver() { - if (gSingleProcess) { - return -1; - } - int fd = open("/dev/binder", O_RDWR); if (fd >= 0) { fcntl(fd, F_SETFD, FD_CLOEXEC); int vers; -#if defined(HAVE_ANDROID_OS) status_t result = ioctl(fd, BINDER_VERSION, &vers); -#else - status_t result = -1; - errno = EPERM; -#endif if (result == -1) { LOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); close(fd); @@ -346,14 +311,11 @@ static int open_driver() close(fd); fd = -1; } -#if defined(HAVE_ANDROID_OS) size_t maxThreads = 15; result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { LOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } -#endif - } else { LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); } @@ -386,9 +348,8 @@ ProcessState::ProcessState() mDriverFD = -1; #endif } - if (mDriverFD < 0) { - // Need to run without the driver, starting our own thread pool. - } + + LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating."); } ProcessState::~ProcessState() diff --git a/libs/camera/Android.mk b/libs/camera/Android.mk index dc00957..7286f92 100644 --- a/libs/camera/Android.mk +++ b/libs/camera/Android.mk @@ -20,8 +20,4 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= libcamera_client -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -lpthread -endif - include $(BUILD_SHARED_LIBRARY) diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 4070eba..ed319f5 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -32,10 +32,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= libgui -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -lpthread -endif - include $(BUILD_SHARED_LIBRARY) ifeq (,$(ONE_SHOT_MAKEFILE)) diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index b9b2310..e203035 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -254,6 +254,12 @@ int SurfaceTextureClient::perform(int operation, va_list args) case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: res = dispatchSetBuffersTimestamp(args); break; + case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: + res = dispatchSetBuffersDimensions(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: + res = dispatchSetBuffersFormat(args); + break; default: res = NAME_NOT_FOUND; break; @@ -290,7 +296,22 @@ int SurfaceTextureClient::dispatchSetBuffersGeometry(va_list args) { int w = va_arg(args, int); int h = va_arg(args, int); int f = va_arg(args, int); - return setBuffersGeometry(w, h, f); + int err = setBuffersDimensions(w, h); + if (err != 0) { + return err; + } + return setBuffersFormat(f); +} + +int SurfaceTextureClient::dispatchSetBuffersDimensions(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + return setBuffersDimensions(w, h); +} + +int SurfaceTextureClient::dispatchSetBuffersFormat(va_list args) { + int f = va_arg(args, int); + return setBuffersFormat(f); } int SurfaceTextureClient::dispatchSetBuffersTransform(va_list args) { @@ -390,12 +411,12 @@ int SurfaceTextureClient::setBufferCount(int bufferCount) return err; } -int SurfaceTextureClient::setBuffersGeometry(int w, int h, int format) +int SurfaceTextureClient::setBuffersDimensions(int w, int h) { - LOGV("SurfaceTextureClient::setBuffersGeometry"); + LOGV("SurfaceTextureClient::setBuffersDimensions"); Mutex::Autolock lock(mMutex); - if (w<0 || h<0 || format<0) + if (w<0 || h<0) return BAD_VALUE; if ((w && !h) || (!w && h)) @@ -403,7 +424,6 @@ int SurfaceTextureClient::setBuffersGeometry(int w, int h, int format) mReqWidth = w; mReqHeight = h; - mReqFormat = format; status_t err = mSurfaceTexture->setCrop(Rect(0, 0)); LOGE_IF(err, "ISurfaceTexture::setCrop(...) returned %s", strerror(-err)); @@ -411,6 +431,19 @@ int SurfaceTextureClient::setBuffersGeometry(int w, int h, int format) return err; } +int SurfaceTextureClient::setBuffersFormat(int format) +{ + LOGV("SurfaceTextureClient::setBuffersFormat"); + Mutex::Autolock lock(mMutex); + + if (format<0) + return BAD_VALUE; + + mReqFormat = format; + + return NO_ERROR; +} + int SurfaceTextureClient::setBuffersTransform(int transform) { LOGV("SurfaceTextureClient::setBuffersTransform"); diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk index 8d3a9b5..0308af3 100644 --- a/libs/gui/tests/Android.mk +++ b/libs/gui/tests/Android.mk @@ -2,8 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_MODULE := SurfaceTexture_test LOCAL_MODULE_TAGS := tests @@ -36,8 +34,6 @@ LOCAL_C_INCLUDES := \ include $(BUILD_EXECUTABLE) -endif - # Include subdirectory makefiles # ============================================================ diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 563d7e4..e232ddd 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -73,7 +73,6 @@ void Caches::dumpMemoryUsage() { String8 stringLog; dumpMemoryUsage(stringLog); LOGD("%s", stringLog.string()); - delete stringLog; } void Caches::dumpMemoryUsage(String8 &log) { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 0310bc3..3c2d80d 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -27,6 +27,7 @@ #include "Rect.h" #include "SkiaColorFilter.h" +#include "Texture.h" #include "Vertex.h" namespace android { @@ -40,14 +41,18 @@ namespace uirenderer { * A layer has dimensions and is backed by an OpenGL texture or FBO. */ struct Layer { - Layer(const uint32_t layerWidth, const uint32_t layerHeight): - width(layerWidth), height(layerHeight) { + Layer(const uint32_t layerWidth, const uint32_t layerHeight) { mesh = NULL; meshIndices = NULL; meshElementCount = 0; - isCacheable = true; - isTextureLayer = false; + cacheable = true; + textureLayer = false; renderTarget = GL_TEXTURE_2D; + texture.width = layerWidth; + texture.height = layerHeight; + colorFilter = NULL; + firstFilter = true; + firstWrap = true; } ~Layer() { @@ -64,12 +69,152 @@ struct Layer { regionRect.set(bounds.leftTop().x, bounds.leftTop().y, bounds.rightBottom().x, bounds.rightBottom().y); - const float texX = 1.0f / float(width); - const float texY = 1.0f / float(height); + const float texX = 1.0f / float(texture.width); + const float texY = 1.0f / float(texture.height); const float height = layer.getHeight(); texCoords.set( regionRect.left * texX, (height - regionRect.top) * texY, regionRect.right * texX, (height - regionRect.bottom) * texY); + + regionRect.translate(layer.left, layer.top); + } + + inline uint32_t getWidth() { + return texture.width; + } + + inline uint32_t getHeight() { + return texture.height; + } + + void setSize(uint32_t width, uint32_t height) { + texture.width = width; + texture.height = height; + } + + inline void setBlend(bool blend) { + texture.blend = blend; + } + + inline bool isBlend() { + return texture.blend; + } + + inline void setAlpha(int alpha) { + this->alpha = alpha; + } + + inline void setAlpha(int alpha, SkXfermode::Mode mode) { + this->alpha = alpha; + this->mode = mode; + } + + inline int getAlpha() { + return alpha; + } + + inline SkXfermode::Mode getMode() { + return mode; + } + + inline void setEmpty(bool empty) { + this->empty = empty; + } + + inline bool isEmpty() { + return empty; + } + + inline void setFbo(GLuint fbo) { + this->fbo = fbo; + } + + inline GLuint getFbo() { + return fbo; + } + + inline GLuint* getTexturePointer() { + return &texture.id; + } + + inline GLuint getTexture() { + return texture.id; + } + + inline GLenum getRenderTarget() { + return renderTarget; + } + + inline void setRenderTarget(GLenum renderTarget) { + this->renderTarget = renderTarget; + } + + void setWrap(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false) { + if (firstWrap || force || wrapS != texture.wrapS || wrapT != texture.wrapT) { + firstWrap = true; + texture.setWrap(wrapS, wrapT); + if (bindTexture) { + glBindTexture(renderTarget, texture.id); + } + glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT); + } + } + + void setFilter(GLenum min, GLenum mag, bool bindTexture = false, bool force = false) { + if (firstFilter || force || min != texture.minFilter || mag != texture.magFilter) { + firstFilter = false; + texture.setFilter(min, mag); + if (bindTexture) { + glBindTexture(renderTarget, texture.id); + } + glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min); + glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag); + } + } + + inline bool isCacheable() { + return cacheable; + } + + inline void setCacheable(bool cacheable) { + this->cacheable = cacheable; + } + + inline bool isTextureLayer() { + return textureLayer; + } + + inline void setTextureLayer(bool textureLayer) { + this->textureLayer = textureLayer; + } + + inline SkiaColorFilter* getColorFilter() { + return colorFilter; + } + + inline void setColorFilter(SkiaColorFilter* filter) { + colorFilter = filter; + } + + inline void bindTexture() { + glBindTexture(renderTarget, texture.id); + } + + inline void generateTexture() { + glGenTextures(1, &texture.id); + } + + inline void deleteTexture() { + if (texture.id) glDeleteTextures(1, &texture.id); + } + + inline void allocateTexture(GLenum format, GLenum storage) { + glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0, format, storage, NULL); + } + + inline mat4& getTexTransform() { + return texTransform; } /** @@ -82,23 +227,29 @@ struct Layer { Rect texCoords; /** - * Name of the FBO used to render the layer. If the name is 0 - * this layer is not backed by an FBO, but a simple texture. + * Dirty region indicating what parts of the layer + * have been drawn. */ - GLuint fbo; - + Region region; /** - * Opacity of the layer. + * If the region is a rectangle, coordinates of the + * region are stored here. */ - int alpha; + Rect regionRect; + /** - * Blending mode of the layer. + * If the layer can be rendered as a mesh, this is non-null. */ - SkXfermode::Mode mode; + TextureVertex* mesh; + uint16_t* meshIndices; + GLsizei meshElementCount; + +private: /** - * Indicates whether this layer should be blended. + * Name of the FBO used to render the layer. If the name is 0 + * this layer is not backed by an FBO, but a simple texture. */ - bool blend; + GLuint fbo; /** * Indicates whether this layer has been used already. @@ -106,28 +257,25 @@ struct Layer { bool empty; /** - * Name of the texture used to render the layer. - */ - GLuint texture; - /** - * Width of the layer texture. + * The texture backing this layer. */ - uint32_t width; + Texture texture; + /** - * Height of the layer texture. + * If set to true (by default), the layer can be reused. */ - uint32_t height; + bool cacheable; /** - * Dirty region indicating what parts of the layer - * have been drawn. + * When set to true, this layer must be treated as a texture + * layer. */ - Region region; + bool textureLayer; + /** - * If the region is a rectangle, coordinates of the - * region are stored here. + * Indicates the render target. */ - Rect regionRect; + GLenum renderTarget; /** * Color filter used to draw this layer. Optional. @@ -135,32 +283,21 @@ struct Layer { SkiaColorFilter* colorFilter; /** - * If the layer can be rendered as a mesh, this is non-null. - */ - TextureVertex* mesh; - uint16_t* meshIndices; - GLsizei meshElementCount; - - /** - * If set to true (by default), the layer can be reused. + * Opacity of the layer. */ - bool isCacheable; - + int alpha; /** - * When set to true, this layer must be treated as a texture - * layer. + * Blending mode of the layer. */ - bool isTextureLayer; + SkXfermode::Mode mode; /** * Optional texture coordinates transform. */ mat4 texTransform; - /** - * Indicates the render target. - */ - GLenum renderTarget; + bool firstFilter; + bool firstWrap; }; // struct Layer }; // namespace uirenderer diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index b2d795f..1a15e87 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -68,8 +68,8 @@ void LayerCache::setMaxSize(uint32_t maxSize) { void LayerCache::deleteLayer(Layer* layer) { if (layer) { - mSize -= layer->width * layer->height * 4; - glDeleteTextures(1, &layer->texture); + mSize -= layer->getWidth() * layer->getHeight() * 4; + layer->deleteTexture(); delete layer; } } @@ -93,29 +93,23 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) { mCache.removeAt(index); layer = entry.mLayer; - mSize -= layer->width * layer->height * 4; + mSize -= layer->getWidth() * layer->getHeight() * 4; - LAYER_LOGD("Reusing layer %dx%d", layer->width, layer->height); + LAYER_LOGD("Reusing layer %dx%d", layer->getWidth(), layer->getHeight()); } else { LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight); layer = new Layer(entry.mWidth, entry.mHeight); - layer->blend = true; - layer->empty = true; - layer->fbo = 0; - layer->colorFilter = NULL; - - glGenTextures(1, &layer->texture); - glBindTexture(GL_TEXTURE_2D, layer->texture); - + layer->setBlend(true); + layer->setEmpty(true); + layer->setFbo(0); + + layer->generateTexture(); + layer->bindTexture(); + layer->setFilter(GL_NEAREST, GL_NEAREST); + layer->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - #if DEBUG_LAYERS size_t size = mCache.size(); for (size_t i = 0; i < size; i++) { @@ -133,30 +127,30 @@ bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t heigh // size already in the cache, and reuse it instead of creating a new one LayerEntry entry(width, height); - if (entry.mWidth <= layer->width && entry.mHeight <= layer->height) { + if (entry.mWidth <= layer->getWidth() && entry.mHeight <= layer->getHeight()) { return true; } - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, layer->texture); + uint32_t oldWidth = layer->getWidth(); + uint32_t oldHeight = layer->getHeight(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, entry.mWidth, entry.mHeight, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glActiveTexture(GL_TEXTURE0); + layer->bindTexture(); + layer->setSize(entry.mWidth, entry.mHeight); + layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE); if (glGetError() != GL_NO_ERROR) { + layer->setSize(oldWidth, oldHeight); return false; } - layer->width = entry.mWidth; - layer->height = entry.mHeight; - return true; } bool LayerCache::put(Layer* layer) { - if (!layer->isCacheable) return false; + if (!layer->isCacheable()) return false; - const uint32_t size = layer->width * layer->height * 4; + const uint32_t size = layer->getWidth() * layer->getHeight() * 4; // Don't even try to cache a layer that's bigger than the cache if (size < mMaxSize) { // TODO: Use an LRU diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index d2d5f39..81b8bf3 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -119,7 +119,7 @@ private: } LayerEntry(Layer* layer): - mLayer(layer), mWidth(layer->width), mHeight(layer->height) { + mLayer(layer), mWidth(layer->getWidth()), mHeight(layer->getHeight()) { } bool operator<(const LayerEntry& rhs) const { diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index e034a86..1fa343b 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -32,9 +32,9 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// void LayerRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { - LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->fbo); + LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo()); - glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, mLayer->getFbo()); const float width = mLayer->layer.getWidth(); const float height = mLayer->layer.getHeight(); @@ -62,14 +62,14 @@ void LayerRenderer::finish() { generateMesh(); - LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->fbo); + LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->getFbo()); // No need to unbind our FBO, this will be taken care of by the caller // who will invoke OpenGLRenderer::resume() } GLint LayerRenderer::getTargetFbo() { - return mLayer->fbo; + return mLayer->getFbo(); } /////////////////////////////////////////////////////////////////////////////// @@ -128,8 +128,8 @@ void LayerRenderer::generateMesh() { } mLayer->meshElementCount = elementCount; - const float texX = 1.0f / float(mLayer->width); - const float texY = 1.0f / float(mLayer->height); + const float texX = 1.0f / float(mLayer->getWidth()); + const float texY = 1.0f / float(mLayer->getHeight()); const float height = mLayer->layer.getHeight(); TextureVertex* mesh = mLayer->mesh; @@ -182,40 +182,41 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque return NULL; } - layer->fbo = fbo; + layer->setFbo(fbo); layer->layer.set(0.0f, 0.0f, width, height); - layer->texCoords.set(0.0f, height / float(layer->height), - width / float(layer->width), 0.0f); - layer->alpha = 255; - layer->mode = SkXfermode::kSrcOver_Mode; - layer->blend = !isOpaque; - layer->colorFilter = NULL; + layer->texCoords.set(0.0f, height / float(layer->getHeight()), + width / float(layer->getWidth()), 0.0f); + layer->setAlpha(255, SkXfermode::kSrcOver_Mode); + layer->setBlend(!isOpaque); + layer->setColorFilter(NULL); layer->region.clear(); GLuint previousFbo; glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - glBindTexture(GL_TEXTURE_2D, layer->texture); + glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo()); + layer->bindTexture(); // Initialize the texture if needed - if (layer->empty) { - layer->empty = false; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (layer->isEmpty()) { + layer->setEmpty(false); + layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE); if (glGetError() != GL_NO_ERROR) { LOGD("Could not allocate texture"); + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteTextures(1, &layer->texture); Caches::getInstance().fboCache.put(fbo); + + layer->deleteTexture(); delete layer; + return NULL; } } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->texture, 0); + layer->getTexture(), 0); glDisable(GL_SCISSOR_TEST); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -229,14 +230,14 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { if (layer) { - LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->fbo, width, height); + LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->getFbo(), width, height); if (Caches::getInstance().layerCache.resize(layer, width, height)) { layer->layer.set(0.0f, 0.0f, width, height); - layer->texCoords.set(0.0f, height / float(layer->height), - width / float(layer->width), 0.0f); + layer->texCoords.set(0.0f, height / float(layer->getHeight()), + width / float(layer->getWidth()), 0.0f); } else { - if (layer->texture) glDeleteTextures(1, &layer->texture); + layer->deleteTexture(); delete layer; return false; } @@ -245,37 +246,23 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { return true; } -static void setTextureParameters(Layer* layer) { - glBindTexture(layer->renderTarget, layer->texture); - - glTexParameteri(layer->renderTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(layer->renderTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - Layer* LayerRenderer::createTextureLayer(bool isOpaque) { LAYER_RENDERER_LOGD("Creating new texture layer"); Layer* layer = new Layer(0, 0); - layer->isCacheable = false; - layer->isTextureLayer = true; - layer->blend = !isOpaque; - layer->empty = true; - layer->fbo = 0; - layer->colorFilter = NULL; - layer->fbo = 0; + layer->setCacheable(false); + layer->setTextureLayer(true); + layer->setBlend(!isOpaque); + layer->setEmpty(true); + layer->setFbo(0); + layer->setAlpha(255, SkXfermode::kSrcOver_Mode); layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f); layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f); - layer->alpha = 255; - layer->mode = SkXfermode::kSrcOver_Mode; - layer->colorFilter = NULL; layer->region.clear(); - layer->renderTarget = GL_NONE; // see ::updateTextureLayer() + layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer() glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &layer->texture); + layer->generateTexture(); return layer; } @@ -283,31 +270,32 @@ Layer* LayerRenderer::createTextureLayer(bool isOpaque) { void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, bool isOpaque, GLenum renderTarget, float* transform) { if (layer) { - layer->blend = !isOpaque; - layer->width = width; - layer->height = height; + layer->setBlend(!isOpaque); + layer->setSize(width, height); layer->layer.set(0.0f, 0.0f, width, height); layer->region.set(width, height); layer->regionRect.set(0.0f, 0.0f, width, height); - layer->texTransform.load(transform); + layer->getTexTransform().load(transform); - if (renderTarget != layer->renderTarget) { - layer->renderTarget = renderTarget; - setTextureParameters(layer); + if (renderTarget != layer->getRenderTarget()) { + layer->setRenderTarget(renderTarget); + layer->bindTexture(); + layer->setFilter(GL_NEAREST, GL_NEAREST, false, true); + layer->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, false, true); } } } void LayerRenderer::destroyLayer(Layer* layer) { if (layer) { - LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo); + LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->getFbo()); - if (layer->fbo) { - Caches::getInstance().fboCache.put(layer->fbo); + if (layer->getFbo()) { + Caches::getInstance().fboCache.put(layer->getFbo()); } if (!Caches::getInstance().layerCache.put(layer)) { - if (layer->texture) glDeleteTextures(1, &layer->texture); + layer->deleteTexture(); delete layer; } else { layer->region.clear(); @@ -317,7 +305,7 @@ void LayerRenderer::destroyLayer(Layer* layer) { void LayerRenderer::destroyLayerDeferred(Layer* layer) { if (layer) { - LAYER_RENDERER_LOGD("Deferring layer destruction, fbo = %d", layer->fbo); + LAYER_RENDERER_LOGD("Deferring layer destruction, fbo = %d", layer->getFbo()); Caches::getInstance().deleteLayerDeferred(layer); } @@ -325,7 +313,7 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) { bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { Caches& caches = Caches::getInstance(); - if (layer && layer->isTextureLayer && bitmap->width() <= caches.maxTextureSize && + if (layer && layer->isTextureLayer() && bitmap->width() <= caches.maxTextureSize && bitmap->height() <= caches.maxTextureSize) { GLuint fbo = caches.fboCache.get(); @@ -365,12 +353,11 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { break; } - float alpha = layer->alpha; - SkXfermode::Mode mode = layer->mode; + float alpha = layer->getAlpha(); + SkXfermode::Mode mode = layer->getMode(); - layer->mode = SkXfermode::kSrc_Mode; - layer->alpha = 255; - layer->fbo = fbo; + layer->setAlpha(255, SkXfermode::kSrc_Mode); + layer->setFbo(fbo); glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); @@ -381,8 +368,8 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -399,7 +386,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { LayerRenderer renderer(layer); renderer.setViewport(bitmap->width(), bitmap->height()); renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f, - bitmap->width(), bitmap->height(), !layer->blend); + bitmap->width(), bitmap->height(), !layer->isBlend()); if ((error = glGetError()) != GL_NO_ERROR) goto error; { @@ -424,9 +411,8 @@ error: #endif glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - layer->mode = mode; - layer->alpha = alpha; - layer->fbo = 0; + layer->setAlpha(alpha, mode); + layer->setFbo(0); glDeleteTextures(1, &texture); caches.fboCache.put(fbo); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 1c06a0b..a349121 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -449,12 +449,11 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, return false; } - layer->mode = mode; - layer->alpha = alpha; + layer->setAlpha(alpha, mode); layer->layer.set(bounds); - layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->height), - bounds.getWidth() / float(layer->width), 0.0f); - layer->colorFilter = mColorFilter; + layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()), + bounds.getWidth() / float(layer->getWidth()), 0.0f); + layer->setColorFilter(mColorFilter); // Save the layer in the snapshot snapshot->flags |= Snapshot::kFlagIsLayer; @@ -464,12 +463,13 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, return createFboLayer(layer, bounds, snapshot, previousFbo); } else { // Copy the framebuffer into the layer - glBindTexture(GL_TEXTURE_2D, layer->texture); + layer->bindTexture(); if (!bounds.isEmpty()) { - if (layer->empty) { - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, - snapshot->height - bounds.bottom, layer->width, layer->height, 0); - layer->empty = false; + if (layer->isEmpty()) { + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + bounds.left, snapshot->height - bounds.bottom, + layer->getWidth(), layer->getHeight(), 0); + layer->setEmpty(false); } else { glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, snapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight()); @@ -485,7 +485,7 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot, GLuint previousFbo) { - layer->fbo = mCaches.fboCache.get(); + layer->setFbo(mCaches.fboCache.get()); #if RENDER_LAYERS_AS_REGIONS snapshot->region = &snapshot->layer->region; @@ -507,7 +507,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> sna clip.translate(-bounds.left, -bounds.top); snapshot->flags |= Snapshot::kFlagIsFboLayer; - snapshot->fbo = layer->fbo; + snapshot->fbo = layer->getFbo(); snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); snapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); @@ -516,18 +516,17 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> sna snapshot->orthoMatrix.load(mOrthoMatrix); // Bind texture to FBO - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - glBindTexture(GL_TEXTURE_2D, layer->texture); + glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo()); + layer->bindTexture(); // Initialize the texture if needed - if (layer->empty) { - layer->empty = false; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (layer->isEmpty()) { + layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE); + layer->setEmpty(false); } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->texture, 0); + layer->getTexture(), 0); #if DEBUG_LAYERS_AS_REGIONS GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -535,8 +534,8 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> sna LOGE("Framebuffer incomplete (GL error code 0x%x)", status); glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteTextures(1, &layer->texture); - mCaches.fboCache.put(layer->fbo); + layer->deleteTexture(); + mCaches.fboCache.put(layer->getFbo()); delete layer; @@ -578,11 +577,11 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { Layer* layer = current->layer; const Rect& rect = layer->layer; - if (!fboLayer && layer->alpha < 255) { + if (!fboLayer && layer->getAlpha() < 255) { drawColorRect(rect.left, rect.top, rect.right, rect.bottom, - layer->alpha << 24, SkXfermode::kDstIn_Mode, true); + layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true); // Required below, composeLayerRect() will divide by 255 - layer->alpha = 255; + layer->setAlpha(255); } mCaches.unbindMeshBuffer(); @@ -593,18 +592,16 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { // drawing only the dirty region if (fboLayer) { dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform); - if (layer->colorFilter) { - setupColorFilter(layer->colorFilter); + if (layer->getColorFilter()) { + setupColorFilter(layer->getColorFilter()); } composeLayerRegion(layer, rect); - if (layer->colorFilter) { + if (layer->getColorFilter()) { resetColorFilter(); } - } else { - if (!rect.isEmpty()) { - dirtyLayer(rect.left, rect.top, rect.right, rect.bottom); - composeLayerRect(layer, rect, true); - } + } else if (!rect.isEmpty()) { + dirtyLayer(rect.left, rect.top, rect.right, rect.bottom); + composeLayerRect(layer, rect, true); } if (fboLayer) { @@ -622,16 +619,16 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { // Failing to add the layer to the cache should happen only if the layer is too large if (!mCaches.layerCache.put(layer)) { LAYER_LOGD("Deleting layer"); - glDeleteTextures(1, &layer->texture); + layer->deleteTexture(); delete layer; } } void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { - float alpha = layer->alpha / 255.0f; + float alpha = layer->getAlpha() / 255.0f; setupDraw(); - if (layer->renderTarget == GL_TEXTURE_2D) { + if (layer->getRenderTarget() == GL_TEXTURE_2D) { setupDrawWithTexture(); } else { setupDrawWithExternalTexture(); @@ -639,17 +636,28 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { setupDrawTextureTransform(); setupDrawColor(alpha, alpha, alpha, alpha); setupDrawColorFilter(); - setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode); + setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode()); setupDrawProgram(); - setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); - if (layer->renderTarget == GL_TEXTURE_2D) { - setupDrawTexture(layer->texture); + if (layer->getRenderTarget() == GL_TEXTURE_2D) { + setupDrawTexture(layer->getTexture()); + } else { + setupDrawExternalTexture(layer->getTexture()); + } + if (mSnapshot->transform->isPureTranslate() && + layer->getWidth() == (uint32_t) rect.getWidth() && + layer->getHeight() == (uint32_t) rect.getHeight()) { + const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f); + const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f); + + layer->setFilter(GL_NEAREST, GL_NEAREST); + setupDrawModelView(x, y, x + rect.getWidth(), y + rect.getHeight(), true); } else { - setupDrawExternalTexture(layer->texture); + layer->setFilter(GL_LINEAR, GL_LINEAR); + setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom); } - setupDrawTextureTransformUniforms(layer->texTransform); + setupDrawTextureTransformUniforms(layer->getTexTransform()); setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]); glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); @@ -658,14 +666,34 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { } void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) { - if (!layer->isTextureLayer) { + if (!layer->isTextureLayer()) { const Rect& texCoords = layer->texCoords; resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom); - drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, - layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], - &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap); + float x = rect.left; + float y = rect.top; + bool simpleTransform = mSnapshot->transform->isPureTranslate() && + layer->getWidth() == (uint32_t) rect.getWidth() && + layer->getHeight() == (uint32_t) rect.getHeight(); + + if (simpleTransform) { + // When we're swapping, the layer is already in screen coordinates + if (!swap) { + x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f); + y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f); + } + + layer->setFilter(GL_NEAREST, GL_NEAREST, true); + } else { + layer->setFilter(GL_LINEAR, GL_LINEAR, true); + } + + drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(), + layer->getTexture(), layer->getAlpha() / 255.0f, + layer->getMode(), layer->isBlend(), + &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform); resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); } else { @@ -690,9 +718,9 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { size_t count; const android::Rect* rects = layer->region.getArray(&count); - const float alpha = layer->alpha / 255.0f; - const float texX = 1.0f / float(layer->width); - const float texY = 1.0f / float(layer->height); + const float alpha = layer->getAlpha() / 255.0f; + const float texX = 1.0f / float(layer->getWidth()); + const float texY = 1.0f / float(layer->getHeight()); const float height = rect.getHeight(); TextureVertex* mesh = mCaches.getRegionMesh(); @@ -702,13 +730,22 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { setupDrawWithTexture(); setupDrawColor(alpha, alpha, alpha, alpha); setupDrawColorFilter(); - setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode, false); + setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode(), false); setupDrawProgram(); setupDrawDirtyRegionsDisabled(); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); - setupDrawTexture(layer->texture); - setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); + setupDrawTexture(layer->getTexture()); + if (mSnapshot->transform->isPureTranslate()) { + const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f); + const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f); + + layer->setFilter(GL_NEAREST, GL_NEAREST); + setupDrawModelViewTranslate(x, y, x + rect.getWidth(), y + rect.getHeight(), true); + } else { + layer->setFilter(GL_LINEAR, GL_LINEAR); + setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); + } setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0]); for (size_t i = 0; i < count; i++) { @@ -2154,8 +2191,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - layer->alpha = alpha; - layer->mode = mode; + layer->setAlpha(alpha, mode); #if RENDER_LAYERS_AS_REGIONS if (!layer->region.isEmpty()) { @@ -2169,13 +2205,23 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { setupDrawWithTexture(); setupDrawColor(a, a, a, a); setupDrawColorFilter(); - setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode, false); + setupDrawBlending(layer->isBlend() || a < 1.0f, layer->getMode(), false); setupDrawProgram(); - setupDrawModelViewTranslate(x, y, - x + layer->layer.getWidth(), y + layer->layer.getHeight()); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); - setupDrawTexture(layer->texture); + setupDrawTexture(layer->getTexture()); + if (mSnapshot->transform->isPureTranslate()) { + x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f); + y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f); + + layer->setFilter(GL_NEAREST, GL_NEAREST); + setupDrawModelViewTranslate(x, y, + x + layer->layer.getWidth(), y + layer->layer.getHeight(), true); + } else { + layer->setFilter(GL_LINEAR, GL_LINEAR); + setupDrawModelViewTranslate(x, y, + x + layer->layer.getWidth(), y + layer->layer.getHeight()); + } setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]); glDrawElements(GL_TRIANGLES, layer->meshElementCount, diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 7c10518..47049e2 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -43,7 +43,7 @@ enum DebugLevel { kDebugDisabled = 0, kDebugMemory = 1, kDebugCaches = 2, - kDebugMoreCaches = 3 + kDebugMoreCaches = kDebugMemory | kDebugCaches }; // These properties are defined in mega-bytes diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h index b048469..f4d9686 100644 --- a/libs/hwui/ShapeCache.h +++ b/libs/hwui/ShapeCache.h @@ -537,7 +537,7 @@ PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *pat const float pathWidth = fmax(bounds.width(), 1.0f); const float pathHeight = fmax(bounds.height(), 1.0f); - const float offset = fmax(paint->getStrokeWidth(), 1.0f) * 1.5f; + const float offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5); const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5); diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index 4922bb3..c6ae326 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -29,8 +29,22 @@ struct Texture { Texture() { cleanup = false; bitmapSize = 0; + wrapS = GL_CLAMP_TO_EDGE; wrapT = GL_CLAMP_TO_EDGE; + + minFilter = GL_NEAREST; + magFilter = GL_NEAREST; + } + + void setWrap(GLenum wrapS, GLenum wrapT) { + this->wrapS = wrapS; + this->wrapT = wrapT; + } + + void setFilter(GLenum min, GLenum mag) { + minFilter = min; + magFilter = mag; } /** @@ -67,6 +81,12 @@ struct Texture { */ GLenum wrapS; GLenum wrapT; + + /** + * Last filters set on this texture. Defaults to GL_NEAREST. + */ + GLenum minFilter; + GLenum magFilter; }; // struct Texture class AutoTexture { diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index d9cc6b6..a8aa0c7 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -77,11 +77,6 @@ rs_generated_source += $(GEN) LOCAL_GENERATED_SOURCES += $(GEN) -# libRS needs libacc, which isn't 64-bit clean, and so can't be built -# for the simulator on gHardy, and therefore libRS needs to be excluded -# from the simulator as well. -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_SRC_FILES:= \ rsAdapter.cpp \ rsAllocation.cpp \ @@ -228,5 +223,3 @@ LOCAL_STATIC_LIBRARIES := libcutils libutils LOCAL_LDLIBS := -lpthread include $(BUILD_HOST_STATIC_LIBRARY) - -endif #simulator diff --git a/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp index 8120864..bbf2836 100644 --- a/libs/rs/driver/rsdBcc.cpp +++ b/libs/rs/driver/rsdBcc.cpp @@ -66,65 +66,6 @@ static Script * setTLS(Script *sc) { } -// Input: cacheDir -// Input: resName -// Input: extName -// -// Note: cacheFile = resName + extName -// -// Output: Returns cachePath == cacheDir + cacheFile -static char *genCacheFileName(const char *cacheDir, - const char *resName, - const char *extName) { - char cachePath[512]; - char cacheFile[sizeof(cachePath)]; - const size_t kBufLen = sizeof(cachePath) - 1; - - cacheFile[0] = '\0'; - // Note: resName today is usually something like - // "/com.android.fountain:raw/fountain" - if (resName[0] != '/') { - // Get the absolute path of the raw/***.bc file. - - // Generate the absolute path. This doesn't do everything it - // should, e.g. if resName is "./out/whatever" it doesn't crunch - // the leading "./" out because this if-block is not triggered, - // but it'll make do. - // - if (getcwd(cacheFile, kBufLen) == NULL) { - LOGE("Can't get CWD while opening raw/***.bc file\n"); - return NULL; - } - // Append "/" at the end of cacheFile so far. - strncat(cacheFile, "/", kBufLen); - } - - // cacheFile = resName + extName - // - strncat(cacheFile, resName, kBufLen); - if (extName != NULL) { - // TODO(srhines): strncat() is a bit dangerous - strncat(cacheFile, extName, kBufLen); - } - - // Turn the path into a flat filename by replacing - // any slashes after the first one with '@' characters. - char *cp = cacheFile + 1; - while (*cp != '\0') { - if (*cp == '/') { - *cp = '@'; - } - cp++; - } - - // Tack on the file name for the actual cache file path. - strncpy(cachePath, cacheDir, kBufLen); - strncat(cachePath, cacheFile, kBufLen); - - LOGV("Cache file for '%s' '%s' is '%s'\n", resName, extName, cachePath); - return strdup(cachePath); -} - bool rsdScriptInit(const Context *rsc, ScriptC *script, char const *resName, @@ -164,15 +105,12 @@ bool rsdScriptInit(const Context *rsc, goto error; } -#if 1 if (bccLinkFile(drv->mBccScript, "/system/lib/libclcore.bc", 0) != 0) { LOGE("bcc: FAILS to link bitcode"); goto error; } -#endif - cachePath = genCacheFileName(cacheDir, resName, ".oBCC"); - if (bccPrepareExecutable(drv->mBccScript, cachePath, 0) != 0) { + if (bccPrepareExecutableEx(drv->mBccScript, cacheDir, resName, 0) != 0) { LOGE("bcc: FAILS to prepare executable"); goto error; } @@ -214,14 +152,13 @@ bool rsdScriptInit(const Context *rsc, const char ** mPragmaKeys; const char ** mPragmaValues; - const static int pragmaMax = 16; drv->mPragmaCount = bccGetPragmaCount(drv->mBccScript); if (drv->mPragmaCount <= 0) { drv->mPragmaKeys = NULL; drv->mPragmaValues = NULL; } else { - drv->mPragmaKeys = (const char **) calloc(drv->mFieldCount, sizeof(const char *)); - drv->mPragmaValues = (const char **) calloc(drv->mFieldCount, sizeof(const char *)); + drv->mPragmaKeys = (const char **) calloc(drv->mPragmaCount, sizeof(const char *)); + drv->mPragmaValues = (const char **) calloc(drv->mPragmaCount, sizeof(const char *)); bccGetPragmaList(drv->mBccScript, drv->mPragmaCount, drv->mPragmaKeys, drv->mPragmaValues); } @@ -332,6 +269,7 @@ static void wc_x(void *usr, uint32_t idx) { void rsdScriptInvokeForEach(const Context *rsc, Script *s, + uint32_t slot, const Allocation * ain, Allocation * aout, const void * usr, diff --git a/libs/rs/driver/rsdBcc.h b/libs/rs/driver/rsdBcc.h index 62b50f4..67929bc 100644 --- a/libs/rs/driver/rsdBcc.h +++ b/libs/rs/driver/rsdBcc.h @@ -32,6 +32,7 @@ void rsdScriptInvokeFunction(const android::renderscript::Context *dc, void rsdScriptInvokeForEach(const android::renderscript::Context *rsc, android::renderscript::Script *s, + uint32_t slot, const android::renderscript::Allocation * ain, android::renderscript::Allocation * aout, const void * usr, diff --git a/libs/rs/driver/rsdGL.cpp b/libs/rs/driver/rsdGL.cpp index 1f7bb0f..04446ad 100644 --- a/libs/rs/driver/rsdGL.cpp +++ b/libs/rs/driver/rsdGL.cpp @@ -99,9 +99,8 @@ static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) { EGLint value = -1; - EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value); - EGLint error = eglGetError(); - if (returnVal && error == EGL_SUCCESS) { + EGLBoolean returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value); + if (returnVal) { LOGV(" %s: %d (0x%x)", names[j].name, value, value); } } @@ -169,6 +168,24 @@ bool rsdGLInit(const Context *rsc) { configAttribsPtr[1] = EGL_OPENGL_ES2_BIT; configAttribsPtr += 2; + configAttribsPtr[0] = EGL_RED_SIZE; + configAttribsPtr[1] = 8; + configAttribsPtr += 2; + + configAttribsPtr[0] = EGL_GREEN_SIZE; + configAttribsPtr[1] = 8; + configAttribsPtr += 2; + + configAttribsPtr[0] = EGL_BLUE_SIZE; + configAttribsPtr[1] = 8; + configAttribsPtr += 2; + + if (rsc->mUserSurfaceConfig.alphaMin > 0) { + configAttribsPtr[0] = EGL_ALPHA_SIZE; + configAttribsPtr[1] = rsc->mUserSurfaceConfig.alphaMin; + configAttribsPtr += 2; + } + if (rsc->mUserSurfaceConfig.depthMin > 0) { configAttribsPtr[0] = EGL_DEPTH_SIZE; configAttribsPtr[1] = rsc->mUserSurfaceConfig.depthMin; @@ -191,16 +208,53 @@ bool rsdGLInit(const Context *rsc) { eglInitialize(dc->gl.egl.display, &dc->gl.egl.majorVersion, &dc->gl.egl.minorVersion); checkEglError("eglInitialize"); - PixelFormat pf = PIXEL_FORMAT_RGBA_8888; - if (rsc->mUserSurfaceConfig.alphaMin == 0) { - pf = PIXEL_FORMAT_RGBX_8888; - } + EGLBoolean ret; - status_t err = EGLUtils::selectConfigForPixelFormat(dc->gl.egl.display, configAttribs, - pf, &dc->gl.egl.config); - if (err) { - LOGE("%p, couldn't find an EGLConfig matching the screen format\n", rsc); + EGLint numConfigs = -1, n = 0; + ret = eglChooseConfig(dc->gl.egl.display, configAttribs, 0, 0, &numConfigs); + checkEglError("eglGetConfigs", ret); + + if (numConfigs) { + EGLConfig* const configs = new EGLConfig[numConfigs]; + + ret = eglChooseConfig(dc->gl.egl.display, + configAttribs, configs, numConfigs, &n); + if (!ret || !n) { + checkEglError("eglChooseConfig", ret); + LOGE("%p, couldn't find an EGLConfig matching the screen format\n", rsc); + } + + // The first config is guaranteed to over-satisfy the constraints + dc->gl.egl.config = configs[0]; + + // go through the list and skip configs that over-satisfy our needs + for (int i=0 ; i<n ; i++) { + if (rsc->mUserSurfaceConfig.alphaMin <= 0) { + EGLint alphaSize; + eglGetConfigAttrib(dc->gl.egl.display, + configs[i], EGL_ALPHA_SIZE, &alphaSize); + if (alphaSize > 0) { + continue; + } + } + + if (rsc->mUserSurfaceConfig.depthMin <= 0) { + EGLint depthSize; + eglGetConfigAttrib(dc->gl.egl.display, + configs[i], EGL_DEPTH_SIZE, &depthSize); + if (depthSize > 0) { + continue; + } + } + + // Found one! + dc->gl.egl.config = configs[i]; + break; + } + + delete [] configs; } + //if (props.mLogVisual) { if (0) { printEGLConfiguration(dc->gl.egl.display, dc->gl.egl.config); @@ -227,8 +281,8 @@ bool rsdGLInit(const Context *rsc) { return false; } - EGLBoolean ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surfaceDefault, - dc->gl.egl.surfaceDefault, dc->gl.egl.context); + ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surfaceDefault, + dc->gl.egl.surfaceDefault, dc->gl.egl.context); if (ret == EGL_FALSE) { LOGE("eglMakeCurrent returned EGL_FALSE"); checkEglError("eglMakeCurrent", ret); diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 44e9d89..8798612 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -693,7 +693,9 @@ RsContext rsContextCreateGL(RsDevice vdev, uint32_t version, LOGV("rsContextCreateGL %p", vdev); Device * dev = static_cast<Device *>(vdev); Context *rsc = Context::createContext(dev, &sc); - rsc->setDPI(dpi); + if (rsc) { + rsc->setDPI(dpi); + } LOGV("rsContextCreateGL ret %p ", rsc); return rsc; } diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index 2b58e9e..b77b18a 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -263,10 +263,10 @@ void Element::incRefs(const void *ptr) const { const uint8_t *p = static_cast<const uint8_t *>(ptr); for (uint32_t i=0; i < mFieldCount; i++) { if (mFields[i].e->mHasReference) { - p = &p[mFields[i].offsetBits >> 3]; + const uint8_t *p2 = &p[mFields[i].offsetBits >> 3]; for (uint32_t ct=0; ct < mFields[i].arraySize; ct++) { - mFields[i].e->incRefs(p); - p += mFields[i].e->getSizeBytes(); + mFields[i].e->incRefs(p2); + p2 += mFields[i].e->getSizeBytes(); } } } @@ -285,10 +285,10 @@ void Element::decRefs(const void *ptr) const { const uint8_t *p = static_cast<const uint8_t *>(ptr); for (uint32_t i=0; i < mFieldCount; i++) { if (mFields[i].e->mHasReference) { - p = &p[mFields[i].offsetBits >> 3]; + const uint8_t *p2 = &p[mFields[i].offsetBits >> 3]; for (uint32_t ct=0; ct < mFields[i].arraySize; ct++) { - mFields[i].e->decRefs(p); - p += mFields[i].e->getSizeBytes(); + mFields[i].e->decRefs(p2); + p2 += mFields[i].e->getSizeBytes(); } } } diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index b230bb5..e8b1014 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -121,7 +121,7 @@ void ScriptC::runForEach(Context *rsc, setupGLState(rsc); setupScript(rsc); - rsc->mHal.funcs.script.invokeForEach(rsc, this, ain, aout, usr, usrBytes, sc); + rsc->mHal.funcs.script.invokeForEach(rsc, this, 0, ain, aout, usr, usrBytes, sc); } void ScriptC::Invoke(Context *rsc, uint32_t slot, const void *data, size_t len) { diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h index 928dca5..6a4537b 100644 --- a/libs/rs/rs_hal.h +++ b/libs/rs/rs_hal.h @@ -70,6 +70,7 @@ typedef struct { int (*invokeRoot)(const Context *rsc, Script *s); void (*invokeForEach)(const Context *rsc, Script *s, + uint32_t slot, const Allocation * ain, Allocation * aout, const void * usr, diff --git a/libs/storage/Android.mk b/libs/storage/Android.mk index 1e52fa4..b42c34f 100644 --- a/libs/storage/Android.mk +++ b/libs/storage/Android.mk @@ -13,8 +13,4 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_MODULE:= libstorage -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -lpthread -endif - include $(BUILD_STATIC_LIBRARY) diff --git a/libs/surfaceflinger_client/Android.mk b/libs/surfaceflinger_client/Android.mk index 267e3edf..5fca1ce 100644 --- a/libs/surfaceflinger_client/Android.mk +++ b/libs/surfaceflinger_client/Android.mk @@ -7,8 +7,4 @@ LOCAL_SHARED_LIBRARIES := LOCAL_MODULE:= libsurfaceflinger_client -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -lpthread -endif - include $(BUILD_SHARED_LIBRARY) diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 427bbba..fbabfc4 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -68,10 +68,6 @@ LOCAL_C_INCLUDES := \ LOCAL_MODULE:= libui -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -lpthread -endif - include $(BUILD_SHARED_LIBRARY) diff --git a/libs/ui/EGLUtils.cpp b/libs/ui/EGLUtils.cpp index f24a71d..020646b 100644 --- a/libs/ui/EGLUtils.cpp +++ b/libs/ui/EGLUtils.cpp @@ -24,6 +24,8 @@ #include <EGL/egl.h> +#include <system/graphics.h> + #include <private/ui/android_natives_priv.h> // ---------------------------------------------------------------------------- @@ -67,31 +69,49 @@ status_t EGLUtils::selectConfigForPixelFormat( return BAD_VALUE; // Get all the "potential match" configs... - if (eglGetConfigs(dpy, NULL, 0, &numConfigs) == EGL_FALSE) + if (eglChooseConfig(dpy, attrs, 0, 0, &numConfigs) == EGL_FALSE) return BAD_VALUE; - EGLConfig* const configs = (EGLConfig*)malloc(sizeof(EGLConfig)*numConfigs); - if (eglChooseConfig(dpy, attrs, configs, numConfigs, &n) == EGL_FALSE) { - free(configs); - return BAD_VALUE; - } - - int i; - EGLConfig config = NULL; - for (i=0 ; i<n ; i++) { - EGLint nativeVisualId = 0; - eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &nativeVisualId); - if (nativeVisualId>0 && format == nativeVisualId) { + if (numConfigs) { + EGLConfig* const configs = new EGLConfig[numConfigs]; + if (eglChooseConfig(dpy, attrs, configs, numConfigs, &n) == EGL_FALSE) { + delete [] configs; + return BAD_VALUE; + } + + bool hasAlpha = false; + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + hasAlpha = true; + break; + } + + // The first config is guaranteed to over-satisfy the constraints + EGLConfig config = configs[0]; + + // go through the list and skip configs that over-satisfy our needs + int i; + for (i=0 ; i<n ; i++) { + if (!hasAlpha) { + EGLint alphaSize; + eglGetConfigAttrib(dpy, configs[i], EGL_ALPHA_SIZE, &alphaSize); + if (alphaSize > 0) { + continue; + } + } config = configs[i]; break; } - } - free(configs); - - if (i<n) { - *outConfig = config; - return NO_ERROR; + delete [] configs; + + if (i<n) { + *outConfig = config; + return NO_ERROR; + } } return NAME_NOT_FOUND; diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index 4393504..9c10c75 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -299,6 +299,7 @@ int FramebufferNativeWindow::perform(ANativeWindow* window, { switch (operation) { case NATIVE_WINDOW_SET_USAGE: + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: case NATIVE_WINDOW_CONNECT: case NATIVE_WINDOW_DISCONNECT: break; diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index c46d6f4..1e602e9 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -83,7 +83,9 @@ status_t InputChannel::openInputChannelPair(const String8& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { status_t result; - int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE); + String8 ashmemName("InputChannel "); + ashmemName.append(name); + int serverAshmemFd = ashmem_create_region(ashmemName.string(), DEFAULT_MESSAGE_BUFFER_SIZE); if (serverAshmemFd < 0) { result = -errno; LOGE("channel '%s' ~ Could not create shared memory region. errno=%d", diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk index e231971..693a32a 100644 --- a/libs/ui/tests/Android.mk +++ b/libs/ui/tests/Android.mk @@ -2,8 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ifneq ($(TARGET_SIMULATOR),true) - # Build the unit tests. test_src_files := \ InputChannel_test.cpp \ @@ -48,5 +46,3 @@ $(foreach file,$(test_src_files), \ # Build the manual test programs. include $(call all-subdir-makefiles) - -endif
\ No newline at end of file diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 774e8c9..f633357 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -103,17 +103,14 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libcutils -ifneq ($(TARGET_SIMULATOR),true) ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) # This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp LOCAL_SHARED_LIBRARIES += libdl endif # linux-x86 -endif # sim LOCAL_MODULE:= libutils include $(BUILD_SHARED_LIBRARY) -ifneq ($(TARGET_SIMULATOR),true) ifeq ($(TARGET_OS),linux) include $(CLEAR_VARS) LOCAL_C_INCLUDES += external/zlib external/icu4c/common @@ -122,7 +119,6 @@ LOCAL_MODULE := libutils LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp include $(BUILD_STATIC_LIBRARY) endif -endif # Include subdirectory makefiles diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 87549fe..7ef30f9 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -481,6 +481,14 @@ static int write_pax_header_entry(char* buf, const char* key, const char* value) return sprintf(buf, "%d %s=%s\n", len, key, value); } +// Wire format to the backup manager service is chunked: each chunk is prefixed by +// a 4-byte count of its size. A chunk size of zero (four zero bytes) indicates EOD. +void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t size) { + uint32_t chunk_size_no = htonl(size); + writer->WriteEntityData(&chunk_size_no, 4); + if (size != 0) writer->WriteEntityData(buffer, size); +} + int write_tarfile(const String8& packageName, const String8& domain, const String8& rootpath, const String8& filepath, BackupDataWriter* writer) { @@ -660,16 +668,16 @@ int write_tarfile(const String8& packageName, const String8& domain, // Checksum and write the pax block header calc_tar_checksum(paxHeader); - writer->WriteEntityData(paxHeader, 512); + send_tarfile_chunk(writer, paxHeader, 512); // Now write the pax data itself int paxblocks = (paxLen + 511) / 512; - writer->WriteEntityData(paxData, 512 * paxblocks); + send_tarfile_chunk(writer, paxData, 512 * paxblocks); } // Checksum and write the 512-byte ustar file header block to the output calc_tar_checksum(buf); - writer->WriteEntityData(buf, 512); + send_tarfile_chunk(writer, buf, 512); // Now write the file data itself, for real files. We honor tar's convention that // only full 512-byte blocks are sent to write(). @@ -699,7 +707,7 @@ int write_tarfile(const String8& packageName, const String8& domain, memset(buf + nRead, 0, remainder); nRead += remainder; } - writer->WriteEntityData(buf, nRead); + send_tarfile_chunk(writer, buf, nRead); toWrite -= nRead; } } diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 289c826..87ae3d5 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -290,7 +290,7 @@ void VectorImpl::clear() void* VectorImpl::editItemLocation(size_t index) { LOG_ASSERT(index<capacity(), - "[%p] itemLocation: index=%d, capacity=%d, count=%d", + "[%p] editItemLocation: index=%d, capacity=%d, count=%d", this, (int)index, (int)capacity(), (int)mCount); void* buffer = editArrayImpl(); @@ -302,7 +302,7 @@ void* VectorImpl::editItemLocation(size_t index) const void* VectorImpl::itemLocation(size_t index) const { LOG_ASSERT(index<capacity(), - "[%p] editItemLocation: index=%d, capacity=%d, count=%d", + "[%p] itemLocation: index=%d, capacity=%d, count=%d", this, (int)index, (int)capacity(), (int)mCount); const void* buffer = arrayImpl(); diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 87ad98e..8726a53 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -2,8 +2,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -ifneq ($(TARGET_SIMULATOR),true) - # Build the unit tests. test_src_files := \ BlobCache_test.cpp \ @@ -43,5 +41,3 @@ $(foreach file,$(test_src_files), \ $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ $(eval include $(BUILD_EXECUTABLE)) \ ) - -endif diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index b97c3c4..b20a6e9 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -32,24 +32,25 @@ import android.util.Log; * It allows to stream PCM audio buffers to the audio hardware for playback. This is * achieved by "pushing" the data to the AudioTrack object using one of the * {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods. - * + * * <p>An AudioTrack instance can operate under two modes: static or streaming.<br> * In Streaming mode, the application writes a continuous stream of data to the AudioTrack, using - * one of the write() methods. These are blocking and return when the data has been transferred - * from the Java layer to the native layer and queued for playback. The streaming mode - * is most useful when playing blocks of audio data that for instance are: + * one of the {@code write()} methods. These are blocking and return when the data has been + * transferred from the Java layer to the native layer and queued for playback. The streaming + * mode is most useful when playing blocks of audio data that for instance are: + * * <ul> * <li>too big to fit in memory because of the duration of the sound to play,</li> * <li>too big to fit in memory because of the characteristics of the audio data * (high sampling rate, bits per sample ...)</li> * <li>received or generated while previously queued audio is playing.</li> * </ul> + * * The static mode is to be chosen when dealing with short sounds that fit in memory and - * that need to be played with the smallest latency possible. AudioTrack instances in static mode - * can play the sound without the need to transfer the audio data from Java to native layer - * each time the sound is to be played. The static mode will therefore be preferred for UI and - * game sounds that are played often, and with the smallest overhead possible. - * + * that need to be played with the smallest latency possible. The static mode will + * therefore be preferred for UI and game sounds that are played often, and with the + * smallest overhead possible. + * * <p>Upon creation, an AudioTrack object initializes its associated audio buffer. * The size of this buffer, specified during the construction, determines how long an AudioTrack * can play before running out of data.<br> @@ -816,6 +817,7 @@ public class AudioTrack //-------------------- /** * Starts playing an AudioTrack. + * * @throws IllegalStateException */ public void play() @@ -832,6 +834,7 @@ public class AudioTrack /** * Stops playing the audio data. + * * @throws IllegalStateException */ public void stop() @@ -848,7 +851,10 @@ public class AudioTrack } /** - * Pauses the playback of the audio data. + * Pauses the playback of the audio data. Data that has not been played + * back will not be discarded. Subsequent calls to {@link #play} will play + * this data back. + * * @throws IllegalStateException */ public void pause() @@ -871,9 +877,9 @@ public class AudioTrack //-------------------- /** - * Flushes the audio data currently queued for playback. + * Flushes the audio data currently queued for playback. Any data that has + * not been played back will be discarded. */ - public void flush() { if (mState == STATE_INITIALIZED) { // flush the data in native layer @@ -883,9 +889,14 @@ public class AudioTrack } /** - * Writes the audio data to the audio hardware for playback. + * Writes the audio data to the audio hardware for playback. Will block until + * all data has been written to the audio mixer. + * Note that the actual playback of this data might occur after this function + * returns. This function is thread safe with respect to {@link #stop} calls, + * in which case all of the specified data might not be written to the mixer. + * * @param audioData the array that holds the data to play. - * @param offsetInBytes the offset expressed in bytes in audioData where the data to play + * @param offsetInBytes the offset expressed in bytes in audioData where the data to play * starts. * @param sizeInBytes the number of bytes to read in audioData after the offset. * @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION} @@ -914,7 +925,12 @@ public class AudioTrack /** - * Writes the audio data to the audio hardware for playback. + * Writes the audio data to the audio hardware for playback. Will block until + * all data has been written to the audio mixer. + * Note that the actual playback of this data might occur after this function + * returns. This function is thread safe with respect to {@link #stop} calls, + * in which case all of the specified data might not be written to the mixer. + * * @param audioData the array that holds the data to play. * @param offsetInShorts the offset expressed in shorts in audioData where the data to play * starts. @@ -988,7 +1004,7 @@ public class AudioTrack /** * Sets the send level of the audio track to the attached auxiliary effect - * {@see #attachAuxEffect(int)}. The level value range is 0 to 1.0. + * {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0. * <p>By default the send level is 0, so even if an effect is attached to the player * this method must be called for the effect to be applied. * <p>Note that the passed level value is a raw scalar. UI controls should be scaled diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 6df2f73..816d215 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -104,10 +104,9 @@ public class MediaFile { public static final int FILE_TYPE_MS_POWERPOINT = 106; public static final int FILE_TYPE_ZIP = 107; - static class MediaFileType { - - int fileType; - String mimeType; + public static class MediaFileType { + public final int fileType; + public final String mimeType; MediaFileType(int fileType, String mimeType) { this.fileType = fileType; diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index c55338a..e89be08 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -101,6 +101,8 @@ import java.util.Iterator; * Java MyMediaScanner handleStringTag. * Once MediaScanner processFile returns, an entry is inserted in to the database. * + * The MediaScanner class is not thread-safe, so it should only be used in a single threaded manner. + * * {@hide} */ public class MediaScanner @@ -368,6 +370,34 @@ public class MediaScanner } } + private class FileInserter { + + ContentValues[] mValues = new ContentValues[1000]; + int mIndex = 0; + + public Uri insert(ContentValues values) { + if (mIndex == mValues.length) { + flush(); + } + mValues[mIndex++] = values; + // URI not needed when doing bulk inserts + return null; + } + + public void flush() { + while (mIndex < mValues.length) { + mValues[mIndex++] = null; + } + try { + mMediaProvider.bulkInsert(mFilesUri, mValues); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in FileInserter.flush()", e); + } + mIndex = 0; + } + } + private FileInserter mFileInserter; + // hashes file path to FileCacheEntry. // path should be lower case if mCaseInsensitivePaths is true private HashMap<String, FileCacheEntry> mFileCache; @@ -805,35 +835,42 @@ public class MediaScanner } } + // For inserts we always use the file URI so we can insert in bulk. + // For updates we compute the URI based on the media type. Uri tableUri = mFilesUri; - if (!mNoMedia) { - if (MediaFile.isVideoFileType(mFileType)) { - tableUri = mVideoUri; - } else if (MediaFile.isImageFileType(mFileType)) { - tableUri = mImagesUri; - } else if (MediaFile.isAudioFileType(mFileType)) { - tableUri = mAudioUri; - } - } Uri result = null; if (rowId == 0) { if (mMtpObjectHandle != 0) { values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle); } - if (tableUri == mFilesUri) { - int format = entry.mFormat; - if (format == 0) { - format = MediaFile.getFormatCode(entry.mPath, mMimeType); - } - values.put(Files.FileColumns.FORMAT, format); + int format = entry.mFormat; + if (format == 0) { + format = MediaFile.getFormatCode(entry.mPath, mMimeType); } + values.put(Files.FileColumns.FORMAT, format); + // new file, insert it - result = mMediaProvider.insert(tableUri, values); + if (mFileInserter != null) { + result = mFileInserter.insert(values); + } else { + result = mMediaProvider.insert(tableUri, values); + } + if (result != null) { rowId = ContentUris.parseId(result); entry.mRowId = rowId; } } else { + if (!mNoMedia) { + if (MediaFile.isVideoFileType(mFileType)) { + tableUri = mVideoUri; + } else if (MediaFile.isImageFileType(mFileType)) { + tableUri = mImagesUri; + } else if (MediaFile.isAudioFileType(mFileType)) { + tableUri = mAudioUri; + } + } + // updated file result = ContentUris.withAppendedId(tableUri, rowId); // path should never change, and we want to avoid replacing mixed cased paths @@ -854,7 +891,7 @@ public class MediaScanner new String[] { genre }, null); if (cursor == null || cursor.getCount() == 0) { // genre does not exist, so create the genre in the genre table - values.clear(); + values = new ContentValues(); values.put(MediaStore.Audio.Genres.NAME, genre); uri = mMediaProvider.insert(mGenresUri, values); } else { @@ -876,7 +913,7 @@ public class MediaScanner if (uri != null) { // add entry to audio_genre_map - values.clear(); + values = new ContentValues(); values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId)); mMediaProvider.insert(uri, values); } @@ -1152,10 +1189,6 @@ public class MediaScanner mPlaylistsUri = Playlists.getContentUri(volumeName); mCaseInsensitivePaths = true; - if (!Process.supportsProcesses()) { - // Simulator uses host file system, so it should be case sensitive. - mCaseInsensitivePaths = false; - } } } @@ -1165,10 +1198,14 @@ public class MediaScanner initialize(volumeName); prescan(null, true); long prescan = System.currentTimeMillis(); + mFileInserter = new FileInserter(); for (int i = 0; i < directories.length; i++) { processDirectory(directories[i], mClient); } + mFileInserter.flush(); + mFileInserter = null; + long scan = System.currentTimeMillis(); postscan(directories); long end = System.currentTimeMillis(); diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java index b75b11a..76c8569 100644 --- a/media/java/android/mtp/MtpPropertyGroup.java +++ b/media/java/android/mtp/MtpPropertyGroup.java @@ -330,7 +330,6 @@ class MtpPropertyGroup { } int count = (c == null ? 1 : c.getCount()); - Log.d(TAG, "count: " + count); MtpPropertyList result = new MtpPropertyList(count * mProperties.length, MtpConstants.RESPONSE_OK); diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java index 0133cf6..f561cc0 100644 --- a/media/java/android/mtp/MtpServer.java +++ b/media/java/android/mtp/MtpServer.java @@ -16,18 +16,13 @@ package android.mtp; -import android.util.Log; - /** * Java wrapper for MTP/PTP support as USB responder. * {@hide} */ -public class MtpServer { - - private final Object mLock = new Object(); - private boolean mStarted; +public class MtpServer implements Runnable { - private static final String TAG = "MtpServer"; + private int mNativeContext; // accessed by native methods static { System.loadLibrary("media_jni"); @@ -38,19 +33,14 @@ public class MtpServer { } public void start() { - synchronized (mLock) { - native_start(); - mStarted = true; - } + Thread thread = new Thread(this, "MtpServer"); + thread.start(); } - public void stop() { - synchronized (mLock) { - if (mStarted) { - native_stop(); - mStarted = false; - } - } + @Override + public void run() { + native_run(); + native_cleanup(); } public void sendObjectAdded(int handle) { @@ -70,8 +60,8 @@ public class MtpServer { } private native final void native_setup(MtpDatabase database, boolean usePtp); - private native final void native_start(); - private native final void native_stop(); + private native final void native_run(); + private native final void native_cleanup(); private native final void native_send_object_added(int handle); private native final void native_send_object_removed(int handle); private native final void native_add_storage(MtpStorage storage); diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index 7932d34..da190a6 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -32,6 +32,7 @@ public class MtpStorage { private final String mDescription; private final long mReserveSpace; private final boolean mRemovable; + private final long mMaxFileSize; public MtpStorage(StorageVolume volume) { mStorageId = volume.getStorageId(); @@ -39,6 +40,7 @@ public class MtpStorage { mDescription = volume.getDescription(); mReserveSpace = volume.getMtpReserveSpace(); mRemovable = volume.isRemovable(); + mMaxFileSize = volume.getMaxFileSize(); } /** @@ -98,4 +100,13 @@ public class MtpStorage { public final boolean isRemovable() { return mRemovable; } + + /** + * Returns maximum file size for the storage, or zero if it is unbounded. + * + * @return maximum file size + */ + public long getMaxFileSize() { + return mMaxFileSize; + } } diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp index 9151799..d0d2d1e 100644 --- a/media/jni/android_media_MediaScanner.cpp +++ b/media/jni/android_media_MediaScanner.cpp @@ -45,7 +45,6 @@ struct fields_t { jfieldID context; }; static fields_t fields; -static Mutex sLock; class MyMediaScannerClient : public MediaScannerClient { @@ -159,13 +158,11 @@ static bool ExceptionCheck(void* env) return ((JNIEnv *)env)->ExceptionCheck(); } -// Call this method with sLock hold static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz) { return (MediaScanner *) env->GetIntField(thiz, fields.context); } -// Call this method with sLock hold static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s) { env->SetIntField(thiz, fields.context, (int)s); @@ -176,7 +173,6 @@ android_media_MediaScanner_processDirectory( JNIEnv *env, jobject thiz, jstring path, jobject client) { LOGV("processDirectory"); - Mutex::Autolock l(sLock); MediaScanner *mp = getNativeScanner_l(env, thiz); if (mp == NULL) { jniThrowException(env, kRunTimeException, "No scanner available"); @@ -243,7 +239,6 @@ android_media_MediaScanner_setLocale( JNIEnv *env, jobject thiz, jstring locale) { LOGV("setLocale"); - Mutex::Autolock l(sLock); MediaScanner *mp = getNativeScanner_l(env, thiz); if (mp == NULL) { jniThrowException(env, kRunTimeException, "No scanner available"); @@ -268,7 +263,6 @@ android_media_MediaScanner_extractAlbumArt( JNIEnv *env, jobject thiz, jobject fileDescriptor) { LOGV("extractAlbumArt"); - Mutex::Autolock l(sLock); MediaScanner *mp = getNativeScanner_l(env, thiz); if (mp == NULL) { jniThrowException(env, kRunTimeException, "No scanner available"); @@ -339,7 +333,6 @@ static void android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz) { LOGV("native_finalize"); - Mutex::Autolock l(sLock); MediaScanner *mp = getNativeScanner_l(env, thiz); if (mp == 0) { return; diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 0f3c063..4dbcb90 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -79,7 +79,6 @@ MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) { return (MtpDatabase *)env->GetIntField(database, field_context); } -#ifdef HAVE_ANDROID_OS // ---------------------------------------------------------------------------- class MyMtpDatabase : public MtpDatabase { @@ -1066,42 +1065,32 @@ void MyMtpDatabase::sessionEnded() { checkAndClearExceptionFromCallback(env, __FUNCTION__); } -#endif // HAVE_ANDROID_OS - // ---------------------------------------------------------------------------- static void android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz) { -#ifdef HAVE_ANDROID_OS MyMtpDatabase* database = new MyMtpDatabase(env, thiz); env->SetIntField(thiz, field_context, (int)database); checkAndClearExceptionFromCallback(env, __FUNCTION__); -#endif } static void android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz) { -#ifdef HAVE_ANDROID_OS MyMtpDatabase* database = (MyMtpDatabase *)env->GetIntField(thiz, field_context); database->cleanup(env); delete database; env->SetIntField(thiz, field_context, 0); checkAndClearExceptionFromCallback(env, __FUNCTION__); -#endif } static jstring android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject thiz, jlong seconds) { -#ifdef HAVE_ANDROID_OS char date[20]; formatDateTime(seconds, date, sizeof(date)); return env->NewStringUTF(date); -#else - return NULL; -#endif } // ---------------------------------------------------------------------------- diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index 40bbaa3..6b73f6c 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -85,8 +85,6 @@ static jfieldID field_objectInfo_dateCreated; static jfieldID field_objectInfo_dateModified; static jfieldID field_objectInfo_keywords; -#ifdef HAVE_ANDROID_OS - MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice) { return (MtpDevice*)env->GetIntField(javaDevice, field_context); @@ -100,15 +98,11 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa } } -#endif // HAVE_ANDROID_OS - // ---------------------------------------------------------------------------- static jboolean android_mtp_MtpDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, jint fd) { -#ifdef HAVE_ANDROID_OS - LOGD("open\n"); const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); if (deviceNameStr == NULL) { return false; @@ -120,27 +114,22 @@ android_mtp_MtpDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, jint f if (device) env->SetIntField(thiz, field_context, (int)device); return (device != NULL); -#endif } static void android_mtp_MtpDevice_close(JNIEnv *env, jobject thiz) { -#ifdef HAVE_ANDROID_OS - LOGD("close\n"); MtpDevice* device = get_device_from_object(env, thiz); if (device) { device->close(); delete device; env->SetIntField(thiz, field_context, 0); } -#endif } static jobject android_mtp_MtpDevice_get_device_info(JNIEnv *env, jobject thiz) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (!device) { LOGD("android_mtp_MtpDevice_get_device_info device is null"); @@ -173,15 +162,11 @@ android_mtp_MtpDevice_get_device_info(JNIEnv *env, jobject thiz) delete deviceInfo; return info; -#else - return NULL; -#endif } static jintArray android_mtp_MtpDevice_get_storage_ids(JNIEnv *env, jobject thiz) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (!device) return NULL; @@ -196,15 +181,11 @@ android_mtp_MtpDevice_get_storage_ids(JNIEnv *env, jobject thiz) delete storageIDs; return array; -#else - return NULL; -#endif } static jobject android_mtp_MtpDevice_get_storage_info(JNIEnv *env, jobject thiz, jint storageID) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (!device) return NULL; @@ -234,16 +215,12 @@ android_mtp_MtpDevice_get_storage_info(JNIEnv *env, jobject thiz, jint storageID delete storageInfo; return info; -#else - return NULL; -#endif } static jintArray android_mtp_MtpDevice_get_object_handles(JNIEnv *env, jobject thiz, jint storageID, jint format, jint objectID) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (!device) return NULL; @@ -258,15 +235,11 @@ android_mtp_MtpDevice_get_object_handles(JNIEnv *env, jobject thiz, delete handles; return array; -#else - return NULL; -#endif } static jobject android_mtp_MtpDevice_get_object_info(JNIEnv *env, jobject thiz, jint objectID) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (!device) return NULL; @@ -324,9 +297,6 @@ android_mtp_MtpDevice_get_object_info(JNIEnv *env, jobject thiz, jint objectID) delete objectInfo; return info; -#else - return NULL; -#endif } struct get_object_callback_data { @@ -344,7 +314,6 @@ static bool get_object_callback(void* data, int offset, int length, void* client static jbyteArray android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jint objectSize) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (!device) return NULL; @@ -361,14 +330,12 @@ android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jint if (device->readObject(objectID, get_object_callback, objectSize, &data)) return array; -#endif return NULL; } static jbyteArray android_mtp_MtpDevice_get_thumbnail(JNIEnv *env, jobject thiz, jint objectID) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (!device) return NULL; @@ -382,51 +349,41 @@ android_mtp_MtpDevice_get_thumbnail(JNIEnv *env, jobject thiz, jint objectID) free(thumbnail); return array; -#else - return NULL; -#endif } static jboolean android_mtp_MtpDevice_delete_object(JNIEnv *env, jobject thiz, jint object_id) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (device) return device->deleteObject(object_id); else - #endif return NULL; } static jlong android_mtp_MtpDevice_get_parent(JNIEnv *env, jobject thiz, jint object_id) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (device) return device->getParent(object_id); else -#endif return -1; } static jlong android_mtp_MtpDevice_get_storage_id(JNIEnv *env, jobject thiz, jint object_id) { - #ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (device) return device->getStorageID(object_id); else -#endif return -1; } static jboolean android_mtp_MtpDevice_import_file(JNIEnv *env, jobject thiz, jint object_id, jstring dest_path) { -#ifdef HAVE_ANDROID_OS MtpDevice* device = get_device_from_object(env, thiz); if (device) { const char *destPathStr = env->GetStringUTFChars(dest_path, NULL); @@ -438,7 +395,7 @@ android_mtp_MtpDevice_import_file(JNIEnv *env, jobject thiz, jint object_id, jst env->ReleaseStringUTFChars(dest_path, destPathStr); return result; } -#endif + return false; } diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp index aaf85c3..107db08 100644 --- a/media/jni/android_mtp_MtpServer.cpp +++ b/media/jni/android_mtp_MtpServer.cpp @@ -22,13 +22,8 @@ #include <limits.h> #include <unistd.h> #include <fcntl.h> -#include <sys/ioctl.h> #include <utils/threads.h> -#ifdef HAVE_ANDROID_OS -#include <linux/usb/f_mtp.h> -#endif - #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" @@ -39,8 +34,8 @@ using namespace android; -// MtpStorage class -jclass clazz_MtpStorage; +// MtpServer fields +static jfieldID field_MtpServer_nativeContext; // MtpStorage fields static jfieldID field_MtpStorage_storageId; @@ -48,6 +43,7 @@ static jfieldID field_MtpStorage_path; static jfieldID field_MtpStorage_description; static jfieldID field_MtpStorage_reserveSpace; static jfieldID field_MtpStorage_removable; +static jfieldID field_MtpStorage_maxFileSize; static Mutex sMutex; @@ -56,185 +52,92 @@ static Mutex sMutex; // in android_mtp_MtpDatabase.cpp extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database); -// ---------------------------------------------------------------------------- - -#ifdef HAVE_ANDROID_OS - -static bool ExceptionCheck(void* env) -{ - return ((JNIEnv *)env)->ExceptionCheck(); +static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) { + return (MtpServer*)env->GetIntField(thiz, field_MtpServer_nativeContext); } -class MtpThread : public Thread { -private: - MtpDatabase* mDatabase; - bool mPtp; - MtpServer* mServer; - MtpStorageList mStorageList; - int mFd; - -public: - MtpThread(MtpDatabase* database, bool usePtp) - : mDatabase(database), - mPtp(usePtp), - mServer(NULL), - mFd(-1) - { - } - - virtual ~MtpThread() { - } - - void addStorage(MtpStorage *storage) { - mStorageList.push(storage); - if (mServer) - mServer->addStorage(storage); - } - - void removeStorage(MtpStorageID id) { - MtpStorage* storage = mServer->getStorage(id); - if (storage) { - for (size_t i = 0; i < mStorageList.size(); i++) { - if (mStorageList[i] == storage) { - mStorageList.removeAt(i); - break; - } - } - if (mServer) - mServer->removeStorage(storage); - delete storage; - } - } - - void start() { - run("MtpThread"); - } - - virtual bool threadLoop() { - sMutex.lock(); - - mFd = open("/dev/mtp_usb", O_RDWR); - if (mFd >= 0) { - mServer = new MtpServer(mFd, mDatabase, mPtp, AID_MEDIA_RW, 0664, 0775); - for (size_t i = 0; i < mStorageList.size(); i++) { - mServer->addStorage(mStorageList[i]); - } - } else { - LOGE("could not open MTP driver, errno: %d", errno); - } - - sMutex.unlock(); - mServer->run(); - sMutex.lock(); - - close(mFd); - mFd = -1; - delete mServer; - mServer = NULL; - - sMutex.unlock(); - // delay a bit before retrying to avoid excessive spin - if (!exitPending()) { - sleep(1); - } - - return true; - } - - void sendObjectAdded(MtpObjectHandle handle) { - if (mServer) - mServer->sendObjectAdded(handle); - } - - void sendObjectRemoved(MtpObjectHandle handle) { - if (mServer) - mServer->sendObjectRemoved(handle); - } -}; - -// This smart pointer is necessary for preventing MtpThread from exiting too early -static sp<MtpThread> sThread; - -#endif // HAVE_ANDROID_OS - static void android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jboolean usePtp) { -#ifdef HAVE_ANDROID_OS - // create the thread and assign it to the smart pointer - sThread = new MtpThread(getMtpDatabase(env, javaDatabase), usePtp); -#endif + int fd = open("/dev/mtp_usb", O_RDWR); + if (fd >= 0) { + MtpServer* server = new MtpServer(fd, getMtpDatabase(env, javaDatabase), + usePtp, AID_MEDIA_RW, 0664, 0775); + env->SetIntField(thiz, field_MtpServer_nativeContext, (int)server); + } else { + LOGE("could not open MTP driver, errno: %d", errno); + } } static void -android_mtp_MtpServer_start(JNIEnv *env, jobject thiz) +android_mtp_MtpServer_run(JNIEnv *env, jobject thiz) { -#ifdef HAVE_ANDROID_OS - sMutex.lock(); - MtpThread *thread = sThread.get(); - if (thread) - thread->start(); - sMutex.unlock(); -#endif // HAVE_ANDROID_OS + MtpServer* server = getMtpServer(env, thiz); + if (server) + server->run(); + else + LOGE("server is null in run"); } static void -android_mtp_MtpServer_stop(JNIEnv *env, jobject thiz) +android_mtp_MtpServer_cleanup(JNIEnv *env, jobject thiz) { -#ifdef HAVE_ANDROID_OS - sMutex.lock(); - MtpThread *thread = sThread.get(); - if (thread) { - thread->requestExitAndWait(); - sThread = NULL; + Mutex::Autolock autoLock(sMutex); + + MtpServer* server = getMtpServer(env, thiz); + if (server) { + delete server; + env->SetIntField(thiz, field_MtpServer_nativeContext, 0); + } else { + LOGE("server is null in cleanup"); } - sMutex.unlock(); -#endif } static void android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle) { -#ifdef HAVE_ANDROID_OS - sMutex.lock(); - MtpThread *thread = sThread.get(); - if (thread) - thread->sendObjectAdded(handle); - sMutex.unlock(); -#endif + Mutex::Autolock autoLock(sMutex); + + MtpServer* server = getMtpServer(env, thiz); + if (server) + server->sendObjectAdded(handle); + else + LOGE("server is null in send_object_added"); } static void android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle) { -#ifdef HAVE_ANDROID_OS - sMutex.lock(); - MtpThread *thread = sThread.get(); - if (thread) - thread->sendObjectRemoved(handle); - sMutex.unlock(); -#endif + Mutex::Autolock autoLock(sMutex); + + MtpServer* server = getMtpServer(env, thiz); + if (server) + server->sendObjectRemoved(handle); + else + LOGE("server is null in send_object_removed"); } static void android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage) { -#ifdef HAVE_ANDROID_OS - sMutex.lock(); - MtpThread *thread = sThread.get(); - if (thread) { + Mutex::Autolock autoLock(sMutex); + + MtpServer* server = getMtpServer(env, thiz); + if (server) { jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId); jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path); jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description); jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace); jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable); + jlong maxFileSize = env->GetLongField(jstorage, field_MtpStorage_maxFileSize); const char *pathStr = env->GetStringUTFChars(path, NULL); if (pathStr != NULL) { const char *descriptionStr = env->GetStringUTFChars(description, NULL); if (descriptionStr != NULL) { - MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace, removable); - thread->addStorage(storage); + MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, + reserveSpace, removable, maxFileSize); + server->addStorage(storage); env->ReleaseStringUTFChars(path, pathStr); env->ReleaseStringUTFChars(description, descriptionStr); } else { @@ -242,24 +145,24 @@ android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage) } } } else { - LOGE("MtpThread is null in add_storage"); + LOGE("server is null in add_storage"); } - sMutex.unlock(); -#endif } static void android_mtp_MtpServer_remove_storage(JNIEnv *env, jobject thiz, jint storageId) { -#ifdef HAVE_ANDROID_OS - sMutex.lock(); - MtpThread *thread = sThread.get(); - if (thread) - thread->removeStorage(storageId); - else - LOGE("MtpThread is null in remove_storage"); - sMutex.unlock(); -#endif + Mutex::Autolock autoLock(sMutex); + + MtpServer* server = getMtpServer(env, thiz); + if (server) { + MtpStorage* storage = server->getStorage(storageId); + if (storage) { + server->removeStorage(storage); + delete storage; + } + } else + LOGE("server is null in remove_storage"); } // ---------------------------------------------------------------------------- @@ -267,8 +170,8 @@ android_mtp_MtpServer_remove_storage(JNIEnv *env, jobject thiz, jint storageId) static JNINativeMethod gMethods[] = { {"native_setup", "(Landroid/mtp/MtpDatabase;Z)V", (void *)android_mtp_MtpServer_setup}, - {"native_start", "()V", (void *)android_mtp_MtpServer_start}, - {"native_stop", "()V", (void *)android_mtp_MtpServer_stop}, + {"native_run", "()V", (void *)android_mtp_MtpServer_run}, + {"native_cleanup", "()V", (void *)android_mtp_MtpServer_cleanup}, {"native_send_object_added", "(I)V", (void *)android_mtp_MtpServer_send_object_added}, {"native_send_object_removed", "(I)V", (void *)android_mtp_MtpServer_send_object_removed}, {"native_add_storage", "(Landroid/mtp/MtpStorage;)V", @@ -312,13 +215,22 @@ int register_android_mtp_MtpServer(JNIEnv *env) LOGE("Can't find MtpStorage.mRemovable"); return -1; } - clazz_MtpStorage = (jclass)env->NewGlobalRef(clazz); + field_MtpStorage_maxFileSize = env->GetFieldID(clazz, "mMaxFileSize", "J"); + if (field_MtpStorage_maxFileSize == NULL) { + LOGE("Can't find MtpStorage.mMaxFileSize"); + return -1; + } clazz = env->FindClass("android/mtp/MtpServer"); if (clazz == NULL) { LOGE("Can't find android/mtp/MtpServer"); return -1; } + field_MtpServer_nativeContext = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_MtpServer_nativeContext == NULL) { + LOGE("Can't find MtpServer.mNativeContext"); + return -1; + } return AndroidRuntime::registerNativeMethods(env, "android/mtp/MtpServer", gMethods, NELEM(gMethods)); diff --git a/media/libeffects/factory/Android.mk b/media/libeffects/factory/Android.mk index 20f58e5..26265ae 100644 --- a/media/libeffects/factory/Android.mk +++ b/media/libeffects/factory/Android.mk @@ -12,14 +12,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES) LOCAL_MODULE:= libeffects -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -endif - -ifneq ($(TARGET_SIMULATOR),true) LOCAL_SHARED_LIBRARIES += libdl -endif - -LOCAL_C_INCLUDES := \ include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c index a3e76d9..a9689bc 100644 --- a/media/libeffects/factory/EffectsFactory.c +++ b/media/libeffects/factory/EffectsFactory.c @@ -130,10 +130,43 @@ int Effect_GetDescriptor(effect_handle_t self, return ret; } +int Effect_ProcessReverse(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) +{ + int ret = init(); + if (ret < 0) { + return ret; + } + effect_entry_t *fx = (effect_entry_t *)self; + pthread_mutex_lock(&gLibLock); + if (fx->lib == NULL) { + pthread_mutex_unlock(&gLibLock); + return -EPIPE; + } + pthread_mutex_lock(&fx->lib->lock); + pthread_mutex_unlock(&gLibLock); + + if ((*fx->subItfe)->process_reverse != NULL) { + ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer); + } else { + ret = -ENOSYS; + } + pthread_mutex_unlock(&fx->lib->lock); + return ret; +} + + const struct effect_interface_s gInterface = { Effect_Process, Effect_Command, - Effect_GetDescriptor + Effect_GetDescriptor, + NULL +}; + +const struct effect_interface_s gInterfaceWithReverse = { + Effect_Process, + Effect_Command, + Effect_GetDescriptor, + Effect_ProcessReverse }; ///////////////////////////////////////////////// @@ -266,7 +299,13 @@ int EffectCreate(effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_ha // add entry to effect list fx = (effect_entry_t *)malloc(sizeof(effect_entry_t)); fx->subItfe = itfe; - fx->itfe = (struct effect_interface_s *)&gInterface; + if ((*itfe)->process_reverse != NULL) { + fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse; + LOGV("EffectCreate() gInterfaceWithReverse"); + } else { + fx->itfe = (struct effect_interface_s *)&gInterface; + LOGV("EffectCreate() gInterface"); + } fx->lib = l; e = (list_elem_t *)malloc(sizeof(list_elem_t)); diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk index ab13605..f097dd0 100644 --- a/media/libeffects/lvm/wrapper/Android.mk +++ b/media/libeffects/lvm/wrapper/Android.mk @@ -19,12 +19,7 @@ LOCAL_STATIC_LIBRARIES += libmusicbundle LOCAL_SHARED_LIBRARIES := \ libcutils \ - -ifeq ($(TARGET_SIMULATOR),true) -LOCAL_LDLIBS += -ldl -else -LOCAL_SHARED_LIBRARIES += libdl -endif + libdl LOCAL_C_INCLUDES += \ @@ -54,12 +49,7 @@ LOCAL_STATIC_LIBRARIES += libreverb LOCAL_SHARED_LIBRARIES := \ libcutils \ - -ifeq ($(TARGET_SIMULATOR),true) -LOCAL_LDLIBS += -ldl -else -LOCAL_SHARED_LIBRARIES += libdl -endif + libdl LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/Reverb \ diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 21c451f..efa1c45 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -3231,7 +3231,8 @@ int Effect_getDescriptor(effect_handle_t self, const struct effect_interface_s gLvmEffectInterface = { Effect_process, Effect_command, - Effect_getDescriptor + Effect_getDescriptor, + NULL, }; /* end gLvmEffectInterface */ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp index 2727375..663f8ff 100755 --- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp +++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp @@ -2134,7 +2134,8 @@ int Reverb_getDescriptor(effect_handle_t self, const struct effect_interface_s gReverbInterface = { Reverb_process, Reverb_command, - Reverb_getDescriptor + Reverb_getDescriptor, + NULL, }; /* end gReverbInterface */ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { diff --git a/media/libeffects/testlibs/EffectEqualizer.cpp b/media/libeffects/testlibs/EffectEqualizer.cpp index c2ffce5..b22ebec 100644 --- a/media/libeffects/testlibs/EffectEqualizer.cpp +++ b/media/libeffects/testlibs/EffectEqualizer.cpp @@ -736,7 +736,8 @@ extern "C" int Equalizer_getDescriptor(effect_handle_t self, const struct effect_interface_s gEqualizerInterface = { Equalizer_process, Equalizer_command, - Equalizer_getDescriptor + Equalizer_getDescriptor, + NULL }; diff --git a/media/libeffects/testlibs/EffectReverb.c b/media/libeffects/testlibs/EffectReverb.c index 02762c9..405f908 100644 --- a/media/libeffects/testlibs/EffectReverb.c +++ b/media/libeffects/testlibs/EffectReverb.c @@ -27,7 +27,8 @@ const struct effect_interface_s gReverbInterface = { Reverb_Process, Reverb_Command, - Reverb_GetDescriptor + Reverb_GetDescriptor, + NULL }; // Google auxiliary environmental reverb UUID: 1f0ae2e0-4ef7-11df-bc09-0002a5d5c51b diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk index dff585f..2160177 100644 --- a/media/libeffects/visualizer/Android.mk +++ b/media/libeffects/visualizer/Android.mk @@ -9,19 +9,12 @@ LOCAL_SRC_FILES:= \ LOCAL_CFLAGS+= -O2 LOCAL_SHARED_LIBRARIES := \ - libcutils + libcutils \ + libdl LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx LOCAL_MODULE:= libvisualizer -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif - LOCAL_C_INCLUDES := \ $(call include-path-for, graphics corecg) \ system/media/audio_effects/include diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index aeebd4d..3c3af8f 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -450,7 +450,8 @@ int Visualizer_getDescriptor(effect_handle_t self, const struct effect_interface_s gVisualizerInterface = { Visualizer_process, Visualizer_command, - Visualizer_getDescriptor + Visualizer_getDescriptor, + NULL, }; diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index f7c54fa..7af4a87 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -49,20 +49,12 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc libexpat \ libcamera_client libstagefright_foundation \ - libgui + libgui libdl LOCAL_WHOLE_STATIC_LIBRARY := libmedia_helper LOCAL_MODULE:= libmedia -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -lpthread -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif - LOCAL_C_INCLUDES := \ $(JNI_H_INCLUDE) \ $(call include-path-for, graphics corecg) \ diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp index c14ee82..b311f35 100644 --- a/media/libmedia/IStreamSource.cpp +++ b/media/libmedia/IStreamSource.cpp @@ -29,6 +29,9 @@ namespace android { // static const char *const IStreamListener::kKeyResumeAtPTS = "resume-at-PTS"; +// static +const char *const IStreamListener::kKeyFormatChange = "format-change"; + enum { // IStreamSource SET_LISTENER = IBinder::FIRST_CALL_TRANSACTION, diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index fadad28..ec7d8a0 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -16,10 +16,6 @@ LOCAL_SRC_FILES:= \ StagefrightPlayer.cpp \ StagefrightRecorder.cpp -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -lpthread -endif - LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ @@ -32,16 +28,13 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright \ libstagefright_omx \ libstagefright_foundation \ - libgui + libgui \ + libdl LOCAL_STATIC_LIBRARIES := \ libstagefright_rtsp \ libstagefright_nuplayer \ -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif - LOCAL_C_INCLUDES := \ $(JNI_H_INCLUDE) \ $(call include-path-for, graphics corecg) \ diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index b003476..223e0be 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -1136,10 +1136,69 @@ status_t StagefrightRecorder::checkVideoEncoderCapabilities() { clipVideoFrameRate(); clipVideoFrameWidth(); clipVideoFrameHeight(); + setDefaultProfileIfNecessary(); } return OK; } +// Set to use AVC baseline profile if the encoding parameters matches +// CAMCORDER_QUALITY_LOW profile; this is for the sake of MMS service. +void StagefrightRecorder::setDefaultProfileIfNecessary() { + LOGV("setDefaultProfileIfNecessary"); + + camcorder_quality quality = CAMCORDER_QUALITY_LOW; + + int64_t durationUs = mEncoderProfiles->getCamcorderProfileParamByName( + "duration", mCameraId, quality) * 1000000LL; + + int fileFormat = mEncoderProfiles->getCamcorderProfileParamByName( + "file.format", mCameraId, quality); + + int videoCodec = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.codec", mCameraId, quality); + + int videoBitRate = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.bps", mCameraId, quality); + + int videoFrameRate = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.fps", mCameraId, quality); + + int videoFrameWidth = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.width", mCameraId, quality); + + int videoFrameHeight = mEncoderProfiles->getCamcorderProfileParamByName( + "vid.height", mCameraId, quality); + + int audioCodec = mEncoderProfiles->getCamcorderProfileParamByName( + "aud.codec", mCameraId, quality); + + int audioBitRate = mEncoderProfiles->getCamcorderProfileParamByName( + "aud.bps", mCameraId, quality); + + int audioSampleRate = mEncoderProfiles->getCamcorderProfileParamByName( + "aud.hz", mCameraId, quality); + + int audioChannels = mEncoderProfiles->getCamcorderProfileParamByName( + "aud.ch", mCameraId, quality); + + if (durationUs == mMaxFileDurationUs && + fileFormat == mOutputFormat && + videoCodec == mVideoEncoder && + videoBitRate == mVideoBitRate && + videoFrameRate == mFrameRate && + videoFrameWidth == mVideoWidth && + videoFrameHeight == mVideoHeight && + audioCodec == mAudioEncoder && + audioBitRate == mAudioBitRate && + audioSampleRate == mSampleRate && + audioChannels == mAudioChannels) { + if (videoCodec == VIDEO_ENCODER_H264) { + LOGI("Force to use AVC baseline profile"); + setParamVideoEncoderProfile(OMX_VIDEO_AVCProfileBaseline); + } + } +} + status_t StagefrightRecorder::checkAudioEncoderCapabilities() { clipAudioBitRate(); clipAudioSampleRate(); diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index cb9c406..034b373 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -174,6 +174,7 @@ private: void clipAudioBitRate(); void clipAudioSampleRate(); void clipNumberOfAudioChannels(); + void setDefaultProfileIfNecessary(); StagefrightRecorder(const StagefrightRecorder &); StagefrightRecorder &operator=(const StagefrightRecorder &); diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index bbc8a6e..a6a3a18 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -63,8 +63,17 @@ bool NuPlayer::StreamingSource::feedMoreTSData() { mEOS = true; break; } else if (n == INFO_DISCONTINUITY) { - mTSParser->signalDiscontinuity( - ATSParser::DISCONTINUITY_SEEK, extra); + ATSParser::DiscontinuityType type = ATSParser::DISCONTINUITY_SEEK; + + int32_t formatChange; + if (extra != NULL + && extra->findInt32( + IStreamListener::kKeyFormatChange, &formatChange) + && formatChange != 0) { + type = ATSParser::DISCONTINUITY_FORMATCHANGE; + } + + mTSParser->signalDiscontinuity(type, extra); } else if (n < 0) { CHECK_EQ(n, -EWOULDBLOCK); break; diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 8c4b274..e17e1e8 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -3,8 +3,6 @@ include $(CLEAR_VARS) include frameworks/base/media/libstagefright/codecs/common/Config.mk -BUILD_WITH_SOFTWARE_DECODERS := false - LOCAL_SRC_FILES:= \ ACodec.cpp \ AACExtractor.cpp \ @@ -24,7 +22,6 @@ LOCAL_SRC_FILES:= \ FileSource.cpp \ FLACExtractor.cpp \ HTTPBase.cpp \ - HTTPStream.cpp \ JPEGSource.cpp \ MP3Extractor.cpp \ MPEG2TSWriter.cpp \ @@ -38,13 +35,11 @@ LOCAL_SRC_FILES:= \ MediaSourceSplitter.cpp \ MetaData.cpp \ NuCachedSource2.cpp \ - NuHTTPDataSource.cpp \ OMXClient.cpp \ OMXCodec.cpp \ OggExtractor.cpp \ SampleIterator.cpp \ SampleTable.cpp \ - ShoutcastSource.cpp \ StagefrightMediaScanner.cpp \ StagefrightMetadataRetriever.cpp \ ThrottledSource.cpp \ @@ -96,26 +91,6 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_id3 \ libFLAC \ -ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true) - -LOCAL_SRC_FILES += \ - ThreadedSource.cpp \ - -LOCAL_STATIC_LIBRARIES += \ - libstagefright_aacdec \ - libstagefright_amrnbdec \ - libstagefright_amrwbdec \ - libstagefright_avcdec \ - libstagefright_g711dec \ - libstagefright_mp3dec \ - libstagefright_m4vh263dec \ - libstagefright_vorbisdec \ - libstagefright_vpxdec \ - libvpx \ - -endif - - ################################################################################ # The following was shamelessly copied from external/webkit/Android.mk and @@ -159,10 +134,8 @@ LOCAL_STATIC_LIBRARIES += \ libchromium_net \ libwebcore \ -ifneq ($(TARGET_SIMULATOR),true) LOCAL_SHARED_LIBRARIES += libstlport include external/stlport/libstlport.mk -endif LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1 @@ -175,27 +148,10 @@ LOCAL_SHARED_LIBRARIES += \ libstagefright_enc_common \ libstagefright_avc_common \ libstagefright_foundation \ - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) - LOCAL_LDLIBS += -lpthread -ldl - LOCAL_SHARED_LIBRARIES += libdvm - LOCAL_CPPFLAGS += -DANDROID_SIMULATOR -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) - LOCAL_LDLIBS += -lpthread -endif + libdl LOCAL_CFLAGS += -Wno-multichar -ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true) - LOCAL_CFLAGS += -DHAVE_SOFTWARE_DECODERS -endif - LOCAL_MODULE:= libstagefright include $(BUILD_SHARED_LIBRARY) diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 77c25d1..788464e 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -890,7 +890,11 @@ status_t AwesomePlayer::play_l() { CHECK(!(mFlags & AUDIO_RUNNING)); if (mVideoSource == NULL) { - status_t err = startAudioPlayer_l(); + // We don't want to post an error notification at this point, + // the error returned from MediaPlayer::start() will suffice. + + status_t err = startAudioPlayer_l( + false /* sendErrorNotification */); if (err != OK) { delete mAudioPlayer; @@ -940,7 +944,7 @@ status_t AwesomePlayer::play_l() { return OK; } -status_t AwesomePlayer::startAudioPlayer_l() { +status_t AwesomePlayer::startAudioPlayer_l(bool sendErrorNotification) { CHECK(!(mFlags & AUDIO_RUNNING)); if (mAudioSource == NULL || mAudioPlayer == NULL) { @@ -958,7 +962,10 @@ status_t AwesomePlayer::startAudioPlayer_l() { true /* sourceAlreadyStarted */); if (err != OK) { - notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); + if (sendErrorNotification) { + notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); + } + return err; } @@ -1684,7 +1691,7 @@ void AwesomePlayer::onVideoEvent() { if (mAudioPlayer != NULL && !(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) { status_t err = startAudioPlayer_l(); if (err != OK) { - LOGE("Startung the audio player failed w/ err %d", err); + LOGE("Starting the audio player failed w/ err %d", err); return; } } diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index ed8149a..1bc2fb9 100644..100755 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -103,6 +103,10 @@ static int32_t getColorFormat(const char* colorFormat) { return OMX_COLOR_Format16bitRGB565; } + if (!strcmp(colorFormat, "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar")) { + return OMX_TI_COLOR_FormatYUV420PackedSemiPlanar; + } + LOGE("Uknown color format (%s), please add it to " "CameraSource::getColorFormat", colorFormat); @@ -158,12 +162,10 @@ CameraSource::CameraSource( mVideoSize.width = -1; mVideoSize.height = -1; - int64_t token = IPCThreadState::self()->clearCallingIdentity(); mInitCheck = init(camera, proxy, cameraId, videoSize, frameRate, storeMetaDataInVideoBuffers); if (mInitCheck != OK) releaseCamera(); - IPCThreadState::self()->restoreCallingIdentity(token); } status_t CameraSource::initCheck() const { @@ -463,6 +465,22 @@ status_t CameraSource::init( bool storeMetaDataInVideoBuffers) { status_t err = OK; + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + err = initWithCameraAccess(camera, proxy, cameraId, + videoSize, frameRate, + storeMetaDataInVideoBuffers); + IPCThreadState::self()->restoreCallingIdentity(token); + return err; +} + +status_t CameraSource::initWithCameraAccess( + const sp<ICamera>& camera, + const sp<ICameraRecordingProxy>& proxy, + int32_t cameraId, + Size videoSize, + int32_t frameRate, + bool storeMetaDataInVideoBuffers) { + status_t err = OK; if ((err = isCameraAvailable(camera, proxy, cameraId)) != OK) { LOGE("Camera connection could not be established."); @@ -525,6 +543,11 @@ status_t CameraSource::init( CameraSource::~CameraSource() { if (mStarted) { stop(); + } else if (mInitCheck == OK) { + // Camera is initialized but because start() is never called, + // the lock on Camera is never released(). This makes sure + // Camera's lock is released in this case. + releaseCamera(); } } @@ -571,6 +594,7 @@ void CameraSource::stopCameraRecording() { void CameraSource::releaseCamera() { LOGV("releaseCamera"); if (mCamera != 0) { + int64_t token = IPCThreadState::self()->clearCallingIdentity(); if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) { LOGV("Camera was cold when we started, stopping preview"); mCamera->stopPreview(); @@ -580,6 +604,7 @@ void CameraSource::releaseCamera() { mCamera->unlock(); } mCamera.clear(); + IPCThreadState::self()->restoreCallingIdentity(token); } if (mCameraRecordingProxy != 0) { mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier); diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp index 0d24551..f9d8501 100644 --- a/media/libstagefright/HTTPBase.cpp +++ b/media/libstagefright/HTTPBase.cpp @@ -24,10 +24,11 @@ #include "include/ChromiumHTTPDataSource.h" #endif -#include "include/NuHTTPDataSource.h" - +#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ALooper.h> + #include <cutils/properties.h> +#include <cutils/qtaguid.h> namespace android { @@ -44,14 +45,12 @@ HTTPBase::HTTPBase() // static sp<HTTPBase> HTTPBase::Create(uint32_t flags) { #if CHROMIUM_AVAILABLE - char value[PROPERTY_VALUE_MAX]; - if (!property_get("media.stagefright.use-chromium", value, NULL) - || (strcasecmp("false", value) && strcmp("0", value))) { return new ChromiumHTTPDataSource(flags); - } else #endif { - return new NuHTTPDataSource(flags); + TRESPASS(); + + return NULL; } } @@ -135,4 +134,10 @@ bool HTTPBase::getUID(uid_t *uid) const { return true; } +// static +void HTTPBase::RegisterSocketUser(int s, uid_t uid) { + static const uint32_t kTag = 0xdeadbeef; + set_qtaguid(s, kTag, uid); +} + } // namespace android diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp deleted file mode 100644 index d526ebd..0000000 --- a/media/libstagefright/HTTPStream.cpp +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright (C) 2009 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 "HTTPStream" -#include <utils/Log.h> - -#include "include/HTTPStream.h" - -#include <sys/socket.h> - -#include <arpa/inet.h> -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <media/stagefright/foundation/ADebug.h> - -#include <openssl/ssl.h> - -namespace android { - -// static -const char *HTTPStream::kStatusKey = ":status:"; // MUST be lowercase. - -HTTPStream::HTTPStream() - : mState(READY), - mUIDValid(false), - mSocket(-1), - mSSLContext(NULL), - mSSL(NULL) { -} - -HTTPStream::~HTTPStream() { - disconnect(); - - if (mSSLContext != NULL) { - SSL_CTX_free((SSL_CTX *)mSSLContext); - mSSLContext = NULL; - } -} - -void HTTPStream::setUID(uid_t uid) { - mUIDValid = true; - mUID = uid; -} - -static bool MakeSocketBlocking(int s, bool blocking) { - // Make socket non-blocking. - int flags = fcntl(s, F_GETFL, 0); - if (flags == -1) { - return false; - } - - if (blocking) { - flags &= ~O_NONBLOCK; - } else { - flags |= O_NONBLOCK; - } - - return fcntl(s, F_SETFL, flags) != -1; -} - -static status_t MyConnect( - int s, const struct sockaddr *addr, socklen_t addrlen) { - status_t result = UNKNOWN_ERROR; - - MakeSocketBlocking(s, false); - - if (connect(s, addr, addrlen) == 0) { - result = OK; - } else if (errno != EINPROGRESS) { - result = -errno; - } else { - for (;;) { - fd_set rs, ws; - FD_ZERO(&rs); - FD_ZERO(&ws); - FD_SET(s, &rs); - FD_SET(s, &ws); - - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 100000ll; - - int nfds = ::select(s + 1, &rs, &ws, NULL, &tv); - - if (nfds < 0) { - if (errno == EINTR) { - continue; - } - - result = -errno; - break; - } - - if (FD_ISSET(s, &ws) && !FD_ISSET(s, &rs)) { - result = OK; - break; - } - - if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) { - // Get the pending error. - int error = 0; - socklen_t errorLen = sizeof(error); - if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &errorLen) == -1) { - // Couldn't get the real error, so report why not. - result = -errno; - } else { - result = -error; - } - break; - } - - // Timeout expired. Try again. - } - } - - MakeSocketBlocking(s, true); - - return result; -} - -// Apparently under out linux closing a socket descriptor from one thread -// will not unblock a pending send/recv on that socket on another thread. -static ssize_t MySendReceive( - int s, void *data, size_t size, int flags, bool sendData) { - ssize_t result = 0; - - if (s < 0) { - return -1; - } - while (size > 0) { - fd_set rs, ws, es; - FD_ZERO(&rs); - FD_ZERO(&ws); - FD_ZERO(&es); - FD_SET(s, sendData ? &ws : &rs); - FD_SET(s, &es); - - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 100000ll; - - int nfds = ::select( - s + 1, - sendData ? NULL : &rs, - sendData ? &ws : NULL, - &es, - &tv); - - if (nfds < 0) { - if (errno == EINTR) { - continue; - } - - result = -errno; - break; - } else if (nfds == 0) { - // timeout - - continue; - } - - CHECK_EQ(nfds, 1); - - ssize_t nbytes = - sendData ? send(s, data, size, flags) : recv(s, data, size, flags); - - if (nbytes < 0) { - if (errno == EINTR) { - continue; - } - - result = -errno; - break; - } else if (nbytes == 0) { - result = 0; - break; - } - - data = (uint8_t *)data + nbytes; - size -= nbytes; - - result = nbytes; - break; - } - - return result; -} - -static ssize_t MySend(int s, const void *data, size_t size, int flags) { - return MySendReceive( - s, const_cast<void *>(data), size, flags, true /* sendData */); -} - -static ssize_t MyReceive(int s, void *data, size_t size, int flags) { - return MySendReceive(s, data, size, flags, false /* sendData */); -} - -status_t HTTPStream::connect(const char *server, int port, bool https) { - if (port < 0) { - port = https ? 443 : 80; - } - - Mutex::Autolock autoLock(mLock); - - status_t err = OK; - - if (mState == CONNECTED) { - return ERROR_ALREADY_CONNECTED; - } - - if (port < 0 || port > (int) USHRT_MAX) { - return UNKNOWN_ERROR; - } - - char service[sizeof("65536")]; - sprintf(service, "%d", port); - struct addrinfo hints, *ai; - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; - hints.ai_socktype = SOCK_STREAM; - - int ret = getaddrinfo(server, service, &hints, &ai); - if (ret) { - return ERROR_UNKNOWN_HOST; - } - - CHECK_EQ(mSocket, -1); - - mState = CONNECTING; - status_t res = -1; - struct addrinfo *tmp; - for (tmp = ai; tmp; tmp = tmp->ai_next) { - mSocket = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol); - if (mSocket < 0) { - continue; - } - - if (mUIDValid) { - RegisterSocketUser(mSocket, mUID); - } - - setReceiveTimeout(30); // Time out reads after 30 secs by default. - - int s = mSocket; - - mLock.unlock(); - - res = MyConnect(s, tmp->ai_addr, tmp->ai_addrlen); - - mLock.lock(); - - if (mState != CONNECTING) { - close(s); - freeaddrinfo(ai); - return UNKNOWN_ERROR; - } - - if (res == OK) { - break; - } - - close(s); - } - - freeaddrinfo(ai); - - if (res != OK) { - close(mSocket); - mSocket = -1; - - mState = READY; - return res; - } - - if (https) { - CHECK(mSSL == NULL); - - if (mSSLContext == NULL) { - SSL_library_init(); - - mSSLContext = SSL_CTX_new(TLSv1_client_method()); - - if (mSSLContext == NULL) { - LOGE("failed to create SSL context"); - mState = READY; - return ERROR_IO; - } - } - - mSSL = SSL_new((SSL_CTX *)mSSLContext); - - if (mSSL == NULL) { - LOGE("failed to create SSL session"); - - mState = READY; - return ERROR_IO; - } - - int res = SSL_set_fd((SSL *)mSSL, mSocket); - - if (res == 1) { - res = SSL_connect((SSL *)mSSL); - } - - if (res != 1) { - SSL_free((SSL *)mSSL); - mSSL = NULL; - - LOGE("failed to connect over SSL"); - mState = READY; - - return ERROR_IO; - } - } - - mState = CONNECTED; - - return OK; -} - -status_t HTTPStream::disconnect() { - Mutex::Autolock autoLock(mLock); - - if (mState != CONNECTED && mState != CONNECTING) { - return ERROR_NOT_CONNECTED; - } - - if (mSSL != NULL) { - SSL_shutdown((SSL *)mSSL); - - SSL_free((SSL *)mSSL); - mSSL = NULL; - } - - CHECK(mSocket >= 0); - close(mSocket); - mSocket = -1; - - mState = READY; - - return OK; -} - -status_t HTTPStream::send(const char *data, size_t size) { - if (mState != CONNECTED) { - return ERROR_NOT_CONNECTED; - } - - while (size > 0) { - ssize_t n; - if (mSSL != NULL) { - n = SSL_write((SSL *)mSSL, data, size); - - if (n < 0) { - n = -SSL_get_error((SSL *)mSSL, n); - } - } else { - n = MySend(mSocket, data, size, 0); - } - - if (n < 0) { - disconnect(); - - return n; - } else if (n == 0) { - disconnect(); - - return ERROR_CONNECTION_LOST; - } - - size -= (size_t)n; - data += (size_t)n; - } - - return OK; -} - -status_t HTTPStream::send(const char *data) { - return send(data, strlen(data)); -} - -// A certain application spawns a local webserver that sends invalid responses, -// specifically it terminates header line with only a newline instead of the -// CRLF (carriage-return followed by newline) required by the HTTP specs. -// The workaround accepts both behaviours but could potentially break -// legitimate responses that use a single newline to "fold" headers, which is -// why it's not yet on by default. -#define WORKAROUND_FOR_MISSING_CR 1 - -status_t HTTPStream::receive_line(char *line, size_t size) { - if (mState != CONNECTED) { - return ERROR_NOT_CONNECTED; - } - - bool saw_CR = false; - size_t length = 0; - - for (;;) { - char c; - ssize_t n; - if (mSSL != NULL) { - n = SSL_read((SSL *)mSSL, &c, 1); - - if (n < 0) { - n = -SSL_get_error((SSL *)mSSL, n); - } - } else { - n = MyReceive(mSocket, &c, 1, 0); - } - - if (n < 0) { - disconnect(); - - return ERROR_IO; - } else if (n == 0) { - disconnect(); - - return ERROR_CONNECTION_LOST; - } - -#if WORKAROUND_FOR_MISSING_CR - if (c == '\n') { - // We have a complete line. - - line[saw_CR ? length - 1 : length] = '\0'; - return OK; - } -#else - if (saw_CR && c == '\n') { - // We have a complete line. - - line[length - 1] = '\0'; - return OK; - } -#endif - - saw_CR = (c == '\r'); - - if (length + 1 >= size) { - return ERROR_MALFORMED; - } - line[length++] = c; - } -} - -status_t HTTPStream::receive_header(int *http_status) { - *http_status = -1; - mHeaders.clear(); - - char line[2048]; - status_t err = receive_line(line, sizeof(line)); - if (err != OK) { - return err; - } - - mHeaders.add(AString(kStatusKey), AString(line)); - - char *spacePos = strchr(line, ' '); - if (spacePos == NULL) { - // Malformed response? - return UNKNOWN_ERROR; - } - - char *status_start = spacePos + 1; - char *status_end = status_start; - while (isdigit(*status_end)) { - ++status_end; - } - - if (status_end == status_start) { - // Malformed response, status missing? - return UNKNOWN_ERROR; - } - - memmove(line, status_start, status_end - status_start); - line[status_end - status_start] = '\0'; - - long tmp = strtol(line, NULL, 10); - if (tmp < 0 || tmp > 999) { - return UNKNOWN_ERROR; - } - - *http_status = (int)tmp; - - for (;;) { - err = receive_line(line, sizeof(line)); - if (err != OK) { - return err; - } - - if (*line == '\0') { - // Empty line signals the end of the header. - break; - } - - // puts(line); - - char *colonPos = strchr(line, ':'); - if (colonPos == NULL) { - AString key = line; - key.tolower(); - - mHeaders.add(key, AString()); - } else { - char *end_of_key = colonPos; - while (end_of_key > line && isspace(end_of_key[-1])) { - --end_of_key; - } - - char *start_of_value = colonPos + 1; - while (isspace(*start_of_value)) { - ++start_of_value; - } - - *end_of_key = '\0'; - - AString key = line; - key.tolower(); - - mHeaders.add(key, AString(start_of_value)); - } - } - - return OK; -} - -ssize_t HTTPStream::receive(void *data, size_t size) { - size_t total = 0; - while (total < size) { - ssize_t n; - if (mSSL != NULL) { - n = SSL_read((SSL *)mSSL, (char *)data + total, size - total); - - if (n < 0) { - n = -SSL_get_error((SSL *)mSSL, n); - } - } else { - n = MyReceive(mSocket, (char *)data + total, size - total, 0); - } - - if (n < 0) { - LOGE("recv failed, errno = %d (%s)", (int)n, strerror(-n)); - - disconnect(); - return (ssize_t)ERROR_IO; - } else if (n == 0) { - disconnect(); - - LOGE("recv failed, server is gone, total received: %d bytes", - total); - - return total == 0 ? (ssize_t)ERROR_CONNECTION_LOST : total; - } - - total += (size_t)n; - } - - return (ssize_t)total; -} - -bool HTTPStream::find_header_value(const AString &key, AString *value) const { - AString key_lower = key; - key_lower.tolower(); - - ssize_t index = mHeaders.indexOfKey(key_lower); - if (index < 0) { - value->clear(); - return false; - } - - *value = mHeaders.valueAt(index); - - return true; -} - -void HTTPStream::setReceiveTimeout(int seconds) { - if (seconds < 0) { - // Disable the timeout. - seconds = 0; - } - - struct timeval tv; - tv.tv_usec = 0; - tv.tv_sec = seconds; - CHECK_EQ(0, setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))); -} - -// static -void HTTPStream::RegisterSocketUser(int s, uid_t uid) { - // Lower bits MUST be 0. - static const uint64_t kTag = 0xdeadbeef00000000ll; - - AString line = StringPrintf("t %d %llu %d", s, kTag, uid); - - int fd = open("/proc/net/xt_qtaguid/ctrl", O_WRONLY); - write(fd, line.c_str(), line.size()); - close(fd); - fd = -1; -} - -} // namespace android - diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index f075699..8c9ff87 100644..100755 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -246,6 +246,7 @@ MPEG4Writer::MPEG4Writer(const char *filename) mIsFileSizeLimitExplicitlyRequested(false), mPaused(false), mStarted(false), + mWriterThreadStarted(false), mOffset(0), mMdatOffset(0), mEstimatedMoovBoxSize(0), @@ -269,6 +270,7 @@ MPEG4Writer::MPEG4Writer(int fd) mIsFileSizeLimitExplicitlyRequested(false), mPaused(false), mStarted(false), + mWriterThreadStarted(false), mOffset(0), mMdatOffset(0), mEstimatedMoovBoxSize(0), @@ -538,6 +540,9 @@ status_t MPEG4Writer::pause() { void MPEG4Writer::stopWriterThread() { LOGD("Stopping writer thread"); + if (!mWriterThreadStarted) { + return; + } { Mutex::Autolock autolock(mLock); @@ -548,6 +553,7 @@ void MPEG4Writer::stopWriterThread() { void *dummy; pthread_join(mThread, &dummy); + mWriterThreadStarted = false; LOGD("Writer thread stopped"); } @@ -603,10 +609,25 @@ void MPEG4Writer::writeCompositionMatrix(int degrees) { writeInt32(0x40000000); // w } +void MPEG4Writer::release() { + close(mFd); + mFd = -1; + mInitCheck = NO_INIT; + mStarted = false; +} status_t MPEG4Writer::stop() { if (mInitCheck != OK) { return OK; + } else { + if (!mWriterThreadStarted || + !mStarted) { + if (mWriterThreadStarted) { + stopWriterThread(); + } + release(); + return OK; + } } status_t err = OK; @@ -637,10 +658,7 @@ status_t MPEG4Writer::stop() { // Do not write out movie header on error. if (err != OK) { - close(mFd); - mFd = -1; - mInitCheck = NO_INIT; - mStarted = false; + release(); return err; } @@ -688,11 +706,7 @@ status_t MPEG4Writer::stop() { CHECK(mBoxes.empty()); - close(mFd); - mFd = -1; - mInitCheck = NO_INIT; - mStarted = false; - + release(); return err; } @@ -1415,6 +1429,7 @@ status_t MPEG4Writer::startWriterThread() { pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&mThread, &attr, ThreadWrapper, this); pthread_attr_destroy(&attr); + mWriterThreadStarted = true; return OK; } @@ -1700,7 +1715,10 @@ status_t MPEG4Writer::Track::parseAVCCodecSpecificData( return ERROR_MALFORMED; } } - +// FIXME: +// Add chromat_format_idc, bit depth values, etc for AVC/h264 high profile and above +// and remove #if 0 +#if 0 { // Check on the profiles // These profiles requires additional parameter set extensions @@ -1710,7 +1728,7 @@ status_t MPEG4Writer::Track::parseAVCCodecSpecificData( return BAD_VALUE; } } - +#endif return OK; } diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp deleted file mode 100644 index 2949767..0000000 --- a/media/libstagefright/NuHTTPDataSource.cpp +++ /dev/null @@ -1,560 +0,0 @@ -/* - * 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_NDEBUG 0 -#define LOG_TAG "NuHTTPDataSource" -#include <utils/Log.h> - -#include "include/NuHTTPDataSource.h" - -#include <cutils/properties.h> -#include <media/stagefright/foundation/ALooper.h> -#include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaErrors.h> - -namespace android { - -static bool ParseSingleUnsignedLong( - const char *from, unsigned long *x) { - char *end; - *x = strtoul(from, &end, 10); - - if (end == from || *end != '\0') { - return false; - } - - return true; -} - -static bool ParseURL( - const char *url, String8 *host, unsigned *port, - String8 *path, bool *https) { - host->setTo(""); - *port = 0; - path->setTo(""); - - size_t hostStart; - if (!strncasecmp("http://", url, 7)) { - hostStart = 7; - *https = false; - } else if (!strncasecmp("https://", url, 8)) { - hostStart = 8; - *https = true; - } else { - return false; - } - - const char *slashPos = strchr(&url[hostStart], '/'); - - if (slashPos == NULL) { - host->setTo(&url[hostStart]); - path->setTo("/"); - } else { - host->setTo(&url[hostStart], slashPos - &url[hostStart]); - path->setTo(slashPos); - } - - const char *colonPos = strchr(host->string(), ':'); - - if (colonPos != NULL) { - unsigned long x; - if (!ParseSingleUnsignedLong(colonPos + 1, &x) || x >= 65536) { - return false; - } - - *port = x; - - size_t colonOffset = colonPos - host->string(); - String8 tmp(host->string(), colonOffset); - *host = tmp; - } else { - *port = (*https) ? 443 : 80; - } - - return true; -} - -NuHTTPDataSource::NuHTTPDataSource(uint32_t flags) - : mFlags(flags), - mState(DISCONNECTED), - mPort(0), - mHTTPS(false), - mOffset(0), - mContentLength(0), - mContentLengthValid(false), - mHasChunkedTransferEncoding(false), - mChunkDataBytesLeft(0), - mDecryptHandle(NULL), - mDrmManagerClient(NULL) { -} - -NuHTTPDataSource::~NuHTTPDataSource() { - if (mDecryptHandle != NULL) { - // To release mDecryptHandle - CHECK(mDrmManagerClient); - mDrmManagerClient->closeDecryptSession(mDecryptHandle); - mDecryptHandle = NULL; - } - - if (mDrmManagerClient != NULL) { - delete mDrmManagerClient; - mDrmManagerClient = NULL; - } -} - -status_t NuHTTPDataSource::connect( - const char *uri, - const KeyedVector<String8, String8> *overrides, - off64_t offset) { - String8 headers; - MakeFullHeaders(overrides, &headers); - - return connect(uri, headers, offset); -} - -status_t NuHTTPDataSource::connect( - const char *uri, - const String8 &headers, - off64_t offset) { - String8 host, path; - unsigned port; - - mUri = uri; - mContentType = String8("application/octet-stream"); - - bool https; - if (!ParseURL(uri, &host, &port, &path, &https)) { - return ERROR_MALFORMED; - } - - uid_t uid; - if (getUID(&uid)) { - mHTTP.setUID(uid); - } - - return connect(host, port, path, https, headers, offset); -} - -static bool IsRedirectStatusCode(int httpStatus) { - return httpStatus == 301 || httpStatus == 302 - || httpStatus == 303 || httpStatus == 307; -} - -status_t NuHTTPDataSource::connect( - const char *host, unsigned port, const char *path, - bool https, - const String8 &headers, - off64_t offset) { - if (!(mFlags & kFlagIncognito)) { - LOGI("connect to %s:%u%s @%lld", host, port, path, offset); - } else { - LOGI("connect to <URL suppressed> @%lld", offset); - } - - bool needsToReconnect = true; - - if (mState == CONNECTED && host == mHost && port == mPort - && https == mHTTPS && offset == mOffset) { - if (mContentLengthValid && mOffset == mContentLength) { - LOGI("Didn't have to reconnect, old one's still good."); - needsToReconnect = false; - } - } - - mHost = host; - mPort = port; - mPath = path; - mHTTPS = https; - mHeaders = headers; - - status_t err = OK; - - mState = CONNECTING; - - if (needsToReconnect) { - mHTTP.disconnect(); - err = mHTTP.connect(host, port, https); - } - - if (err != OK) { - mState = DISCONNECTED; - } else if (mState != CONNECTING) { - err = UNKNOWN_ERROR; - } else { - mState = CONNECTED; - - mOffset = offset; - mContentLength = 0; - mContentLengthValid = false; - - String8 request("GET "); - request.append(mPath); - request.append(" HTTP/1.1\r\n"); - request.append("Host: "); - request.append(mHost); - if (mPort != 80) { - request.append(StringPrintf(":%u", mPort).c_str()); - } - request.append("\r\n"); - - if (offset != 0) { - char rangeHeader[128]; - sprintf(rangeHeader, "Range: bytes=%lld-\r\n", offset); - request.append(rangeHeader); - } - - request.append(mHeaders); - request.append("\r\n"); - - int httpStatus; - if ((err = mHTTP.send(request.string(), request.size())) != OK - || (err = mHTTP.receive_header(&httpStatus)) != OK) { - mHTTP.disconnect(); - mState = DISCONNECTED; - return err; - } - - if (IsRedirectStatusCode(httpStatus)) { - AString value; - CHECK(mHTTP.find_header_value("Location", &value)); - - mState = DISCONNECTED; - - mHTTP.disconnect(); - - return connect(value.c_str(), headers, offset); - } - - if (httpStatus < 200 || httpStatus >= 300) { - mState = DISCONNECTED; - mHTTP.disconnect(); - - return ERROR_IO; - } - - mHasChunkedTransferEncoding = false; - - { - AString value; - if (mHTTP.find_header_value("Transfer-Encoding", &value)) { - // We don't currently support any transfer encodings but - // chunked. - - if (!strcasecmp(value.c_str(), "chunked")) { - LOGI("Chunked transfer encoding applied."); - mHasChunkedTransferEncoding = true; - mChunkDataBytesLeft = 0; - } else { - mState = DISCONNECTED; - mHTTP.disconnect(); - - LOGE("We don't support '%s' transfer encoding.", value.c_str()); - - return ERROR_UNSUPPORTED; - } - } - } - - { - AString value; - if (mHTTP.find_header_value("Content-Type", &value)) { - mContentType = String8(value.c_str()); - } else { - mContentType = String8("application/octet-stream"); - } - } - - applyTimeoutResponse(); - - if (offset == 0) { - AString value; - unsigned long x; - if (mHTTP.find_header_value(AString("Content-Length"), &value) - && ParseSingleUnsignedLong(value.c_str(), &x)) { - mContentLength = (off64_t)x; - mContentLengthValid = true; - } else { - LOGW("Server did not give us the content length!"); - } - } else { - if (httpStatus != 206 /* Partial Content */) { - // We requested a range but the server didn't support that. - LOGE("We requested a range but the server didn't " - "support that."); - return ERROR_UNSUPPORTED; - } - - AString value; - unsigned long x; - if (mHTTP.find_header_value(AString("Content-Range"), &value)) { - const char *slashPos = strchr(value.c_str(), '/'); - if (slashPos != NULL - && ParseSingleUnsignedLong(slashPos + 1, &x)) { - mContentLength = x; - mContentLengthValid = true; - } - } - } - } - - return err; -} - -void NuHTTPDataSource::disconnect() { - if (mState == CONNECTING || mState == CONNECTED) { - mHTTP.disconnect(); - } - mState = DISCONNECTED; -} - -status_t NuHTTPDataSource::initCheck() const { - return mState == CONNECTED ? OK : NO_INIT; -} - -ssize_t NuHTTPDataSource::internalRead(void *data, size_t size) { - if (!mHasChunkedTransferEncoding) { - return mHTTP.receive(data, size); - } - - if (mChunkDataBytesLeft < 0) { - return 0; - } else if (mChunkDataBytesLeft == 0) { - char line[1024]; - status_t err = mHTTP.receive_line(line, sizeof(line)); - - if (err != OK) { - return err; - } - - LOGV("line = '%s'", line); - - char *end; - unsigned long n = strtoul(line, &end, 16); - - if (end == line || (*end != ';' && *end != '\0')) { - LOGE("malformed HTTP chunk '%s'", line); - return ERROR_MALFORMED; - } - - mChunkDataBytesLeft = n; - LOGV("chunk data size = %lu", n); - - if (mChunkDataBytesLeft == 0) { - mChunkDataBytesLeft = -1; - return 0; - } - - // fall through - } - - if (size > (size_t)mChunkDataBytesLeft) { - size = mChunkDataBytesLeft; - } - - ssize_t n = mHTTP.receive(data, size); - - if (n < 0) { - return n; - } - - mChunkDataBytesLeft -= (size_t)n; - - if (mChunkDataBytesLeft == 0) { - char line[1024]; - status_t err = mHTTP.receive_line(line, sizeof(line)); - - if (err != OK) { - return err; - } - - if (line[0] != '\0') { - LOGE("missing HTTP chunk terminator."); - return ERROR_MALFORMED; - } - } - - return n; -} - -ssize_t NuHTTPDataSource::readAt(off64_t offset, void *data, size_t size) { - LOGV("readAt offset %ld, size %d", offset, size); - - Mutex::Autolock autoLock(mLock); - - if (offset != mOffset) { - String8 host = mHost; - String8 path = mPath; - String8 headers = mHeaders; - status_t err = connect(host, mPort, path, mHTTPS, headers, offset); - - if (err != OK) { - return err; - } - } - - if (mContentLengthValid) { - size_t avail = - (offset >= mContentLength) ? 0 : mContentLength - offset; - - if (size > avail) { - size = avail; - } - } - - size_t numBytesRead = 0; - while (numBytesRead < size) { - int64_t startTimeUs = ALooper::GetNowUs(); - - ssize_t n = - internalRead((uint8_t *)data + numBytesRead, size - numBytesRead); - - if (n < 0) { - if (numBytesRead == 0 || mContentLengthValid) { - return n; - } - - // If there was an error we want to at least return the data - // we've already successfully read. The next call to read will - // then return the error. - n = 0; - } - - int64_t delayUs = ALooper::GetNowUs() - startTimeUs; - addBandwidthMeasurement(n, delayUs); - - numBytesRead += (size_t)n; - - if (n == 0) { - if (mContentLengthValid) { - // We know the content length and made sure not to read beyond - // it and yet the server closed the connection on us. - return ERROR_IO; - } - - break; - } - } - - mOffset += numBytesRead; - - return numBytesRead; -} - -status_t NuHTTPDataSource::getSize(off64_t *size) { - *size = 0; - - if (mState != CONNECTED) { - return ERROR_IO; - } - - if (mContentLengthValid) { - *size = mContentLength; - return OK; - } - - return ERROR_UNSUPPORTED; -} - -uint32_t NuHTTPDataSource::flags() { - return kWantsPrefetching | kIsHTTPBasedSource; -} - -// static -void NuHTTPDataSource::MakeFullHeaders( - const KeyedVector<String8, String8> *overrides, String8 *headers) { - headers->setTo(""); - - headers->append("User-Agent: stagefright/1.1 (Linux;Android "); - -#if (PROPERTY_VALUE_MAX < 8) -#error "PROPERTY_VALUE_MAX must be at least 8" -#endif - - char value[PROPERTY_VALUE_MAX]; - property_get("ro.build.version.release", value, "Unknown"); - headers->append(value); - headers->append(")\r\n"); - - if (overrides == NULL) { - return; - } - - for (size_t i = 0; i < overrides->size(); ++i) { - String8 line; - line.append(overrides->keyAt(i)); - line.append(": "); - line.append(overrides->valueAt(i)); - line.append("\r\n"); - - headers->append(line); - } -} - -void NuHTTPDataSource::applyTimeoutResponse() { - AString timeout; - if (mHTTP.find_header_value("X-SocketTimeout", &timeout)) { - const char *s = timeout.c_str(); - char *end; - long tmp = strtol(s, &end, 10); - if (end == s || *end != '\0') { - LOGW("Illegal X-SocketTimeout value given."); - return; - } - - LOGI("overriding default timeout, new timeout is %ld seconds", tmp); - mHTTP.setReceiveTimeout(tmp); - } -} - -sp<DecryptHandle> NuHTTPDataSource::DrmInitialization() { - if (mDrmManagerClient == NULL) { - mDrmManagerClient = new DrmManagerClient(); - } - - if (mDrmManagerClient == NULL) { - return NULL; - } - - if (mDecryptHandle == NULL) { - /* Note if redirect occurs, mUri is the redirect uri instead of the - * original one - */ - mDecryptHandle = mDrmManagerClient->openDecryptSession(mUri); - } - - if (mDecryptHandle == NULL) { - delete mDrmManagerClient; - mDrmManagerClient = NULL; - } - - return mDecryptHandle; -} - -void NuHTTPDataSource::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) { - handle = mDecryptHandle; - - *client = mDrmManagerClient; -} - -String8 NuHTTPDataSource::getUri() { - return mUri; -} - -String8 NuHTTPDataSource::getMIMEType() const { - return mContentType; -} - -} // namespace android diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 1ac2c1f..b7b0dc0 100644..100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -18,20 +18,11 @@ #define LOG_TAG "OMXCodec" #include <utils/Log.h> -#include "include/AACDecoder.h" #include "include/AACEncoder.h" -#include "include/AMRNBDecoder.h" #include "include/AMRNBEncoder.h" -#include "include/AMRWBDecoder.h" #include "include/AMRWBEncoder.h" -#include "include/AVCDecoder.h" #include "include/AVCEncoder.h" -#include "include/G711Decoder.h" -#include "include/M4vH263Decoder.h" #include "include/M4vH263Encoder.h" -#include "include/MP3Decoder.h" -#include "include/VorbisDecoder.h" -#include "include/VPXDecoder.h" #include "include/ESDS.h" @@ -53,10 +44,6 @@ #include <OMX_Audio.h> #include <OMX_Component.h> -#if HAVE_SOFTWARE_DECODERS -#include "include/ThreadedSource.h" -#endif - #include "include/avc_utils.h" namespace android { @@ -79,24 +66,6 @@ FACTORY_CREATE_ENCODER(AACEncoder) FACTORY_CREATE_ENCODER(AVCEncoder) FACTORY_CREATE_ENCODER(M4vH263Encoder) -#if HAVE_SOFTWARE_DECODERS - -#define FACTORY_CREATE(name) \ -static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \ - return new name(source); \ -} - -FACTORY_CREATE(AMRNBDecoder) -FACTORY_CREATE(AMRWBDecoder) -FACTORY_CREATE(AACDecoder) -FACTORY_CREATE(AVCDecoder) -FACTORY_CREATE(G711Decoder) -FACTORY_CREATE(MP3Decoder) -FACTORY_CREATE(M4vH263Decoder) -FACTORY_CREATE(VorbisDecoder) -FACTORY_CREATE(VPXDecoder) -#endif - static sp<MediaSource> InstantiateSoftwareEncoder( const char *name, const sp<MediaSource> &source, const sp<MetaData> &meta) { @@ -122,40 +91,6 @@ static sp<MediaSource> InstantiateSoftwareEncoder( return NULL; } -static sp<MediaSource> InstantiateSoftwareCodec( - const char *name, const sp<MediaSource> &source) { -#if HAVE_SOFTWARE_DECODERS - struct FactoryInfo { - const char *name; - sp<MediaSource> (*CreateFunc)(const sp<MediaSource> &); - }; - - static const FactoryInfo kFactoryInfo[] = { - FACTORY_REF(AMRNBDecoder) - FACTORY_REF(AMRWBDecoder) - FACTORY_REF(AACDecoder) - FACTORY_REF(AVCDecoder) - FACTORY_REF(G711Decoder) - FACTORY_REF(MP3Decoder) - FACTORY_REF(M4vH263Decoder) - FACTORY_REF(VorbisDecoder) - FACTORY_REF(VPXDecoder) - }; - for (size_t i = 0; - i < sizeof(kFactoryInfo) / sizeof(kFactoryInfo[0]); ++i) { - if (!strcmp(name, kFactoryInfo[i].name)) { - if (!strcmp(name, "VPXDecoder")) { - return new ThreadedSource( - (*kFactoryInfo[i].CreateFunc)(source)); - } - return (*kFactoryInfo[i].CreateFunc)(source); - } - } -#endif - - return NULL; -} - #undef FACTORY_REF #undef FACTORY_CREATE @@ -163,23 +98,17 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" }, // { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" }, { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.google.mp3.decoder" }, - { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" }, // { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" }, // { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amr.decoder" }, { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.decoder" }, - { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" }, // { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amrwb.decoder" }, { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" }, { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.decoder" }, - { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" }, // { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.Nvidia.aac.decoder" }, { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" }, { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.decoder" }, - { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" }, { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "OMX.google.g711.alaw.decoder" }, - { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" }, { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "OMX.google.g711.mlaw.decoder" }, - { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.DECODER" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.decode" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" }, @@ -187,14 +116,12 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.google.mpeg4.decoder" }, - { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.DUCATI1.VIDEO.DECODER" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.decode" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Decoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.google.h263.decoder" }, - { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.DUCATI1.VIDEO.DECODER" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.decode" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" }, @@ -203,11 +130,8 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.google.h264.decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.google.avc.decoder" }, - { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" }, { MEDIA_MIMETYPE_AUDIO_VORBIS, "OMX.google.vorbis.decoder" }, - { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" }, { MEDIA_MIMETYPE_VIDEO_VPX, "OMX.google.vpx.decoder" }, - { MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG2, "OMX.Nvidia.mpeg2v.decode" }, }; @@ -218,18 +142,21 @@ static const CodecInfo kEncoderInfo[] = { { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBEncoder" }, { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" }, { MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" }, + { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.MPEG4E" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.encoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.encoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Encoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Encoder" }, + { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.DUCATI1.VIDEO.MPEG4E" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.encoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.encoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Encoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Encoder" }, + { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.DUCATI1.VIDEO.H264E" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.encoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" }, @@ -393,7 +320,17 @@ uint32_t OMXCodec::getComponentQuirks( if (!strcmp(componentName, "OMX.TI.DUCATI1.VIDEO.DECODER")) { quirks |= kRequiresAllocateBufferOnInputPorts; quirks |= kRequiresAllocateBufferOnOutputPorts; - } else if (!strncmp(componentName, "OMX.TI.", 7)) { + } + + // FIXME: + // Remove the quirks after the work is done. + else if (!strcmp(componentName, "OMX.TI.DUCATI1.VIDEO.MPEG4E") || + !strcmp(componentName, "OMX.TI.DUCATI1.VIDEO.H264E")) { + + quirks |= kRequiresAllocateBufferOnInputPorts; + quirks |= kRequiresAllocateBufferOnOutputPorts; + } + else if (!strncmp(componentName, "OMX.TI.", 7)) { // Apparently I must not use OMX_UseBuffer on either input or // output ports on any of the TI components or quote: // "(I) may have unexpected problem (sic) which can be timing related @@ -505,14 +442,15 @@ sp<MediaSource> OMXCodec::Create( for (size_t i = 0; i < matchingCodecs.size(); ++i) { componentName = matchingCodecs[i].string(); - sp<MediaSource> softwareCodec = createEncoder? - InstantiateSoftwareEncoder(componentName, source, meta): - InstantiateSoftwareCodec(componentName, source); + if (createEncoder) { + sp<MediaSource> softwareCodec = + InstantiateSoftwareEncoder(componentName, source, meta); - if (softwareCodec != NULL) { - LOGV("Successfully allocated software codec '%s'", componentName); + if (softwareCodec != NULL) { + LOGV("Successfully allocated software codec '%s'", componentName); - return softwareCodec; + return softwareCodec; + } } LOGV("Attempting to allocate OMX node '%s'", componentName); @@ -887,6 +825,7 @@ static size_t getFrameSize( case OMX_COLOR_FormatYUV420Planar: case OMX_COLOR_FormatYUV420SemiPlanar: + case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: return (width * height * 3) / 2; default: @@ -910,6 +849,7 @@ status_t OMXCodec::findTargetColorFormat( } } + // Check whether the target color format is supported. return isColorFormatSupported(*colorFormat, kPortIndexInput); } @@ -1032,6 +972,11 @@ void OMXCodec::setVideoInputFormat( video_def->nFrameWidth = width; video_def->nFrameHeight = height; video_def->xFramerate = 0; // No need for output port + // FIXME: + // Revmoe this workaround after work is done. + if (!strncmp(mComponentName, "OMX.TI.DUCATI1", 14)) { + video_def->xFramerate = (frameRate << 16); + } video_def->nBitrate = bitRate; // Q16 format video_def->eCompressionFormat = compressionFormat; video_def->eColorFormat = OMX_COLOR_FormatUnused; @@ -1295,13 +1240,6 @@ status_t OMXCodec::setupAVCEncoderParameters(const sp<MetaData>& meta) { h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; - h264type.nSliceHeaderSpacing = 0; - h264type.nBFrames = 0; // No B frames support yet - h264type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate); - if (h264type.nPFrames == 0) { - h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; - } - // Check profile and level parameters CodecProfileLevel defaultProfileLevel, profileLevel; defaultProfileLevel.mProfile = h264type.eProfile; @@ -1311,9 +1249,21 @@ status_t OMXCodec::setupAVCEncoderParameters(const sp<MetaData>& meta) { h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profileLevel.mProfile); h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(profileLevel.mLevel); + // FIXME: + // Remove the workaround after the work in done. + if (!strncmp(mComponentName, "OMX.TI.DUCATI1", 14)) { + h264type.eProfile = OMX_VIDEO_AVCProfileBaseline; + } + if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) { + h264type.nSliceHeaderSpacing = 0; h264type.bUseHadamard = OMX_TRUE; h264type.nRefFrames = 1; + h264type.nBFrames = 0; + h264type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate); + if (h264type.nPFrames == 0) { + h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; + } h264type.nRefIdx10ActiveMinus1 = 0; h264type.nRefIdx11ActiveMinus1 = 0; h264type.bEntropyCodingCABAC = OMX_FALSE; @@ -4405,12 +4355,9 @@ status_t QueryCodecs( if (strncmp(componentName, "OMX.", 4)) { // Not an OpenMax component but a software codec. -#if HAVE_SOFTWARE_DECODERS results->push(); CodecCapabilities *caps = &results->editItemAt(results->size() - 1); caps->mComponentName = componentName; -#endif - continue; } diff --git a/media/libstagefright/ShoutcastSource.cpp b/media/libstagefright/ShoutcastSource.cpp deleted file mode 100644 index 783f2d0..0000000 --- a/media/libstagefright/ShoutcastSource.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2009 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 "include/HTTPStream.h" - -#include <stdlib.h> - -#include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MetaData.h> -#include <media/stagefright/ShoutcastSource.h> - -namespace android { - -ShoutcastSource::ShoutcastSource(HTTPStream *http) - : mHttp(http), - mMetaDataOffset(0), - mBytesUntilMetaData(0), - mGroup(NULL), - mStarted(false) { - AString metaint; - if (mHttp->find_header_value("icy-metaint", &metaint)) { - char *end; - const char *start = metaint.c_str(); - mMetaDataOffset = strtol(start, &end, 10); - CHECK(end > start && *end == '\0'); - CHECK(mMetaDataOffset > 0); - - mBytesUntilMetaData = mMetaDataOffset; - } -} - -ShoutcastSource::~ShoutcastSource() { - if (mStarted) { - stop(); - } - - delete mHttp; - mHttp = NULL; -} - -status_t ShoutcastSource::start(MetaData *) { - CHECK(!mStarted); - - mGroup = new MediaBufferGroup; - mGroup->add_buffer(new MediaBuffer(4096)); // XXX - - mStarted = true; - - return OK; -} - -status_t ShoutcastSource::stop() { - CHECK(mStarted); - - delete mGroup; - mGroup = NULL; - - mStarted = false; - - return OK; -} - -sp<MetaData> ShoutcastSource::getFormat() { - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); - meta->setInt32(kKeySampleRate, 44100); - meta->setInt32(kKeyChannelCount, 2); // XXX - - return meta; -} - -status_t ShoutcastSource::read( - MediaBuffer **out, const ReadOptions *options) { - CHECK(mStarted); - - *out = NULL; - - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - return ERROR_UNSUPPORTED; - } - - MediaBuffer *buffer; - status_t err = mGroup->acquire_buffer(&buffer); - if (err != OK) { - return err; - } - - *out = buffer; - - size_t num_bytes = buffer->size(); - if (mMetaDataOffset > 0 && num_bytes > mBytesUntilMetaData) { - num_bytes = mBytesUntilMetaData; - } - - ssize_t n = mHttp->receive(buffer->data(), num_bytes); - - if (n <= 0) { - return (status_t)n; - } - - buffer->set_range(0, n); - - mBytesUntilMetaData -= (size_t)n; - - if (mBytesUntilMetaData == 0) { - unsigned char num_16_byte_blocks = 0; - n = mHttp->receive((char *)&num_16_byte_blocks, 1); - CHECK_EQ(n, 1); - - char meta[255 * 16]; - size_t meta_size = num_16_byte_blocks * 16; - size_t meta_length = 0; - while (meta_length < meta_size) { - n = mHttp->receive(&meta[meta_length], meta_size - meta_length); - if (n <= 0) { - return (status_t)n; - } - - meta_length += (size_t) n; - } - - while (meta_length > 0 && meta[meta_length - 1] == '\0') { - --meta_length; - } - - if (meta_length > 0) { - // Technically we should probably attach this meta data to the - // next buffer. XXX - buffer->meta_data()->setData('shou', 'shou', meta, meta_length); - } - - mBytesUntilMetaData = mMetaDataOffset; - } - - return OK; -} - -} // namespace android - diff --git a/media/libstagefright/ThreadedSource.cpp b/media/libstagefright/ThreadedSource.cpp deleted file mode 100644 index 38c6e2d..0000000 --- a/media/libstagefright/ThreadedSource.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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 "ThreadedSource" -//#define LOG_NDEBUG 0 -#include <utils/Log.h> - -#include "include/ThreadedSource.h" - -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/MetaData.h> - -namespace android { - -static const size_t kMaxQueueSize = 2; - -ThreadedSource::ThreadedSource(const sp<MediaSource> &source) - : mSource(source), - mReflector(new AHandlerReflector<ThreadedSource>(this)), - mLooper(new ALooper), - mStarted(false) { - mLooper->registerHandler(mReflector); -} - -ThreadedSource::~ThreadedSource() { - if (mStarted) { - stop(); - } -} - -status_t ThreadedSource::start(MetaData *params) { - CHECK(!mStarted); - - status_t err = mSource->start(params); - - if (err != OK) { - return err; - } - - mFinalResult = OK; - mSeekTimeUs = -1; - mDecodePending = false; - - Mutex::Autolock autoLock(mLock); - postDecodeMore_l(); - - CHECK_EQ(mLooper->start(), (status_t)OK); - - mStarted = true; - - return OK; -} - -status_t ThreadedSource::stop() { - CHECK(mStarted); - - CHECK_EQ(mLooper->stop(), (status_t)OK); - - Mutex::Autolock autoLock(mLock); - clearQueue_l(); - - status_t err = mSource->stop(); - - mStarted = false; - - return err; -} - -sp<MetaData> ThreadedSource::getFormat() { - return mSource->getFormat(); -} - -status_t ThreadedSource::read( - MediaBuffer **buffer, const ReadOptions *options) { - *buffer = NULL; - - Mutex::Autolock autoLock(mLock); - - int64_t seekTimeUs; - ReadOptions::SeekMode seekMode; - if (options && options->getSeekTo(&seekTimeUs, &seekMode)) { - int32_t seekComplete = 0; - - sp<AMessage> msg = new AMessage(kWhatSeek, mReflector->id()); - msg->setInt64("timeUs", seekTimeUs); - msg->setInt32("mode", seekMode); - msg->setPointer("complete", &seekComplete); - msg->post(); - - while (!seekComplete) { - mCondition.wait(mLock); - } - } - - while (mQueue.empty() && mFinalResult == OK) { - mCondition.wait(mLock); - } - - if (!mQueue.empty()) { - *buffer = *mQueue.begin(); - mQueue.erase(mQueue.begin()); - - if (mFinalResult == OK) { - postDecodeMore_l(); - } - - return OK; - } - - return mFinalResult; -} - -void ThreadedSource::onMessageReceived(const sp<AMessage> &msg) { - switch (msg->what()) { - case kWhatSeek: - { - CHECK(msg->findInt64("timeUs", &mSeekTimeUs)); - CHECK_GE(mSeekTimeUs, 0ll); - - int32_t x; - CHECK(msg->findInt32("mode", &x)); - mSeekMode = (ReadOptions::SeekMode)x; - - int32_t *seekComplete; - CHECK(msg->findPointer("complete", (void **)&seekComplete)); - - Mutex::Autolock autoLock(mLock); - clearQueue_l(); - mFinalResult = OK; - - *seekComplete = 1; - mCondition.signal(); - - postDecodeMore_l(); - break; - } - - case kWhatDecodeMore: - { - { - Mutex::Autolock autoLock(mLock); - mDecodePending = false; - - if (mQueue.size() == kMaxQueueSize) { - break; - } - } - - MediaBuffer *buffer; - ReadOptions options; - if (mSeekTimeUs >= 0) { - options.setSeekTo(mSeekTimeUs, mSeekMode); - mSeekTimeUs = -1ll; - } - status_t err = mSource->read(&buffer, &options); - - Mutex::Autolock autoLock(mLock); - - if (err != OK) { - mFinalResult = err; - } else { - mQueue.push_back(buffer); - - if (mQueue.size() < kMaxQueueSize) { - postDecodeMore_l(); - } - } - - mCondition.signal(); - break; - } - - default: - TRESPASS(); - break; - } -} - -void ThreadedSource::postDecodeMore_l() { - if (mDecodePending) { - return; - } - - mDecodePending = true; - (new AMessage(kWhatDecodeMore, mReflector->id()))->post(); -} - -void ThreadedSource::clearQueue_l() { - while (!mQueue.empty()) { - MediaBuffer *buffer = *mQueue.begin(); - mQueue.erase(mQueue.begin()); - - buffer->release(); - buffer = NULL; - } -} - -} // namespace android diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk index 80b2478..6573e3c 100644 --- a/media/libstagefright/chromium_http/Android.mk +++ b/media/libstagefright/chromium_http/Android.mk @@ -15,10 +15,8 @@ LOCAL_C_INCLUDES:= \ LOCAL_CFLAGS += -Wno-multichar -ifneq ($(TARGET_SIMULATOR),true) LOCAL_SHARED_LIBRARIES += libstlport include external/stlport/libstlport.mk -endif LOCAL_MODULE:= libstagefright_chromium_http diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp deleted file mode 100644 index d2e3eaa..0000000 --- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2009 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 "AACDecoder.h" -#define LOG_TAG "AACDecoder" - -#include "../../include/ESDS.h" - -#include "pvmp4audiodecoder_api.h" - -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MetaData.h> - -namespace android { - -AACDecoder::AACDecoder(const sp<MediaSource> &source) - : mSource(source), - mStarted(false), - mBufferGroup(NULL), - mConfig(new tPVMP4AudioDecoderExternal), - mDecoderBuf(NULL), - mAnchorTimeUs(0), - mNumSamplesOutput(0), - mInputBuffer(NULL) { - - sp<MetaData> srcFormat = mSource->getFormat(); - - int32_t sampleRate; - CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); - - mMeta = new MetaData; - mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - - // We'll always output stereo, regardless of how many channels are - // present in the input due to decoder limitations. - mMeta->setInt32(kKeyChannelCount, 2); - mMeta->setInt32(kKeySampleRate, sampleRate); - - int64_t durationUs; - if (srcFormat->findInt64(kKeyDuration, &durationUs)) { - mMeta->setInt64(kKeyDuration, durationUs); - } - mMeta->setCString(kKeyDecoderComponent, "AACDecoder"); - - mInitCheck = initCheck(); -} - -status_t AACDecoder::initCheck() { - memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal)); - mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED; - mConfig->aacPlusEnabled = 1; - - // The software decoder doesn't properly support mono output on - // AACplus files. Always output stereo. - mConfig->desiredChannels = 2; - - UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements(); - mDecoderBuf = malloc(memRequirements); - - status_t err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf); - if (err != MP4AUDEC_SUCCESS) { - LOGE("Failed to initialize MP4 audio decoder"); - return UNKNOWN_ERROR; - } - - uint32_t type; - const void *data; - size_t size; - sp<MetaData> meta = mSource->getFormat(); - if (meta->findData(kKeyESDS, &type, &data, &size)) { - ESDS esds((const char *)data, size); - CHECK_EQ(esds.InitCheck(), (status_t)OK); - - const void *codec_specific_data; - size_t codec_specific_data_size; - esds.getCodecSpecificInfo( - &codec_specific_data, &codec_specific_data_size); - - mConfig->pInputBuffer = (UChar *)codec_specific_data; - mConfig->inputBufferCurrentLength = codec_specific_data_size; - mConfig->inputBufferMaxLength = 0; - - if (PVMP4AudioDecoderConfig(mConfig, mDecoderBuf) - != MP4AUDEC_SUCCESS) { - return ERROR_UNSUPPORTED; - } - } - return OK; -} - -AACDecoder::~AACDecoder() { - if (mStarted) { - stop(); - } - - delete mConfig; - mConfig = NULL; -} - -status_t AACDecoder::start(MetaData *params) { - CHECK(!mStarted); - - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer(new MediaBuffer(4096 * 2)); - - mSource->start(); - - mAnchorTimeUs = 0; - mNumSamplesOutput = 0; - mStarted = true; - mNumDecodedBuffers = 0; - mUpsamplingFactor = 2; - - return OK; -} - -status_t AACDecoder::stop() { - CHECK(mStarted); - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - free(mDecoderBuf); - mDecoderBuf = NULL; - - delete mBufferGroup; - mBufferGroup = NULL; - - mSource->stop(); - - mStarted = false; - - return OK; -} - -sp<MetaData> AACDecoder::getFormat() { - return mMeta; -} - -status_t AACDecoder::read( - MediaBuffer **out, const ReadOptions *options) { - status_t err; - - *out = NULL; - - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - CHECK(seekTimeUs >= 0); - - mNumSamplesOutput = 0; - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - // Make sure that the next buffer output does not still - // depend on fragments from the last one decoded. - PVMP4AudioDecoderResetBuffer(mDecoderBuf); - } else { - seekTimeUs = -1; - } - - if (mInputBuffer == NULL) { - err = mSource->read(&mInputBuffer, options); - - if (err != OK) { - return err; - } - - int64_t timeUs; - if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) { - mAnchorTimeUs = timeUs; - mNumSamplesOutput = 0; - } else { - // We must have a new timestamp after seeking. - CHECK(seekTimeUs < 0); - } - } - - MediaBuffer *buffer; - CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK); - - mConfig->pInputBuffer = - (UChar *)mInputBuffer->data() + mInputBuffer->range_offset(); - - mConfig->inputBufferCurrentLength = mInputBuffer->range_length(); - mConfig->inputBufferMaxLength = 0; - mConfig->inputBufferUsedLength = 0; - mConfig->remainderBits = 0; - - mConfig->pOutputBuffer = static_cast<Int16 *>(buffer->data()); - mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048]; - mConfig->repositionFlag = false; - - Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf); - - /* - * AAC+/eAAC+ streams can be signalled in two ways: either explicitly - * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual - * rate system and the sampling rate in the final output is actually - * doubled compared with the core AAC decoder sampling rate. - * - * Explicit signalling is done by explicitly defining SBR audio object - * type in the bitstream. Implicit signalling is done by embedding - * SBR content in AAC extension payload specific to SBR, and hence - * requires an AAC decoder to perform pre-checks on actual audio frames. - * - * Thus, we could not say for sure whether a stream is - * AAC+/eAAC+ until the first data frame is decoded. - */ - if (++mNumDecodedBuffers <= 2) { - LOGV("audio/extended audio object type: %d + %d", - mConfig->audioObjectType, mConfig->extendedAudioObjectType); - LOGV("aac+ upsampling factor: %d desired channels: %d", - mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels); - - CHECK(mNumDecodedBuffers > 0); - - if (decoderErr != MP4AUDEC_SUCCESS) { - // If decoding fails this early, the fields in mConfig may - // not be valid and we cannot recover. - - LOGE("Unable to decode aac content, decoder returned error %d", - decoderErr); - - buffer->release(); - buffer = NULL; - - mInputBuffer->release(); - mInputBuffer = NULL; - - return ERROR_UNSUPPORTED; - } - - if (mNumDecodedBuffers == 1) { - mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor; - // Check on the sampling rate to see whether it is changed. - int32_t sampleRate; - CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate)); - if (mConfig->samplingRate != sampleRate) { - mMeta->setInt32(kKeySampleRate, mConfig->samplingRate); - LOGW("Sample rate was %d Hz, but now is %d Hz", - sampleRate, mConfig->samplingRate); - buffer->release(); - mInputBuffer->release(); - mInputBuffer = NULL; - return INFO_FORMAT_CHANGED; - } - } else { // mNumDecodedBuffers == 2 - if (mConfig->extendedAudioObjectType == MP4AUDIO_AAC_LC || - mConfig->extendedAudioObjectType == MP4AUDIO_LTP) { - if (mUpsamplingFactor == 2) { - // The stream turns out to be not aacPlus mode anyway - LOGW("Disable AAC+/eAAC+ since extended audio object type is %d", - mConfig->extendedAudioObjectType); - mConfig->aacPlusEnabled = 0; - } - } else { - if (mUpsamplingFactor == 1) { - // aacPlus mode does not buy us anything, but to cause - // 1. CPU load to increase, and - // 2. a half speed of decoding - LOGW("Disable AAC+/eAAC+ since upsampling factor is 1"); - mConfig->aacPlusEnabled = 0; - } - } - } - } - - size_t numOutBytes = - mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels; - if (mUpsamplingFactor == 2) { - if (mConfig->desiredChannels == 1) { - memcpy(&mConfig->pOutputBuffer[1024], &mConfig->pOutputBuffer[2048], numOutBytes * 2); - } - numOutBytes *= 2; - } - - if (decoderErr != MP4AUDEC_SUCCESS) { - LOGW("AAC decoder returned error %d, substituting silence", decoderErr); - - memset(buffer->data(), 0, numOutBytes); - - // Discard input buffer. - mInputBuffer->release(); - mInputBuffer = NULL; - - // fall through - } - - buffer->set_range(0, numOutBytes); - - if (mInputBuffer != NULL) { - mInputBuffer->set_range( - mInputBuffer->range_offset() + mConfig->inputBufferUsedLength, - mInputBuffer->range_length() - mConfig->inputBufferUsedLength); - - if (mInputBuffer->range_length() == 0) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - } - - buffer->meta_data()->setInt64( - kKeyTime, - mAnchorTimeUs - + (mNumSamplesOutput * 1000000) / mConfig->samplingRate); - - mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor; - - *out = buffer; - - return OK; -} - -} // namespace android diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk index 359a2ec..20c7bc0 100644 --- a/media/libstagefright/codecs/aacdec/Android.mk +++ b/media/libstagefright/codecs/aacdec/Android.mk @@ -143,7 +143,6 @@ LOCAL_SRC_FILES := \ unpack_idx.cpp \ window_tables_fxp.cpp \ pvmp4setaudioconfig.cpp \ - AACDecoder.cpp \ LOCAL_CFLAGS := -DAAC_PLUS -DHQ_SBR -DPARAMETRICSTEREO -DOSCL_IMPORT_REF= -DOSCL_EXPORT_REF= -DOSCL_UNUSED_ARG= diff --git a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp deleted file mode 100644 index a11d46b..0000000 --- a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2009 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 "AMRNBDecoder" -#include <utils/Log.h> - -#include "AMRNBDecoder.h" - -#include "gsmamr_dec.h" - -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MediaErrors.h> -#include <media/stagefright/MetaData.h> - -namespace android { - -static const int32_t kNumSamplesPerFrame = 160; -static const int32_t kSampleRate = 8000; - -AMRNBDecoder::AMRNBDecoder(const sp<MediaSource> &source) - : mSource(source), - mStarted(false), - mBufferGroup(NULL), - mState(NULL), - mAnchorTimeUs(0), - mNumSamplesOutput(0), - mInputBuffer(NULL) { -} - -AMRNBDecoder::~AMRNBDecoder() { - if (mStarted) { - stop(); - } -} - -status_t AMRNBDecoder::start(MetaData *params) { - CHECK(!mStarted); - - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer( - new MediaBuffer(kNumSamplesPerFrame * sizeof(int16_t))); - - CHECK_EQ(GSMInitDecode(&mState, (Word8 *)"AMRNBDecoder"), 0); - - mSource->start(); - - mAnchorTimeUs = 0; - mNumSamplesOutput = 0; - mStarted = true; - - return OK; -} - -status_t AMRNBDecoder::stop() { - CHECK(mStarted); - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - delete mBufferGroup; - mBufferGroup = NULL; - - GSMDecodeFrameExit(&mState); - - mSource->stop(); - - mStarted = false; - - return OK; -} - -sp<MetaData> AMRNBDecoder::getFormat() { - sp<MetaData> srcFormat = mSource->getFormat(); - - int32_t numChannels; - int32_t sampleRate; - - CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels)); - CHECK_EQ(numChannels, 1); - - CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); - CHECK_EQ(sampleRate, kSampleRate); - - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - meta->setInt32(kKeyChannelCount, numChannels); - meta->setInt32(kKeySampleRate, sampleRate); - - int64_t durationUs; - if (srcFormat->findInt64(kKeyDuration, &durationUs)) { - meta->setInt64(kKeyDuration, durationUs); - } - - meta->setCString(kKeyDecoderComponent, "AMRNBDecoder"); - - return meta; -} - -status_t AMRNBDecoder::read( - MediaBuffer **out, const ReadOptions *options) { - status_t err; - - *out = NULL; - - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - CHECK(seekTimeUs >= 0); - - mNumSamplesOutput = 0; - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - } else { - seekTimeUs = -1; - } - - if (mInputBuffer == NULL) { - err = mSource->read(&mInputBuffer, options); - - if (err != OK) { - return err; - } - - int64_t timeUs; - if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) { - mAnchorTimeUs = timeUs; - mNumSamplesOutput = 0; - } else { - // We must have a new timestamp after seeking. - CHECK(seekTimeUs < 0); - } - } - - MediaBuffer *buffer; - CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK); - - const uint8_t *inputPtr = - (const uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset(); - - int32_t numBytesRead = - AMRDecode(mState, - (Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f), - (UWord8 *)&inputPtr[1], - static_cast<int16_t *>(buffer->data()), - MIME_IETF); - - if (numBytesRead == -1 ) { - LOGE("PV AMR decoder AMRDecode() call failed"); - buffer->release(); - buffer = NULL; - return ERROR_MALFORMED; - } - ++numBytesRead; // Include the frame type header byte. - - buffer->set_range(0, kNumSamplesPerFrame * sizeof(int16_t)); - - if (static_cast<size_t>(numBytesRead) > mInputBuffer->range_length()) { - // This is bad, should never have happened, but did. Abort now. - - buffer->release(); - buffer = NULL; - - return ERROR_MALFORMED; - } - - mInputBuffer->set_range( - mInputBuffer->range_offset() + numBytesRead, - mInputBuffer->range_length() - numBytesRead); - - if (mInputBuffer->range_length() == 0) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - buffer->meta_data()->setInt64( - kKeyTime, - mAnchorTimeUs - + (mNumSamplesOutput * 1000000) / kSampleRate); - - mNumSamplesOutput += kNumSamplesPerFrame; - - *out = buffer; - - return OK; -} - -} // namespace android diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk index 5862abc..23a22ef 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.mk +++ b/media/libstagefright/codecs/amrnb/dec/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - AMRNBDecoder.cpp \ src/a_refl.cpp \ src/agc.cpp \ src/amrdecode.cpp \ diff --git a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp deleted file mode 100644 index 5b111ef..0000000 --- a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2009 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 "AMRWBDecoder.h" - -#include "pvamrwbdecoder.h" - -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MetaData.h> - -namespace android { - -static const int32_t kNumSamplesPerFrame = 320; -static const int32_t kSampleRate = 16000; - -AMRWBDecoder::AMRWBDecoder(const sp<MediaSource> &source) - : mSource(source), - mStarted(false), - mBufferGroup(NULL), - mState(NULL), - mDecoderBuf(NULL), - mDecoderCookie(NULL), - mAnchorTimeUs(0), - mNumSamplesOutput(0), - mInputBuffer(NULL) { -} - -AMRWBDecoder::~AMRWBDecoder() { - if (mStarted) { - stop(); - } -} - -status_t AMRWBDecoder::start(MetaData *params) { - CHECK(!mStarted); - - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer( - new MediaBuffer(kNumSamplesPerFrame * sizeof(int16_t))); - - int32_t memReq = pvDecoder_AmrWbMemRequirements(); - mDecoderBuf = malloc(memReq); - - pvDecoder_AmrWb_Init(&mState, mDecoderBuf, &mDecoderCookie); - - mSource->start(); - - mAnchorTimeUs = 0; - mNumSamplesOutput = 0; - mStarted = true; - - return OK; -} - -status_t AMRWBDecoder::stop() { - CHECK(mStarted); - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - delete mBufferGroup; - mBufferGroup = NULL; - - free(mDecoderBuf); - mDecoderBuf = NULL; - - mSource->stop(); - - mStarted = false; - - return OK; -} - -sp<MetaData> AMRWBDecoder::getFormat() { - sp<MetaData> srcFormat = mSource->getFormat(); - - int32_t numChannels; - int32_t sampleRate; - - CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels)); - CHECK_EQ(numChannels, 1); - - CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); - CHECK_EQ(sampleRate, kSampleRate); - - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - meta->setInt32(kKeyChannelCount, numChannels); - meta->setInt32(kKeySampleRate, sampleRate); - - int64_t durationUs; - if (srcFormat->findInt64(kKeyDuration, &durationUs)) { - meta->setInt64(kKeyDuration, durationUs); - } - - meta->setCString(kKeyDecoderComponent, "AMRWBDecoder"); - - return meta; -} - -static size_t getFrameSize(unsigned FT) { - static const size_t kFrameSizeWB[9] = { - 132, 177, 253, 285, 317, 365, 397, 461, 477 - }; - - size_t frameSize = kFrameSizeWB[FT]; - - // Round up bits to bytes and add 1 for the header byte. - frameSize = (frameSize + 7) / 8 + 1; - - return frameSize; -} - -status_t AMRWBDecoder::read( - MediaBuffer **out, const ReadOptions *options) { - status_t err; - - *out = NULL; - - int64_t seekTimeUs; - ReadOptions::SeekMode seekMode; - if (options && options->getSeekTo(&seekTimeUs, &seekMode)) { - CHECK(seekTimeUs >= 0); - - mNumSamplesOutput = 0; - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - } else { - seekTimeUs = -1; - } - - if (mInputBuffer == NULL) { - err = mSource->read(&mInputBuffer, options); - - if (err != OK) { - return err; - } - - int64_t timeUs; - if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) { - mAnchorTimeUs = timeUs; - mNumSamplesOutput = 0; - } else { - // We must have a new timestamp after seeking. - CHECK(seekTimeUs < 0); - } - } - - MediaBuffer *buffer; - CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK); - - const uint8_t *inputPtr = - (const uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset(); - - int16 mode = ((inputPtr[0] >> 3) & 0x0f); - size_t frameSize = getFrameSize(mode); - CHECK(mInputBuffer->range_length() >= frameSize); - - int16 frameType; - RX_State_wb rx_state; - mime_unsorting( - const_cast<uint8_t *>(&inputPtr[1]), - mInputSampleBuffer, - &frameType, &mode, 1, &rx_state); - - int16_t *outPtr = (int16_t *)buffer->data(); - - int16_t numSamplesOutput; - pvDecoder_AmrWb( - mode, mInputSampleBuffer, - outPtr, - &numSamplesOutput, - mDecoderBuf, frameType, mDecoderCookie); - - CHECK_EQ(numSamplesOutput, kNumSamplesPerFrame); - - for (int i = 0; i < kNumSamplesPerFrame; ++i) { - /* Delete the 2 LSBs (14-bit output) */ - outPtr[i] &= 0xfffC; - } - - buffer->set_range(0, numSamplesOutput * sizeof(int16_t)); - - mInputBuffer->set_range( - mInputBuffer->range_offset() + frameSize, - mInputBuffer->range_length() - frameSize); - - if (mInputBuffer->range_length() == 0) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - buffer->meta_data()->setInt64( - kKeyTime, - mAnchorTimeUs - + (mNumSamplesOutput * 1000000) / kSampleRate); - - mNumSamplesOutput += kNumSamplesPerFrame; - - *out = buffer; - - return OK; -} - -} // namespace android diff --git a/media/libstagefright/codecs/amrwb/Android.mk b/media/libstagefright/codecs/amrwb/Android.mk index ab591d7..c9e1c25 100644 --- a/media/libstagefright/codecs/amrwb/Android.mk +++ b/media/libstagefright/codecs/amrwb/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - AMRWBDecoder.cpp \ src/agc2_amr_wb.cpp \ src/band_pass_6k_7k.cpp \ src/dec_acelp_2p_in_64.cpp \ diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp deleted file mode 100644 index 490129f..0000000 --- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright (C) 2009 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 "AVCDecoder" -#include <utils/Log.h> - -#include "AVCDecoder.h" - -#include "avcdec_api.h" -#include "avcdec_int.h" - -#include <OMX_Component.h> - -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MediaErrors.h> -#include <media/stagefright/MetaData.h> -#include <media/stagefright/Utils.h> -#include <media/stagefright/foundation/hexdump.h> - -namespace android { - -static const char kStartCode[4] = { 0x00, 0x00, 0x00, 0x01 }; - -static int32_t Malloc(void *userData, int32_t size, int32_t attrs) { - return reinterpret_cast<int32_t>(malloc(size)); -} - -static void Free(void *userData, int32_t ptr) { - free(reinterpret_cast<void *>(ptr)); -} - -AVCDecoder::AVCDecoder(const sp<MediaSource> &source) - : mSource(source), - mStarted(false), - mHandle(new tagAVCHandle), - mInputBuffer(NULL), - mAnchorTimeUs(0), - mNumSamplesOutput(0), - mPendingSeekTimeUs(-1), - mPendingSeekMode(MediaSource::ReadOptions::SEEK_CLOSEST_SYNC), - mTargetTimeUs(-1), - mSPSSeen(false), - mPPSSeen(false) { - memset(mHandle, 0, sizeof(tagAVCHandle)); - mHandle->AVCObject = NULL; - mHandle->userData = this; - mHandle->CBAVC_DPBAlloc = ActivateSPSWrapper; - mHandle->CBAVC_FrameBind = BindFrameWrapper; - mHandle->CBAVC_FrameUnbind = UnbindFrame; - mHandle->CBAVC_Malloc = Malloc; - mHandle->CBAVC_Free = Free; - - mFormat = new MetaData; - mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); - int32_t width, height; - CHECK(mSource->getFormat()->findInt32(kKeyWidth, &width)); - CHECK(mSource->getFormat()->findInt32(kKeyHeight, &height)); - mFormat->setInt32(kKeyWidth, width); - mFormat->setInt32(kKeyHeight, height); - mFormat->setRect(kKeyCropRect, 0, 0, width - 1, height - 1); - mFormat->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); - mFormat->setCString(kKeyDecoderComponent, "AVCDecoder"); - - int64_t durationUs; - if (mSource->getFormat()->findInt64(kKeyDuration, &durationUs)) { - mFormat->setInt64(kKeyDuration, durationUs); - } -} - -AVCDecoder::~AVCDecoder() { - if (mStarted) { - stop(); - } - - PVAVCCleanUpDecoder(mHandle); - - delete mHandle; - mHandle = NULL; -} - -status_t AVCDecoder::start(MetaData *) { - CHECK(!mStarted); - - uint32_t type; - const void *data; - size_t size; - sp<MetaData> meta = mSource->getFormat(); - if (meta->findData(kKeyAVCC, &type, &data, &size)) { - // Parse the AVCDecoderConfigurationRecord - - const uint8_t *ptr = (const uint8_t *)data; - - CHECK(size >= 7); - CHECK_EQ(ptr[0], 1); // configurationVersion == 1 - uint8_t profile = ptr[1]; - uint8_t level = ptr[3]; - - // There is decodable content out there that fails the following - // assertion, let's be lenient for now... - // CHECK((ptr[4] >> 2) == 0x3f); // reserved - - size_t lengthSize = 1 + (ptr[4] & 3); - - // commented out check below as H264_QVGA_500_NO_AUDIO.3gp - // violates it... - // CHECK((ptr[5] >> 5) == 7); // reserved - - size_t numSeqParameterSets = ptr[5] & 31; - - ptr += 6; - size -= 6; - - for (size_t i = 0; i < numSeqParameterSets; ++i) { - CHECK(size >= 2); - size_t length = U16_AT(ptr); - - ptr += 2; - size -= 2; - - CHECK(size >= length); - - addCodecSpecificData(ptr, length); - - ptr += length; - size -= length; - } - - CHECK(size >= 1); - size_t numPictureParameterSets = *ptr; - ++ptr; - --size; - - for (size_t i = 0; i < numPictureParameterSets; ++i) { - CHECK(size >= 2); - size_t length = U16_AT(ptr); - - ptr += 2; - size -= 2; - - CHECK(size >= length); - - addCodecSpecificData(ptr, length); - - ptr += length; - size -= length; - } - } - - mSource->start(); - - mAnchorTimeUs = 0; - mNumSamplesOutput = 0; - mPendingSeekTimeUs = -1; - mPendingSeekMode = ReadOptions::SEEK_CLOSEST_SYNC; - mTargetTimeUs = -1; - mSPSSeen = false; - mPPSSeen = false; - mStarted = true; - - return OK; -} - -void AVCDecoder::addCodecSpecificData(const uint8_t *data, size_t size) { - MediaBuffer *buffer = new MediaBuffer(size + 4); - memcpy(buffer->data(), kStartCode, 4); - memcpy((uint8_t *)buffer->data() + 4, data, size); - buffer->set_range(0, size + 4); - - mCodecSpecificData.push(buffer); -} - -status_t AVCDecoder::stop() { - CHECK(mStarted); - - for (size_t i = 0; i < mCodecSpecificData.size(); ++i) { - (*mCodecSpecificData.editItemAt(i)).release(); - } - mCodecSpecificData.clear(); - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - mSource->stop(); - - releaseFrames(); - - mStarted = false; - - return OK; -} - -sp<MetaData> AVCDecoder::getFormat() { - return mFormat; -} - -static void findNALFragment( - const MediaBuffer *buffer, const uint8_t **fragPtr, size_t *fragSize) { - const uint8_t *data = - (const uint8_t *)buffer->data() + buffer->range_offset(); - - size_t size = buffer->range_length(); - - CHECK(size >= 4); - CHECK(!memcmp(kStartCode, data, 4)); - - size_t offset = 4; - while (offset + 3 < size && memcmp(kStartCode, &data[offset], 4)) { - ++offset; - } - - *fragPtr = &data[4]; - if (offset + 3 >= size) { - *fragSize = size - 4; - } else { - *fragSize = offset - 4; - } -} - -MediaBuffer *AVCDecoder::drainOutputBuffer() { - int32_t index; - int32_t Release; - AVCFrameIO Output; - Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL; - AVCDec_Status status = PVAVCDecGetOutput(mHandle, &index, &Release, &Output); - - if (status != AVCDEC_SUCCESS) { - LOGV("PVAVCDecGetOutput returned error %d", status); - return NULL; - } - - CHECK(index >= 0); - CHECK(index < (int32_t)mFrames.size()); - - MediaBuffer *mbuf = mFrames.editItemAt(index); - - bool skipFrame = false; - - if (mTargetTimeUs >= 0) { - int64_t timeUs; - CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs)); - CHECK(timeUs <= mTargetTimeUs); - - if (timeUs < mTargetTimeUs) { - // We're still waiting for the frame with the matching - // timestamp and we won't return the current one. - skipFrame = true; - - LOGV("skipping frame at %lld us", timeUs); - } else { - LOGV("found target frame at %lld us", timeUs); - - mTargetTimeUs = -1; - } - } - - if (!skipFrame) { - mbuf->set_range(0, mbuf->size()); - mbuf->add_ref(); - - return mbuf; - } - - return new MediaBuffer(0); -} - -status_t AVCDecoder::read( - MediaBuffer **out, const ReadOptions *options) { - *out = NULL; - - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - LOGV("seek requested to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6); - - CHECK(seekTimeUs >= 0); - mPendingSeekTimeUs = seekTimeUs; - mPendingSeekMode = mode; - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - PVAVCDecReset(mHandle); - } - - if (mInputBuffer == NULL) { - LOGV("fetching new input buffer."); - - bool seeking = false; - - if (!mCodecSpecificData.isEmpty()) { - mInputBuffer = mCodecSpecificData.editItemAt(0); - mCodecSpecificData.removeAt(0); - } else { - for (;;) { - if (mPendingSeekTimeUs >= 0) { - LOGV("reading data from timestamp %lld (%.2f secs)", - mPendingSeekTimeUs, mPendingSeekTimeUs / 1E6); - } - - ReadOptions seekOptions; - if (mPendingSeekTimeUs >= 0) { - seeking = true; - - seekOptions.setSeekTo(mPendingSeekTimeUs, mPendingSeekMode); - mPendingSeekTimeUs = -1; - } - status_t err = mSource->read(&mInputBuffer, &seekOptions); - seekOptions.clearSeekTo(); - - if (err != OK) { - *out = drainOutputBuffer(); - return (*out == NULL) ? err : (status_t)OK; - } - - if (mInputBuffer->range_length() > 0) { - break; - } - - mInputBuffer->release(); - mInputBuffer = NULL; - } - } - - if (seeking) { - int64_t targetTimeUs; - if (mInputBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs) - && targetTimeUs >= 0) { - mTargetTimeUs = targetTimeUs; - } else { - mTargetTimeUs = -1; - } - } - } - - const uint8_t *fragPtr; - size_t fragSize; - findNALFragment(mInputBuffer, &fragPtr, &fragSize); - - bool releaseFragment = true; - status_t err = UNKNOWN_ERROR; - - int nalType; - int nalRefIdc; - AVCDec_Status res = - PVAVCDecGetNALType( - const_cast<uint8_t *>(fragPtr), fragSize, - &nalType, &nalRefIdc); - - if (res != AVCDEC_SUCCESS) { - LOGV("cannot determine nal type"); - } else if (nalType == AVC_NALTYPE_SPS || nalType == AVC_NALTYPE_PPS - || (mSPSSeen && mPPSSeen)) { - switch (nalType) { - case AVC_NALTYPE_SPS: - { - mSPSSeen = true; - - res = PVAVCDecSeqParamSet( - mHandle, const_cast<uint8_t *>(fragPtr), - fragSize); - - if (res != AVCDEC_SUCCESS) { - LOGV("PVAVCDecSeqParamSet returned error %d", res); - break; - } - - AVCDecObject *pDecVid = (AVCDecObject *)mHandle->AVCObject; - - int32_t width = - (pDecVid->seqParams[0]->pic_width_in_mbs_minus1 + 1) * 16; - - int32_t height = - (pDecVid->seqParams[0]->pic_height_in_map_units_minus1 + 1) * 16; - - int32_t crop_left, crop_right, crop_top, crop_bottom; - if (pDecVid->seqParams[0]->frame_cropping_flag) - { - crop_left = 2 * pDecVid->seqParams[0]->frame_crop_left_offset; - crop_right = - width - (2 * pDecVid->seqParams[0]->frame_crop_right_offset + 1); - - if (pDecVid->seqParams[0]->frame_mbs_only_flag) - { - crop_top = 2 * pDecVid->seqParams[0]->frame_crop_top_offset; - crop_bottom = - height - - (2 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1); - } - else - { - crop_top = 4 * pDecVid->seqParams[0]->frame_crop_top_offset; - crop_bottom = - height - - (4 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1); - } - } else { - crop_bottom = height - 1; - crop_right = width - 1; - crop_top = crop_left = 0; - } - - int32_t prevCropLeft, prevCropTop; - int32_t prevCropRight, prevCropBottom; - if (!mFormat->findRect( - kKeyCropRect, - &prevCropLeft, &prevCropTop, - &prevCropRight, &prevCropBottom)) { - prevCropLeft = prevCropTop = 0; - prevCropRight = width - 1; - prevCropBottom = height - 1; - } - - int32_t oldWidth, oldHeight; - CHECK(mFormat->findInt32(kKeyWidth, &oldWidth)); - CHECK(mFormat->findInt32(kKeyHeight, &oldHeight)); - - if (oldWidth != width || oldHeight != height - || prevCropLeft != crop_left - || prevCropTop != crop_top - || prevCropRight != crop_right - || prevCropBottom != crop_bottom) { - mFormat->setRect( - kKeyCropRect, - crop_left, crop_top, crop_right, crop_bottom); - - mFormat->setInt32(kKeyWidth, width); - mFormat->setInt32(kKeyHeight, height); - - err = INFO_FORMAT_CHANGED; - } else { - *out = new MediaBuffer(0); - err = OK; - } - break; - } - - case AVC_NALTYPE_PPS: - { - mPPSSeen = true; - - res = PVAVCDecPicParamSet( - mHandle, const_cast<uint8_t *>(fragPtr), - fragSize); - - if (res != AVCDEC_SUCCESS) { - LOGV("PVAVCDecPicParamSet returned error %d", res); - break; - } - - *out = new MediaBuffer(0); - - err = OK; - break; - } - - case AVC_NALTYPE_SLICE: - case AVC_NALTYPE_IDR: - { - res = PVAVCDecodeSlice( - mHandle, const_cast<uint8_t *>(fragPtr), - fragSize); - - if (res == AVCDEC_PICTURE_OUTPUT_READY) { - MediaBuffer *mbuf = drainOutputBuffer(); - if (mbuf == NULL) { - break; - } - - *out = mbuf; - - // Do _not_ release input buffer yet. - - releaseFragment = false; - err = OK; - break; - } - - if (res == AVCDEC_PICTURE_READY || res == AVCDEC_SUCCESS) { - *out = new MediaBuffer(0); - - err = OK; - } else { - LOGV("PVAVCDecodeSlice returned error %d", res); - } - break; - } - - case AVC_NALTYPE_SEI: - { - res = PVAVCDecSEI( - mHandle, const_cast<uint8_t *>(fragPtr), - fragSize); - - if (res != AVCDEC_SUCCESS) { - break; - } - - *out = new MediaBuffer(0); - - err = OK; - break; - } - - case AVC_NALTYPE_AUD: - case AVC_NALTYPE_FILL: - case AVC_NALTYPE_EOSEQ: - { - *out = new MediaBuffer(0); - - err = OK; - break; - } - - default: - { - LOGE("Should not be here, unknown nalType %d", nalType); - - err = ERROR_MALFORMED; - break; - } - } - } else { - // We haven't seen SPS or PPS yet. - - *out = new MediaBuffer(0); - err = OK; - } - - if (releaseFragment) { - size_t offset = mInputBuffer->range_offset(); - if (fragSize + 4 == mInputBuffer->range_length()) { - mInputBuffer->release(); - mInputBuffer = NULL; - } else { - mInputBuffer->set_range( - offset + fragSize + 4, - mInputBuffer->range_length() - fragSize - 4); - } - } - - return err; -} - -// static -int32_t AVCDecoder::ActivateSPSWrapper( - void *userData, unsigned int sizeInMbs, unsigned int numBuffers) { - return static_cast<AVCDecoder *>(userData)->activateSPS(sizeInMbs, numBuffers); -} - -// static -int32_t AVCDecoder::BindFrameWrapper( - void *userData, int32_t index, uint8_t **yuv) { - return static_cast<AVCDecoder *>(userData)->bindFrame(index, yuv); -} - -// static -void AVCDecoder::UnbindFrame(void *userData, int32_t index) { -} - -int32_t AVCDecoder::activateSPS( - unsigned int sizeInMbs, unsigned int numBuffers) { - CHECK(mFrames.isEmpty()); - - size_t frameSize = (sizeInMbs << 7) * 3; - for (unsigned int i = 0; i < numBuffers; ++i) { - MediaBuffer *buffer = new MediaBuffer(frameSize); - buffer->setObserver(this); - - mFrames.push(buffer); - } - - return 1; -} - -int32_t AVCDecoder::bindFrame(int32_t index, uint8_t **yuv) { - CHECK(index >= 0); - CHECK(index < (int32_t)mFrames.size()); - - CHECK(mInputBuffer != NULL); - int64_t timeUs; - CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); - mFrames[index]->meta_data()->setInt64(kKeyTime, timeUs); - - *yuv = (uint8_t *)mFrames[index]->data(); - - return 1; -} - -void AVCDecoder::releaseFrames() { - for (size_t i = 0; i < mFrames.size(); ++i) { - MediaBuffer *buffer = mFrames.editItemAt(i); - - buffer->setObserver(NULL); - buffer->release(); - } - mFrames.clear(); -} - -void AVCDecoder::signalBufferReturned(MediaBuffer *buffer) { -} - -} // namespace android diff --git a/media/libstagefright/codecs/avc/dec/Android.mk b/media/libstagefright/codecs/avc/dec/Android.mk index 4d4533b..2949a04 100644 --- a/media/libstagefright/codecs/avc/dec/Android.mk +++ b/media/libstagefright/codecs/avc/dec/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - AVCDecoder.cpp \ src/avcdec_api.cpp \ src/avc_bitstream.cpp \ src/header.cpp \ diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.cpp b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp index 9f141ac..6a476f6 100644 --- a/media/libstagefright/codecs/avc/dec/SoftAVC.cpp +++ b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp @@ -23,6 +23,7 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> +#include <media/IOMX.h> #include "avcdec_api.h" #include "avcdec_int.h" @@ -31,6 +32,13 @@ namespace android { static const char kStartCode[4] = { 0x00, 0x00, 0x00, 0x01 }; +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 }, +}; + template<class T> static void InitOMXParams(T *params) { params->nSize = sizeof(T); @@ -181,6 +189,28 @@ OMX_ERRORTYPE SoftAVC::internalGetParameter( return OMX_ErrorNone; } + case OMX_IndexParamVideoProfileLevelQuerySupported: + { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; + + if (profileLevel->nPortIndex != 0) { // Input port only + LOGE("Invalid port index: %ld", profileLevel->nPortIndex); + return OMX_ErrorUnsupportedIndex; + } + + size_t index = profileLevel->nProfileIndex; + size_t nProfileLevels = + sizeof(kProfileLevels) / sizeof(kProfileLevels[0]); + if (index >= nProfileLevels) { + return OMX_ErrorNoMore; + } + + profileLevel->eProfile = kProfileLevels[index].mProfile; + profileLevel->eLevel = kProfileLevels[index].mLevel; + return OMX_ErrorNone; + } + default: return SimpleSoftOMXComponent::internalGetParameter(index, params); } diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk index 6e98559..6692533 100644 --- a/media/libstagefright/codecs/g711/dec/Android.mk +++ b/media/libstagefright/codecs/g711/dec/Android.mk @@ -2,20 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - G711Decoder.cpp - -LOCAL_C_INCLUDES := \ - frameworks/base/media/libstagefright/include \ - -LOCAL_MODULE := libstagefright_g711dec - -include $(BUILD_STATIC_LIBRARY) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ SoftG711.cpp LOCAL_C_INCLUDES := \ diff --git a/media/libstagefright/codecs/g711/dec/G711Decoder.cpp b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp deleted file mode 100644 index 4414e4e..0000000 --- a/media/libstagefright/codecs/g711/dec/G711Decoder.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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_NDEBUG 0 -#define LOG_TAG "G711Decoder" -#include <utils/Log.h> - -#include "G711Decoder.h" - -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MediaErrors.h> -#include <media/stagefright/MetaData.h> - -static const size_t kMaxNumSamplesPerFrame = 16384; - -namespace android { - -G711Decoder::G711Decoder(const sp<MediaSource> &source) - : mSource(source), - mStarted(false), - mBufferGroup(NULL) { -} - -G711Decoder::~G711Decoder() { - if (mStarted) { - stop(); - } -} - -status_t G711Decoder::start(MetaData *params) { - CHECK(!mStarted); - - const char *mime; - CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); - - mIsMLaw = false; - if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) { - mIsMLaw = true; - } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)) { - return ERROR_UNSUPPORTED; - } - - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer( - new MediaBuffer(kMaxNumSamplesPerFrame * sizeof(int16_t))); - - mSource->start(); - - mStarted = true; - - return OK; -} - -status_t G711Decoder::stop() { - CHECK(mStarted); - - delete mBufferGroup; - mBufferGroup = NULL; - - mSource->stop(); - - mStarted = false; - - return OK; -} - -sp<MetaData> G711Decoder::getFormat() { - sp<MetaData> srcFormat = mSource->getFormat(); - - int32_t numChannels; - int32_t sampleRate; - - CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels)); - CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); - - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - meta->setInt32(kKeyChannelCount, numChannels); - meta->setInt32(kKeySampleRate, sampleRate); - - int64_t durationUs; - if (srcFormat->findInt64(kKeyDuration, &durationUs)) { - meta->setInt64(kKeyDuration, durationUs); - } - - meta->setCString(kKeyDecoderComponent, "G711Decoder"); - - return meta; -} - -status_t G711Decoder::read( - MediaBuffer **out, const ReadOptions *options) { - status_t err; - - *out = NULL; - - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - CHECK(seekTimeUs >= 0); - } else { - seekTimeUs = -1; - } - - MediaBuffer *inBuffer; - err = mSource->read(&inBuffer, options); - - if (err != OK) { - return err; - } - - if (inBuffer->range_length() > kMaxNumSamplesPerFrame) { - LOGE("input buffer too large (%d).", inBuffer->range_length()); - - inBuffer->release(); - inBuffer = NULL; - - return ERROR_UNSUPPORTED; - } - - int64_t timeUs; - CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); - - const uint8_t *inputPtr = - (const uint8_t *)inBuffer->data() + inBuffer->range_offset(); - - MediaBuffer *outBuffer; - CHECK_EQ(mBufferGroup->acquire_buffer(&outBuffer), OK); - - if (mIsMLaw) { - DecodeMLaw( - static_cast<int16_t *>(outBuffer->data()), - inputPtr, inBuffer->range_length()); - } else { - DecodeALaw( - static_cast<int16_t *>(outBuffer->data()), - inputPtr, inBuffer->range_length()); - } - - // Each 8-bit byte is converted into a 16-bit sample. - outBuffer->set_range(0, inBuffer->range_length() * 2); - - outBuffer->meta_data()->setInt64(kKeyTime, timeUs); - - inBuffer->release(); - inBuffer = NULL; - - *out = outBuffer; - - return OK; -} - -// static -void G711Decoder::DecodeALaw( - int16_t *out, const uint8_t *in, size_t inSize) { - while (inSize-- > 0) { - int32_t x = *in++; - - int32_t ix = x ^ 0x55; - ix &= 0x7f; - - int32_t iexp = ix >> 4; - int32_t mant = ix & 0x0f; - - if (iexp > 0) { - mant += 16; - } - - mant = (mant << 4) + 8; - - if (iexp > 1) { - mant = mant << (iexp - 1); - } - - *out++ = (x > 127) ? mant : -mant; - } -} - -// static -void G711Decoder::DecodeMLaw( - int16_t *out, const uint8_t *in, size_t inSize) { - while (inSize-- > 0) { - int32_t x = *in++; - - int32_t mantissa = ~x; - int32_t exponent = (mantissa >> 4) & 7; - int32_t segment = exponent + 1; - mantissa &= 0x0f; - - int32_t step = 4 << segment; - - int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33; - - *out++ = (x < 0x80) ? -abs : abs; - } -} - -} // namespace android diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk index f1bec08..2ffa5f2 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk +++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - M4vH263Decoder.cpp \ src/adaptive_smooth_no_mmx.cpp \ src/bitstream.cpp \ src/block_idct.cpp \ diff --git a/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp b/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp deleted file mode 100644 index 2bdb3ef..0000000 --- a/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2009 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 "M4vH263Decoder" -#include <utils/Log.h> -#include <stdlib.h> // for free -#include "ESDS.h" -#include "M4vH263Decoder.h" - -#include "mp4dec_api.h" - -#include <OMX_Component.h> -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MediaErrors.h> -#include <media/stagefright/MetaData.h> -#include <media/stagefright/Utils.h> - -namespace android { - -M4vH263Decoder::M4vH263Decoder(const sp<MediaSource> &source) - : mSource(source), - mStarted(false), - mHandle(new tagvideoDecControls), - mInputBuffer(NULL), - mNumSamplesOutput(0), - mTargetTimeUs(-1) { - - LOGV("M4vH263Decoder"); - memset(mHandle, 0, sizeof(tagvideoDecControls)); - mFormat = new MetaData; - mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); - - // CHECK(mSource->getFormat()->findInt32(kKeyWidth, &mWidth)); - // CHECK(mSource->getFormat()->findInt32(kKeyHeight, &mHeight)); - - // We'll ignore the dimension advertised by the source, the decoder - // appears to require us to always start with the default dimensions - // of 352 x 288 to operate correctly and later react to changes in - // the dimensions as needed. - mWidth = 352; - mHeight = 288; - - mFormat->setInt32(kKeyWidth, mWidth); - mFormat->setInt32(kKeyHeight, mHeight); - mFormat->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); - mFormat->setCString(kKeyDecoderComponent, "M4vH263Decoder"); -} - -M4vH263Decoder::~M4vH263Decoder() { - if (mStarted) { - stop(); - } - - delete mHandle; - mHandle = NULL; -} - -void M4vH263Decoder::allocateFrames(int32_t width, int32_t height) { - size_t frameSize = - (((width + 15) & - 16) * ((height + 15) & - 16) * 3) / 2; - - for (uint32_t i = 0; i < 2; ++i) { - mFrames[i] = new MediaBuffer(frameSize); - mFrames[i]->setObserver(this); - } - - PVSetReferenceYUV( - mHandle, - (uint8_t *)mFrames[1]->data()); -} - -status_t M4vH263Decoder::start(MetaData *) { - CHECK(!mStarted); - - const char *mime = NULL; - sp<MetaData> meta = mSource->getFormat(); - CHECK(meta->findCString(kKeyMIMEType, &mime)); - - MP4DecodingMode mode; - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { - mode = MPEG4_MODE; - } else { - CHECK(!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)); - mode = H263_MODE; - } - - uint32_t type; - const void *data = NULL; - size_t size = 0; - uint8_t *vol_data[1] = {0}; - int32_t vol_size = 0; - if (meta->findData(kKeyESDS, &type, &data, &size)) { - ESDS esds((const uint8_t *)data, size); - CHECK_EQ(esds.InitCheck(), (status_t)OK); - - const void *codec_specific_data; - size_t codec_specific_data_size; - esds.getCodecSpecificInfo( - &codec_specific_data, &codec_specific_data_size); - - vol_data[0] = (uint8_t *) malloc(codec_specific_data_size); - memcpy(vol_data[0], codec_specific_data, codec_specific_data_size); - vol_size = codec_specific_data_size; - } else { - vol_data[0] = NULL; - vol_size = 0; - - } - - Bool success = PVInitVideoDecoder( - mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode); - if (vol_data[0]) free(vol_data[0]); - - if (success != PV_TRUE) { - LOGW("PVInitVideoDecoder failed. Unsupported content?"); - return ERROR_UNSUPPORTED; - } - - MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle); - if (mode != actualMode) { - PVCleanUpVideoDecoder(mHandle); - return UNKNOWN_ERROR; - } - - PVSetPostProcType((VideoDecControls *) mHandle, 0); - - int32_t width, height; - PVGetVideoDimensions(mHandle, &width, &height); - if (mode == H263_MODE && (width == 0 || height == 0)) { - width = 352; - height = 288; - } - allocateFrames(width, height); - - mSource->start(); - - mNumSamplesOutput = 0; - mTargetTimeUs = -1; - mStarted = true; - - return OK; -} - -status_t M4vH263Decoder::stop() { - CHECK(mStarted); - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - mSource->stop(); - - releaseFrames(); - - mStarted = false; - return (PVCleanUpVideoDecoder(mHandle) == PV_TRUE)? OK: UNKNOWN_ERROR; -} - -sp<MetaData> M4vH263Decoder::getFormat() { - return mFormat; -} - -status_t M4vH263Decoder::read( - MediaBuffer **out, const ReadOptions *options) { - *out = NULL; - - bool seeking = false; - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - seeking = true; - CHECK_EQ((int)PVResetVideoDecoder(mHandle), PV_TRUE); - } - - MediaBuffer *inputBuffer = NULL; - status_t err = mSource->read(&inputBuffer, options); - if (err != OK) { - return err; - } - - if (seeking) { - int64_t targetTimeUs; - if (inputBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs) - && targetTimeUs >= 0) { - mTargetTimeUs = targetTimeUs; - } else { - mTargetTimeUs = -1; - } - } - - uint8_t *bitstream = - (uint8_t *) inputBuffer->data() + inputBuffer->range_offset(); - - uint32_t timestamp = 0xFFFFFFFF; - int32_t bufferSize = inputBuffer->range_length(); - uint32_t useExtTimestamp = 0; - if (PVDecodeVideoFrame( - mHandle, &bitstream, ×tamp, &bufferSize, - &useExtTimestamp, - (uint8_t *)mFrames[mNumSamplesOutput & 0x01]->data()) - != PV_TRUE) { - LOGE("failed to decode video frame."); - - inputBuffer->release(); - inputBuffer = NULL; - - return UNKNOWN_ERROR; - } - - int32_t disp_width, disp_height; - PVGetVideoDimensions(mHandle, &disp_width, &disp_height); - - int32_t buf_width, buf_height; - PVGetBufferDimensions(mHandle, &buf_width, &buf_height); - - if (buf_width != mWidth || buf_height != mHeight) { - ++mNumSamplesOutput; // The client will never get to see this frame. - - inputBuffer->release(); - inputBuffer = NULL; - - mWidth = buf_width; - mHeight = buf_height; - mFormat->setInt32(kKeyWidth, mWidth); - mFormat->setInt32(kKeyHeight, mHeight); - - CHECK_LE(disp_width, buf_width); - CHECK_LE(disp_height, buf_height); - - mFormat->setRect(kKeyCropRect, 0, 0, disp_width - 1, disp_height - 1); - - return INFO_FORMAT_CHANGED; - } - - int64_t timeUs; - CHECK(inputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); - - inputBuffer->release(); - inputBuffer = NULL; - - bool skipFrame = false; - - if (mTargetTimeUs >= 0) { - CHECK(timeUs <= mTargetTimeUs); - - if (timeUs < mTargetTimeUs) { - // We're still waiting for the frame with the matching - // timestamp and we won't return the current one. - skipFrame = true; - - LOGV("skipping frame at %lld us", timeUs); - } else { - LOGV("found target frame at %lld us", timeUs); - - mTargetTimeUs = -1; - } - } - - if (skipFrame) { - *out = new MediaBuffer(0); - } else { - *out = mFrames[mNumSamplesOutput & 0x01]; - (*out)->add_ref(); - (*out)->meta_data()->setInt64(kKeyTime, timeUs); - } - - ++mNumSamplesOutput; - - return OK; -} - -void M4vH263Decoder::releaseFrames() { - for (size_t i = 0; i < sizeof(mFrames) / sizeof(mFrames[0]); ++i) { - MediaBuffer *buffer = mFrames[i]; - - buffer->setObserver(NULL); - buffer->release(); - - mFrames[i] = NULL; - } -} - -void M4vH263Decoder::signalBufferReturned(MediaBuffer *buffer) { - LOGV("signalBufferReturned"); -} - - -} // namespace android diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index cffbfb5..ddced5f 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -23,11 +23,31 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> +#include <media/IOMX.h> #include "mp4dec_api.h" namespace android { +static const CodecProfileLevel kM4VProfileLevels[] = { + { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level0 }, + { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level0b }, + { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level1 }, + { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level2 }, + { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level3 }, +}; + +static const CodecProfileLevel kH263ProfileLevels[] = { + { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level10 }, + { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level20 }, + { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level30 }, + { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level45 }, + { OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level10 }, + { OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level20 }, + { OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level30 }, + { OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level45 }, +}; + template<class T> static void InitOMXParams(T *params) { params->nSize = sizeof(T); @@ -181,6 +201,39 @@ OMX_ERRORTYPE SoftMPEG4::internalGetParameter( return OMX_ErrorNone; } + case OMX_IndexParamVideoProfileLevelQuerySupported: + { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; + + if (profileLevel->nPortIndex != 0) { // Input port only + LOGE("Invalid port index: %ld", profileLevel->nPortIndex); + return OMX_ErrorUnsupportedIndex; + } + + size_t index = profileLevel->nProfileIndex; + if (mMode == MODE_H263) { + size_t nProfileLevels = + sizeof(kH263ProfileLevels) / sizeof(kH263ProfileLevels[0]); + if (index >= nProfileLevels) { + return OMX_ErrorNoMore; + } + + profileLevel->eProfile = kH263ProfileLevels[index].mProfile; + profileLevel->eLevel = kH263ProfileLevels[index].mLevel; + } else { + size_t nProfileLevels = + sizeof(kM4VProfileLevels) / sizeof(kM4VProfileLevels[0]); + if (index >= nProfileLevels) { + return OMX_ErrorNoMore; + } + + profileLevel->eProfile = kM4VProfileLevels[index].mProfile; + profileLevel->eLevel = kM4VProfileLevels[index].mLevel; + } + return OMX_ErrorNone; + } + default: return SimpleSoftOMXComponent::internalGetParameter(index, params); } diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk index 229988e..a08c9f0 100644 --- a/media/libstagefright/codecs/mp3dec/Android.mk +++ b/media/libstagefright/codecs/mp3dec/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - MP3Decoder.cpp \ src/pvmp3_normalize.cpp \ src/pvmp3_alias_reduction.cpp \ src/pvmp3_crc.cpp \ diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp deleted file mode 100644 index 0ba42ff..0000000 --- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2009 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 "MP3Decoder" - -#include "MP3Decoder.h" - -#include "include/pvmp3decoder_api.h" - -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MetaData.h> - -namespace android { - -MP3Decoder::MP3Decoder(const sp<MediaSource> &source) - : mSource(source), - mNumChannels(0), - mStarted(false), - mBufferGroup(NULL), - mConfig(new tPVMP3DecoderExternal), - mDecoderBuf(NULL), - mAnchorTimeUs(0), - mNumFramesOutput(0), - mInputBuffer(NULL) { - init(); -} - -void MP3Decoder::init() { - sp<MetaData> srcFormat = mSource->getFormat(); - - int32_t sampleRate; - CHECK(srcFormat->findInt32(kKeyChannelCount, &mNumChannels)); - CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); - - mMeta = new MetaData; - mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - mMeta->setInt32(kKeyChannelCount, mNumChannels); - mMeta->setInt32(kKeySampleRate, sampleRate); - - int64_t durationUs; - if (srcFormat->findInt64(kKeyDuration, &durationUs)) { - mMeta->setInt64(kKeyDuration, durationUs); - } - - mMeta->setCString(kKeyDecoderComponent, "MP3Decoder"); -} - -MP3Decoder::~MP3Decoder() { - if (mStarted) { - stop(); - } - - delete mConfig; - mConfig = NULL; -} - -status_t MP3Decoder::start(MetaData *params) { - CHECK(!mStarted); - - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer(new MediaBuffer(4608 * 2)); - - mConfig->equalizerType = flat; - mConfig->crcEnabled = false; - - uint32_t memRequirements = pvmp3_decoderMemRequirements(); - mDecoderBuf = malloc(memRequirements); - - pvmp3_InitDecoder(mConfig, mDecoderBuf); - - mSource->start(); - - mAnchorTimeUs = 0; - mNumFramesOutput = 0; - mStarted = true; - - return OK; -} - -status_t MP3Decoder::stop() { - CHECK(mStarted); - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - free(mDecoderBuf); - mDecoderBuf = NULL; - - delete mBufferGroup; - mBufferGroup = NULL; - - mSource->stop(); - - mStarted = false; - - return OK; -} - -sp<MetaData> MP3Decoder::getFormat() { - return mMeta; -} - -status_t MP3Decoder::read( - MediaBuffer **out, const ReadOptions *options) { - status_t err; - - *out = NULL; - - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - CHECK(seekTimeUs >= 0); - - mNumFramesOutput = 0; - - if (mInputBuffer) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - // Make sure that the next buffer output does not still - // depend on fragments from the last one decoded. - pvmp3_InitDecoder(mConfig, mDecoderBuf); - } else { - seekTimeUs = -1; - } - - if (mInputBuffer == NULL) { - err = mSource->read(&mInputBuffer, options); - - if (err != OK) { - return err; - } - - int64_t timeUs; - if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) { - mAnchorTimeUs = timeUs; - mNumFramesOutput = 0; - } else { - // We must have a new timestamp after seeking. - CHECK(seekTimeUs < 0); - } - } - - MediaBuffer *buffer; - CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK); - - mConfig->pInputBuffer = - (uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset(); - - mConfig->inputBufferCurrentLength = mInputBuffer->range_length(); - mConfig->inputBufferMaxLength = 0; - mConfig->inputBufferUsedLength = 0; - - mConfig->outputFrameSize = buffer->size() / sizeof(int16_t); - mConfig->pOutputBuffer = static_cast<int16_t *>(buffer->data()); - - ERROR_CODE decoderErr; - if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf)) - != NO_DECODING_ERROR) { - LOGV("mp3 decoder returned error %d", decoderErr); - - if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR || - mConfig->outputFrameSize == 0) { - - if (mConfig->outputFrameSize == 0) { - LOGE("Output frame size is 0"); - } - buffer->release(); - buffer = NULL; - - mInputBuffer->release(); - mInputBuffer = NULL; - - return UNKNOWN_ERROR; - } - - // This is recoverable, just ignore the current frame and - // play silence instead. - memset(buffer->data(), 0, mConfig->outputFrameSize * sizeof(int16_t)); - mConfig->inputBufferUsedLength = mInputBuffer->range_length(); - } - - buffer->set_range( - 0, mConfig->outputFrameSize * sizeof(int16_t)); - - mInputBuffer->set_range( - mInputBuffer->range_offset() + mConfig->inputBufferUsedLength, - mInputBuffer->range_length() - mConfig->inputBufferUsedLength); - - if (mInputBuffer->range_length() == 0) { - mInputBuffer->release(); - mInputBuffer = NULL; - } - - buffer->meta_data()->setInt64( - kKeyTime, - mAnchorTimeUs - + (mNumFramesOutput * 1000000) / mConfig->samplingRate); - - mNumFramesOutput += mConfig->outputFrameSize / mNumChannels; - - *out = buffer; - - return OK; -} - -} // namespace android diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk index 832b885..32bbd6b 100644 --- a/media/libstagefright/codecs/on2/dec/Android.mk +++ b/media/libstagefright/codecs/on2/dec/Android.mk @@ -2,24 +2,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - VPXDecoder.cpp \ - -LOCAL_MODULE := libstagefright_vpxdec - -LOCAL_C_INCLUDES := \ - $(TOP)/frameworks/base/media/libstagefright/include \ - frameworks/base/include/media/stagefright/openmax \ - $(TOP)/external/libvpx \ - $(TOP)/external/libvpx/vpx_codec \ - $(TOP)/external/libvpx/vpx_ports - -include $(BUILD_STATIC_LIBRARY) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ SoftVPX.cpp LOCAL_C_INCLUDES := \ @@ -30,7 +12,6 @@ LOCAL_C_INCLUDES := \ frameworks/base/include/media/stagefright/openmax \ LOCAL_STATIC_LIBRARIES := \ - libstagefright_vpxdec \ libvpx LOCAL_SHARED_LIBRARIES := \ diff --git a/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp b/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp deleted file mode 100644 index 489e5ad..0000000 --- a/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* - * 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_NDEBUG 0 -#define LOG_TAG "VPXDecoder" -#include <utils/Log.h> - -#include "VPXDecoder.h" - -#include <OMX_Component.h> - -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MediaErrors.h> -#include <media/stagefright/MetaData.h> -#include <media/stagefright/Utils.h> - -#include "vpx/vpx_decoder.h" -#include "vpx/vpx_codec.h" -#include "vpx/vp8dx.h" - -namespace android { - -VPXDecoder::VPXDecoder(const sp<MediaSource> &source) - : mSource(source), - mStarted(false), - mBufferSize(0), - mCtx(NULL), - mBufferGroup(NULL), - mTargetTimeUs(-1) { - sp<MetaData> inputFormat = source->getFormat(); - const char *mime; - CHECK(inputFormat->findCString(kKeyMIMEType, &mime)); - CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_VPX)); - - CHECK(inputFormat->findInt32(kKeyWidth, &mWidth)); - CHECK(inputFormat->findInt32(kKeyHeight, &mHeight)); - - mBufferSize = (mWidth * mHeight * 3) / 2; - - mFormat = new MetaData; - mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); - mFormat->setInt32(kKeyWidth, mWidth); - mFormat->setInt32(kKeyHeight, mHeight); - mFormat->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); - mFormat->setCString(kKeyDecoderComponent, "VPXDecoder"); - - int64_t durationUs; - if (inputFormat->findInt64(kKeyDuration, &durationUs)) { - mFormat->setInt64(kKeyDuration, durationUs); - } -} - -VPXDecoder::~VPXDecoder() { - if (mStarted) { - stop(); - } -} - -status_t VPXDecoder::start(MetaData *) { - if (mStarted) { - return UNKNOWN_ERROR; - } - - status_t err = mSource->start(); - - if (err != OK) { - return err; - } - - mCtx = new vpx_codec_ctx_t; - vpx_codec_err_t vpx_err; - if ((vpx_err = vpx_codec_dec_init( - (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0))) { - LOGE("on2 decoder failed to initialize. (%d)", vpx_err); - - mSource->stop(); - - return UNKNOWN_ERROR; - } - - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); - mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); - - mTargetTimeUs = -1; - - mStarted = true; - - return OK; -} - -status_t VPXDecoder::stop() { - if (!mStarted) { - return UNKNOWN_ERROR; - } - - delete mBufferGroup; - mBufferGroup = NULL; - - vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); - delete (vpx_codec_ctx_t *)mCtx; - mCtx = NULL; - - mSource->stop(); - - mStarted = false; - - return OK; -} - -sp<MetaData> VPXDecoder::getFormat() { - return mFormat; -} - -status_t VPXDecoder::read( - MediaBuffer **out, const ReadOptions *options) { - *out = NULL; - - bool seeking = false; - int64_t seekTimeUs; - ReadOptions::SeekMode seekMode; - if (options && options->getSeekTo(&seekTimeUs, &seekMode)) { - seeking = true; - } - - MediaBuffer *input; - status_t err = mSource->read(&input, options); - - if (err != OK) { - return err; - } - - LOGV("read %d bytes from source\n", input->range_length()); - - if (seeking) { - int64_t targetTimeUs; - if (input->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs) - && targetTimeUs >= 0) { - mTargetTimeUs = targetTimeUs; - } else { - mTargetTimeUs = -1; - } - } - - if (vpx_codec_decode( - (vpx_codec_ctx_t *)mCtx, - (uint8_t *)input->data() + input->range_offset(), - input->range_length(), - NULL, - 0)) { - LOGE("on2 decoder failed to decode frame."); - input->release(); - input = NULL; - - return UNKNOWN_ERROR; - } - - LOGV("successfully decoded 1 or more frames."); - - int64_t timeUs; - CHECK(input->meta_data()->findInt64(kKeyTime, &timeUs)); - - input->release(); - input = NULL; - - bool skipFrame = false; - - if (mTargetTimeUs >= 0) { - CHECK(timeUs <= mTargetTimeUs); - - if (timeUs < mTargetTimeUs) { - // We're still waiting for the frame with the matching - // timestamp and we won't return the current one. - skipFrame = true; - - LOGV("skipping frame at %lld us", timeUs); - } else { - LOGV("found target frame at %lld us", timeUs); - - mTargetTimeUs = -1; - } - } - - if (skipFrame) { - *out = new MediaBuffer(0); - return OK; - } - - vpx_codec_iter_t iter = NULL; - vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); - - if (img == NULL) { - // The VPX format supports "internal-only" frames that are - // referenced by future content but never actually displayed, so - // this is a perfectly valid scenario. - - *out = new MediaBuffer(0); - return OK; - } - - CHECK_EQ(img->fmt, IMG_FMT_I420); - - int32_t width = img->d_w; - int32_t height = img->d_h; - - if (width != mWidth || height != mHeight) { - LOGI("Image dimensions changed, width = %d, height = %d", - width, height); - - mWidth = width; - mHeight = height; - mFormat->setInt32(kKeyWidth, width); - mFormat->setInt32(kKeyHeight, height); - - mBufferSize = (mWidth * mHeight * 3) / 2; - delete mBufferGroup; - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); - mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); - - return INFO_FORMAT_CHANGED; - } - - MediaBuffer *output; - CHECK_EQ(mBufferGroup->acquire_buffer(&output), OK); - - const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y]; - uint8_t *dst = (uint8_t *)output->data(); - for (size_t i = 0; i < img->d_h; ++i) { - memcpy(dst, srcLine, img->d_w); - - srcLine += img->stride[PLANE_Y]; - dst += img->d_w; - } - - srcLine = (const uint8_t *)img->planes[PLANE_U]; - for (size_t i = 0; i < img->d_h / 2; ++i) { - memcpy(dst, srcLine, img->d_w / 2); - - srcLine += img->stride[PLANE_U]; - dst += img->d_w / 2; - } - - srcLine = (const uint8_t *)img->planes[PLANE_V]; - for (size_t i = 0; i < img->d_h / 2; ++i) { - memcpy(dst, srcLine, img->d_w / 2); - - srcLine += img->stride[PLANE_V]; - dst += img->d_w / 2; - } - - output->set_range(0, (width * height * 3) / 2); - - output->meta_data()->setInt64(kKeyTime, timeUs); - - *out = output; - - return OK; -} - -} // namespace android - diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp index ec7bd1c..740c957 100644 --- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp +++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp @@ -23,10 +23,30 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> +#include <media/IOMX.h> namespace android { +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel42 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel5 }, + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 }, +}; + template<class T> static void InitOMXParams(T *params) { params->nSize = sizeof(T); @@ -177,6 +197,28 @@ OMX_ERRORTYPE SoftAVC::internalGetParameter( return OMX_ErrorNone; } + case OMX_IndexParamVideoProfileLevelQuerySupported: + { + OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel = + (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) params; + + if (profileLevel->nPortIndex != kInputPortIndex) { + LOGE("Invalid port index: %ld", profileLevel->nPortIndex); + return OMX_ErrorUnsupportedIndex; + } + + size_t index = profileLevel->nProfileIndex; + size_t nProfileLevels = + sizeof(kProfileLevels) / sizeof(kProfileLevels[0]); + if (index >= nProfileLevels) { + return OMX_ErrorNoMore; + } + + profileLevel->eProfile = kProfileLevels[index].mProfile; + profileLevel->eLevel = kProfileLevels[index].mLevel; + return OMX_ErrorNone; + } + default: return SimpleSoftOMXComponent::internalGetParameter(index, params); } diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk index 9251229..f33f3ac 100644 --- a/media/libstagefright/codecs/vorbis/dec/Android.mk +++ b/media/libstagefright/codecs/vorbis/dec/Android.mk @@ -2,21 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - VorbisDecoder.cpp \ - -LOCAL_C_INCLUDES := \ - frameworks/base/media/libstagefright/include \ - external/tremolo \ - -LOCAL_MODULE := libstagefright_vorbisdec - -include $(BUILD_STATIC_LIBRARY) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ SoftVorbis.cpp LOCAL_C_INCLUDES := \ @@ -24,9 +9,6 @@ LOCAL_C_INCLUDES := \ frameworks/base/media/libstagefright/include \ frameworks/base/include/media/stagefright/openmax \ -LOCAL_STATIC_LIBRARIES := \ - libstagefright_vorbisdec - LOCAL_SHARED_LIBRARIES := \ libvorbisidec libstagefright libstagefright_omx \ libstagefright_foundation libutils diff --git a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp deleted file mode 100644 index e14fb95..0000000 --- a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * 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_NDEBUG 0 -#define LOG_TAG "VorbisDecoder" -#include <utils/Log.h> - -#include "VorbisDecoder.h" - -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MediaErrors.h> -#include <media/stagefright/MetaData.h> - -extern "C" { - #include <Tremolo/codec_internal.h> - - int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb); - int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb); - int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb); -} - -namespace android { - -VorbisDecoder::VorbisDecoder(const sp<MediaSource> &source) - : mSource(source), - mStarted(false), - mBufferGroup(NULL), - mAnchorTimeUs(0), - mNumFramesOutput(0), - mState(NULL), - mVi(NULL) { - sp<MetaData> srcFormat = mSource->getFormat(); - CHECK(srcFormat->findInt32(kKeyChannelCount, &mNumChannels)); - CHECK(srcFormat->findInt32(kKeySampleRate, &mSampleRate)); -} - -VorbisDecoder::~VorbisDecoder() { - if (mStarted) { - stop(); - } -} - -static void makeBitReader( - const void *data, size_t size, - ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) { - buf->data = (uint8_t *)data; - buf->size = size; - buf->refcount = 1; - buf->ptr.owner = NULL; - - ref->buffer = buf; - ref->begin = 0; - ref->length = size; - ref->next = NULL; - - oggpack_readinit(bits, ref); -} - -status_t VorbisDecoder::start(MetaData *params) { - CHECK(!mStarted); - - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer( - new MediaBuffer(kMaxNumSamplesPerBuffer * sizeof(int16_t))); - - mSource->start(); - - sp<MetaData> meta = mSource->getFormat(); - - mVi = new vorbis_info; - vorbis_info_init(mVi); - - /////////////////////////////////////////////////////////////////////////// - - uint32_t type; - const void *data; - size_t size; - CHECK(meta->findData(kKeyVorbisInfo, &type, &data, &size)); - - ogg_buffer buf; - ogg_reference ref; - oggpack_buffer bits; - makeBitReader((const uint8_t *)data + 7, size - 7, &buf, &ref, &bits); - CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits)); - - /////////////////////////////////////////////////////////////////////////// - - CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size)); - - makeBitReader((const uint8_t *)data + 7, size - 7, &buf, &ref, &bits); - CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits)); - - /////////////////////////////////////////////////////////////////////////// - - mState = new vorbis_dsp_state; - CHECK_EQ(0, vorbis_dsp_init(mState, mVi)); - - mAnchorTimeUs = 0; - mNumFramesOutput = 0; - - // If the source never limits the number of valid frames contained - // in the input data, we'll assume that all of the decoded frames are - // valid. - mNumFramesLeftOnPage = -1; - - mStarted = true; - - return OK; -} - -status_t VorbisDecoder::stop() { - CHECK(mStarted); - - vorbis_dsp_clear(mState); - delete mState; - mState = NULL; - - vorbis_info_clear(mVi); - delete mVi; - mVi = NULL; - - delete mBufferGroup; - mBufferGroup = NULL; - - mSource->stop(); - - mStarted = false; - - return OK; -} - -sp<MetaData> VorbisDecoder::getFormat() { - sp<MetaData> srcFormat = mSource->getFormat(); - - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - meta->setInt32(kKeyChannelCount, mNumChannels); - meta->setInt32(kKeySampleRate, mSampleRate); - - int64_t durationUs; - if (srcFormat->findInt64(kKeyDuration, &durationUs)) { - meta->setInt64(kKeyDuration, durationUs); - } - - meta->setCString(kKeyDecoderComponent, "VorbisDecoder"); - - return meta; -} - -int VorbisDecoder::decodePacket(MediaBuffer *packet, MediaBuffer *out) { - ogg_buffer buf; - buf.data = (uint8_t *)packet->data() + packet->range_offset(); - buf.size = packet->range_length(); - buf.refcount = 1; - buf.ptr.owner = NULL; - - ogg_reference ref; - ref.buffer = &buf; - ref.begin = 0; - ref.length = packet->range_length(); - ref.next = NULL; - - ogg_packet pack; - pack.packet = &ref; - pack.bytes = packet->range_length(); - pack.b_o_s = 0; - pack.e_o_s = 0; - pack.granulepos = 0; - pack.packetno = 0; - - int numFrames = 0; - - int err = vorbis_dsp_synthesis(mState, &pack, 1); - if (err != 0) { - LOGW("vorbis_dsp_synthesis returned %d", err); - } else { - numFrames = vorbis_dsp_pcmout( - mState, (int16_t *)out->data(), kMaxNumSamplesPerBuffer); - - if (numFrames < 0) { - LOGE("vorbis_dsp_pcmout returned %d", numFrames); - numFrames = 0; - } - } - - if (mNumFramesLeftOnPage >= 0) { - if (numFrames > mNumFramesLeftOnPage) { - LOGV("discarding %d frames at end of page", - numFrames - mNumFramesLeftOnPage); - numFrames = mNumFramesLeftOnPage; - } - mNumFramesLeftOnPage -= numFrames; - } - - out->set_range(0, numFrames * sizeof(int16_t) * mNumChannels); - - return numFrames; -} - -status_t VorbisDecoder::read( - MediaBuffer **out, const ReadOptions *options) { - status_t err; - - *out = NULL; - - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - CHECK(seekTimeUs >= 0); - - mNumFramesOutput = 0; - vorbis_dsp_restart(mState); - } else { - seekTimeUs = -1; - } - - MediaBuffer *inputBuffer; - err = mSource->read(&inputBuffer, options); - - if (err != OK) { - return ERROR_END_OF_STREAM; - } - - int64_t timeUs; - if (inputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) { - mAnchorTimeUs = timeUs; - mNumFramesOutput = 0; - } else { - // We must have a new timestamp after seeking. - CHECK(seekTimeUs < 0); - } - - int32_t numPageSamples; - if (inputBuffer->meta_data()->findInt32( - kKeyValidSamples, &numPageSamples)) { - CHECK(numPageSamples >= 0); - mNumFramesLeftOnPage = numPageSamples; - } - - MediaBuffer *outputBuffer; - CHECK_EQ(mBufferGroup->acquire_buffer(&outputBuffer), OK); - - int numFrames = decodePacket(inputBuffer, outputBuffer); - - inputBuffer->release(); - inputBuffer = NULL; - - outputBuffer->meta_data()->setInt64( - kKeyTime, - mAnchorTimeUs - + (mNumFramesOutput * 1000000ll) / mSampleRate); - - mNumFramesOutput += numFrames; - - *out = outputBuffer; - - return OK; -} - -} // namespace android diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index e069b4d..95f2ae8 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -291,7 +291,7 @@ private: void finishSeekIfNecessary(int64_t videoTimeUs); void ensureCacheIsFetching_l(); - status_t startAudioPlayer_l(); + status_t startAudioPlayer_l(bool sendErrorNotification = true); void postAudioSeekComplete_l(); void shutdownVideoDecoder_l(); diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h index 2e25dd9..0e9af69 100644 --- a/media/libstagefright/include/HTTPBase.h +++ b/media/libstagefright/include/HTTPBase.h @@ -53,6 +53,8 @@ struct HTTPBase : public DataSource { static sp<HTTPBase> Create(uint32_t flags = 0); + static void RegisterSocketUser(int s, uid_t uid); + protected: void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h deleted file mode 100644 index 88ba9d6..0000000 --- a/media/libstagefright/include/HTTPStream.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2009 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 HTTP_STREAM_H_ - -#define HTTP_STREAM_H_ - -#include <sys/types.h> - -#include <media/stagefright/foundation/AString.h> -#include <media/stagefright/MediaErrors.h> -#include <utils/KeyedVector.h> -#include <utils/threads.h> - -namespace android { - -class HTTPStream { -public: - HTTPStream(); - ~HTTPStream(); - - void setUID(uid_t uid); - - status_t connect(const char *server, int port = -1, bool https = false); - status_t disconnect(); - - status_t send(const char *data, size_t size); - - // Assumes data is a '\0' terminated string. - status_t send(const char *data); - - // Receive up to "size" bytes of data. - ssize_t receive(void *data, size_t size); - - status_t receive_header(int *http_status); - - // The header key used to retrieve the status line. - static const char *kStatusKey; - - bool find_header_value( - const AString &key, AString *value) const; - - // Pass a negative value to disable the timeout. - void setReceiveTimeout(int seconds); - - // Receive a line of data terminated by CRLF, line will be '\0' terminated - // _excluding_ the termianting CRLF. - status_t receive_line(char *line, size_t size); - - static void RegisterSocketUser(int s, uid_t uid); - -private: - enum State { - READY, - CONNECTING, - CONNECTED - }; - - State mState; - Mutex mLock; - - bool mUIDValid; - uid_t mUID; - - int mSocket; - - KeyedVector<AString, AString> mHeaders; - - void *mSSLContext; - void *mSSL; - - HTTPStream(const HTTPStream &); - HTTPStream &operator=(const HTTPStream &); -}; - -} // namespace android - -#endif // HTTP_STREAM_H_ diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h deleted file mode 100644 index c265b3a..0000000 --- a/media/libstagefright/include/NuHTTPDataSource.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 NU_HTTP_DATA_SOURCE_H_ - -#define NU_HTTP_DATA_SOURCE_H_ - -#include <utils/List.h> -#include <utils/String8.h> -#include <utils/threads.h> - -#include "HTTPStream.h" -#include "include/HTTPBase.h" - -namespace android { - -struct NuHTTPDataSource : public HTTPBase { - NuHTTPDataSource(uint32_t flags = 0); - - virtual status_t connect( - const char *uri, - const KeyedVector<String8, String8> *headers = NULL, - off64_t offset = 0); - - virtual void disconnect(); - - virtual status_t initCheck() const; - - virtual ssize_t readAt(off64_t offset, void *data, size_t size); - virtual status_t getSize(off64_t *size); - virtual uint32_t flags(); - - virtual sp<DecryptHandle> DrmInitialization(); - virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client); - virtual String8 getUri(); - - virtual String8 getMIMEType() const; - -protected: - virtual ~NuHTTPDataSource(); - -private: - enum State { - DISCONNECTED, - CONNECTING, - CONNECTED - }; - - Mutex mLock; - - uint32_t mFlags; - - State mState; - - String8 mHost; - unsigned mPort; - String8 mPath; - bool mHTTPS; - String8 mHeaders; - String8 mUri; - - HTTPStream mHTTP; - off64_t mOffset; - off64_t mContentLength; - bool mContentLengthValid; - bool mHasChunkedTransferEncoding; - - String8 mContentType; - - // The number of data bytes in the current chunk before any subsequent - // chunk header (or -1 if no more chunks). - ssize_t mChunkDataBytesLeft; - - sp<DecryptHandle> mDecryptHandle; - DrmManagerClient *mDrmManagerClient; - - status_t connect( - const char *uri, const String8 &headers, off64_t offset); - - status_t connect( - const char *host, unsigned port, const char *path, - bool https, - const String8 &headers, - off64_t offset); - - // Read up to "size" bytes of data, respect transfer encoding. - ssize_t internalRead(void *data, size_t size); - - void applyTimeoutResponse(); - - static void MakeFullHeaders( - const KeyedVector<String8, String8> *overrides, - String8 *headers); - - NuHTTPDataSource(const NuHTTPDataSource &); - NuHTTPDataSource &operator=(const NuHTTPDataSource &); -}; - -} // namespace android - -#endif // NU_HTTP_DATA_SOURCE_H_ diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index ec3e5fa..d54b1c1 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -121,6 +121,7 @@ protected: virtual ~OMX(); private: + struct CallbackDispatcherThread; struct CallbackDispatcher; Mutex mLock; diff --git a/media/libstagefright/include/ThreadedSource.h b/media/libstagefright/include/ThreadedSource.h deleted file mode 100644 index c67295c..0000000 --- a/media/libstagefright/include/ThreadedSource.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 THREADED_SOURCE_H_ - -#define THREADED_SOURCE_H_ - -#include <media/stagefright/foundation/ABase.h> -#include <media/stagefright/foundation/AHandlerReflector.h> -#include <media/stagefright/foundation/ALooper.h> -#include <media/stagefright/MediaSource.h> -#include <utils/threads.h> - -namespace android { - -struct ThreadedSource : public MediaSource { - ThreadedSource(const sp<MediaSource> &source); - - virtual status_t start(MetaData *params); - virtual status_t stop(); - - virtual sp<MetaData> getFormat(); - - virtual status_t read( - MediaBuffer **buffer, const ReadOptions *options); - - virtual void onMessageReceived(const sp<AMessage> &msg); - -protected: - virtual ~ThreadedSource(); - -private: - enum { - kWhatDecodeMore = 'deco', - kWhatSeek = 'seek', - }; - - sp<MediaSource> mSource; - sp<AHandlerReflector<ThreadedSource> > mReflector; - sp<ALooper> mLooper; - - Mutex mLock; - Condition mCondition; - List<MediaBuffer *> mQueue; - status_t mFinalResult; - bool mDecodePending; - bool mStarted; - - int64_t mSeekTimeUs; - ReadOptions::SeekMode mSeekMode; - - void postDecodeMore_l(); - void clearQueue_l(); - - DISALLOW_EVIL_CONSTRUCTORS(ThreadedSource); -}; - -} // namespace android - -#endif // THREADED_SOURCE_H_ diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index 08ad6f3..d844f3d 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -23,14 +23,7 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libcutils \ libstagefright_foundation \ - -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) - LOCAL_LDLIBS += -lpthread -ldl -endif - -ifneq ($(TARGET_SIMULATOR),true) -LOCAL_SHARED_LIBRARIES += libdl -endif + libdl LOCAL_MODULE:= libstagefright_omx diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index d23aa3a..25f30d6 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -20,8 +20,6 @@ #include <dlfcn.h> -#include <sys/prctl.h> - #include "../include/OMX.h" #include "../include/OMXNodeInstance.h" @@ -38,11 +36,32 @@ namespace android { //////////////////////////////////////////////////////////////////////////////// +// This provides the underlying Thread used by CallbackDispatcher. +// Note that deriving CallbackDispatcher from Thread does not work. + +struct OMX::CallbackDispatcherThread : public Thread { + CallbackDispatcherThread(CallbackDispatcher *dispatcher) + : mDispatcher(dispatcher) { + } + +private: + CallbackDispatcher *mDispatcher; + + bool threadLoop(); + + CallbackDispatcherThread(const CallbackDispatcherThread &); + CallbackDispatcherThread &operator=(const CallbackDispatcherThread &); +}; + +//////////////////////////////////////////////////////////////////////////////// + struct OMX::CallbackDispatcher : public RefBase { CallbackDispatcher(OMXNodeInstance *owner); void post(const omx_message &msg); + bool loop(); + protected: virtual ~CallbackDispatcher(); @@ -54,13 +73,10 @@ private: Condition mQueueChanged; List<omx_message> mQueue; - pthread_t mThread; + sp<CallbackDispatcherThread> mThread; void dispatch(const omx_message &msg); - static void *ThreadWrapper(void *me); - void threadEntry(); - CallbackDispatcher(const CallbackDispatcher &); CallbackDispatcher &operator=(const CallbackDispatcher &); }; @@ -68,13 +84,8 @@ private: OMX::CallbackDispatcher::CallbackDispatcher(OMXNodeInstance *owner) : mOwner(owner), mDone(false) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - - pthread_create(&mThread, &attr, ThreadWrapper, this); - - pthread_attr_destroy(&attr); + mThread = new CallbackDispatcherThread(this); + mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_AUDIO); } OMX::CallbackDispatcher::~CallbackDispatcher() { @@ -86,10 +97,8 @@ OMX::CallbackDispatcher::~CallbackDispatcher() { } // Don't call join on myself - CHECK(mThread != pthread_self()); - - void *dummy; - pthread_join(mThread, &dummy); + status_t status = mThread->join(); + CHECK(status == NO_ERROR); } void OMX::CallbackDispatcher::post(const omx_message &msg) { @@ -107,17 +116,7 @@ void OMX::CallbackDispatcher::dispatch(const omx_message &msg) { mOwner->onMessage(msg); } -// static -void *OMX::CallbackDispatcher::ThreadWrapper(void *me) { - static_cast<CallbackDispatcher *>(me)->threadEntry(); - - return NULL; -} - -void OMX::CallbackDispatcher::threadEntry() { - androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); - prctl(PR_SET_NAME, (unsigned long)"OMXCallbackDisp", 0, 0, 0); - +bool OMX::CallbackDispatcher::loop() { for (;;) { omx_message msg; @@ -137,6 +136,14 @@ void OMX::CallbackDispatcher::threadEntry() { dispatch(msg); } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// + +bool OMX::CallbackDispatcherThread::threadLoop() { + return mDispatcher->loop(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index 072d6b2..b398c9d 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -34,7 +34,7 @@ #include <openssl/md5.h> #include <sys/socket.h> -#include "HTTPStream.h" +#include "HTTPBase.h" namespace android { @@ -251,7 +251,7 @@ void ARTSPConnection::onConnect(const sp<AMessage> &msg) { mSocket = socket(AF_INET, SOCK_STREAM, 0); if (mUIDValid) { - HTTPStream::RegisterSocketUser(mSocket, mUID); + HTTPBase::RegisterSocketUser(mSocket, mUID); } MakeSocketBlocking(mSocket, false); diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 3188959..71d68f6 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -40,7 +40,7 @@ #include <sys/socket.h> #include <netdb.h> -#include "HTTPStream.h" +#include "HTTPBase.h" // If no access units are received within 5 secs, assume that the rtp // stream has ended and signal end of stream. @@ -1181,8 +1181,8 @@ private: &info->mRTPSocket, &info->mRTCPSocket, &rtpPort); if (mUIDValid) { - HTTPStream::RegisterSocketUser(info->mRTPSocket, mUID); - HTTPStream::RegisterSocketUser(info->mRTCPSocket, mUID); + HTTPBase::RegisterSocketUser(info->mRTPSocket, mUID); + HTTPBase::RegisterSocketUser(info->mRTCPSocket, mUID); } request.append("Transport: RTP/AVP/UDP;unicast;client_port="); diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk index c25285e..e590bab 100644 --- a/media/mtp/Android.mk +++ b/media/mtp/Android.mk @@ -16,8 +16,6 @@ LOCAL_PATH:= $(call my-dir) -ifneq ($(TARGET_SIMULATOR),true) - include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ @@ -45,8 +43,6 @@ LOCAL_SHARED_LIBRARIES := libutils libcutils libusbhost libbinder include $(BUILD_SHARED_LIBRARY) -endif - ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index bc04e8c..4047e2e 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -237,6 +237,8 @@ void MtpServer::run() { if (mSessionOpen) mDatabase->sessionEnded(); + close(fd); + mFD = -1; } void MtpServer::sendObjectAdded(MtpObjectHandle handle) { @@ -871,6 +873,14 @@ MtpResponseCode MtpServer::doSendObjectInfo() { // check space first if (mSendObjectFileSize > storage->getFreeSpace()) return MTP_RESPONSE_STORAGE_FULL; + uint64_t maxFileSize = storage->getMaxFileSize(); + // check storage max file size + if (maxFileSize != 0) { + // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size + // is >= 0xFFFFFFFF + if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF) + return MTP_RESPONSE_OBJECT_TOO_LARGE; + } LOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID); MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp index fef8066..941e303 100644 --- a/media/mtp/MtpStorage.cpp +++ b/media/mtp/MtpStorage.cpp @@ -33,11 +33,13 @@ namespace android { MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, - const char* description, uint64_t reserveSpace, bool removable) + const char* description, uint64_t reserveSpace, + bool removable, uint64_t maxFileSize) : mStorageID(id), mFilePath(filePath), mDescription(description), mMaxCapacity(0), + mMaxFileSize(maxFileSize), mReserveSpace(reserveSpace), mRemovable(removable) { diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h index 3e4f40d..e5a2e57 100644 --- a/media/mtp/MtpStorage.h +++ b/media/mtp/MtpStorage.h @@ -31,6 +31,7 @@ private: MtpString mFilePath; MtpString mDescription; uint64_t mMaxCapacity; + uint64_t mMaxFileSize; // amount of free space to leave unallocated uint64_t mReserveSpace; bool mRemovable; @@ -38,7 +39,7 @@ private: public: MtpStorage(MtpStorageID id, const char* filePath, const char* description, uint64_t reserveSpace, - bool removable); + bool removable, uint64_t maxFileSize); virtual ~MtpStorage(); inline MtpStorageID getStorageID() const { return mStorageID; } @@ -50,6 +51,7 @@ public: const char* getDescription() const; inline const char* getPath() const { return (const char *)mFilePath; } inline bool isRemovable() const { return mRemovable; } + inline uint64_t getMaxFileSize() const { return mMaxFileSize; } }; }; // namespace android diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java index 5120694..afe2001 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java @@ -1335,16 +1335,19 @@ public class VideoEditorAPITest extends @LargeTest public void testEffectKenBurn() throws Exception { // Test ken burn effect using a JPEG file. - testEffectKenBurn(INPUT_FILE_PATH + "IMG_640x480.jpg"); + testEffectKenBurn(INPUT_FILE_PATH + "IMG_640x480.jpg", + "mediaImageItem1"); // Test ken burn effect using a PNG file - testEffectKenBurn(INPUT_FILE_PATH + "IMG_640x480.png"); + testEffectKenBurn(INPUT_FILE_PATH + "IMG_640x480.png", + "mediaImageItem2"); } - private void testEffectKenBurn(final String imageItemFileName) throws Exception { + private void testEffectKenBurn(final String imageItemFileName, + final String MediaId) throws Exception { final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; final MediaImageItem mediaImageItem = - mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + mVideoEditorHelper.createMediaItem(mVideoEditor, MediaId, imageItemFileName, 5000, imageItemRenderingMode); mVideoEditor.addMediaItem(mediaImageItem); diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 32d1a23..3f547fd 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -88,7 +88,7 @@ import android.view.SurfaceView; * well as how many bits are allocated to each channel. Therefore, the first thing * GLSurfaceView has to do when starting to render is choose what EGLConfig to use. * <p> - * By default GLSurfaceView chooses a EGLConfig that has an RGB_656 pixel format, + * By default GLSurfaceView chooses a EGLConfig that has an RGB_565 pixel format, * with at least a 16-bit depth buffer and no stencil. * <p> * If you would prefer a different EGLConfig diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk index b5c018f..15e58f2 100644 --- a/opengl/libagl/Android.mk +++ b/opengl/libagl/Android.mk @@ -38,15 +38,13 @@ ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif -ifneq ($(TARGET_SIMULATOR),true) - # we need to access the private Bionic header <bionic_tls.h> - # on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER - # behavior from the bionic Android.mk file - ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) - LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER - endif - LOCAL_C_INCLUDES += bionic/libc/private +# we need to access the private Bionic header <bionic_tls.h> +# on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER +# behavior from the bionic Android.mk file +ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif +LOCAL_C_INCLUDES += bionic/libc/private LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl LOCAL_MODULE:= libGLES_android diff --git a/opengl/libagl2/Android.mk b/opengl/libagl2/Android.mk index 564932f..b442a2d 100644 --- a/opengl/libagl2/Android.mk +++ b/opengl/libagl2/Android.mk @@ -39,15 +39,13 @@ ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif -ifneq ($(TARGET_SIMULATOR),true) - # we need to access the private Bionic header <bionic_tls.h> - # on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER - # behavior from the bionic Android.mk file - ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) - LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER - endif - LOCAL_C_INCLUDES += bionic/libc/private +# we need to access the private Bionic header <bionic_tls.h> +# on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER +# behavior from the bionic Android.mk file +ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif +LOCAL_C_INCLUDES += bionic/libc/private LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl #replace libagl for now diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk index ff45edc..3e66a13 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -21,18 +21,14 @@ LOCAL_SHARED_LIBRARIES += libcutils libutils libGLESv2_dbg LOCAL_LDLIBS := -lpthread -ldl LOCAL_MODULE:= libEGL LOCAL_LDFLAGS += -Wl,--exclude-libs=ALL -# needed on sim build because of weird logging issues -ifeq ($(TARGET_SIMULATOR),true) -else - LOCAL_SHARED_LIBRARIES += libdl - # Bionic's private TLS header relies on the ARCH_ARM_HAVE_TLS_REGISTER to - # select the appropriate TLS codepath - ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) - LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER - endif - # we need to access the private Bionic header <bionic_tls.h> - LOCAL_C_INCLUDES += bionic/libc/private +LOCAL_SHARED_LIBRARIES += libdl +# Bionic's private TLS header relies on the ARCH_ARM_HAVE_TLS_REGISTER to +# select the appropriate TLS codepath +ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif +# we need to access the private Bionic header <bionic_tls.h> +LOCAL_C_INCLUDES += bionic/libc/private LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES @@ -81,16 +77,12 @@ LOCAL_SHARED_LIBRARIES += libcutils libEGL LOCAL_LDLIBS := -lpthread -ldl LOCAL_MODULE:= libGLESv1_CM -# needed on sim build because of weird logging issues -ifeq ($(TARGET_SIMULATOR),true) -else - LOCAL_SHARED_LIBRARIES += libdl - # we need to access the private Bionic header <bionic_tls.h> - ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) - LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER - endif - LOCAL_C_INCLUDES += bionic/libc/private +LOCAL_SHARED_LIBRARIES += libdl +# we need to access the private Bionic header <bionic_tls.h> +ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif +LOCAL_C_INCLUDES += bionic/libc/private LOCAL_CFLAGS += -DLOG_TAG=\"libGLESv1\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES @@ -117,16 +109,12 @@ LOCAL_SHARED_LIBRARIES += libcutils libEGL LOCAL_LDLIBS := -lpthread -ldl LOCAL_MODULE:= libGLESv2 -# needed on sim build because of weird logging issues -ifeq ($(TARGET_SIMULATOR),true) -else - LOCAL_SHARED_LIBRARIES += libdl - # we need to access the private Bionic header <bionic_tls.h> - ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) - LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER - endif - LOCAL_C_INCLUDES += bionic/libc/private +LOCAL_SHARED_LIBRARIES += libdl +# we need to access the private Bionic header <bionic_tls.h> +ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif +LOCAL_C_INCLUDES += bionic/libc/private LOCAL_CFLAGS += -DLOG_TAG=\"libGLESv2\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index b11db32..ddad2d3 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -31,6 +31,7 @@ #include <cutils/properties.h> #include <cutils/memory.h> +#include <utils/CallStack.h> #include <utils/String8.h> #include "egldefs.h" @@ -147,6 +148,10 @@ static int gl_no_context() { if (egl_tls_t::logNoContextCall()) { LOGE("call to OpenGL ES API with no current context " "(logged once per thread)"); + LOGE("call stack before error:"); + CallStack stack; + stack.update(); + stack.dump(); } return 0; } diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 7d5d010..ba5d29a 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -367,7 +367,12 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, if (cnx->egl.eglGetConfigAttrib(iDpy, iConfig, EGL_NATIVE_VISUAL_ID, &format)) { if (format != 0) { - native_window_set_buffers_geometry(window, 0, 0, format); + int err = native_window_set_buffers_format(window, format); + if (err != 0) { + LOGE("error setting native window pixel format: %s (%d)", + strerror(-err), err); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } } } diff --git a/opengl/libs/GLES2_dbg/Android.mk b/opengl/libs/GLES2_dbg/Android.mk index 9f6e68c..c2b1142 100644 --- a/opengl/libs/GLES2_dbg/Android.mk +++ b/opengl/libs/GLES2_dbg/Android.mk @@ -31,15 +31,13 @@ ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif -ifneq ($(TARGET_SIMULATOR),true) - # we need to access the private Bionic header <bionic_tls.h> - # on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER - # behavior from the bionic Android.mk file - ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) - LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER - endif - LOCAL_C_INCLUDES += bionic/libc/private +# we need to access the private Bionic header <bionic_tls.h> +# on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER +# behavior from the bionic Android.mk file +ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif +LOCAL_C_INCLUDES += bionic/libc/private LOCAL_MODULE:= libGLESv2_dbg LOCAL_MODULE_TAGS := optional diff --git a/opengl/libs/GLES2_dbg/test/Android.mk b/opengl/libs/GLES2_dbg/test/Android.mk index 14a84b4..8708d43 100644 --- a/opengl/libs/GLES2_dbg/test/Android.mk +++ b/opengl/libs/GLES2_dbg/test/Android.mk @@ -27,9 +27,7 @@ LOCAL_MODULE:= libGLESv2_dbg_test ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER endif -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_C_INCLUDES += bionic/libc/private -endif +LOCAL_C_INCLUDES += bionic/libc/private LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES diff --git a/opengl/specs/EGL_ANDROID_blob_cache.txt b/opengl/specs/EGL_ANDROID_blob_cache.txt new file mode 100644 index 0000000..55dc900 --- /dev/null +++ b/opengl/specs/EGL_ANDROID_blob_cache.txt @@ -0,0 +1,208 @@ +Name + + ANDROID_blob_cache + +Name Strings + + EGL_ANDROID_blob_cache + +Contributors + + Jamie Gennis + +Contact + + Jamie Gennis, Google Inc. (jgennis 'at' google.com) + +Status + + Draft. + +Version + + Version 1, April 22, 2011 + +Number + + EGL Extension #XXX + +Dependencies + + Requires EGL 1.0 + + This extension is written against the wording of the EGL 1.4 Specification + +Overview + + Shader compilation and optimization has been a troublesome aspect of OpenGL + programming for a long time. It can consume seconds of CPU cycles during + application start-up. Additionally, state-based re-compiles done + internally by the drivers add an unpredictable element to application + performance tuning, often leading to occasional pauses in otherwise smooth + animations. + + This extension provides a mechanism through which client API + implementations may cache shader binaries after they are compiled. It may + then retrieve those cached shaders during subsequent executions of the same + program. The management of the cache is handled by the application (or + middleware), allowing it to be tuned to a particular platform or + environment. + + While the focus of this extension is on providing a persistent cache for + shader binaries, it may also be useful for caching other data. This is + perfectly acceptable, but the guarantees provided (or lack thereof) were + designed around the shader use case. + + Note that although this extension is written as if the application + implements the caching functionality, on the Android OS it is implemented + as part of the Android EGL module. This extension is not exposed to + applications on Android, but will be used automatically in every + application that uses EGL if it is supported by the underlying + device-specific EGL implementation. + +New Types + + /* + * EGLsizei is a signed integer type for representing the size of a memory + * buffer. + */ + #include <khrplatform.h> + typedef khronos_ssize_t EGLsizei; + + /* + * EGLSetBlobFunc is a pointer to an application-provided function that a + * client API implementation may use to insert a key/value pair into the + * cache. + */ + typedef void (*EGLSetBlobFunc) (const void* key, EGLsizei keySize, + const void* value, EGLsizei valueSize) + + /* + * EGLGetBlobFunc is a pointer to an application-provided function that a + * client API implementation may use to retrieve a cached value from the + * cache. + */ + typedef EGLsizei (*EGLGetBlobFunc) (const void* key, EGLsizei keySize, + void* value, EGLsizei valueSize) + +New Procedures and Functions + + void eglSetBlobCacheFuncs(EGLDisplay dpy, + EGLSetBlobFunc set, + EGLGetBlobFunc get); + +New Tokens + + None. + +Changes to Chapter 3 of the EGL 1.4 Specification (EGL Functions and Errors) + + Add a new subsection after Section 3.8, page 50 + (Synchronization Primitives) + + "3.9 Persistent Caching + + In order to facilitate persistent caching of internal client API state that + is slow to compute or collect, the application may specify callback + function pointers through which the client APIs can request data be cached + and retrieved. The command + + void eglSetBlobCacheFuncs(EGLDisplay dpy, + EGLSetBlobFunc set, EGLGetBlobFunc get); + + sets the callback function pointers that client APIs associated with + display <dpy> can use to interact with caching functionality provided by + the application. <set> points to a function that inserts a new value into + the cache and associates it with the given key. <get> points to a function + that retrieves from the cache the value associated with a given key. The + semantics of these callback functions are described in Section 3.9.1 (Cache + Operations). + + Cache functions may only be specified once during the lifetime of an + EGLDisplay. The <set> and <get> functions may be called at any time and + from any thread from the time at which eglSetBlobCacheFuncs is called until + the time that the last resource associated with <dpy> is deleted and <dpy> + itself is terminated. Concurrent calls to these functions from different + threads is also allowed. + + If eglSetBlobCacheFuncs generates an error then all client APIs must behave + as though eglSetBlobCacheFuncs was not called for the display <dpy>. If + <set> or <get> is NULL then an EGL_BAD_PARAMETER error is generated. If a + successful eglSetBlobCacheFuncs call was already made for <dpy> and the + display has not since been terminated then an EGL_BAD_PARAMETER error is + generated. + + 3.9.1 Cache Operations + + To insert a new binary value into the cache and associate it with a given + key, a client API implementation can call the application-provided callback + function + + void (*set) (const void* key, EGLsizei keySize, const void* value, + EGLsizei valueSize) + + <key> and <value> are pointers to the beginning of the key and value, + respectively, that are to be inserted. <keySize> and <valueSize> specify + the size in bytes of the data pointed to by <key> and <value>, + respectively. + + No guarantees are made as to whether a given key/value pair is present in + the cache after the set call. If a different value has been associated + with the given key in the past then it is undefined which value, if any, is + associated with the key after the set call. Note that while there are no + guarantees, the cache implementation should attempt to cache the most + recently set value for a given key. + + To retrieve the binary value associated with a given key from the cache, a + client API implementation can call the application-provided callback + function + + EGLsizei (*get) (const void* key, EGLsizei keySize, void* value, + EGLsizei valueSize) + + <key> is a pointer to the beginning of the key. <keySize> specifies the + size in bytes of the binary key pointed to by <key>. If the cache contains + a value associated with the given key then the size of that binary value in + bytes is returned. Otherwise 0 is returned. + + If the cache contains a value for the given key and its size in bytes is + less than or equal to <valueSize> then the value is written to the memory + pointed to by <value>. Otherwise nothing is written to the memory pointed + to by <value>. + +Issues + + 1. How should errors be handled in the callback functions? + + RESOLVED: No guarantees are made about the presence of values in the cache, + so there should not be a need to return error information to the client API + implementation. The cache implementation can simply drop a value if it + encounters an error during the 'set' callback. Similarly, it can simply + return 0 if it encouters an error in a 'get' callback. + + 2. When a client API driver gets updated, that may need to invalidate + previously cached entries. How can the driver handle this situation? + + RESPONSE: There are a number of ways the driver can handle this situation. + The recommended way is to include the driver version in all cache keys. + That way each driver version will use a set of cache keys that are unique + to that version, and conflicts should never occur. Updating the driver + could then leave a number of values in the cache that will never be + requested again. If needed, the cache implementation can handle those + values in some way, but the driver does not need to take any special + action. + + 3. How much data can be stored in the cache? + + RESPONSE: This is entirely dependent upon the cache implementation. + Presumably it will be tuned to store enough data to be useful, but not + enough to become problematic. :) + +Revision History + +#2 (Jamie Gennis, April 25, 2011) + - Swapped the order of the size and pointer arguments to the get and set + functions. + +#1 (Jamie Gennis, April 22, 2011) + - Initial draft. diff --git a/opengl/specs/EGL_ANDROID_recordable.txt b/opengl/specs/EGL_ANDROID_recordable.txt new file mode 100644 index 0000000..cf44465 --- /dev/null +++ b/opengl/specs/EGL_ANDROID_recordable.txt @@ -0,0 +1,113 @@ +Name + + ANDROID_recordable + +Name Strings + + EGL_ANDROID_recordable + +Contributors + + Jamie Gennis + +Contact + + Jamie Gennis, Google Inc. (jgennis 'at' google.com) + +Status + + Draft. + +Version + + Version 1, July 8, 2011 + +Number + + EGL Extension #XXX + +Dependencies + + Requires EGL 1.0 + + This extension is written against the wording of the EGL 1.4 Specification + +Overview + + Android supports a number of different ANativeWindow implementations that + can be used to create an EGLSurface. One implementation, which records the + rendered image as a video each time eglSwapBuffers gets called, may have + some device-specific restrictions. Because of this, some EGLConfigs may be + incompatible with these ANativeWindows. This extension introduces a new + boolean EGLConfig attribute that indicates whether the EGLConfig supports + rendering to an ANativeWindow that records images to a video. + +New Types + + None. + +New Procedures and Functions + + None. + +New Tokens + + Accepted by the <attribute> parameter of eglGetConfigAttrib and + the <attrib_list> parameter of eglChooseConfig: + + EGL_RECORDABLE_ANDROID 0xXXXX + +Changes to Chapter 3 of the EGL 1.4 Specification (EGL Functions and Errors) + + Section 3.4, Configuration Management, add a row to Table 3.1. + + Attribute Type Notes + ---------------------- ------- -------------------------- + EGL_RECORDABLE_ANDROID boolean whether video recording is + supported + + Section 3.4, Configuration Management, add a row to Table 3.4. + + Attribute Default Selection Sort Sort + Criteria Order Priority + ---------------------- ------------- --------- ----- -------- + EGL_RECORDABLE_ANDROID EGL_DONT_CARE Exact None + + Section 3.4, Configuration Management, add a paragraph at the end of the + subsection titled Other EGLConfig Attribute Descriptions. + + EGL_RECORDABLE_ANDROID is a boolean indicating whether the config may + be used to create an EGLSurface from an ANativeWindow that is a video + recorder as indicated by the NATIVE_WINDOW_IS_VIDEO_RECORDER query on + the ANativeWindow. + + Section 3.4.1, Querying Configurations, change the last paragraph as follow + + EGLConfigs are not sorted with respect to the parameters + EGL_BIND_TO_TEXTURE_RGB, EGL_BIND_TO_TEXTURE_RGBA, EGL_CONFORMANT, + EGL_LEVEL, EGL_NATIVE_RENDERABLE, EGL_MAX_SWAP_INTERVAL, + EGL_MIN_SWAP_INTERVAL, EGL_RENDERABLE_TYPE, EGL_SURFACE_TYPE, + EGL_TRANSPARENT_TYPE, EGL_TRANSPARENT_RED_VALUE, + EGL_TRANSPARENT_GREEN_VALUE, EGL_TRANSPARENT_BLUE_VALUE, and + EGL_RECORDABLE_ANDROID. + +Issues + + 1. Should this functionality be exposed as a new attribute or as a bit in + the EGL_SURFACE_TYPE bitfield? + + RESOLVED: It should be a new attribute. It does not make sense to use up a + bit in the limit-size bitfield for a platform-specific extension. + + 2. How should the new attribute affect the sorting of EGLConfigs? + + RESOLVED: It should not affect sorting. Some implementations may not have + any drawback associated with using a recordable EGLConfig. Such + implementations should not have to double-up some of their configs to one sort earlier than . + Implementations that do have drawbacks can use the existing caveat + mechanism to report this drawback to the client. + +Revision History + +#1 (Jamie Gennis, July 8, 2011) + - Initial draft. diff --git a/opengl/specs/README b/opengl/specs/README new file mode 100644 index 0000000..2fa2587 --- /dev/null +++ b/opengl/specs/README @@ -0,0 +1,12 @@ +This directory contains OpenGL ES and EGL extension specifications that have +been or are being defined for Android. + +The table below tracks usage of EGL enumerant values that have been reserved +for use by Android extensions. + + Value Extension +---------------- ---------------------------------- +0x3140 EGL_ANDROID_image_native_buffer +0x3141 (unused) +0x3142 EGL_ANDROID_recordable +0x3143 - 0x314F (unused) diff --git a/opengl/tests/EGLTest/Android.mk b/opengl/tests/EGLTest/Android.mk new file mode 100644 index 0000000..92d7eb1 --- /dev/null +++ b/opengl/tests/EGLTest/Android.mk @@ -0,0 +1,37 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := EGL_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + EGL_test.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libEGL \ + libcutils \ + libstlport \ + libutils \ + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main \ + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + +include $(BUILD_EXECUTABLE) + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp new file mode 100644 index 0000000..337ad33 --- /dev/null +++ b/opengl/tests/EGLTest/EGL_test.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2011 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 <gtest/gtest.h> + +#include <utils/String8.h> + +#include <EGL/egl.h> + +namespace android { + +class EGLTest : public ::testing::Test { +protected: + EGLDisplay mEglDisplay; + +protected: + EGLTest() : + mEglDisplay(EGL_NO_DISPLAY) { + } + + virtual void SetUp() { + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EGLint majorVersion; + EGLint minorVersion; + EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + RecordProperty("EglVersionMajor", majorVersion); + RecordProperty("EglVersionMajor", minorVersion); + } + + virtual void TearDown() { + EGLBoolean success = eglTerminate(mEglDisplay); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + } +}; + +TEST_F(EGLTest, DISABLED_EGLConfigEightBitFirst) { + + EGLint numConfigs; + EGLConfig config; + EGLBoolean success; + EGLint attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_GE(numConfigs, 1); + + EGLint components[3]; + + success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EXPECT_GE(components[0], 8); + EXPECT_GE(components[1], 8); + EXPECT_GE(components[2], 8); +} + +TEST_F(EGLTest, EGLConfigRGBA8888First) { + + EGLint numConfigs; + EGLConfig config; + EGLBoolean success; + EGLint attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE + }; + + success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_GE(numConfigs, 1); + + EGLint components[4]; + + success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + success = eglGetConfigAttrib(mEglDisplay, config, EGL_ALPHA_SIZE, &components[3]); + ASSERT_EQ(EGL_TRUE, success); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EXPECT_GE(components[0], 8); + EXPECT_GE(components[1], 8); + EXPECT_GE(components[2], 8); + EXPECT_GE(components[3], 8); +} + + +} diff --git a/opengl/tests/gl2_jni/Android.mk b/opengl/tests/gl2_jni/Android.mk index e8b6c57..5d90ff6 100644 --- a/opengl/tests/gl2_jni/Android.mk +++ b/opengl/tests/gl2_jni/Android.mk @@ -2,8 +2,6 @@ # OpenGL ES JNI sample # This makefile builds both an activity and a shared library. ######################################################################### -ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean - TOP_LOCAL_PATH:= $(call my-dir) # Build activity @@ -47,5 +45,3 @@ LOCAL_MODULE := libgl2jni include $(BUILD_SHARED_LIBRARY) - -endif # TARGET_SIMULATOR diff --git a/opengl/tests/gl_jni/Android.mk b/opengl/tests/gl_jni/Android.mk index 4acd91f..3d20e72 100644 --- a/opengl/tests/gl_jni/Android.mk +++ b/opengl/tests/gl_jni/Android.mk @@ -2,8 +2,6 @@ # OpenGL ES JNI sample # This makefile builds both an activity and a shared library. ######################################################################### -ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean - TOP_LOCAL_PATH:= $(call my-dir) # Build activity @@ -49,5 +47,3 @@ LOCAL_ARM_MODE := arm include $(BUILD_SHARED_LIBRARY) - -endif # TARGET_SIMULATOR diff --git a/opengl/tests/gl_perfapp/Android.mk b/opengl/tests/gl_perfapp/Android.mk index 4b79569..65e50e9 100644 --- a/opengl/tests/gl_perfapp/Android.mk +++ b/opengl/tests/gl_perfapp/Android.mk @@ -2,8 +2,6 @@ # OpenGL ES Perf App # This makefile builds both an activity and a shared library. ######################################################################### -ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean - TOP_LOCAL_PATH:= $(call my-dir) # Build activity @@ -50,5 +48,3 @@ LOCAL_MODULE := libglperf include $(BUILD_SHARED_LIBRARY) - -endif # TARGET_SIMULATOR diff --git a/opengl/tests/gldual/Android.mk b/opengl/tests/gldual/Android.mk index f1a998a..b4b378e 100644 --- a/opengl/tests/gldual/Android.mk +++ b/opengl/tests/gldual/Android.mk @@ -2,8 +2,6 @@ # OpenGL ES JNI sample # This makefile builds both an activity and a shared library. ######################################################################### -ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean - TOP_LOCAL_PATH:= $(call my-dir) # Build activity @@ -47,5 +45,3 @@ LOCAL_MODULE := libgldualjni include $(BUILD_SHARED_LIBRARY) - -endif # TARGET_SIMULATOR diff --git a/opengl/tests/testPauseResume/Android.mk b/opengl/tests/testPauseResume/Android.mk index 450473a..cf8bdc3 100644 --- a/opengl/tests/testPauseResume/Android.mk +++ b/opengl/tests/testPauseResume/Android.mk @@ -2,8 +2,6 @@ # OpenGL ES JNI sample # This makefile builds both an activity and a shared library. ######################################################################### -ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean - TOP_LOCAL_PATH:= $(call my-dir) # Build activity @@ -18,5 +16,3 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := TestEGL include $(BUILD_PACKAGE) - -endif # TARGET_SIMULATOR diff --git a/opengl/tests/testViewport/Android.mk b/opengl/tests/testViewport/Android.mk index ab37809..9980e7d 100644 --- a/opengl/tests/testViewport/Android.mk +++ b/opengl/tests/testViewport/Android.mk @@ -2,8 +2,6 @@ # OpenGL ES JNI sample # This makefile builds both an activity and a shared library. ######################################################################### -ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean - TOP_LOCAL_PATH:= $(call my-dir) # Build activity @@ -22,5 +20,3 @@ LOCAL_PACKAGE_NAME := TestViewport LOCAL_SDK_VERSION := 8 include $(BUILD_PACKAGE) - -endif # TARGET_SIMULATOR diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 47ab150..afe4246 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -16,17 +16,6 @@ package com.android.providers.settings; -import com.android.internal.content.PackageHelper; -import com.android.internal.telephony.BaseCommands; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.RILConstants; -import com.android.internal.util.XmlUtils; -import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.LockPatternView; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; @@ -47,6 +36,17 @@ import android.provider.Settings.Secure; import android.text.TextUtils; import android.util.Log; +import com.android.internal.content.PackageHelper; +import com.android.internal.telephony.BaseCommands; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.RILConstants; +import com.android.internal.util.XmlUtils; +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockPatternView; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; import java.util.HashSet; import java.util.List; @@ -63,7 +63,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 65; + private static final int DATABASE_VERSION = 66; private Context mContext; @@ -839,6 +839,28 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 65; } + if (upgradeVersion == 65) { + /* + * Animations are removed from Settings. Turned on by default + */ + db.beginTransaction(); + SQLiteStatement stmt = null; + try { + db.execSQL("DELETE FROM system WHERE name='" + + Settings.System.WINDOW_ANIMATION_SCALE + "'"); + db.execSQL("DELETE FROM system WHERE name='" + + Settings.System.TRANSITION_ANIMATION_SCALE + "'"); + stmt = db.compileStatement("INSERT INTO system(name,value)" + + " VALUES(?,?);"); + loadDefaultAnimationSettings(stmt); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + if (stmt != null) stmt.close(); + } + upgradeVersion = 66; + } + // *** Remember to update DATABASE_VERSION above! if (upgradeVersion != currentVersion) { diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml index 386182d..28ef239 100644 --- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml @@ -29,21 +29,17 @@ android:background="@drawable/status_bar_recents_background" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_alignParentBottom="true" - android:paddingBottom="@*android:dimen/status_bar_height" - android:clipToPadding="false" - android:clipChildren="false"> + android:layout_alignParentBottom="true"> <LinearLayout android:id="@+id/recents_glow" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="0dp" android:layout_gravity="bottom" android:orientation="horizontal" - android:clipToPadding="false" android:clipChildren="false" - > - <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container" + android:layout_marginTop="@*android:dimen/status_bar_height"> + <com.android.systemui.recent.RecentsVerticalScrollView + android:id="@+id/recents_container" android:layout_width="@dimen/status_bar_recents_width" android:layout_height="wrap_content" android:layout_marginRight="0dp" @@ -51,7 +47,7 @@ android:stackFromBottom="true" android:fadingEdge="vertical" android:scrollbars="none" - android:fadingEdgeLength="20dip" + android:fadingEdgeLength="@*android:dimen/status_bar_height" android:listSelector="@drawable/recents_thumbnail_bg_selector" android:layout_gravity="bottom|left" android:clipToPadding="false" diff --git a/packages/SystemUI/src/com/android/systemui/recent/Constants.java b/packages/SystemUI/src/com/android/systemui/recent/Constants.java new file mode 100644 index 0000000..8252a9f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recent/Constants.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 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.systemui.recent; + +public class Constants { + static final int MAX_ESCAPE_ANIMATION_DURATION = 500; // in ms + static final int SNAP_BACK_DURATION = 250; // in ms + static final int ESCAPE_VELOCITY = 100; // speed of item required to "curate" it in dp/s + public static float ALPHA_FADE_START = 0.8f; // fraction of thumbnail width where fade starts + static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width beyond which alpha->0 +} diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java index 3dbcc59..f984aac 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java @@ -25,6 +25,7 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.Context; +import android.content.res.Configuration; import android.database.DataSetObserver; import android.graphics.RectF; import android.util.AttributeSet; @@ -33,6 +34,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; +import android.view.ViewConfiguration; import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; import android.widget.HorizontalScrollView; @@ -42,12 +44,9 @@ import com.android.systemui.R; public class RecentsHorizontalScrollView extends HorizontalScrollView implements View.OnClickListener, View.OnTouchListener { - private static final float FADE_CONSTANT = 0.5f; - private static final int SNAP_BACK_DURATION = 250; - private static final int ESCAPE_VELOCITY = 100; // speed of item required to "curate" it - private static final String TAG = RecentsPanelView.TAG; - private static final float THRESHHOLD = 50; private static final boolean DEBUG_INVALIDATE = false; + private static final String TAG = RecentsPanelView.TAG; + private static final boolean DEBUG = RecentsPanelView.DEBUG; private LinearLayout mLinearLayout; private ActivityDescriptionAdapter mAdapter; private RecentsCallback mCallback; @@ -56,6 +55,8 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView private float mLastY; private boolean mDragging; private VelocityTracker mVelocityTracker; + private float mDensityScale; + private float mPagingTouchSlop; public RecentsHorizontalScrollView(Context context) { this(context, null); @@ -63,6 +64,8 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView public RecentsHorizontalScrollView(Context context, AttributeSet attrs) { super(context, attrs, 0); + mDensityScale = getResources().getDisplayMetrics().density; + mPagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop(); } private int scrollPositionOfMostRecent() { @@ -101,7 +104,8 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView case MotionEvent.ACTION_MOVE: float delta = ev.getY() - mLastY; - if (Math.abs(delta) > THRESHHOLD) { + if (DEBUG) Log.v(TAG, "ACTION_MOVE : " + delta); + if (Math.abs(delta) > mPagingTouchSlop) { mDragging = true; } break; @@ -114,13 +118,14 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView } private float getAlphaForOffset(View view, float thumbHeight) { - final float fadeHeight = FADE_CONSTANT * thumbHeight; + final float fadeHeight = Constants.ALPHA_FADE_END * thumbHeight; float result = 1.0f; - if (view.getY() >= thumbHeight) { - result = 1.0f - (view.getY() - thumbHeight) / fadeHeight; - } else if (view.getY() < 0.0f) { - result = 1.0f + (thumbHeight + view.getY()) / fadeHeight; + if (view.getY() >= thumbHeight * Constants.ALPHA_FADE_START) { + result = 1.0f - (view.getY() - thumbHeight * Constants.ALPHA_FADE_START) / fadeHeight; + } else if (view.getY() < thumbHeight * (1.0f - Constants.ALPHA_FADE_START)) { + result = 1.0f + (thumbHeight * Constants.ALPHA_FADE_START + view.getY()) / fadeHeight; } + if (DEBUG) Log.v(TAG, "FADE AMOUNT: " + result); return result; } @@ -155,12 +160,13 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView final float velocityY = velocityTracker.getYVelocity(); final float curY = animView.getY(); final float newY = (velocityY >= 0.0f ? 1 : -1) * animView.getHeight(); - + final float maxVelocity = Constants.ESCAPE_VELOCITY * mDensityScale; if (Math.abs(velocityY) > Math.abs(velocityX) - && Math.abs(velocityY) > ESCAPE_VELOCITY + && Math.abs(velocityY) > maxVelocity && (velocityY >= 0.0f) == (animView.getY() >= 0)) { - final long duration = + long duration = (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY)); + duration = Math.min(duration, Constants.MAX_ESCAPE_ANIMATION_DURATION); anim = ObjectAnimator.ofFloat(animView, "y", curY, newY); anim.setInterpolator(new LinearInterpolator()); final int swipeDirection = animView.getY() >= 0.0f ? @@ -181,9 +187,10 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView }); anim.setDuration(duration); } else { // Animate back to position - final long duration = Math.abs(velocityY) > 0.0f ? + long duration = Math.abs(velocityY) > 0.0f ? (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY)) - : SNAP_BACK_DURATION; + : Constants.SNAP_BACK_DURATION; + duration = Math.min(duration, Constants.SNAP_BACK_DURATION); anim = ObjectAnimator.ofFloat(animView, "y", animView.getY(), 0.0f); anim.setInterpolator(new DecelerateInterpolator(2.0f)); anim.setDuration(duration); @@ -241,8 +248,15 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView setOverScrollEffectPadding(leftPadding, 0); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDensityScale = getResources().getDisplayMetrics().density; + mPagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop(); + } + private void setOverScrollEffectPadding(int leftPadding, int i) { - // TODO Add to RecentsHorizontalScrollView + // TODO Add to (Vertical)ScrollView } @Override @@ -255,7 +269,7 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView // This has to happen post-layout, so run it "in the future" post(new Runnable() { public void run() { - scrollTo(0, mLastScrollPosition); + scrollTo(mLastScrollPosition, 0); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index 6190c9b..a55fe9c 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -417,11 +417,11 @@ public class RecentsPanelView extends RelativeLayout mActivityDescriptions = getRecentTasks(); mListAdapter.notifyDataSetInvalidated(); if (mActivityDescriptions.size() > 0) { - Log.v(TAG, "Showing " + mActivityDescriptions.size() + " apps"); + if (DEBUG) Log.v(TAG, "Showing " + mActivityDescriptions.size() + " apps"); updateUiElements(getResources().getConfiguration()); } else { // Immediately hide this panel - Log.v(TAG, "Nothing to show"); + if (DEBUG) Log.v(TAG, "Nothing to show"); hide(false); } } @@ -436,7 +436,7 @@ public class RecentsPanelView extends RelativeLayout paint.setAlpha(255); final int srcWidth = thumbnail.getWidth(); final int srcHeight = thumbnail.getHeight(); - Log.v(TAG, "Source thumb: " + srcWidth + "x" + srcHeight); + if (DEBUG) Log.v(TAG, "Source thumb: " + srcWidth + "x" + srcHeight); canvas.drawBitmap(thumbnail, new Rect(0, 0, srcWidth-1, srcHeight-1), new RectF(mGlowBitmapPaddingLeftPx, mGlowBitmapPaddingTopPx, @@ -486,7 +486,7 @@ public class RecentsPanelView extends RelativeLayout public void handleSwipe(View view, int direction) { ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription; - Log.v(TAG, "Jettison " + ad.label); + if (DEBUG) Log.v(TAG, "Jettison " + ad.label); mActivityDescriptions.remove(ad); // Handled by widget containers to enable LayoutTransitions properly diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java index 6a962cb..27bb0b5 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java @@ -25,6 +25,7 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.Context; +import android.content.res.Configuration; import android.database.DataSetObserver; import android.graphics.RectF; import android.util.AttributeSet; @@ -33,6 +34,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; +import android.view.ViewConfiguration; import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; import android.widget.LinearLayout; @@ -42,12 +44,9 @@ import com.android.systemui.R; public class RecentsVerticalScrollView extends ScrollView implements View.OnClickListener, View.OnTouchListener { - private static final float FADE_CONSTANT = 0.5f; - private static final int SNAP_BACK_DURATION = 250; - private static final int ESCAPE_VELOCITY = 100; // speed of item required to "curate" it private static final String TAG = RecentsPanelView.TAG; - private static final float THRESHHOLD = 50; private static final boolean DEBUG_INVALIDATE = false; + private static final boolean DEBUG = RecentsPanelView.DEBUG; private LinearLayout mLinearLayout; private ActivityDescriptionAdapter mAdapter; private RecentsCallback mCallback; @@ -56,6 +55,8 @@ public class RecentsVerticalScrollView extends ScrollView private float mLastX; private boolean mDragging; private VelocityTracker mVelocityTracker; + private float mDensityScale; + private float mPagingTouchSlop; public RecentsVerticalScrollView(Context context) { this(context, null); @@ -63,6 +64,8 @@ public class RecentsVerticalScrollView extends ScrollView public RecentsVerticalScrollView(Context context, AttributeSet attrs) { super(context, attrs, 0); + mDensityScale = getResources().getDisplayMetrics().density; + mPagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop(); } private int scrollPositionOfMostRecent() { @@ -101,8 +104,8 @@ public class RecentsVerticalScrollView extends ScrollView case MotionEvent.ACTION_MOVE: float delta = ev.getX() - mLastX; - Log.v(TAG, "ACTION_MOVE : " + delta); - if (Math.abs(delta) > THRESHHOLD) { + if (DEBUG) Log.v(TAG, "ACTION_MOVE : " + delta); + if (Math.abs(delta) > mPagingTouchSlop) { mDragging = true; } break; @@ -115,14 +118,14 @@ public class RecentsVerticalScrollView extends ScrollView } private float getAlphaForOffset(View view, float thumbWidth) { - final float fadeWidth = FADE_CONSTANT * thumbWidth; + final float fadeWidth = Constants.ALPHA_FADE_END * thumbWidth; float result = 1.0f; - if (view.getX() >= thumbWidth) { - result = 1.0f - (view.getX() - thumbWidth) / fadeWidth; - } else if (view.getX() < 0.0f) { - result = 1.0f + (thumbWidth + view.getX()) / fadeWidth; + if (view.getX() >= thumbWidth*Constants.ALPHA_FADE_START) { + result = 1.0f - (view.getX() - thumbWidth*Constants.ALPHA_FADE_START) / fadeWidth; + } else if (view.getX() < thumbWidth* (1.0f - Constants.ALPHA_FADE_START)) { + result = 1.0f + (thumbWidth*Constants.ALPHA_FADE_START + view.getX()) / fadeWidth; } - Log.v(TAG, "FADE AMOUNT: " + result); + if (DEBUG) Log.v(TAG, "FADE AMOUNT: " + result); return result; } @@ -157,12 +160,13 @@ public class RecentsVerticalScrollView extends ScrollView final float velocityY = velocityTracker.getYVelocity(); final float curX = animView.getX(); final float newX = (velocityX >= 0.0f ? 1 : -1) * animView.getWidth(); - + final float maxVelocity = Constants.ESCAPE_VELOCITY * mDensityScale; if (Math.abs(velocityX) > Math.abs(velocityY) - && Math.abs(velocityX) > ESCAPE_VELOCITY + && Math.abs(velocityX) > maxVelocity && (velocityX > 0.0f) == (animView.getX() >= 0)) { - final long duration = + long duration = (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX)); + duration = Math.min(duration, Constants.MAX_ESCAPE_ANIMATION_DURATION); anim = ObjectAnimator.ofFloat(animView, "x", curX, newX); anim.setInterpolator(new LinearInterpolator()); final int swipeDirection = animView.getX() >= 0.0f ? @@ -183,9 +187,10 @@ public class RecentsVerticalScrollView extends ScrollView }); anim.setDuration(duration); } else { // Animate back to position - final long duration = Math.abs(velocityX) > 0.0f ? + long duration = Math.abs(velocityX) > 0.0f ? (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX)) - : SNAP_BACK_DURATION; + : Constants.SNAP_BACK_DURATION; + duration = Math.min(duration, Constants.SNAP_BACK_DURATION); anim = ObjectAnimator.ofFloat(animView, "x", animView.getX(), 0.0f); anim.setInterpolator(new DecelerateInterpolator(4.0f)); anim.setDuration(duration); @@ -243,6 +248,13 @@ public class RecentsVerticalScrollView extends ScrollView setOverScrollEffectPadding(leftPadding, 0); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDensityScale = getResources().getDisplayMetrics().density; + mPagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop(); + } + private void setOverScrollEffectPadding(int leftPadding, int i) { // TODO Add to (Vertical)ScrollView } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 5eacad7..b50fd81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -307,7 +307,7 @@ public class PhoneStatusBar extends StatusBar { mDateView.setVisibility(View.INVISIBLE); // Recents Panel - initializeRecentsPanel(); + updateRecentsPanel(); // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -338,7 +338,7 @@ public class PhoneStatusBar extends StatusBar { return lp; } - protected void initializeRecentsPanel() { + protected void updateRecentsPanel() { // Recents Panel boolean visible = false; if (mRecentsPanel != null) { @@ -385,9 +385,9 @@ public class PhoneStatusBar extends StatusBar { // For small-screen devices (read: phones) that lack hardware navigation buttons private void addNavigationBar() { if (mNavigationBarView == null) return; - + mNavigationBarView.reorient(); - + mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener); WindowManagerImpl.getDefault().addView( @@ -396,7 +396,7 @@ public class PhoneStatusBar extends StatusBar { private void repositionNavigationBar() { if (mNavigationBarView == null) return; - + mNavigationBarView.reorient(); mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener); @@ -656,7 +656,7 @@ public class PhoneStatusBar extends StatusBar { @Override protected void onConfigurationChanged(Configuration newConfig) { - initializeRecentsPanel(); + updateRecentsPanel(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 826ac92..dedbe5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -785,7 +785,7 @@ public class PhoneStatusBarPolicy { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); mBluetoothEnabled = state == BluetoothAdapter.STATE_ON; } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { - int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.STATE_DISCONNECTED); if (state == BluetoothAdapter.STATE_CONNECTED) { iconId = R.drawable.stat_sys_data_bluetooth_connected; diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java index f862d01..75e799c 100644 --- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java @@ -29,7 +29,9 @@ import android.os.CountDownTimer; import android.os.SystemClock; import android.security.KeyStore; import android.telephony.TelephonyManager; +import android.text.Editable; import android.text.InputType; +import android.text.TextWatcher; import android.text.method.DigitsKeyListener; import android.text.method.TextKeyListener; import android.util.Log; @@ -120,15 +122,6 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen } }); - // We don't currently use the IME for PIN mode, but this will make it work if we ever do... - if (!mIsAlpha) { - mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER - | InputType.TYPE_NUMBER_VARIATION_PASSWORD); - } else { - mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_PASSWORD); - } - mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton); mEmergencyCallButton.setOnClickListener(this); mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); @@ -151,14 +144,17 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen mPasswordEntry.requestFocus(); - // This allows keyboards with overlapping qwerty/numeric keys to choose just the - // numeric keys. + // This allows keyboards with overlapping qwerty/numeric keys to choose just numeric keys. if (mIsAlpha) { mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); + mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_PASSWORD); // mStatusView.setHelpMessage(R.string.keyguard_password_enter_password_code, // StatusView.LOCK_ICON); } else { mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance()); + mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER + | InputType.TYPE_NUMBER_VARIATION_PASSWORD); //mStatusView.setHelpMessage(R.string.keyguard_password_enter_pin_code, // StatusView.LOCK_ICON); } @@ -179,6 +175,19 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen //mUpdateMonitor.registerSimStateCallback(this); resetStatusInfo(); + + // Poke the wakelock any time the text is modified + mPasswordEntry.addTextChangedListener(new TextWatcher() { + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + public void afterTextChanged(Editable s) { + mCallback.pokeWakelock(); + } + }); } @Override diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 7f6327d..dff0556 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -1555,9 +1555,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // We restore the panel if it was last open; we skip it if it // now is open, to avoid a race condition if the user immediately // opens it when we are resuming. - if ((st != null) && !st.isOpen && st.wasLastOpen) { - st.isInExpandedMode = st.wasLastExpanded; - openPanel(st, null); + if (st != null) { + st.applyFrozenState(); + if (!st.isOpen && st.wasLastOpen) { + st.isInExpandedMode = st.wasLastExpanded; + openPanel(st, null); + } } } } @@ -2235,6 +2238,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } mActionModePopup = null; } + + PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); + if (st != null && st.menu != null) { + st.menu.close(); + } } @Override @@ -3046,46 +3054,33 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { getIconMenuView(cb); // Need this initialized to know where our offset goes - boolean init = false; if (expandedMenuPresenter == null) { expandedMenuPresenter = new ListMenuPresenter( com.android.internal.R.layout.list_menu_item_layout, com.android.internal.R.style.Theme_ExpandedMenu); expandedMenuPresenter.setCallback(cb); + expandedMenuPresenter.setId(com.android.internal.R.id.list_menu_presenter); menu.addMenuPresenter(expandedMenuPresenter); - init = true; } expandedMenuPresenter.setItemIndexOffset(iconMenuPresenter.getNumActualItemsShown()); MenuView result = expandedMenuPresenter.getMenuView(decorView); - if (init && frozenMenuState != null) { - expandedMenuPresenter.restoreHierarchyState(frozenMenuState); - // Once we initialize the expanded menu we're done with the frozen state - // since we will have also restored any icon menu state. - frozenMenuState = null; - } - return result; } MenuView getIconMenuView(MenuPresenter.Callback cb) { if (menu == null) return null; - boolean init = false; if (iconMenuPresenter == null) { iconMenuPresenter = new IconMenuPresenter(); iconMenuPresenter.setCallback(cb); + iconMenuPresenter.setId(com.android.internal.R.id.icon_menu_presenter); menu.addMenuPresenter(iconMenuPresenter); - init = true; } MenuView result = iconMenuPresenter.getMenuView(decorView); - if (init && frozenMenuState != null) { - iconMenuPresenter.restoreHierarchyState(frozenMenuState); - } - return result; } @@ -3097,12 +3092,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (menu != null) { savedState.menuState = new Bundle(); - if (iconMenuPresenter != null) { - iconMenuPresenter.saveHierarchyState(savedState.menuState); - } - if (expandedMenuPresenter != null) { - expandedMenuPresenter.saveHierarchyState(savedState.menuState); - } + menu.savePresenterStates(savedState.menuState); } return savedState; @@ -3127,6 +3117,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { decorView = null; } + void applyFrozenState() { + if (menu != null && frozenMenuState != null) { + menu.restorePresenterStates(frozenMenuState); + frozenMenuState = null; + } + } + private static class SavedState implements Parcelable { int featureId; boolean isOpen; diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 75d24a1..6bb1f56 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -20,23 +20,12 @@ LOCAL_SHARED_LIBRARIES := \ libmedia \ libhardware \ libhardware_legacy \ - libeffects + libeffects \ + libdl LOCAL_STATIC_LIBRARIES := \ libmedia_helper -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -ldl -else - LOCAL_SHARED_LIBRARIES += libdl -endif - LOCAL_MODULE:= libaudioflinger -ifeq ($(TARGET_SIMULATOR),true) - ifeq ($(HOST_OS),linux) - LOCAL_LDLIBS += -lrt -lpthread - endif -endif - include $(BUILD_SHARED_LIBRARY) diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp index 47ca3a0..8e16d94 100644 --- a/services/audioflinger/AudioPolicyService.cpp +++ b/services/audioflinger/AudioPolicyService.cpp @@ -48,9 +48,6 @@ static const int kDumpLockRetries = 50; static const int kDumpLockSleep = 20000; static bool checkPermission() { -#ifndef HAVE_ANDROID_OS - return true; -#endif if (getpid() == IPCThreadState::self()->getCallingPid()) return true; bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")); if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp index 1d247bd..4d721f6 100644 --- a/services/audioflinger/AudioResamplerCubic.cpp +++ b/services/audioflinger/AudioResamplerCubic.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "AudioSRC" + #include <stdint.h> #include <string.h> #include <sys/types.h> @@ -22,8 +24,6 @@ #include "AudioResampler.h" #include "AudioResamplerCubic.h" -#define LOG_TAG "AudioSRC" - namespace android { // ---------------------------------------------------------------------------- diff --git a/services/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h index 9b66a76..c6d8756 100644 --- a/services/camera/libcameraservice/CameraHardwareStub.h +++ b/services/camera/libcameraservice/CameraHardwareStub.h @@ -73,14 +73,7 @@ private: CameraHardwareStub* mHardware; public: PreviewThread(CameraHardwareStub* hw) : -#ifdef SINGLE_PROCESS - // In single process mode this thread needs to be a java thread, - // since we won't be calling through the binder. - Thread(true), -#else - Thread(false), -#endif - mHardware(hw) { } + Thread(false), mHardware(hw) { } virtual void onFirstRef() { run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY); } diff --git a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp index f86ca47..e390ae2 100644 --- a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp +++ b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp @@ -29,7 +29,6 @@ #include <camera/ICamera.h> #include <camera/ICameraClient.h> #include <camera/ICameraService.h> -#include <ui/Overlay.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/ProcessState.h> @@ -311,8 +310,6 @@ public: virtual status_t registerBuffers(const BufferHeap& buffers); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); - virtual sp<OverlayRef> createOverlay( - uint32_t w, uint32_t h, int32_t format, int32_t orientation); virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage); virtual status_t setBufferCount(int bufferCount); @@ -381,13 +378,6 @@ void MSurface::waitUntil(int c0, int c1, int c2) { } } -sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format, - int32_t orientation) { - // Not implemented. - ASSERT(0); - return NULL; -} - // // Utilities to use the Holder service // diff --git a/services/input/Android.mk b/services/input/Android.mk index 836c081..e36507a 100644 --- a/services/input/Android.mk +++ b/services/input/Android.mk @@ -41,10 +41,6 @@ LOCAL_MODULE:= libinput LOCAL_MODULE_TAGS := optional -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -lpthread -endif - include $(BUILD_SHARED_LIBRARY) diff --git a/services/input/InputApplication.h b/services/input/InputApplication.h index cc80062..8902f7a 100644 --- a/services/input/InputApplication.h +++ b/services/input/InputApplication.h @@ -26,26 +26,32 @@ namespace android { /* - * A handle to an application that can receive input. - * Used by the native input dispatcher to indirectly refer to the window manager objects + * Describes the properties of an application that can receive input. + * + * Used by the native input dispatcher as a handle for the window manager objects * that describe an application. */ class InputApplicationHandle : public RefBase { +public: + String8 name; + nsecs_t dispatchingTimeout; + + /** + * Requests that the state of this object be updated to reflect + * the most current available information about the application. + * + * This method should only be called from within the input dispatcher's + * critical section. + * + * Returns true on success, or false if the handle is no longer valid. + */ + virtual bool update() = 0; + protected: InputApplicationHandle() { } virtual ~InputApplicationHandle() { } }; - -/* - * An input application describes properties of an application that can receive input. - */ -struct InputApplication { - sp<InputApplicationHandle> inputApplicationHandle; - String8 name; - nsecs_t dispatchingTimeout; -}; - } // namespace android #endif // _UI_INPUT_APPLICATION_H diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 10b9083..1cac502 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -211,11 +211,8 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(NULL), mDispatchEnabled(true), mDispatchFrozen(false), mInputFilterEnabled(false), - mFocusedWindow(NULL), - mFocusedApplication(NULL), mCurrentInputTargetsValid(false), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE), - mLastHoverWindow(NULL) { + mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mLooper = new Looper(false); mInboundQueue.headSentinel.refCount = -1; @@ -501,16 +498,15 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY - && mInputTargetWaitApplication != NULL) { + && mInputTargetWaitApplicationHandle != NULL) { int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0]. getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0]. getAxisValue(AMOTION_EVENT_AXIS_Y)); - const InputWindow* touchedWindow = findTouchedWindowAtLocked(x, y); - if (touchedWindow - && touchedWindow->inputWindowHandle != NULL - && touchedWindow->inputWindowHandle->getInputApplicationHandle() - != mInputTargetWaitApplication) { + sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(x, y); + if (touchedWindowHandle != NULL + && touchedWindowHandle->inputApplicationHandle + != mInputTargetWaitApplicationHandle) { // User touched a different application than the one we are waiting on. // Flag the event, and start pruning the input queue. mNextUnblockedEvent = motionEntry; @@ -524,25 +520,25 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { return needWake; } -const InputWindow* InputDispatcher::findTouchedWindowAtLocked(int32_t x, int32_t y) { +sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t x, int32_t y) { // Traverse windows from front to back to find touched window. - size_t numWindows = mWindows.size(); + size_t numWindows = mWindowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - const InputWindow* window = & mWindows.editItemAt(i); - int32_t flags = window->layoutParamsFlags; - - if (window->visible) { - if (!(flags & InputWindow::FLAG_NOT_TOUCHABLE)) { - bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE - | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0; - if (isTouchModal || window->touchableRegionContainsPoint(x, y)) { + sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); + int32_t flags = windowHandle->layoutParamsFlags; + + if (windowHandle->visible) { + if (!(flags & InputWindowHandle::FLAG_NOT_TOUCHABLE)) { + bool isTouchModal = (flags & (InputWindowHandle::FLAG_NOT_FOCUSABLE + | InputWindowHandle::FLAG_NOT_TOUCH_MODAL)) == 0; + if (isTouchModal || windowHandle->touchableRegionContainsPoint(x, y)) { // Found window. - return window; + return windowHandle; } } } - if (flags & InputWindow::FLAG_SYSTEM_ERROR) { + if (flags & InputWindowHandle::FLAG_SYSTEM_ERROR) { // Error window is on top but not visible, so touch is dropped. return NULL; } @@ -781,8 +777,8 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); - if (mFocusedWindow) { - commandEntry->inputWindowHandle = mFocusedWindow->inputWindowHandle; + if (mFocusedWindowHandle != NULL) { + commandEntry->inputWindowHandle = mFocusedWindowHandle; } commandEntry->keyEntry = entry; entry->refCount += 1; @@ -1011,7 +1007,7 @@ void InputDispatcher::resetTargetsLocked() { mCurrentInputTargetsValid = false; mCurrentInputTargets.clear(); mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; - mInputTargetWaitApplication.clear(); + mInputTargetWaitApplicationHandle.clear(); } void InputDispatcher::commitTargetsLocked() { @@ -1019,9 +1015,11 @@ void InputDispatcher::commitTargetsLocked() { } int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, - const EventEntry* entry, const InputApplication* application, const InputWindow* window, + const EventEntry* entry, + const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime) { - if (application == NULL && window == NULL) { + if (applicationHandle == NULL && windowHandle == NULL) { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { #if DEBUG_FOCUS LOGD("Waiting for system to become ready for input."); @@ -1030,29 +1028,29 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, mInputTargetWaitStartTime = currentTime; mInputTargetWaitTimeoutTime = LONG_LONG_MAX; mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplication.clear(); + mInputTargetWaitApplicationHandle.clear(); } } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { #if DEBUG_FOCUS LOGD("Waiting for application to become ready for input: %s", - getApplicationWindowLabelLocked(application, window).string()); + getApplicationWindowLabelLocked(applicationHandle, windowHandle).string()); #endif - nsecs_t timeout = window ? window->dispatchingTimeout : - application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT; + nsecs_t timeout = windowHandle != NULL ? windowHandle->dispatchingTimeout : + applicationHandle != NULL ? + applicationHandle->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT; mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; mInputTargetWaitStartTime = currentTime; mInputTargetWaitTimeoutTime = currentTime + timeout; mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplication.clear(); + mInputTargetWaitApplicationHandle.clear(); - if (window && window->inputWindowHandle != NULL) { - mInputTargetWaitApplication = - window->inputWindowHandle->getInputApplicationHandle(); + if (windowHandle != NULL) { + mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle; } - if (mInputTargetWaitApplication == NULL && application) { - mInputTargetWaitApplication = application->inputApplicationHandle; + if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) { + mInputTargetWaitApplicationHandle = applicationHandle; } } } @@ -1062,7 +1060,8 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, } if (currentTime >= mInputTargetWaitTimeoutTime) { - onANRLocked(currentTime, application, window, entry->eventTime, mInputTargetWaitStartTime); + onANRLocked(currentTime, applicationHandle, windowHandle, + entry->eventTime, mInputTargetWaitStartTime); // Force poll loop to wake up immediately on next iteration once we get the // ANR response back from the policy. @@ -1129,15 +1128,15 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, // If there is no currently focused window and no focused application // then drop the event. - if (! mFocusedWindow) { - if (mFocusedApplication) { + if (mFocusedWindowHandle == NULL) { + if (mFocusedApplicationHandle != NULL) { #if DEBUG_FOCUS LOGD("Waiting because there is no focused window but there is a " "focused application that may eventually add a window: %s.", - getApplicationWindowLabelLocked(mFocusedApplication, NULL).string()); + getApplicationWindowLabelLocked(mFocusedApplicationHandle, NULL).string()); #endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, NULL, nextWakeupTime); + mFocusedApplicationHandle, NULL, nextWakeupTime); goto Unresponsive; } @@ -1147,34 +1146,34 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, } // Check permissions. - if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) { + if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; goto Failed; } // If the currently focused window is paused then keep waiting. - if (mFocusedWindow->paused) { + if (mFocusedWindowHandle->paused) { #if DEBUG_FOCUS LOGD("Waiting because focused window is paused."); #endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, mFocusedWindow, nextWakeupTime); + mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime); goto Unresponsive; } // If the currently focused window is still working on previous events then keep waiting. - if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) { + if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindowHandle)) { #if DEBUG_FOCUS LOGD("Waiting because focused window still processing previous input."); #endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, mFocusedWindow, nextWakeupTime); + mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime); goto Unresponsive; } // Success! Output targets. injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - addWindowTargetLocked(mFocusedWindow, + addWindowTargetLocked(mFocusedWindowHandle, InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0)); // Done. @@ -1236,7 +1235,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Update the touch state as needed based on the properties of the touch event. int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING; InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; - const InputWindow* newHoverWindow = NULL; + sp<InputWindowHandle> newHoverWindowHandle; bool isSplit = mTouchState.split; bool switchedDevice = mTouchState.deviceId >= 0 @@ -1279,42 +1278,44 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(sample->pointerCoords[pointerIndex]. getAxisValue(AMOTION_EVENT_AXIS_Y)); - const InputWindow* newTouchedWindow = NULL; - const InputWindow* topErrorWindow = NULL; + sp<InputWindowHandle> newTouchedWindowHandle; + sp<InputWindowHandle> topErrorWindowHandle; bool isTouchModal = false; // Traverse windows from front to back to find touched window and outside targets. - size_t numWindows = mWindows.size(); + size_t numWindows = mWindowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - const InputWindow* window = & mWindows.editItemAt(i); - int32_t flags = window->layoutParamsFlags; + sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); + int32_t flags = windowHandle->layoutParamsFlags; - if (flags & InputWindow::FLAG_SYSTEM_ERROR) { - if (! topErrorWindow) { - topErrorWindow = window; + if (flags & InputWindowHandle::FLAG_SYSTEM_ERROR) { + if (topErrorWindowHandle == NULL) { + topErrorWindowHandle = windowHandle; } } - if (window->visible) { - if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) { - isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE - | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0; - if (isTouchModal || window->touchableRegionContainsPoint(x, y)) { - if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) { - newTouchedWindow = window; + if (windowHandle->visible) { + if (! (flags & InputWindowHandle::FLAG_NOT_TOUCHABLE)) { + isTouchModal = (flags & (InputWindowHandle::FLAG_NOT_FOCUSABLE + | InputWindowHandle::FLAG_NOT_TOUCH_MODAL)) == 0; + if (isTouchModal || windowHandle->touchableRegionContainsPoint(x, y)) { + if (! screenWasOff + || (flags & InputWindowHandle::FLAG_TOUCHABLE_WHEN_WAKING)) { + newTouchedWindowHandle = windowHandle; } break; // found touched window, exit window loop } } if (maskedAction == AMOTION_EVENT_ACTION_DOWN - && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) { + && (flags & InputWindowHandle::FLAG_WATCH_OUTSIDE_TOUCH)) { int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE; - if (isWindowObscuredAtPointLocked(window, x, y)) { + if (isWindowObscuredAtPointLocked(windowHandle, x, y)) { outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; } - mTempTouchState.addOrUpdateWindow(window, outsideTargetFlags, BitSet32(0)); + mTempTouchState.addOrUpdateWindow( + windowHandle, outsideTargetFlags, BitSet32(0)); } } } @@ -1322,7 +1323,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // If there is an error window but it is not taking focus (typically because // it is invisible) then wait for it. Any other focused window may in // fact be in ANR state. - if (topErrorWindow && newTouchedWindow != topErrorWindow) { + if (topErrorWindowHandle != NULL && newTouchedWindowHandle != topErrorWindowHandle) { #if DEBUG_FOCUS LOGD("Waiting because system error window is pending."); #endif @@ -1333,26 +1334,26 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Figure out whether splitting will be allowed for this window. - if (newTouchedWindow && newTouchedWindow->supportsSplitTouch()) { + if (newTouchedWindowHandle != NULL && newTouchedWindowHandle->supportsSplitTouch()) { // New window supports splitting. isSplit = true; } else if (isSplit) { // New window does not support splitting but we have already split events. // Assign the pointer to the first foreground window we find. // (May be NULL which is why we put this code block before the next check.) - newTouchedWindow = mTempTouchState.getFirstForegroundWindow(); + newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); } // If we did not find a touched window then fail. - if (! newTouchedWindow) { - if (mFocusedApplication) { + if (newTouchedWindowHandle == NULL) { + if (mFocusedApplicationHandle != NULL) { #if DEBUG_FOCUS LOGD("Waiting because there is no touched window but there is a " "focused application that may eventually add a new window: %s.", - getApplicationWindowLabelLocked(mFocusedApplication, NULL).string()); + getApplicationWindowLabelLocked(mFocusedApplicationHandle, NULL).string()); #endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, NULL, nextWakeupTime); + mFocusedApplicationHandle, NULL, nextWakeupTime); goto Unresponsive; } @@ -1366,20 +1367,20 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (isSplit) { targetFlags |= InputTarget::FLAG_SPLIT; } - if (isWindowObscuredAtPointLocked(newTouchedWindow, x, y)) { + if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; } // Update hover state. if (isHoverAction) { - newHoverWindow = newTouchedWindow; + newHoverWindowHandle = newTouchedWindowHandle; // Ensure all subsequent motion samples are also within the touched window. // Set *outSplitBatchAfterSample to the sample before the first one that is not // within the touched window. if (!isTouchModal) { while (sample->next) { - if (!newHoverWindow->touchableRegionContainsPoint( + if (!newHoverWindowHandle->touchableRegionContainsPoint( sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y))) { *outSplitBatchAfterSample = sample; @@ -1389,7 +1390,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } } } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { - newHoverWindow = mLastHoverWindow; + newHoverWindowHandle = mLastHoverWindowHandle; } // Update the temporary touch state. @@ -1398,7 +1399,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, uint32_t pointerId = entry->pointerProperties[pointerIndex].id; pointerIds.markBit(pointerId); } - mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds); + mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); } else { /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ @@ -1420,19 +1421,22 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, int32_t x = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - const InputWindow* oldTouchedWindow = mTempTouchState.getFirstForegroundWindow(); - const InputWindow* newTouchedWindow = findTouchedWindowAtLocked(x, y); - if (oldTouchedWindow != newTouchedWindow && newTouchedWindow) { + sp<InputWindowHandle> oldTouchedWindowHandle = + mTempTouchState.getFirstForegroundWindowHandle(); + sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(x, y); + if (oldTouchedWindowHandle != newTouchedWindowHandle + && newTouchedWindowHandle != NULL) { #if DEBUG_FOCUS LOGD("Touch is slipping out of window %s into window %s.", - oldTouchedWindow->name.string(), newTouchedWindow->name.string()); + oldTouchedWindowHandle->name.string(), + newTouchedWindowHandle->name.string()); #endif // Make a slippery exit from the old window. - mTempTouchState.addOrUpdateWindow(oldTouchedWindow, + mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0)); // Make a slippery entrance into the new window. - if (newTouchedWindow->supportsSplitTouch()) { + if (newTouchedWindowHandle->supportsSplitTouch()) { isSplit = true; } @@ -1441,7 +1445,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (isSplit) { targetFlags |= InputTarget::FLAG_SPLIT; } - if (isWindowObscuredAtPointLocked(newTouchedWindow, x, y)) { + if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; } @@ -1449,7 +1453,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (isSplit) { pointerIds.markBit(entry->pointerProperties[0].id); } - mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds); + mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); // Split the batch here so we send exactly one sample. *outSplitBatchAfterSample = &entry->firstSample; @@ -1457,25 +1461,25 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } } - if (newHoverWindow != mLastHoverWindow) { + if (newHoverWindowHandle != mLastHoverWindowHandle) { // Split the batch here so we send exactly one sample as part of ENTER or EXIT. *outSplitBatchAfterSample = &entry->firstSample; // Let the previous window know that the hover sequence is over. - if (mLastHoverWindow) { + if (mLastHoverWindowHandle != NULL) { #if DEBUG_HOVER - LOGD("Sending hover exit event to window %s.", mLastHoverWindow->name.string()); + LOGD("Sending hover exit event to window %s.", mLastHoverWindowHandle->name.string()); #endif - mTempTouchState.addOrUpdateWindow(mLastHoverWindow, + mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); } // Let the new window know that the hover sequence is starting. - if (newHoverWindow) { + if (newHoverWindowHandle != NULL) { #if DEBUG_HOVER - LOGD("Sending hover enter event to window %s.", newHoverWindow->name.string()); + LOGD("Sending hover enter event to window %s.", newHoverWindowHandle->name.string()); #endif - mTempTouchState.addOrUpdateWindow(newHoverWindow, + mTempTouchState.addOrUpdateWindow(newHoverWindowHandle, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0)); } } @@ -1488,7 +1492,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const TouchedWindow& touchedWindow = mTempTouchState.windows[i]; if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { haveForegroundWindow = true; - if (! checkInjectionPermission(touchedWindow.window, entry->injectionState)) { + if (! checkInjectionPermission(touchedWindow.windowHandle, + entry->injectionState)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; injectionPermission = INJECTION_PERMISSION_DENIED; goto Failed; @@ -1510,14 +1515,15 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Check whether windows listening for outside touches are owned by the same UID. If it is // set the policy flag that we will not reveal coordinate information to this window. if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow(); - const int32_t foregroundWindowUid = foregroundWindow->ownerUid; + sp<InputWindowHandle> foregroundWindowHandle = + mTempTouchState.getFirstForegroundWindowHandle(); + const int32_t foregroundWindowUid = foregroundWindowHandle->ownerUid; for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { const TouchedWindow& touchedWindow = mTempTouchState.windows[i]; if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { - const InputWindow* inputWindow = touchedWindow.window; - if (inputWindow->ownerUid != foregroundWindowUid) { - mTempTouchState.addOrUpdateWindow(inputWindow, + sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle; + if (inputWindowHandle->ownerUid != foregroundWindowUid) { + mTempTouchState.addOrUpdateWindow(inputWindowHandle, InputTarget::FLAG_ZERO_COORDS, BitSet32(0)); } } @@ -1529,22 +1535,22 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const TouchedWindow& touchedWindow = mTempTouchState.windows[i]; if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { // If the touched window is paused then keep waiting. - if (touchedWindow.window->paused) { + if (touchedWindow.windowHandle->paused) { #if DEBUG_FOCUS LOGD("Waiting because touched window is paused."); #endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.window, nextWakeupTime); + NULL, touchedWindow.windowHandle, nextWakeupTime); goto Unresponsive; } // If the touched window is still working on previous events then keep waiting. - if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.window)) { + if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.windowHandle)) { #if DEBUG_FOCUS LOGD("Waiting because touched window still processing previous input."); #endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.window, nextWakeupTime); + NULL, touchedWindow.windowHandle, nextWakeupTime); goto Unresponsive; } } @@ -1557,12 +1563,13 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // engine only supports touch events. We would need to add a mechanism similar // to View.onGenericMotionEvent to enable wallpapers to handle these events. if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow(); - if (foregroundWindow->hasWallpaper) { - for (size_t i = 0; i < mWindows.size(); i++) { - const InputWindow* window = & mWindows[i]; - if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) { - mTempTouchState.addOrUpdateWindow(window, + sp<InputWindowHandle> foregroundWindowHandle = + mTempTouchState.getFirstForegroundWindowHandle(); + if (foregroundWindowHandle->hasWallpaper) { + for (size_t i = 0; i < mWindowHandles.size(); i++) { + sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); + if (windowHandle->layoutParamsType == InputWindowHandle::TYPE_WALLPAPER) { + mTempTouchState.addOrUpdateWindow(windowHandle, InputTarget::FLAG_WINDOW_IS_OBSCURED | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0)); @@ -1576,7 +1583,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i); - addWindowTargetLocked(touchedWindow.window, touchedWindow.targetFlags, + addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, touchedWindow.pointerIds); } @@ -1658,7 +1665,7 @@ Failed: } // Update hover state. - mLastHoverWindow = newHoverWindow; + mLastHoverWindowHandle = newHoverWindowHandle; } } else { #if DEBUG_FOCUS @@ -1681,16 +1688,16 @@ Unresponsive: return injectionResult; } -void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, - BitSet32 pointerIds) { +void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, + int32_t targetFlags, BitSet32 pointerIds) { mCurrentInputTargets.push(); InputTarget& target = mCurrentInputTargets.editTop(); - target.inputChannel = window->inputChannel; + target.inputChannel = windowHandle->inputChannel; target.flags = targetFlags; - target.xOffset = - window->frameLeft; - target.yOffset = - window->frameTop; - target.scaleFactor = window->scaleFactor; + target.xOffset = - windowHandle->frameLeft; + target.yOffset = - windowHandle->frameTop; + target.scaleFactor = windowHandle->scaleFactor; target.pointerIds = pointerIds; } @@ -1708,17 +1715,17 @@ void InputDispatcher::addMonitoringTargetsLocked() { } } -bool InputDispatcher::checkInjectionPermission(const InputWindow* window, +bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, const InjectionState* injectionState) { if (injectionState - && (window == NULL || window->ownerUid != injectionState->injectorUid) + && (windowHandle == NULL || windowHandle->ownerUid != injectionState->injectorUid) && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) { - if (window) { - LOGW("Permission denied: injecting event from pid %d uid %d to window " - "with input channel %s owned by uid %d", + if (windowHandle != NULL) { + LOGW("Permission denied: injecting event from pid %d uid %d to window %s " + "owned by uid %d", injectionState->injectorPid, injectionState->injectorUid, - window->inputChannel->getName().string(), - window->ownerUid); + windowHandle->name.string(), + windowHandle->ownerUid); } else { LOGW("Permission denied: injecting event from pid %d uid %d", injectionState->injectorPid, injectionState->injectorUid); @@ -1729,22 +1736,24 @@ bool InputDispatcher::checkInjectionPermission(const InputWindow* window, } bool InputDispatcher::isWindowObscuredAtPointLocked( - const InputWindow* window, int32_t x, int32_t y) const { - size_t numWindows = mWindows.size(); + const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const { + size_t numWindows = mWindowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - const InputWindow* other = & mWindows.itemAt(i); - if (other == window) { + sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i); + if (otherHandle == windowHandle) { break; } - if (other->visible && ! other->isTrustedOverlay() && other->frameContainsPoint(x, y)) { + if (otherHandle->visible && ! otherHandle->isTrustedOverlay() + && otherHandle->frameContainsPoint(x, y)) { return true; } } return false; } -bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* window) { - ssize_t connectionIndex = getConnectionIndexLocked(window->inputChannel); +bool InputDispatcher::isWindowFinishedWithPreviousInputLocked( + const sp<InputWindowHandle>& windowHandle) { + ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->inputChannel); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); return connection->outboundQueue.isEmpty(); @@ -1753,19 +1762,20 @@ bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* } } -String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* application, - const InputWindow* window) { - if (application) { - if (window) { - String8 label(application->name); +String8 InputDispatcher::getApplicationWindowLabelLocked( + const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle) { + if (applicationHandle != NULL) { + if (windowHandle != NULL) { + String8 label(applicationHandle->name); label.append(" - "); - label.append(window->name); + label.append(windowHandle->name); return label; } else { - return application->name; + return applicationHandle->name; } - } else if (window) { - return window->name; + } else if (windowHandle != NULL) { + return windowHandle->name; } else { return String8("<unknown application or window>"); } @@ -2422,11 +2432,11 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } InputTarget target; - const InputWindow* window = getWindowLocked(connection->inputChannel); - if (window) { - target.xOffset = -window->frameLeft; - target.yOffset = -window->frameTop; - target.scaleFactor = window->scaleFactor; + sp<InputWindowHandle> windowHandle = getWindowHandleLocked(connection->inputChannel); + if (windowHandle != NULL) { + target.xOffset = -windowHandle->frameLeft; + target.yOffset = -windowHandle->frameTop; + target.scaleFactor = windowHandle->scaleFactor; } else { target.xOffset = 0; target.yOffset = 0; @@ -2809,7 +2819,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t } if (action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - if (!mLastHoverWindow) { + if (mLastHoverWindowHandle == NULL) { #if DEBUG_BATCHING LOGD("Not streaming hover move because there is no " "last hovered window."); @@ -2817,15 +2827,16 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t goto NoBatchingOrStreaming; } - const InputWindow* hoverWindow = findTouchedWindowAtLocked( + sp<InputWindowHandle> hoverWindowHandle = findTouchedWindowAtLocked( pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - if (mLastHoverWindow != hoverWindow) { + if (mLastHoverWindowHandle != hoverWindowHandle) { #if DEBUG_BATCHING LOGD("Not streaming hover move because the last hovered window " "is '%s' but the currently hovered window is '%s'.", - mLastHoverWindow->name.string(), - hoverWindow ? hoverWindow->name.string() : "<null>"); + mLastHoverWindowHandle->name.string(), + hoverWindowHandle != NULL + ? hoverWindowHandle->name.string() : "<null>"); #endif goto NoBatchingOrStreaming; } @@ -3125,113 +3136,109 @@ void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* ent } } -const InputWindow* InputDispatcher::getWindowLocked(const sp<InputChannel>& inputChannel) { - for (size_t i = 0; i < mWindows.size(); i++) { - const InputWindow* window = & mWindows[i]; - if (window->inputChannel == inputChannel) { - return window; +sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( + const sp<InputChannel>& inputChannel) const { + size_t numWindows = mWindowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i); + if (windowHandle->inputChannel == inputChannel) { + return windowHandle; } } return NULL; } -void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { +bool InputDispatcher::hasWindowHandleLocked( + const sp<InputWindowHandle>& windowHandle) const { + size_t numWindows = mWindowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + if (mWindowHandles.itemAt(i) == windowHandle) { + return true; + } + } + return false; +} + +void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) { #if DEBUG_FOCUS LOGD("setInputWindows"); #endif { // acquire lock AutoMutex _l(mLock); - // Clear old window pointers. - sp<InputChannel> oldFocusedWindowChannel; - if (mFocusedWindow) { - oldFocusedWindowChannel = mFocusedWindow->inputChannel; - mFocusedWindow = NULL; - } - sp<InputChannel> oldLastHoverWindowChannel; - if (mLastHoverWindow) { - oldLastHoverWindowChannel = mLastHoverWindow->inputChannel; - mLastHoverWindow = NULL; - } - - mWindows.clear(); - - // Loop over new windows and rebuild the necessary window pointers for - // tracking focus and touch. - mWindows.appendVector(inputWindows); + mWindowHandles = inputWindowHandles; - size_t numWindows = mWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - const InputWindow* window = & mWindows.itemAt(i); - if (window->hasFocus) { - mFocusedWindow = window; - break; + sp<InputWindowHandle> newFocusedWindowHandle; + bool foundHoveredWindow = false; + for (size_t i = 0; i < mWindowHandles.size(); i++) { + const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i); + if (!windowHandle->update() || windowHandle->inputChannel == NULL) { + mWindowHandles.removeAt(i--); + continue; } + if (windowHandle->hasFocus) { + newFocusedWindowHandle = windowHandle; + } + if (windowHandle == mLastHoverWindowHandle) { + foundHoveredWindow = true; + } + } + + if (!foundHoveredWindow) { + mLastHoverWindowHandle = NULL; } - if (oldFocusedWindowChannel != NULL) { - if (!mFocusedWindow || oldFocusedWindowChannel != mFocusedWindow->inputChannel) { + if (mFocusedWindowHandle != newFocusedWindowHandle) { + if (mFocusedWindowHandle != NULL) { #if DEBUG_FOCUS LOGD("Focus left window: %s", - oldFocusedWindowChannel->getName().string()); + mFocusedWindowHandle->name.string()); #endif CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, "focus left window"); - synthesizeCancelationEventsForInputChannelLocked(oldFocusedWindowChannel, options); - oldFocusedWindowChannel.clear(); + synthesizeCancelationEventsForInputChannelLocked( + mFocusedWindowHandle->inputChannel, options); } - } - if (mFocusedWindow && oldFocusedWindowChannel == NULL) { + if (newFocusedWindowHandle != NULL) { #if DEBUG_FOCUS - LOGD("Focus entered window: %s", - mFocusedWindow->inputChannel->getName().string()); + LOGD("Focus entered window: %s", + newFocusedWindowHandle->name.string()); #endif + } + mFocusedWindowHandle = newFocusedWindowHandle; } - for (size_t i = 0; i < mTouchState.windows.size(); ) { + for (size_t i = 0; i < mTouchState.windows.size(); i++) { TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i); - const InputWindow* window = getWindowLocked(touchedWindow.channel); - if (window) { - touchedWindow.window = window; - i += 1; - } else { + if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { #if DEBUG_FOCUS - LOGD("Touched window was removed: %s", touchedWindow.channel->getName().string()); + LOGD("Touched window was removed: %s", touchedWindow.windowHandle->name.string()); #endif CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "touched window was removed"); - synthesizeCancelationEventsForInputChannelLocked(touchedWindow.channel, options); - mTouchState.windows.removeAt(i); + synthesizeCancelationEventsForInputChannelLocked( + touchedWindow.windowHandle->inputChannel, options); + mTouchState.windows.removeAt(i--); } } - - // Recover the last hovered window. - if (oldLastHoverWindowChannel != NULL) { - mLastHoverWindow = getWindowLocked(oldLastHoverWindowChannel); - oldLastHoverWindowChannel.clear(); - } - -#if DEBUG_FOCUS - //logDispatchStateLocked(); -#endif } // release lock // Wake up poll loop since it may need to make new input dispatching choices. mLooper->wake(); } -void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) { +void InputDispatcher::setFocusedApplication( + const sp<InputApplicationHandle>& inputApplicationHandle) { #if DEBUG_FOCUS LOGD("setFocusedApplication"); #endif { // acquire lock AutoMutex _l(mLock); - releaseFocusedApplicationLocked(); - - if (inputApplication) { - mFocusedApplicationStorage = *inputApplication; - mFocusedApplication = & mFocusedApplicationStorage; + if (inputApplicationHandle != NULL && inputApplicationHandle->update()) { + mFocusedApplicationHandle = inputApplicationHandle; + } else { + mFocusedApplicationHandle.clear(); } #if DEBUG_FOCUS @@ -3243,13 +3250,6 @@ void InputDispatcher::setFocusedApplication(const InputApplication* inputApplica mLooper->wake(); } -void InputDispatcher::releaseFocusedApplicationLocked() { - if (mFocusedApplication) { - mFocusedApplication = NULL; - mFocusedApplicationStorage.inputApplicationHandle.clear(); - } -} - void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { #if DEBUG_FOCUS LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); @@ -3315,15 +3315,15 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel, { // acquire lock AutoMutex _l(mLock); - const InputWindow* fromWindow = getWindowLocked(fromChannel); - const InputWindow* toWindow = getWindowLocked(toChannel); - if (! fromWindow || ! toWindow) { + sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromChannel); + sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toChannel); + if (fromWindowHandle == NULL || toWindowHandle == NULL) { #if DEBUG_FOCUS LOGD("Cannot transfer focus because from or to window not found."); #endif return false; } - if (fromWindow == toWindow) { + if (fromWindowHandle == toWindowHandle) { #if DEBUG_FOCUS LOGD("Trivial transfer to same window."); #endif @@ -3333,7 +3333,7 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel, bool found = false; for (size_t i = 0; i < mTouchState.windows.size(); i++) { const TouchedWindow& touchedWindow = mTouchState.windows[i]; - if (touchedWindow.window == fromWindow) { + if (touchedWindow.windowHandle == fromWindowHandle) { int32_t oldTargetFlags = touchedWindow.targetFlags; BitSet32 pointerIds = touchedWindow.pointerIds; @@ -3342,7 +3342,7 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel, int32_t newTargetFlags = oldTargetFlags & (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); - mTouchState.addOrUpdateWindow(toWindow, newTargetFlags, pointerIds); + mTouchState.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); found = true; break; @@ -3392,7 +3392,7 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { resetTargetsLocked(); mTouchState.reset(); - mLastHoverWindow = NULL; + mLastHoverWindowHandle.clear(); } void InputDispatcher::logDispatchStateLocked() { @@ -3415,15 +3415,15 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { dump.appendFormat(INDENT "DispatchEnabled: %d\n", mDispatchEnabled); dump.appendFormat(INDENT "DispatchFrozen: %d\n", mDispatchFrozen); - if (mFocusedApplication) { + if (mFocusedApplicationHandle != NULL) { dump.appendFormat(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n", - mFocusedApplication->name.string(), - mFocusedApplication->dispatchingTimeout / 1000000.0); + mFocusedApplicationHandle->name.string(), + mFocusedApplicationHandle->dispatchingTimeout / 1000000.0); } else { dump.append(INDENT "FocusedApplication: <null>\n"); } dump.appendFormat(INDENT "FocusedWindow: name='%s'\n", - mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>"); + mFocusedWindowHandle != NULL ? mFocusedWindowHandle->name.string() : "<null>"); dump.appendFormat(INDENT "TouchDown: %s\n", toString(mTouchState.down)); dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split)); @@ -3434,37 +3434,37 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { for (size_t i = 0; i < mTouchState.windows.size(); i++) { const TouchedWindow& touchedWindow = mTouchState.windows[i]; dump.appendFormat(INDENT2 "%d: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n", - i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value, + i, touchedWindow.windowHandle->name.string(), touchedWindow.pointerIds.value, touchedWindow.targetFlags); } } else { dump.append(INDENT "TouchedWindows: <none>\n"); } - if (!mWindows.isEmpty()) { + if (!mWindowHandles.isEmpty()) { dump.append(INDENT "Windows:\n"); - for (size_t i = 0; i < mWindows.size(); i++) { - const InputWindow& window = mWindows[i]; + for (size_t i = 0; i < mWindowHandles.size(); i++) { + const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i); dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, " "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, " "frame=[%d,%d][%d,%d], scale=%f, " "touchableRegion=", - i, window.name.string(), - toString(window.paused), - toString(window.hasFocus), - toString(window.hasWallpaper), - toString(window.visible), - toString(window.canReceiveKeys), - window.layoutParamsFlags, window.layoutParamsType, - window.layer, - window.frameLeft, window.frameTop, - window.frameRight, window.frameBottom, - window.scaleFactor); - dumpRegion(dump, window.touchableRegion); - dump.appendFormat(", inputFeatures=0x%08x", window.inputFeatures); + i, windowHandle->name.string(), + toString(windowHandle->paused), + toString(windowHandle->hasFocus), + toString(windowHandle->hasWallpaper), + toString(windowHandle->visible), + toString(windowHandle->canReceiveKeys), + windowHandle->layoutParamsFlags, windowHandle->layoutParamsType, + windowHandle->layer, + windowHandle->frameLeft, windowHandle->frameTop, + windowHandle->frameRight, windowHandle->frameBottom, + windowHandle->scaleFactor); + dumpRegion(dump, windowHandle->touchableRegion); + dump.appendFormat(", inputFeatures=0x%08x", windowHandle->inputFeatures); dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", - window.ownerPid, window.ownerUid, - window.dispatchingTimeout / 1000000.0); + windowHandle->ownerPid, windowHandle->ownerUid, + windowHandle->dispatchingTimeout / 1000000.0); } } else { dump.append(INDENT "Windows: <none>\n"); @@ -3636,23 +3636,19 @@ void InputDispatcher::onDispatchCycleBrokenLocked( } void InputDispatcher::onANRLocked( - nsecs_t currentTime, const InputApplication* application, const InputWindow* window, + nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime, nsecs_t waitStartTime) { LOGI("Application is not responding: %s. " "%01.1fms since event, %01.1fms since wait started", - getApplicationWindowLabelLocked(application, window).string(), + getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(), (currentTime - eventTime) / 1000000.0, (currentTime - waitStartTime) / 1000000.0); CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doNotifyANRLockedInterruptible); - if (application) { - commandEntry->inputApplicationHandle = application->inputApplicationHandle; - } - if (window) { - commandEntry->inputWindowHandle = window->inputWindowHandle; - commandEntry->inputChannel = window->inputChannel; - } + commandEntry->inputApplicationHandle = applicationHandle; + commandEntry->inputWindowHandle = windowHandle; } void InputDispatcher::doNotifyConfigurationChangedInterruptible( @@ -3686,7 +3682,9 @@ void InputDispatcher::doNotifyANRLockedInterruptible( mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel); + resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, + commandEntry->inputWindowHandle != NULL + ? commandEntry->inputWindowHandle->inputChannel : NULL); } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( @@ -4560,11 +4558,10 @@ void InputDispatcher::TouchState::copyFrom(const TouchState& other) { split = other.split; deviceId = other.deviceId; source = other.source; - windows.clear(); - windows.appendVector(other.windows); + windows = other.windows; } -void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window, +void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds) { if (targetFlags & InputTarget::FLAG_SPLIT) { split = true; @@ -4572,7 +4569,7 @@ void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window, for (size_t i = 0; i < windows.size(); i++) { TouchedWindow& touchedWindow = windows.editItemAt(i); - if (touchedWindow.window == window) { + if (touchedWindow.windowHandle == windowHandle) { touchedWindow.targetFlags |= targetFlags; if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS; @@ -4585,10 +4582,9 @@ void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window, windows.push(); TouchedWindow& touchedWindow = windows.editTop(); - touchedWindow.window = window; + touchedWindow.windowHandle = windowHandle; touchedWindow.targetFlags = targetFlags; touchedWindow.pointerIds = pointerIds; - touchedWindow.channel = window->inputChannel; } void InputDispatcher::TouchState::filterNonAsIsTouchWindows() { @@ -4605,11 +4601,11 @@ void InputDispatcher::TouchState::filterNonAsIsTouchWindows() { } } -const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() const { +sp<InputWindowHandle> InputDispatcher::TouchState::getFirstForegroundWindowHandle() const { for (size_t i = 0; i < windows.size(); i++) { const TouchedWindow& window = windows.itemAt(i); if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { - return window.window; + return window.windowHandle; } } return NULL; @@ -4621,8 +4617,8 @@ bool InputDispatcher::TouchState::isSlippery() const { for (size_t i = 0; i < windows.size(); i++) { const TouchedWindow& window = windows.itemAt(i); if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { - if (haveSlipperyForegroundWindow - || !(window.window->layoutParamsFlags & InputWindow::FLAG_SLIPPERY)) { + if (haveSlipperyForegroundWindow || !(window.windowHandle->layoutParamsFlags + & InputWindowHandle::FLAG_SLIPPERY)) { return false; } haveSlipperyForegroundWindow = true; diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h index bdd1922..15fd274 100644 --- a/services/input/InputDispatcher.h +++ b/services/input/InputDispatcher.h @@ -321,13 +321,14 @@ public: * * This method may be called on any thread (usually by the input manager). */ - virtual void setInputWindows(const Vector<InputWindow>& inputWindows) = 0; + virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) = 0; /* Sets the focused application. * * This method may be called on any thread (usually by the input manager). */ - virtual void setFocusedApplication(const InputApplication* inputApplication) = 0; + virtual void setFocusedApplication( + const sp<InputApplicationHandle>& inputApplicationHandle) = 0; /* Sets the input dispatching mode. * @@ -406,8 +407,8 @@ public: int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags); - virtual void setInputWindows(const Vector<InputWindow>& inputWindows); - virtual void setFocusedApplication(const InputApplication* inputApplication); + virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles); + virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle); virtual void setInputDispatchMode(bool enabled, bool frozen); virtual void setInputFilterEnabled(bool enabled); @@ -578,7 +579,6 @@ private: sp<Connection> connection; nsecs_t eventTime; KeyEntry* keyEntry; - sp<InputChannel> inputChannel; sp<InputApplicationHandle> inputApplicationHandle; sp<InputWindowHandle> inputWindowHandle; int32_t userActivityEventType; @@ -894,7 +894,7 @@ private: // to transfer focus to a new application. EventEntry* mNextUnblockedEvent; - const InputWindow* findTouchedWindowAtLocked(int32_t x, int32_t y); + sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t x, int32_t y); // All registered connections mapped by receive pipe file descriptor. KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd; @@ -953,19 +953,19 @@ private: bool mDispatchFrozen; bool mInputFilterEnabled; - Vector<InputWindow> mWindows; + Vector<sp<InputWindowHandle> > mWindowHandles; - const InputWindow* getWindowLocked(const sp<InputChannel>& inputChannel); + sp<InputWindowHandle> getWindowHandleLocked(const sp<InputChannel>& inputChannel) const; + bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const; // Focus tracking for keys, trackball, etc. - const InputWindow* mFocusedWindow; + sp<InputWindowHandle> mFocusedWindowHandle; // Focus tracking for touch. struct TouchedWindow { - const InputWindow* window; + sp<InputWindowHandle> windowHandle; int32_t targetFlags; BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set - sp<InputChannel> channel; }; struct TouchState { bool down; @@ -978,9 +978,10 @@ private: ~TouchState(); void reset(); void copyFrom(const TouchState& other); - void addOrUpdateWindow(const InputWindow* window,int32_t targetFlags, BitSet32 pointerIds); + void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, + int32_t targetFlags, BitSet32 pointerIds); void filterNonAsIsTouchWindows(); - const InputWindow* getFirstForegroundWindow() const; + sp<InputWindowHandle> getFirstForegroundWindowHandle() const; bool isSlippery() const; }; @@ -988,9 +989,7 @@ private: TouchState mTempTouchState; // Focused application. - InputApplication* mFocusedApplication; - InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication - void releaseFocusedApplicationLocked(); + sp<InputApplicationHandle> mFocusedApplicationHandle; // Dispatch inbound events. bool dispatchConfigurationChangedLocked( @@ -1021,16 +1020,17 @@ private: nsecs_t mInputTargetWaitStartTime; nsecs_t mInputTargetWaitTimeoutTime; bool mInputTargetWaitTimeoutExpired; - sp<InputApplicationHandle> mInputTargetWaitApplication; + sp<InputApplicationHandle> mInputTargetWaitApplicationHandle; // Contains the last window which received a hover event. - const InputWindow* mLastHoverWindow; + sp<InputWindowHandle> mLastHoverWindowHandle; // Finding targets for input events. void resetTargetsLocked(); void commitTargetsLocked(); int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, - const InputApplication* application, const InputWindow* window, + const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime); void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, const sp<InputChannel>& inputChannel); @@ -1043,15 +1043,17 @@ private: nsecs_t* nextWakeupTime, bool* outConflictingPointerActions, const MotionSample** outSplitBatchAfterSample); - void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, - BitSet32 pointerIds); + void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, + int32_t targetFlags, BitSet32 pointerIds); void addMonitoringTargetsLocked(); void pokeUserActivityLocked(const EventEntry* eventEntry); - bool checkInjectionPermission(const InputWindow* window, const InjectionState* injectionState); - bool isWindowObscuredAtPointLocked(const InputWindow* window, int32_t x, int32_t y) const; - bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window); - String8 getApplicationWindowLabelLocked(const InputApplication* application, - const InputWindow* window); + bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, + const InjectionState* injectionState); + bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, + int32_t x, int32_t y) const; + bool isWindowFinishedWithPreviousInputLocked(const sp<InputWindowHandle>& windowHandle); + String8 getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle); // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work @@ -1100,7 +1102,8 @@ private: void onDispatchCycleBrokenLocked( nsecs_t currentTime, const sp<Connection>& connection); void onANRLocked( - nsecs_t currentTime, const InputApplication* application, const InputWindow* window, + nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime, nsecs_t waitStartTime); // Outbound policy interactions. diff --git a/services/input/InputWindow.cpp b/services/input/InputWindow.cpp index b552f6d..0ce8867 100644 --- a/services/input/InputWindow.cpp +++ b/services/input/InputWindow.cpp @@ -22,25 +22,25 @@ namespace android { -// --- InputWindow --- +// --- InputWindowHandle --- -bool InputWindow::touchableRegionContainsPoint(int32_t x, int32_t y) const { +bool InputWindowHandle::touchableRegionContainsPoint(int32_t x, int32_t y) const { return touchableRegion.contains(x, y); } -bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const { +bool InputWindowHandle::frameContainsPoint(int32_t x, int32_t y) const { return x >= frameLeft && x <= frameRight && y >= frameTop && y <= frameBottom; } -bool InputWindow::isTrustedOverlay() const { +bool InputWindowHandle::isTrustedOverlay() const { return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY; } -bool InputWindow::supportsSplitTouch() const { - return layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH; +bool InputWindowHandle::supportsSplitTouch() const { + return layoutParamsFlags & FLAG_SPLIT_TOUCH; } } // namespace android diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h index d166ad4..272081c 100644 --- a/services/input/InputWindow.h +++ b/services/input/InputWindow.h @@ -31,29 +31,14 @@ namespace android { /* * A handle to a window that can receive input. + * * Used by the native input dispatcher to indirectly refer to the window manager objects * that describe a window. */ class InputWindowHandle : public RefBase { -protected: - InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) : - mInputApplicationHandle(inputApplicationHandle) { } - virtual ~InputWindowHandle() { } - public: - inline sp<InputApplicationHandle> getInputApplicationHandle() { - return mInputApplicationHandle; - } - -private: - sp<InputApplicationHandle> mInputApplicationHandle; -}; - + const sp<InputApplicationHandle> inputApplicationHandle; -/* - * An input window describes the bounds of a window that can receive input. - */ -struct InputWindow { // Window flags from WindowManager.LayoutParams enum { FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, @@ -164,6 +149,22 @@ struct InputWindow { bool isTrustedOverlay() const; bool supportsSplitTouch() const; + + /** + * Requests that the state of this object be updated to reflect + * the most current available information about the application. + * + * This method should only be called from within the input dispatcher's + * critical section. + * + * Returns true on success, or false if the handle is no longer valid. + */ + virtual bool update() = 0; + +protected: + InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) : + inputApplicationHandle(inputApplicationHandle) { } + virtual ~InputWindowHandle() { } }; } // namespace android diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk index cabbccb..d92fc74 100644 --- a/services/input/tests/Android.mk +++ b/services/input/tests/Android.mk @@ -2,8 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ifneq ($(TARGET_SIMULATOR),true) - # Build the unit tests. test_src_files := \ InputReader_test.cpp \ @@ -45,5 +43,3 @@ $(foreach file,$(test_src_files), \ # Build the manual test programs. include $(call all-subdir-makefiles) - -endif
\ No newline at end of file diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 67067de..131894a 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -369,11 +369,12 @@ private: return INPUT_EVENT_INJECTION_FAILED; } - virtual void setInputWindows(const Vector<InputWindow>& inputWindows) { + virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) { ADD_FAILURE() << "Should never be called by input reader."; } - virtual void setFocusedApplication(const InputApplication* inputApplication) { + virtual void setFocusedApplication( + const sp<InputApplicationHandle>& inputApplicationHandle) { ADD_FAILURE() << "Should never be called by input reader."; } diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index 158c778..0b15221 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -1072,7 +1072,7 @@ class AppWidgetService extends IAppWidgetService.Stub throw new IllegalArgumentException("packageName and uid don't match packageName=" + packageName); } - if (callingUid != packageUid && Process.supportsProcesses()) { + if (callingUid != packageUid) { throw new IllegalArgumentException("packageName and uid don't match packageName=" + packageName); } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 6afccec..168b894 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -76,6 +76,7 @@ import com.android.internal.backup.IBackupTransport; import com.android.internal.backup.LocalTransport; import com.android.server.PackageManagerBackupAgent.Metadata; +import java.io.DataInputStream; import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; @@ -96,6 +97,10 @@ import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "BackupManagerService"; @@ -224,6 +229,7 @@ class BackupManagerService extends IBackupManager.Stub { public PackageInfo pkgInfo; public int pmToken; // in post-install restore, the PM's token for this transaction public boolean needFullBackup; + public String[] filterSet; RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) { @@ -233,6 +239,7 @@ class BackupManagerService extends IBackupManager.Stub { pkgInfo = _pkg; pmToken = _pmToken; needFullBackup = _needFullBackup; + filterSet = null; } RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, @@ -243,6 +250,18 @@ class BackupManagerService extends IBackupManager.Stub { pkgInfo = null; pmToken = 0; needFullBackup = _needFullBackup; + filterSet = null; + } + + RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, + String[] _filterSet, boolean _needFullBackup) { + transport = _transport; + observer = _obs; + token = _token; + pkgInfo = null; + pmToken = 0; + needFullBackup = _needFullBackup; + filterSet = _filterSet; } } @@ -404,7 +423,7 @@ class BackupManagerService extends IBackupManager.Stub { Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); (new PerformRestoreTask(params.transport, params.observer, params.token, params.pkgInfo, params.pmToken, - params.needFullBackup)).run(); + params.needFullBackup, params.filterSet)).run(); break; } @@ -1665,6 +1684,7 @@ class BackupManagerService extends IBackupManager.Stub { class PerformFullBackupTask implements Runnable { ParcelFileDescriptor mOutputFile; + DeflaterOutputStream mDeflater; IFullBackupRestoreObserver mObserver; boolean mIncludeApks; boolean mIncludeShared; @@ -1674,6 +1694,55 @@ class BackupManagerService extends IBackupManager.Stub { File mFilesDir; File mManifestFile; + class FullBackupRunner implements Runnable { + PackageInfo mPackage; + IBackupAgent mAgent; + ParcelFileDescriptor mPipe; + int mToken; + boolean mSendApk; + + FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe, + int token, boolean sendApk) throws IOException { + mPackage = pack; + mAgent = agent; + mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor()); + mToken = token; + mSendApk = sendApk; + } + + @Override + public void run() { + try { + BackupDataOutput output = new BackupDataOutput( + mPipe.getFileDescriptor()); + + if (DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName); + writeAppManifest(mPackage, mManifestFile, mSendApk); + FullBackup.backupToTar(mPackage.packageName, null, null, + mFilesDir.getAbsolutePath(), + mManifestFile.getAbsolutePath(), + output); + + if (mSendApk) { + writeApkToBackup(mPackage, output); + } + + if (DEBUG) Slog.d(TAG, "Calling doFullBackup()"); + prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL); + mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder); + } catch (IOException e) { + Slog.e(TAG, "Error running full backup for " + mPackage.packageName); + } catch (RemoteException e) { + Slog.e(TAG, "Remote agent vanished during full backup of " + + mPackage.packageName); + } finally { + try { + mPipe.close(); + } catch (IOException e) {} + } + } + } + PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, boolean includeApks, boolean includeShared, boolean doAllApps, String[] packages, AtomicBoolean latch) { @@ -1722,13 +1791,21 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Set up the compression stage + FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor()); + Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION); + DeflaterOutputStream out = new DeflaterOutputStream(ofstream, deflater, true); + + // !!! TODO: if using encryption, set up the encryption stage + // and emit the tar header stating the password salt. + PackageInfo pkg = null; try { // Now back up the app data via the agent mechanism int N = packagesToBackup.size(); for (int i = 0; i < N; i++) { pkg = packagesToBackup.get(i); - backupOnePackage(pkg); + backupOnePackage(pkg, out); } // Finally, shared storage if requested @@ -1740,6 +1817,7 @@ class BackupManagerService extends IBackupManager.Stub { } finally { tearDown(pkg); try { + out.close(); mOutputFile.close(); } catch (IOException e) { /* nothing we can do about this */ @@ -1757,13 +1835,17 @@ class BackupManagerService extends IBackupManager.Stub { } } - private void backupOnePackage(PackageInfo pkg) throws RemoteException { + private void backupOnePackage(PackageInfo pkg, DeflaterOutputStream out) + throws RemoteException { Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName); IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo, IApplicationThread.BACKUP_MODE_FULL); if (agent != null) { + ParcelFileDescriptor[] pipes = null; try { + pipes = ParcelFileDescriptor.createPipe(); + ApplicationInfo app = pkg.applicationInfo; final boolean sendApk = mIncludeApks && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0) @@ -1772,31 +1854,54 @@ class BackupManagerService extends IBackupManager.Stub { sendOnBackupPackage(pkg.packageName); - BackupDataOutput output = new BackupDataOutput( - mOutputFile.getFileDescriptor()); - - if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName); - writeAppManifest(pkg, mManifestFile, sendApk); - FullBackup.backupToTar(pkg.packageName, null, null, - mFilesDir.getAbsolutePath(), - mManifestFile.getAbsolutePath(), - output); - - if (sendApk) { - writeApkToBackup(pkg, output); + final int token = generateToken(); + FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1], + token, sendApk); + pipes[1].close(); // the runner has dup'd it + pipes[1] = null; + Thread t = new Thread(runner); + t.start(); + + // Now pull data from the app and stuff it into the compressor + try { + FileInputStream raw = new FileInputStream(pipes[0].getFileDescriptor()); + DataInputStream in = new DataInputStream(raw); + + byte[] buffer = new byte[16 * 1024]; + int chunkTotal; + while ((chunkTotal = in.readInt()) > 0) { + while (chunkTotal > 0) { + int toRead = (chunkTotal > buffer.length) + ? buffer.length : chunkTotal; + int nRead = in.read(buffer, 0, toRead); + out.write(buffer, 0, nRead); + chunkTotal -= nRead; + } + } + } catch (IOException e) { + Slog.i(TAG, "Caught exception reading from agent", e); } - if (DEBUG) Slog.d(TAG, "Calling doFullBackup()"); - final int token = generateToken(); - prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL); - agent.doFullBackup(mOutputFile, token, mBackupManagerBinder); if (!waitUntilOperationComplete(token)) { Slog.e(TAG, "Full backup failed on package " + pkg.packageName); } else { - if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName); + if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName); } + } catch (IOException e) { Slog.e(TAG, "Error backing up " + pkg.packageName, e); + } finally { + try { + if (pipes != null) { + if (pipes[0] != null) pipes[0].close(); + if (pipes[1] != null) pipes[1].close(); + } + + // Apply a full sync/flush after each application's data + out.flush(); + } catch (IOException e) { + Slog.w(TAG, "Error bringing down backup stack"); + } } } else { Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName); @@ -2070,11 +2175,12 @@ class BackupManagerService extends IBackupManager.Stub { try { mBytes = 0; byte[] buffer = new byte[32 * 1024]; - FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor()); + FileInputStream rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); + InflaterInputStream in = new InflaterInputStream(rawInStream); boolean didRestore; do { - didRestore = restoreOneFile(instream, buffer); + didRestore = restoreOneFile(in, buffer); } while (didRestore); if (DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); @@ -3020,6 +3126,7 @@ class BackupManagerService extends IBackupManager.Stub { private File mStateDir; private int mPmToken; private boolean mNeedFullBackup; + private HashSet<String> mFilterSet; class RestoreRequest { public PackageInfo app; @@ -3033,7 +3140,7 @@ class BackupManagerService extends IBackupManager.Stub { PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer, long restoreSetToken, PackageInfo targetPackage, int pmToken, - boolean needFullBackup) { + boolean needFullBackup, String[] filterSet) { mTransport = transport; mObserver = observer; mToken = restoreSetToken; @@ -3041,6 +3148,15 @@ class BackupManagerService extends IBackupManager.Stub { mPmToken = pmToken; mNeedFullBackup = needFullBackup; + if (filterSet != null) { + mFilterSet = new HashSet<String>(); + for (String pkg : filterSet) { + mFilterSet.add(pkg); + } + } else { + mFilterSet = null; + } + try { mStateDir = new File(mBaseStateDir, transport.transportDirName()); } catch (RemoteException e) { @@ -3052,7 +3168,8 @@ class BackupManagerService extends IBackupManager.Stub { long startRealtime = SystemClock.elapsedRealtime(); if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken) - + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken); + + " mTargetPackage=" + mTargetPackage + " mFilterSet=" + mFilterSet + + " mPmToken=" + mPmToken); PackageManagerBackupAgent pmAgent = null; int error = -1; // assume error @@ -3071,6 +3188,22 @@ class BackupManagerService extends IBackupManager.Stub { List<PackageInfo> agentPackages = allAgentPackages(); if (mTargetPackage == null) { + // if there's a filter set, strip out anything that isn't + // present before proceeding + if (mFilterSet != null) { + for (int i = agentPackages.size() - 1; i >= 0; i--) { + final PackageInfo pkg = agentPackages.get(i); + if (! mFilterSet.contains(pkg.packageName)) { + agentPackages.remove(i); + } + } + if (DEBUG) { + Slog.i(TAG, "Post-filter package set for restore:"); + for (PackageInfo p : agentPackages) { + Slog.i(TAG, " " + p); + } + } + } restorePackages.addAll(agentPackages); } else { // Just one package to attempt restore of @@ -4266,6 +4399,67 @@ class BackupManagerService extends IBackupManager.Stub { return -1; } + public synchronized int restoreSome(long token, IRestoreObserver observer, + String[] packages) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, + "performRestore"); + + if (DEBUG) { + StringBuilder b = new StringBuilder(128); + b.append("restoreSome token="); + b.append(Long.toHexString(token)); + b.append(" observer="); + b.append(observer.toString()); + b.append(" packages="); + if (packages == null) { + b.append("null"); + } else { + b.append('{'); + boolean first = true; + for (String s : packages) { + if (!first) { + b.append(", "); + } else first = false; + b.append(s); + } + b.append('}'); + } + Slog.d(TAG, b.toString()); + } + + if (mEnded) { + throw new IllegalStateException("Restore session already ended"); + } + + if (mRestoreTransport == null || mRestoreSets == null) { + Slog.e(TAG, "Ignoring restoreAll() with no restore set"); + return -1; + } + + if (mPackageName != null) { + Slog.e(TAG, "Ignoring restoreAll() on single-package session"); + return -1; + } + + synchronized (mQueueLock) { + for (int i = 0; i < mRestoreSets.length; i++) { + if (token == mRestoreSets[i].token) { + long oldId = Binder.clearCallingIdentity(); + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); + msg.obj = new RestoreParams(mRestoreTransport, observer, token, + packages, true); + mBackupHandler.sendMessage(msg); + Binder.restoreCallingIdentity(oldId); + return 0; + } + } + } + + Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); + return -1; + } + public synchronized int restorePackage(String packageName, IRestoreObserver observer) { if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index b98d2a2..85891a2 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -33,7 +33,9 @@ import android.net.EthernetDataTracker; import android.net.IConnectivityManager; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; +import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.LinkProperties.CompareAddressesResult; import android.net.MobileDataStateTracker; import android.net.NetworkConfig; import android.net.NetworkInfo; @@ -76,6 +78,8 @@ import com.google.android.collect.Sets; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; @@ -92,6 +96,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class ConnectivityService extends IConnectivityManager.Stub { private static final boolean DBG = true; + private static final boolean VDBG = true; private static final String TAG = "ConnectivityService"; private static final boolean LOGD_RULES = false; @@ -126,6 +131,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { private NetworkStateTracker mNetTrackers[]; /** + * The link properties that define the current links + */ + private LinkProperties mCurrentLinkProperties[]; + + /** * A per Net list of the PID's that requested access to the net * used both as a refcount and for per-PID DNS selection */ @@ -332,6 +342,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1]; + mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1]; mNetworkPreference = getPersistedNetworkPreference(); @@ -468,6 +479,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetConfigs[netType].radio); continue; } + mCurrentLinkProperties[netType] = mNetTrackers[netType].getLinkProperties(); } IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); @@ -1563,6 +1575,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { * right routing table entries exist. */ private void handleConnectivityChange(int netType, boolean doReset) { + int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0; + /* * If a non-default network is enabled, add the host routes that * will allow it's DNS servers to be accessed. @@ -1570,6 +1584,45 @@ public class ConnectivityService extends IConnectivityManager.Stub { handleDnsConfigurationChange(netType); if (mNetTrackers[netType].getNetworkInfo().isConnected()) { + LinkProperties newLp = mNetTrackers[netType].getLinkProperties(); + LinkProperties curLp = mCurrentLinkProperties[netType]; + mCurrentLinkProperties[netType] = newLp; + if (VDBG) { + log("handleConnectivityChange: changed linkProperty[" + netType + "]:" + + " doReset=" + doReset + " resetMask=" + resetMask + + "\n curLp=" + curLp + + "\n newLp=" + newLp); + } + + if (curLp.isIdenticalInterfaceName(newLp)) { + CompareAddressesResult car = curLp.compareAddresses(newLp); + if ((car.removed.size() != 0) || (car.added.size() != 0)) { + for (LinkAddress linkAddr : car.removed) { + if (linkAddr.getAddress() instanceof Inet4Address) { + resetMask |= NetworkUtils.RESET_IPV4_ADDRESSES; + } + if (linkAddr.getAddress() instanceof Inet6Address) { + resetMask |= NetworkUtils.RESET_IPV6_ADDRESSES; + } + } + if (DBG) { + log("handleConnectivityChange: addresses changed" + + " linkProperty[" + netType + "]:" + " resetMask=" + resetMask + + "\n car=" + car); + } + } else { + if (DBG) { + log("handleConnectivityChange: address are the same reset per doReset" + + " linkProperty[" + netType + "]:" + + " resetMask=" + resetMask); + } + } + } else { + resetMask = NetworkUtils.RESET_ALL_ADDRESSES; + log("handleConnectivityChange: interface not not equivalent reset both" + + " linkProperty[" + netType + "]:" + + " resetMask=" + resetMask); + } if (mNetConfigs[netType].isDefault()) { handleApplyDefaultProxy(netType); addDefaultRoute(mNetTrackers[netType]); @@ -1597,13 +1650,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - if (doReset) { + if (doReset || resetMask != 0) { LinkProperties linkProperties = mNetTrackers[netType].getLinkProperties(); if (linkProperties != null) { String iface = linkProperties.getInterfaceName(); if (TextUtils.isEmpty(iface) == false) { - if (DBG) log("resetConnections(" + iface + ")"); - NetworkUtils.resetConnections(iface); + if (DBG) log("resetConnections(" + iface + ", " + resetMask + ")"); + NetworkUtils.resetConnections(iface, resetMask); } } } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 2d55433..18d393f 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -531,10 +531,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mImeSwitcherNotification.sound = null; mImeSwitcherNotification.vibrate = null; Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mImeSwitchPendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0); + mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); mShowOngoingImeSwitcherForPhones = mRes.getBoolean( com.android.internal.R.bool.show_ongoing_ime_switcher); diff --git a/services/java/com/android/server/LoadAverageService.java b/services/java/com/android/server/LoadAverageService.java index da9fc99..e05b570 100644 --- a/services/java/com/android/server/LoadAverageService.java +++ b/services/java/com/android/server/LoadAverageService.java @@ -28,7 +28,6 @@ import android.os.Message; import android.view.Gravity; import android.view.View; import android.view.WindowManager; -import android.view.WindowManagerImpl; public class LoadAverageService extends Service { private View mView; @@ -91,32 +90,46 @@ public class LoadAverageService extends Service { setPadding(4, 4, 4, 4); //setBackgroundResource(com.android.internal.R.drawable.load_average_background); + // Need to scale text size by density... but we won't do it + // linearly, because with higher dps it is nice to squeeze the + // text a bit to fit more of it. And with lower dps, trying to + // go much smaller will result in unreadable text. + int textSize = 10; + float density = c.getResources().getDisplayMetrics().density; + if (density < 1) { + textSize = 9; + } else { + textSize = (int)(10*density); + if (textSize < 10) { + textSize = 10; + } + } mLoadPaint = new Paint(); mLoadPaint.setAntiAlias(true); - mLoadPaint.setTextSize(10); + mLoadPaint.setTextSize(textSize); mLoadPaint.setARGB(255, 255, 255, 255); mAddedPaint = new Paint(); mAddedPaint.setAntiAlias(true); - mAddedPaint.setTextSize(10); + mAddedPaint.setTextSize(textSize); mAddedPaint.setARGB(255, 128, 255, 128); mRemovedPaint = new Paint(); mRemovedPaint.setAntiAlias(true); mRemovedPaint.setStrikeThruText(true); - mRemovedPaint.setTextSize(10); + mRemovedPaint.setTextSize(textSize); mRemovedPaint.setARGB(255, 255, 128, 128); mShadowPaint = new Paint(); mShadowPaint.setAntiAlias(true); - mShadowPaint.setTextSize(10); + mShadowPaint.setTextSize(textSize); //mShadowPaint.setFakeBoldText(true); mShadowPaint.setARGB(192, 0, 0, 0); mLoadPaint.setShadowLayer(4, 0, 0, 0xff000000); mShadow2Paint = new Paint(); mShadow2Paint.setAntiAlias(true); - mShadow2Paint.setTextSize(10); + mShadow2Paint.setTextSize(textSize); //mShadow2Paint.setFakeBoldText(true); mShadow2Paint.setARGB(192, 0, 0, 0); mLoadPaint.setShadowLayer(2, 0, 0, 0xff000000); @@ -153,14 +166,16 @@ public class LoadAverageService extends Service { } @Override - protected void onMeasure(int widthMeasureSpect, int heightMeasureSpec) { - setMeasuredDimension(mNeededWidth, mNeededHeight); + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(resolveSize(mNeededWidth, widthMeasureSpec), + resolveSize(mNeededHeight, heightMeasureSpec)); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - final int W = getWidth(); + final int W = mNeededWidth; + final int RIGHT = getWidth()-1; final Stats stats = mStats; final int userTime = stats.getLastUserTime(); @@ -178,7 +193,7 @@ public class LoadAverageService extends Service { int systemW = (systemTime*W)/totalTime; int irqW = ((iowaitTime+irqTime+softIrqTime)*W)/totalTime; - int x = W - mPaddingRight; + int x = RIGHT - mPaddingRight; int top = mPaddingTop + 2; int bottom = mPaddingTop + mFH - 2; @@ -196,15 +211,15 @@ public class LoadAverageService extends Service { } int y = mPaddingTop - (int)mAscent; - canvas.drawText(stats.mLoadText, W-mPaddingRight-stats.mLoadWidth-1, + canvas.drawText(stats.mLoadText, RIGHT-mPaddingRight-stats.mLoadWidth-1, y-1, mShadowPaint); - canvas.drawText(stats.mLoadText, W-mPaddingRight-stats.mLoadWidth-1, + canvas.drawText(stats.mLoadText, RIGHT-mPaddingRight-stats.mLoadWidth-1, y+1, mShadowPaint); - canvas.drawText(stats.mLoadText, W-mPaddingRight-stats.mLoadWidth+1, + canvas.drawText(stats.mLoadText, RIGHT-mPaddingRight-stats.mLoadWidth+1, y-1, mShadow2Paint); - canvas.drawText(stats.mLoadText, W-mPaddingRight-stats.mLoadWidth+1, + canvas.drawText(stats.mLoadText, RIGHT-mPaddingRight-stats.mLoadWidth+1, y+1, mShadow2Paint); - canvas.drawText(stats.mLoadText, W-mPaddingRight-stats.mLoadWidth, + canvas.drawText(stats.mLoadText, RIGHT-mPaddingRight-stats.mLoadWidth, y, mLoadPaint); int N = stats.countWorkingStats(); @@ -216,7 +231,7 @@ public class LoadAverageService extends Service { userW = (st.rel_utime*W)/totalTime; systemW = (st.rel_stime*W)/totalTime; - x = W - mPaddingRight; + x = RIGHT - mPaddingRight; if (systemW > 0) { canvas.drawRect(x-systemW, top, x, bottom, mSystemPaint); x -= systemW; @@ -226,18 +241,18 @@ public class LoadAverageService extends Service { x -= userW; } - canvas.drawText(st.name, W-mPaddingRight-st.nameWidth-1, + canvas.drawText(st.name, RIGHT-mPaddingRight-st.nameWidth-1, y-1, mShadowPaint); - canvas.drawText(st.name, W-mPaddingRight-st.nameWidth-1, + canvas.drawText(st.name, RIGHT-mPaddingRight-st.nameWidth-1, y+1, mShadowPaint); - canvas.drawText(st.name, W-mPaddingRight-st.nameWidth+1, + canvas.drawText(st.name, RIGHT-mPaddingRight-st.nameWidth+1, y-1, mShadow2Paint); - canvas.drawText(st.name, W-mPaddingRight-st.nameWidth+1, + canvas.drawText(st.name, RIGHT-mPaddingRight-st.nameWidth+1, y+1, mShadow2Paint); Paint p = mLoadPaint; if (st.added) p = mAddedPaint; if (st.removed) p = mRemovedPaint; - canvas.drawText(st.name, W-mPaddingRight-st.nameWidth, y, p); + canvas.drawText(st.name, RIGHT-mPaddingRight-st.nameWidth, y, p); } } @@ -270,7 +285,7 @@ public class LoadAverageService extends Service { super.onCreate(); mView = new LoadView(this); WindowManager.LayoutParams params = new WindowManager.LayoutParams( - WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 54e5432..94465fd 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -1075,18 +1075,22 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC com.android.internal.R.styleable.Storage_mtpReserve, 0); boolean allowMassStorage = a.getBoolean( com.android.internal.R.styleable.Storage_allowMassStorage, false); + // resource parser does not support longs, so XML value is in megabytes + long maxFileSize = a.getInt( + com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L; Slog.d(TAG, "got storage path: " + path + " description: " + description + " primary: " + primary + " removable: " + removable + " emulated: " + emulated + " mtpReserve: " + mtpReserve + - " allowMassStorage: " + allowMassStorage); + " allowMassStorage: " + allowMassStorage + + " maxFileSize: " + maxFileSize); if (path == null || description == null) { Slog.e(TAG, "path or description is null in readStorageList"); } else { String pathString = path.toString(); StorageVolume volume = new StorageVolume(pathString, description.toString(), removable, emulated, - mtpReserve, allowMassStorage); + mtpReserve, allowMassStorage, maxFileSize); if (primary) { if (mPrimaryVolume == null) { mPrimaryVolume = volume; @@ -2357,6 +2361,19 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC pw.print(" -> "); pw.println(e.getValue().toString()); } } + + pw.println(""); + + synchronized (mVolumes) { + pw.println(" mVolumes:"); + + final int N = mVolumes.size(); + for (int i = 0; i < N; i++) { + final StorageVolume v = mVolumes.get(i); + pw.print(" "); + pw.println(v.toString()); + } + } } } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index da1bf83..41e8a31 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -77,11 +77,15 @@ class NetworkManagementService extends INetworkManagementService.Stub { /** Path to {@code /proc/uid_stat}. */ @Deprecated - private final File mProcStatsUidstat; + private final File mStatsUid; + /** Path to {@code /proc/net/dev}. */ + private final File mStatsIface; /** Path to {@code /proc/net/xt_qtaguid/stats}. */ - private final File mProcStatsNetfilter; + private final File mStatsXtUid; + /** Path to {@code /proc/net/xt_qtaguid/iface_stat}. */ + private final File mStatsXtIface; - /** {@link #mProcStatsNetfilter} headers. */ + /** {@link #mStatsXtUid} headers. */ private static final String KEY_IFACE = "iface"; private static final String KEY_TAG_HEX = "acct_tag_hex"; private static final String KEY_UID = "uid_tag_int"; @@ -137,8 +141,10 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext = context; mObservers = new ArrayList<INetworkManagementEventObserver>(); - mProcStatsUidstat = new File(procRoot, "uid_stat"); - mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats"); + mStatsUid = new File(procRoot, "uid_stat"); + mStatsIface = new File(procRoot, "net/dev"); + mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); + mStatsXtIface = new File(procRoot, "net/xt_qtaguid/iface_stat"); if ("simulator".equals(SystemProperties.get("ro.product.device"))) { return; @@ -161,9 +167,12 @@ class NetworkManagementService extends INetworkManagementService.Stub { } // @VisibleForTesting - public static NetworkManagementService createForTest(Context context, File procRoot) { + public static NetworkManagementService createForTest( + Context context, File procRoot, boolean bandwidthControlEnabled) { // TODO: eventually connect with mock netd - return new NetworkManagementService(context, procRoot); + final NetworkManagementService service = new NetworkManagementService(context, procRoot); + service.mBandwidthControlEnabled = bandwidthControlEnabled; + return service; } public void systemReady() { @@ -187,6 +196,9 @@ class NetworkManagementService extends INetworkManagementService.Stub { } else { Slog.d(TAG, "not enabling bandwidth control"); } + + SystemProperties.set(NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED, + mBandwidthControlEnabled ? "1" : "0"); } public void registerObserver(INetworkManagementEventObserver obs) { @@ -930,13 +942,68 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - final String[] ifaces = listInterfaces(); - final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length); + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); + final NetworkStats.Entry entry = new NetworkStats.Entry(); + + final HashSet<String> activeIfaces = Sets.newHashSet(); + final ArrayList<String> values = Lists.newArrayList(); + + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(mStatsIface)); + + // skip first two header lines + reader.readLine(); + reader.readLine(); - for (String iface : ifaces) { - final long rx = getInterfaceCounter(iface, true); - final long tx = getInterfaceCounter(iface, false); - stats.addEntry(iface, UID_ALL, TAG_NONE, rx, tx); + // parse remaining lines + String line; + while ((line = reader.readLine()) != null) { + splitLine(line, values); + + try { + entry.iface = values.get(0); + entry.uid = UID_ALL; + entry.tag = TAG_NONE; + entry.rxBytes = Long.parseLong(values.get(1)); + entry.rxPackets = Long.parseLong(values.get(2)); + entry.txBytes = Long.parseLong(values.get(9)); + entry.txPackets = Long.parseLong(values.get(10)); + + activeIfaces.add(entry.iface); + stats.addValues(entry); + } catch (NumberFormatException e) { + Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); + } + } + } catch (IOException e) { + Slog.w(TAG, "problem parsing stats: " + e); + } finally { + IoUtils.closeQuietly(reader); + } + + if (DBG) Slog.d(TAG, "recorded active stats from " + activeIfaces); + + // splice in stats from any disabled ifaces + if (mBandwidthControlEnabled) { + final HashSet<String> xtIfaces = Sets.newHashSet(fileListWithoutNull(mStatsXtIface)); + xtIfaces.removeAll(activeIfaces); + + for (String iface : xtIfaces) { + final File ifacePath = new File(mStatsXtIface, iface); + + entry.iface = iface; + entry.uid = UID_ALL; + entry.tag = TAG_NONE; + entry.rxBytes = readSingleLongFromFile(new File(ifacePath, "rx_bytes")); + entry.rxPackets = readSingleLongFromFile(new File(ifacePath, "rx_packets")); + entry.txBytes = readSingleLongFromFile(new File(ifacePath, "tx_bytes")); + entry.txPackets = readSingleLongFromFile(new File(ifacePath, "tx_packets")); + + stats.addValues(entry); + } + + if (DBG) Slog.d(TAG, "recorded stale stats from " + xtIfaces); } return stats; @@ -1063,13 +1130,15 @@ class NetworkManagementService extends INetworkManagementService.Stub { */ private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) { final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); + final NetworkStats.Entry entry = new NetworkStats.Entry(); + final ArrayList<String> keys = Lists.newArrayList(); final ArrayList<String> values = Lists.newArrayList(); final HashMap<String, String> parsed = Maps.newHashMap(); BufferedReader reader = null; try { - reader = new BufferedReader(new FileReader(mProcStatsNetfilter)); + reader = new BufferedReader(new FileReader(mStatsXtUid)); // parse first line as header String line = reader.readLine(); @@ -1081,15 +1150,16 @@ class NetworkManagementService extends INetworkManagementService.Stub { parseLine(keys, values, parsed); try { - final String iface = parsed.get(KEY_IFACE); - final int tag = NetworkManagementSocketTagger.kernelToTag( + // TODO: add rxPackets/txPackets once kernel exports + entry.iface = parsed.get(KEY_IFACE); + entry.tag = NetworkManagementSocketTagger.kernelToTag( parsed.get(KEY_TAG_HEX)); - final int uid = Integer.parseInt(parsed.get(KEY_UID)); - final long rx = Long.parseLong(parsed.get(KEY_RX)); - final long tx = Long.parseLong(parsed.get(KEY_TX)); + entry.uid = Integer.parseInt(parsed.get(KEY_UID)); + entry.rxBytes = Long.parseLong(parsed.get(KEY_RX)); + entry.txBytes = Long.parseLong(parsed.get(KEY_TX)); - if (limitUid == UID_ALL || limitUid == uid) { - stats.addEntry(iface, uid, tag, rx, tx); + if (limitUid == UID_ALL || limitUid == entry.uid) { + stats.addValues(entry); } } catch (NumberFormatException e) { Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); @@ -1114,19 +1184,27 @@ class NetworkManagementService extends INetworkManagementService.Stub { private NetworkStats getNetworkStatsDetailUidstat(int limitUid) { final String[] knownUids; if (limitUid == UID_ALL) { - knownUids = mProcStatsUidstat.list(); + knownUids = fileListWithoutNull(mStatsUid); } else { knownUids = new String[] { String.valueOf(limitUid) }; } final NetworkStats stats = new NetworkStats( SystemClock.elapsedRealtime(), knownUids.length); + final NetworkStats.Entry entry = new NetworkStats.Entry(); for (String uid : knownUids) { final int uidInt = Integer.parseInt(uid); - final File uidPath = new File(mProcStatsUidstat, uid); - final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); - final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd")); - stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx); + final File uidPath = new File(mStatsUid, uid); + + entry.iface = IFACE_ALL; + entry.uid = uidInt; + entry.tag = TAG_NONE; + entry.rxBytes = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); + entry.rxPackets = readSingleLongFromFile(new File(uidPath, "tcp_rcv_pkt")); + entry.txBytes = readSingleLongFromFile(new File(uidPath, "tcp_snd")); + entry.txPackets = readSingleLongFromFile(new File(uidPath, "tcp_snd_pkt")); + + stats.addValues(entry); } return stats; @@ -1197,7 +1275,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { private static void splitLine(String line, ArrayList<String> outSplit) { outSplit.clear(); - final StringTokenizer t = new StringTokenizer(line); + final StringTokenizer t = new StringTokenizer(line, " \t\n\r\f:"); while (t.hasMoreTokens()) { outSplit.add(t.nextToken()); } @@ -1232,6 +1310,15 @@ class NetworkManagementService extends INetworkManagementService.Stub { } } + /** + * Wrapper for {@link File#list()} that returns empty array instead of + * {@code null}. + */ + private static String[] fileListWithoutNull(File file) { + final String[] list = file.list(); + return list != null ? list : new String[0]; + } + public void setDefaultInterfaceForDns(String iface) throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java index 15f22c0..f7fe39e 100644 --- a/services/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/java/com/android/server/NetworkTimeUpdateService.java @@ -16,8 +16,6 @@ package com.android.server; -import com.android.internal.telephony.TelephonyIntents; - import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -28,7 +26,6 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.SntpClient; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -36,12 +33,10 @@ import android.os.Message; import android.os.SystemClock; import android.provider.Settings; import android.util.Log; -import android.util.Slog; +import android.util.NtpTrustedTime; +import android.util.TrustedTime; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Properties; +import com.android.internal.telephony.TelephonyIntents; /** * Monitors the network time and updates the system time if it is out of sync @@ -68,14 +63,11 @@ public class NetworkTimeUpdateService { private static final long POLLING_INTERVAL_SHORTER_MS = 60 * 1000L; // 60 seconds /** Number of times to try again */ private static final int TRY_AGAIN_TIMES_MAX = 3; - /** How long to wait for the NTP server to respond. */ - private static final int MAX_NTP_FETCH_WAIT_MS = 20 * 1000; /** If the time difference is greater than this threshold, then update the time. */ private static final int TIME_ERROR_THRESHOLD_MS = 5 * 1000; private static final String ACTION_POLL = "com.android.server.NetworkTimeUpdateService.action.POLL"; - private static final String PROPERTIES_FILE = "/etc/gps.conf"; private static int POLL_REQUEST = 0; private static final long NOT_SET = -1; @@ -84,14 +76,14 @@ public class NetworkTimeUpdateService { private long mNitzZoneSetTime = NOT_SET; private Context mContext; + private TrustedTime mTime; + // NTP lookup is done on this thread and handler private Handler mHandler; private HandlerThread mThread; private AlarmManager mAlarmManager; private PendingIntent mPendingPollIntent; private SettingsObserver mSettingsObserver; - // Address of the NTP server - private String mNtpServer; // The last time that we successfully fetched the NTP time. private long mLastNtpFetchTime = NOT_SET; // Keeps track of how many quick attempts were made to fetch NTP time. @@ -101,6 +93,7 @@ public class NetworkTimeUpdateService { public NetworkTimeUpdateService(Context context) { mContext = context; + mTime = NtpTrustedTime.getInstance(context); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); Intent pollIntent = new Intent(ACTION_POLL, null); mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); @@ -108,12 +101,6 @@ public class NetworkTimeUpdateService { /** Initialize the receivers and initiate the first NTP request */ public void systemReady() { - mNtpServer = getNtpServerAddress(); - if (mNtpServer == null) { - Slog.e(TAG, "NTP server address not found, not syncing to NTP time"); - return; - } - registerForTelephonyIntents(); registerForAlarms(); registerForConnectivityIntents(); @@ -128,27 +115,6 @@ public class NetworkTimeUpdateService { mSettingsObserver.observe(mContext); } - private String getNtpServerAddress() { - String serverAddress = null; - FileInputStream stream = null; - try { - Properties properties = new Properties(); - File file = new File(PROPERTIES_FILE); - stream = new FileInputStream(file); - properties.load(stream); - serverAddress = properties.getProperty("NTP_SERVER", null); - } catch (IOException e) { - Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (Exception e) {} - } - } - return serverAddress; - } - private void registerForTelephonyIntents() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME); @@ -189,9 +155,15 @@ public class NetworkTimeUpdateService { if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + POLLING_INTERVAL_MS || event == EVENT_AUTO_TIME_CHANGED) { if (DBG) Log.d(TAG, "Before Ntp fetch"); - long ntp = getNtpTime(); - if (DBG) Log.d(TAG, "Ntp = " + ntp); - if (ntp > 0) { + + // force refresh NTP cache when outdated + if (mTime.getCacheAge() >= POLLING_INTERVAL_MS) { + mTime.forceRefresh(); + } + + // only update when NTP time is fresh + if (mTime.getCacheAge() < POLLING_INTERVAL_MS) { + final long ntp = mTime.currentTimeMillis(); mTryAgainCounter = 0; mLastNtpFetchTime = SystemClock.elapsedRealtime(); if (Math.abs(ntp - currentTime) > TIME_ERROR_THRESHOLD_MS) { @@ -232,15 +204,6 @@ public class NetworkTimeUpdateService { mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent); } - private long getNtpTime() { - SntpClient client = new SntpClient(); - if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT_MS)) { - return client.getNtpTime(); - } else { - return 0; - } - } - /** * Checks if the user prefers to automatically set the time. */ diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java index d81dfdb..b8890aa 100644 --- a/services/java/com/android/server/ThrottleService.java +++ b/services/java/com/android/server/ThrottleService.java @@ -16,9 +16,6 @@ package com.android.server; -import com.android.internal.R; -import com.android.internal.telephony.TelephonyProperties; - import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; @@ -54,6 +51,9 @@ import android.util.NtpTrustedTime; import android.util.Slog; import android.util.TrustedTime; +import com.android.internal.R; +import com.android.internal.telephony.TelephonyProperties; + import java.io.BufferedWriter; import java.io.File; import java.io.FileDescriptor; @@ -63,7 +63,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Calendar; import java.util.GregorianCalendar; -import java.util.Properties; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -87,7 +86,6 @@ public class ThrottleService extends IThrottleManager.Stub { private static final long TESTING_THRESHOLD = 1 * 1024 * 1024; private static final long MAX_NTP_CACHE_AGE = 24 * 60 * 60 * 1000; - private static final long MAX_NTP_FETCH_WAIT = 20 * 1000; private long mMaxNtpCacheAge = MAX_NTP_CACHE_AGE; @@ -127,8 +125,6 @@ public class ThrottleService extends IThrottleManager.Stub { private static final int THROTTLE_INDEX_UNINITIALIZED = -1; private static final int THROTTLE_INDEX_UNTHROTTLED = 0; - private static final String PROPERTIES_FILE = "/etc/gps.conf"; - private Intent mPollStickyBroadcast; private TrustedTime mTime; @@ -139,8 +135,7 @@ public class ThrottleService extends IThrottleManager.Stub { } public ThrottleService(Context context) { - // TODO: move to using cached NtpTrustedTime - this(context, getNetworkManagementService(), new NtpTrustedTime(), + this(context, getNetworkManagementService(), NtpTrustedTime.getInstance(context), context.getResources().getString(R.string.config_datause_iface)); } @@ -341,26 +336,6 @@ public class ThrottleService extends IThrottleManager.Stub { } }, new IntentFilter(ACTION_RESET)); - FileInputStream stream = null; - try { - Properties properties = new Properties(); - File file = new File(PROPERTIES_FILE); - stream = new FileInputStream(file); - properties.load(stream); - final String ntpServer = properties.getProperty("NTP_SERVER", null); - if (mTime instanceof NtpTrustedTime) { - ((NtpTrustedTime) mTime).setNtpServer(ntpServer, MAX_NTP_FETCH_WAIT); - } - } catch (IOException e) { - Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (Exception e) {} - } - } - // use a new thread as we don't want to stall the system for file writes mThread = new HandlerThread(TAG); mThread.start(); @@ -540,8 +515,9 @@ public class ThrottleService extends IThrottleManager.Stub { mIface, NetworkStats.UID_ALL, NetworkStats.TAG_NONE); if (index != -1) { - incRead = stats.rx[index] - mLastRead; - incWrite = stats.tx[index] - mLastWrite; + final NetworkStats.Entry entry = stats.getValues(index, null); + incRead = entry.rxBytes - mLastRead; + incWrite = entry.txBytes - mLastWrite; } else { // missing iface, assume stats are 0 Slog.w(TAG, "unable to find stats for iface " + mIface); diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 80cdf6b..f99951fa 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1155,9 +1155,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION; private static final int RETRIEVAL_ALLOWING_EVENT_TYPES = - AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END - | AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START - | AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED + AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java index 1af7015..dbd9474 100644 --- a/services/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/java/com/android/server/accessibility/TouchExplorer.java @@ -838,8 +838,6 @@ public class TouchExplorer implements Explorer { */ private void sendAccessibilityEvent(int eventType) { AccessibilityEvent event = AccessibilityEvent.obtain(eventType); - event.setPackageName(mContext.getPackageName()); - event.setClassName(getClass().getName()); mAccessibilityManager.sendAccessibilityEvent(event); } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 29cccb6..fd93bcf 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -45,7 +45,6 @@ import android.app.INotificationManager; import android.app.IProcessObserver; import android.app.IServiceConnection; import android.app.IThumbnailReceiver; -import android.app.IThumbnailRetriever; import android.app.Instrumentation; import android.app.Notification; import android.app.NotificationManager; @@ -54,6 +53,7 @@ import android.app.Service; import android.app.backup.IBackupManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; +import android.content.ComponentCallbacks; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -181,22 +181,8 @@ public final class ActivityManagerService extends ActivityManagerNative // The flags that are set for all calls we make to the package manager. static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES; - private static final String SYSTEM_SECURE = "ro.secure"; private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; - // This is the maximum number of application processes we would like - // to have running. Due to the asynchronous nature of things, we can - // temporarily go beyond this limit. - static final int MAX_PROCESSES = 2; - - // Set to false to leave processes running indefinitely, relying on - // the kernel killing them as resources are required. - static final boolean ENFORCE_PROCESS_LIMIT = false; - - // This is the maximum number of activities that we would like to have - // running at a given time. - static final int MAX_ACTIVITIES = 20; - // Maximum number of recent tasks that we can remember. static final int MAX_RECENT_TASKS = 20; @@ -208,6 +194,12 @@ public final class ActivityManagerService extends ActivityManagerNative // before we decide it's never going to come up for real. static final int PROC_START_TIMEOUT = 10*1000; + // How long we wait for a launched process to attach to the activity manager + // before we decide it's never going to come up for real, when the process was + // started with a wrapper for instrumentation (such as Valgrind) because it + // could take much longer than usual. + static final int PROC_START_TIMEOUT_WITH_WRAPPER = 300*1000; + // How long to wait after going idle before forcing apps to GC. static final int GC_TIMEOUT = 5*1000; @@ -502,15 +494,6 @@ public final class ActivityManagerService extends ActivityManagerNative = new ArrayList<ProcessRecord>(); /** - * List of records for processes that we have started and are waiting - * for them to call back. This is really only needed when running in - * single processes mode, in which case we do not have a unique pid for - * each process. - */ - final ArrayList<ProcessRecord> mStartingProcesses - = new ArrayList<ProcessRecord>(); - - /** * List of persistent applications that are in the process * of being started. */ @@ -917,7 +900,8 @@ public final class ActivityManagerService extends ActivityManagerNative */ boolean mBooted = false; - int mProcessLimit = 0; + int mProcessLimit = MAX_HIDDEN_APPS; + int mProcessLimitOverride = -1; WindowManagerService mWindowManager; @@ -1959,8 +1943,13 @@ public final class ActivityManagerService extends ActivityManagerNative if ("1".equals(SystemProperties.get("debug.assert"))) { debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; } - int pid = Process.start("android.app.ActivityThread", - app.processName, uid, uid, gids, debugFlags, null); + + // Start the process. It will either succeed and return a result containing + // the PID of the new process, or else throw a RuntimeException. + Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", + app.processName, uid, uid, gids, debugFlags, + app.info.targetSdkVersion, null); + BatteryStatsImpl bs = app.batteryStats.getBatteryStats(); synchronized (bs) { if (bs.isOnBattery()) { @@ -1968,12 +1957,12 @@ public final class ActivityManagerService extends ActivityManagerNative } } - EventLog.writeEvent(EventLogTags.AM_PROC_START, pid, uid, + EventLog.writeEvent(EventLogTags.AM_PROC_START, startResult.pid, uid, app.processName, hostingType, hostingNameStr != null ? hostingNameStr : ""); if (app.persistent) { - Watchdog.getInstance().processStarted(app.processName, pid); + Watchdog.getInstance().processStarted(app.processName, startResult.pid); } StringBuilder buf = mStringBuilder; @@ -1987,7 +1976,7 @@ public final class ActivityManagerService extends ActivityManagerNative buf.append(hostingNameStr); } buf.append(": pid="); - buf.append(pid); + buf.append(startResult.pid); buf.append(" uid="); buf.append(uid); buf.append(" gids={"); @@ -2000,26 +1989,15 @@ public final class ActivityManagerService extends ActivityManagerNative } buf.append("}"); Slog.i(TAG, buf.toString()); - if (pid == 0 || pid == MY_PID) { - // Processes are being emulated with threads. - app.pid = MY_PID; - app.removed = false; - mStartingProcesses.add(app); - } else if (pid > 0) { - app.pid = pid; - app.removed = false; - synchronized (mPidsSelfLocked) { - this.mPidsSelfLocked.put(pid, app); - Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); - msg.obj = app; - mHandler.sendMessageDelayed(msg, PROC_START_TIMEOUT); - } - } else { - app.pid = 0; - RuntimeException e = new RuntimeException( - "Failure starting process " + app.processName - + ": returned pid=" + pid); - Slog.e(TAG, e.getMessage(), e); + app.pid = startResult.pid; + app.usingWrapper = startResult.usingWrapper; + app.removed = false; + synchronized (mPidsSelfLocked) { + this.mPidsSelfLocked.put(startResult.pid, app); + Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); + msg.obj = app; + mHandler.sendMessageDelayed(msg, startResult.usingWrapper + ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT); } } catch (RuntimeException e) { // XXX do better error recovery. @@ -2300,11 +2278,10 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized (this) { - int index = mMainStack.indexOfTokenLocked(callingActivity); - if (index < 0) { + ActivityRecord r = mMainStack.isInStackLocked(callingActivity); + if (r == null) { return false; } - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); if (r.app == null || r.app.thread == null) { // The caller is not running... d'oh! return false; @@ -2451,11 +2428,10 @@ public final class ActivityManagerService extends ActivityManagerNative public void setRequestedOrientation(IBinder token, int requestedOrientation) { synchronized (this) { - int index = mMainStack.indexOfTokenLocked(token); - if (index < 0) { + ActivityRecord r = mMainStack.isInStackLocked(token); + if (r == null) { return; } - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); final long origId = Binder.clearCallingIdentity(); mWindowManager.setAppOrientation(r, requestedOrientation); Configuration config = mWindowManager.updateOrientationFromAppTokens( @@ -2473,11 +2449,10 @@ public final class ActivityManagerService extends ActivityManagerNative public int getRequestedOrientation(IBinder token) { synchronized (this) { - int index = mMainStack.indexOfTokenLocked(token); - if (index < 0) { + ActivityRecord r = mMainStack.isInStackLocked(token); + if (r == null) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); return mWindowManager.getAppOrientation(r); } } @@ -2618,11 +2593,10 @@ public final class ActivityManagerService extends ActivityManagerNative public final void finishSubActivity(IBinder token, String resultWho, int requestCode) { synchronized(this) { - int index = mMainStack.indexOfTokenLocked(token); - if (index < 0) { + ActivityRecord self = mMainStack.isInStackLocked(token); + if (self == null) { return; } - ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index); final long origId = Binder.clearCallingIdentity(); @@ -2661,11 +2635,10 @@ public final class ActivityManagerService extends ActivityManagerNative public void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim) { synchronized(this) { - int index = mMainStack.indexOfTokenLocked(token); - if (index < 0) { + ActivityRecord self = mMainStack.isInStackLocked(token); + if (self == null) { return; } - ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index); final long origId = Binder.clearCallingIdentity(); @@ -2744,8 +2717,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - r.stack.cleanUpActivityLocked(r, true); - r.state = ActivityState.STOPPED; + r.stack.cleanUpActivityLocked(r, true, true); } atTop = false; } @@ -3605,9 +3577,6 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); } - } else if (mStartingProcesses.size() > 0) { - app = mStartingProcesses.remove(0); - app.setPid(pid); } else { app = null; } @@ -3932,20 +3901,9 @@ public final class ActivityManagerService extends ActivityManagerNative final long origId = Binder.clearCallingIdentity(); synchronized (this) { - int index = mMainStack.indexOfTokenLocked(token); - if (index >= 0) { - r = (ActivityRecord)mMainStack.mHistory.get(index); - r.icicle = icicle; - r.haveState = true; - r.updateThumbnail(thumbnail, description); - r.stopped = true; - r.state = ActivityState.STOPPED; - if (!r.finishing) { - if (r.configDestroy) { - r.stack.destroyActivityLocked(r, true); - r.stack.resumeTopActivityLocked(null); - } - } + r = mMainStack.isInStackLocked(token); + if (r != null) { + r.stack.activityStoppedLocked(r, icicle, thumbnail, description); } } @@ -3978,35 +3936,30 @@ public final class ActivityManagerService extends ActivityManagerNative } private ActivityRecord getCallingRecordLocked(IBinder token) { - int index = mMainStack.indexOfTokenLocked(token); - if (index >= 0) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); - if (r != null) { - return r.resultTo; - } + ActivityRecord r = mMainStack.isInStackLocked(token); + if (r == null) { + return null; } - return null; + return r.resultTo; } public ComponentName getActivityClassForToken(IBinder token) { synchronized(this) { - int index = mMainStack.indexOfTokenLocked(token); - if (index >= 0) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); - return r.intent.getComponent(); + ActivityRecord r = mMainStack.isInStackLocked(token); + if (r == null) { + return null; } - return null; + return r.intent.getComponent(); } } public String getPackageForToken(IBinder token) { synchronized(this) { - int index = mMainStack.indexOfTokenLocked(token); - if (index >= 0) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); - return r.packageName; + ActivityRecord r = mMainStack.isInStackLocked(token); + if (r == null) { + return null; } - return null; + return r.packageName; } } @@ -4041,8 +3994,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { int callingUid = Binder.getCallingUid(); try { - if (callingUid != 0 && callingUid != Process.SYSTEM_UID && - Process.supportsProcesses()) { + if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { int uid = AppGlobals.getPackageManager() .getPackageUid(packageName); if (uid != Binder.getCallingUid()) { @@ -4070,11 +4022,10 @@ public final class ActivityManagerService extends ActivityManagerNative int requestCode, Intent[] intents, String[] resolvedTypes, int flags) { ActivityRecord activity = null; if (type == INTENT_SENDER_ACTIVITY_RESULT) { - int index = mMainStack.indexOfTokenLocked(token); - if (index < 0) { + activity = mMainStack.isInStackLocked(token); + if (activity == null) { return null; } - activity = (ActivityRecord)mMainStack.mHistory.get(index); if (activity.finishing) { return null; } @@ -4196,11 +4147,17 @@ public final class ActivityManagerService extends ActivityManagerNative public void setProcessLimit(int max) { enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, "setProcessLimit()"); - mProcessLimit = max; + synchronized (this) { + mProcessLimit = max < 0 ? MAX_HIDDEN_APPS : max; + mProcessLimitOverride = max; + } + trimApplications(); } public int getProcessLimit() { - return mProcessLimit; + synchronized (this) { + return mProcessLimitOverride; + } } void foregroundTokenDied(ForegroundToken token) { @@ -4301,8 +4258,7 @@ public final class ActivityManagerService extends ActivityManagerNative } // Root, system server and our own process get to do everything. - if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID || - !Process.supportsProcesses()) { + if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) { return PackageManager.PERMISSION_GRANTED; } // If there is a uid that owns whatever is being accessed, it has @@ -4446,7 +4402,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final boolean checkUriPermissionLocked(Uri uri, int uid, int modeFlags) { // Root gets to do everything. - if (uid == 0 || !Process.supportsProcesses()) { + if (uid == 0) { return true; } HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid); @@ -5465,11 +5421,10 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { if (r == null) { - int index = mMainStack.indexOfTokenLocked(token); - if (index < 0) { + r = mMainStack.isInStackLocked(token); + if (r == null) { return; } - r = (ActivityRecord)mMainStack.mHistory.get(index); } if (thumbnail == null && r.thumbHolder != null) { thumbnail = r.thumbHolder.lastThumbnail; @@ -5527,8 +5482,8 @@ public final class ActivityManagerService extends ActivityManagerNative // CONTENT PROVIDERS // ========================================================= - private final List generateApplicationProvidersLocked(ProcessRecord app) { - List providers = null; + private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) { + List<ProviderInfo> providers = null; try { providers = AppGlobals.getPackageManager(). queryContentProviders(app.processName, app.info.uid, @@ -5966,7 +5921,7 @@ public final class ActivityManagerService extends ActivityManagerNative } public static final void installSystemProviders() { - List providers; + List<ProviderInfo> providers; synchronized (mSelf) { ProcessRecord app = mSelf.mProcessNames.get("system", Process.SYSTEM_UID); providers = mSelf.generateApplicationProvidersLocked(app); @@ -6183,9 +6138,8 @@ public final class ActivityManagerService extends ActivityManagerNative final long origId = Binder.clearCallingIdentity(); synchronized (this) { - int index = mMainStack.indexOfTokenLocked(token); - if (index >= 0) { - r = (ActivityRecord)mMainStack.mHistory.get(index); + r = mMainStack.isInStackLocked(token); + if (r != null) { mMainStack.activitySleptLocked(r); } } @@ -6336,22 +6290,20 @@ public final class ActivityManagerService extends ActivityManagerNative public void setImmersive(IBinder token, boolean immersive) { synchronized(this) { - int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1; - if (index < 0) { + ActivityRecord r = mMainStack.isInStackLocked(token); + if (r == null) { throw new IllegalArgumentException(); } - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); r.immersive = immersive; } } public boolean isImmersive(IBinder token) { synchronized (this) { - int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1; - if (index < 0) { + ActivityRecord r = mMainStack.isInStackLocked(token); + if (r == null) { throw new IllegalArgumentException(); } - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); return r.immersive; } } @@ -6584,13 +6536,6 @@ public final class ActivityManagerService extends ActivityManagerNative } public void systemReady(final Runnable goingCallback) { - // In the simulator, startRunning will never have been called, which - // normally sets a few crucial variables. Do it here instead. - if (!Process.supportsProcesses()) { - mStartRunning = true; - mTopAction = Intent.ACTION_MAIN; - } - synchronized(this) { if (mSystemReady) { if (goingCallback != null) goingCallback.run(); @@ -7954,14 +7899,6 @@ public final class ActivityManagerService extends ActivityManagerNative "Starting Norm", "Restarting PERS"); } - if (mStartingProcesses.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Processes that are starting:"); - dumpProcessList(pw, this, mStartingProcesses, " ", - "Starting Norm", "Starting PERS"); - } - if (mRemovedProcesses.size() > 0) { if (needSep) pw.println(" "); needSep = true; @@ -8829,9 +8766,10 @@ public final class ActivityManagerService extends ActivityManagerNative } else { foreground = " "; } - pw.println(String.format("%s%s #%2d: adj=%s/%s%s %s (%s)", + pw.println(String.format("%s%s #%2d: adj=%s/%s%s trm=%2d %s (%s)", prefix, (r.persistent ? persistentLabel : normalLabel), - N-i, oomAdj, schedGroup, foreground, r.toShortString(), r.adjType)); + N-i, oomAdj, schedGroup, foreground, r.trimMemoryLevel, + r.toShortString(), r.adjType)); if (r.adjSource != null || r.adjTarget != null) { pw.print(prefix); pw.print(" "); @@ -10366,12 +10304,11 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityRecord activity = null; if (token != null) { - int aindex = mMainStack.indexOfTokenLocked(token); - if (aindex < 0) { + activity = mMainStack.isInStackLocked(token); + if (activity == null) { Slog.w(TAG, "Binding with unknown activity: " + token); return 0; } - activity = (ActivityRecord)mMainStack.mHistory.get(aindex); } int clientLabel = 0; @@ -11585,8 +11522,8 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } int state = r.state; - r.state = r.IDLE; - if (state == r.IDLE) { + r.state = BroadcastRecord.IDLE; + if (state == BroadcastRecord.IDLE) { if (explicit) { Slog.w(TAG, "finishReceiver called but state is IDLE"); } @@ -12970,7 +12907,7 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord proc = mProcessesToGc.get(0); Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG); - long when = mProcessesToGc.get(0).lastRequestedGc + GC_MIN_INTERVAL; + long when = proc.lastRequestedGc + GC_MIN_INTERVAL; long now = SystemClock.uptimeMillis(); if (when < (now+GC_TIMEOUT)) { when = now + GC_TIMEOUT; @@ -13113,95 +13050,88 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final boolean updateOomAdjLocked( + private final void updateOomAdjLocked( ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) { app.hiddenAdj = hiddenAdj; if (app.thread == null) { - return true; + return; } - boolean success = true; - final boolean wasKeeping = app.keeping; int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false); - if ((app.pid != 0 && app.pid != MY_PID) || Process.supportsProcesses()) { - if (app.curRawAdj != app.setRawAdj) { - if (app.curRawAdj > FOREGROUND_APP_ADJ - && app.setRawAdj <= FOREGROUND_APP_ADJ) { - // If this app is transitioning from foreground to - // non-foreground, have it do a gc. - scheduleAppGcLocked(app); - } else if (app.curRawAdj >= HIDDEN_APP_MIN_ADJ - && app.setRawAdj < HIDDEN_APP_MIN_ADJ) { - // Likewise do a gc when an app is moving in to the - // background (such as a service stopping). - scheduleAppGcLocked(app); - } - - if (wasKeeping && !app.keeping) { - // This app is no longer something we want to keep. Note - // its current wake lock time to later know to kill it if - // it is not behaving well. - BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - synchronized (stats) { - app.lastWakeTime = stats.getProcessWakeTime(app.info.uid, - app.pid, SystemClock.elapsedRealtime()); - } - app.lastCpuTime = app.curCpuTime; + if (app.curRawAdj != app.setRawAdj) { + if (app.curRawAdj > FOREGROUND_APP_ADJ + && app.setRawAdj <= FOREGROUND_APP_ADJ) { + // If this app is transitioning from foreground to + // non-foreground, have it do a gc. + scheduleAppGcLocked(app); + } else if (app.curRawAdj >= HIDDEN_APP_MIN_ADJ + && app.setRawAdj < HIDDEN_APP_MIN_ADJ) { + // Likewise do a gc when an app is moving in to the + // background (such as a service stopping). + scheduleAppGcLocked(app); + } + + if (wasKeeping && !app.keeping) { + // This app is no longer something we want to keep. Note + // its current wake lock time to later know to kill it if + // it is not behaving well. + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + app.lastWakeTime = stats.getProcessWakeTime(app.info.uid, + app.pid, SystemClock.elapsedRealtime()); } - - app.setRawAdj = app.curRawAdj; + app.lastCpuTime = app.curCpuTime; } - if (adj != app.setAdj) { - if (Process.setOomAdj(app.pid, adj)) { - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( - TAG, "Set app " + app.processName + - " oom adj to " + adj); - app.setAdj = adj; - } else { - success = false; - } + + app.setRawAdj = app.curRawAdj; + } + if (adj != app.setAdj) { + if (Process.setOomAdj(app.pid, adj)) { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( + TAG, "Set app " + app.processName + + " oom adj to " + adj + " because " + app.adjType); + app.setAdj = adj; + } else { + Slog.w(TAG, "Failed setting oom adj of " + app + " to " + adj); } - if (app.setSchedGroup != app.curSchedGroup) { - app.setSchedGroup = app.curSchedGroup; - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, - "Setting process group of " + app.processName - + " to " + app.curSchedGroup); - if (app.waitingToKill != null && - app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { - Slog.i(TAG, "Killing " + app.toShortString() + ": " + app.waitingToKill); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, - app.processName, app.setAdj, app.waitingToKill); - Process.killProcessQuiet(app.pid); + } + if (app.setSchedGroup != app.curSchedGroup) { + app.setSchedGroup = app.curSchedGroup; + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, + "Setting process group of " + app.processName + + " to " + app.curSchedGroup); + if (app.waitingToKill != null && + app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { + Slog.i(TAG, "Killing " + app.toShortString() + ": " + app.waitingToKill); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, app.waitingToKill); + Process.killProcessQuiet(app.pid); + } else { + if (true) { + long oldId = Binder.clearCallingIdentity(); + try { + Process.setProcessGroup(app.pid, app.curSchedGroup); + } catch (Exception e) { + Slog.w(TAG, "Failed setting process group of " + app.pid + + " to " + app.curSchedGroup); + e.printStackTrace(); + } finally { + Binder.restoreCallingIdentity(oldId); + } } else { - if (true) { - long oldId = Binder.clearCallingIdentity(); + if (app.thread != null) { try { - Process.setProcessGroup(app.pid, app.curSchedGroup); - } catch (Exception e) { - Slog.w(TAG, "Failed setting process group of " + app.pid - + " to " + app.curSchedGroup); - e.printStackTrace(); - } finally { - Binder.restoreCallingIdentity(oldId); - } - } - if (false) { - if (app.thread != null) { - try { - app.thread.setSchedulingGroup(app.curSchedGroup); - } catch (RemoteException e) { - } + app.thread.setSchedulingGroup(app.curSchedGroup); + } catch (RemoteException e) { } } } } } - - return success; } private final ActivityRecord resumedAppLocked() { @@ -13215,30 +13145,26 @@ public final class ActivityManagerService extends ActivityManagerNative return resumedActivity; } - private final boolean updateOomAdjLocked(ProcessRecord app) { + private final void updateOomAdjLocked(ProcessRecord app) { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; int curAdj = app.curAdj; - final boolean wasHidden = app.curAdj >= HIDDEN_APP_MIN_ADJ - && app.curAdj <= HIDDEN_APP_MAX_ADJ; + final boolean wasHidden = curAdj >= HIDDEN_APP_MIN_ADJ + && curAdj <= HIDDEN_APP_MAX_ADJ; mAdjSeq++; - final boolean res = updateOomAdjLocked(app, app.hiddenAdj, TOP_APP); - if (res) { - final boolean nowHidden = app.curAdj >= HIDDEN_APP_MIN_ADJ - && app.curAdj <= HIDDEN_APP_MAX_ADJ; - if (nowHidden != wasHidden) { - // Changed to/from hidden state, so apps after it in the LRU - // list may also be changed. - updateOomAdjLocked(); - } + updateOomAdjLocked(app, app.hiddenAdj, TOP_APP); + final boolean nowHidden = app.curAdj >= HIDDEN_APP_MIN_ADJ + && app.curAdj <= HIDDEN_APP_MAX_ADJ; + if (nowHidden != wasHidden) { + // Changed to/from hidden state, so apps after it in the LRU + // list may also be changed. + updateOomAdjLocked(); } - return res; } - final boolean updateOomAdjLocked() { - boolean didOomAdj = true; + final void updateOomAdjLocked() { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; @@ -13260,45 +13186,104 @@ public final class ActivityManagerService extends ActivityManagerNative int step = 0; int numHidden = 0; - // First try updating the OOM adjustment for each of the + // First update the OOM adjustment for each of the // application processes based on their current state. int i = mLruProcesses.size(); int curHiddenAdj = HIDDEN_APP_MIN_ADJ; + int numBg = 0; while (i > 0) { i--; ProcessRecord app = mLruProcesses.get(i); //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj); - if (updateOomAdjLocked(app, curHiddenAdj, TOP_APP)) { - if (curHiddenAdj < EMPTY_APP_ADJ - && app.curAdj == curHiddenAdj) { + updateOomAdjLocked(app, curHiddenAdj, TOP_APP); + if (curHiddenAdj < EMPTY_APP_ADJ + && app.curAdj == curHiddenAdj) { + step++; + if (step >= factor) { + step = 0; + curHiddenAdj++; + } + } + if (!app.killedBackground) { + if (app.curAdj >= HIDDEN_APP_MIN_ADJ) { + numHidden++; + if (numHidden > mProcessLimit) { + Slog.i(TAG, "No longer want " + app.processName + + " (pid " + app.pid + "): hidden #" + numHidden); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "too many background"); + app.killedBackground = true; + Process.killProcessQuiet(app.pid); + } else { + numBg++; + } + } else if (app.curAdj >= HOME_APP_ADJ) { + numBg++; + } + } + } + + // Now determine the memory trimming level of background processes. + // Unfortunately we need to start at the back of the list to do this + // properly. We only do this if the number of background apps we + // are managing to keep around is less than half the maximum we desire; + // if we are keeping a good number around, we'll let them use whatever + // memory they want. + if (numHidden <= (MAX_HIDDEN_APPS/2)) { + final int N = mLruProcesses.size(); + factor = numBg/3; + step = 0; + int curLevel = ComponentCallbacks.TRIM_MEMORY_COMPLETE; + for (i=0; i<N; i++) { + ProcessRecord app = mLruProcesses.get(i); + if (app.curAdj >= HIDDEN_APP_MIN_ADJ && !app.killedBackground) { + if (app.trimMemoryLevel < curLevel && app.thread != null) { + try { + app.thread.scheduleTrimMemory(curLevel); + } catch (RemoteException e) { + } + if (curLevel >= ComponentCallbacks.TRIM_MEMORY_COMPLETE) { + // For these apps we will also finish their activities + // to help them free memory. + mMainStack.destroyActivitiesLocked(app, false); + } + } + app.trimMemoryLevel = curLevel; step++; if (step >= factor) { - step = 0; - curHiddenAdj++; + switch (curLevel) { + case ComponentCallbacks.TRIM_MEMORY_COMPLETE: + curLevel = ComponentCallbacks.TRIM_MEMORY_MODERATE; + break; + case ComponentCallbacks.TRIM_MEMORY_MODERATE: + curLevel = ComponentCallbacks.TRIM_MEMORY_BACKGROUND; + break; + } } - } - if (app.curAdj >= HIDDEN_APP_MIN_ADJ) { - if (!app.killedBackground) { - numHidden++; - if (numHidden > MAX_HIDDEN_APPS) { - Slog.i(TAG, "No longer want " + app.processName - + " (pid " + app.pid + "): hidden #" + numHidden); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, - app.processName, app.setAdj, "too many background"); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + } else if (app.curAdj >= PERCEPTIBLE_APP_ADJ) { + if (app.trimMemoryLevel < ComponentCallbacks.TRIM_MEMORY_INVISIBLE + && app.thread != null) { + try { + app.thread.scheduleTrimMemory(ComponentCallbacks.TRIM_MEMORY_INVISIBLE); + } catch (RemoteException e) { } } + app.trimMemoryLevel = ComponentCallbacks.TRIM_MEMORY_INVISIBLE; + } else { + app.trimMemoryLevel = 0; } - } else { - didOomAdj = false; + } + } else { + final int N = mLruProcesses.size(); + for (i=0; i<N; i++) { + ProcessRecord app = mLruProcesses.get(i); + app.trimMemoryLevel = 0; } } - // If we return false, we will fall back on killing processes to - // have a fixed limit. Do this if a limit has been requested; else - // only return false if one of the adjustments failed. - return ENFORCE_PROCESS_LIMIT || mProcessLimit > 0 ? false : didOomAdj; + if (mAlwaysFinishActivities) { + mMainStack.destroyActivitiesLocked(null, false); + } } final void trimApplications() { @@ -13338,165 +13323,8 @@ public final class ActivityManagerService extends ActivityManagerNative } } - // Now try updating the OOM adjustment for each of the - // application processes based on their current state. - // If the setOomAdj() API is not supported, then go with our - // back-up plan... - if (!updateOomAdjLocked()) { - - // Count how many processes are running services. - int numServiceProcs = 0; - for (i=mLruProcesses.size()-1; i>=0; i--) { - final ProcessRecord app = mLruProcesses.get(i); - - if (app.persistent || app.services.size() != 0 - || app.curReceiver != null) { - // Don't count processes holding services against our - // maximum process count. - if (localLOGV) Slog.v( - TAG, "Not trimming app " + app + " with services: " - + app.services); - numServiceProcs++; - } - } - - int curMaxProcs = mProcessLimit; - if (curMaxProcs <= 0) curMaxProcs = MAX_PROCESSES; - if (mAlwaysFinishActivities) { - curMaxProcs = 1; - } - curMaxProcs += numServiceProcs; - - // Quit as many processes as we can to get down to the desired - // process count. First remove any processes that no longer - // have activites running in them. - for ( i=0; - i<mLruProcesses.size() - && mLruProcesses.size() > curMaxProcs; - i++) { - final ProcessRecord app = mLruProcesses.get(i); - // Quit an application only if it is not currently - // running any activities. - if (!app.persistent && app.activities.size() == 0 - && app.curReceiver == null && app.services.size() == 0) { - Slog.i( - TAG, "Exiting empty application process " - + app.processName + " (" - + (app.thread != null ? app.thread.asBinder() : null) - + ")\n"); - if (app.pid > 0 && app.pid != MY_PID) { - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, - app.processName, app.setAdj, "empty"); - Process.killProcessQuiet(app.pid); - } else { - try { - app.thread.scheduleExit(); - } catch (Exception e) { - // Ignore exceptions. - } - } - // todo: For now we assume the application is not buggy - // or evil, and will quit as a result of our request. - // Eventually we need to drive this off of the death - // notification, and kill the process if it takes too long. - cleanUpApplicationRecordLocked(app, false, i); - i--; - } - } - - // If we still have too many processes, now from the least - // recently used process we start finishing activities. - if (false) Slog.v( - TAG, "*** NOW HAVE " + mLruProcesses.size() + - " of " + curMaxProcs + " processes"); - for ( i=0; - i<mLruProcesses.size() - && mLruProcesses.size() > curMaxProcs; - i++) { - final ProcessRecord app = mLruProcesses.get(i); - // Quit the application only if we have a state saved for - // all of its activities. - boolean canQuit = !app.persistent && app.curReceiver == null - && app.services.size() == 0; - int NUMA = app.activities.size(); - int j; - if (false) Slog.v( - TAG, "Looking to quit " + app.processName); - for (j=0; j<NUMA && canQuit; j++) { - ActivityRecord r = app.activities.get(j); - if (false) Slog.v( - TAG, " " + r.intent.getComponent().flattenToShortString() - + ": frozen=" + r.haveState + ", visible=" + r.visible); - canQuit = (r.haveState || !r.stateNotNeeded) - && !r.visible && r.stopped; - } - if (canQuit) { - // Finish all of the activities, and then the app itself. - for (j=0; j<NUMA; j++) { - ActivityRecord r = app.activities.get(j); - if (!r.finishing) { - r.stack.destroyActivityLocked(r, false); - } - r.resultTo = null; - } - Slog.i(TAG, "Exiting application process " - + app.processName + " (" - + (app.thread != null ? app.thread.asBinder() : null) - + ")\n"); - if (app.pid > 0 && app.pid != MY_PID) { - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, - app.processName, app.setAdj, "old background"); - Process.killProcessQuiet(app.pid); - } else { - try { - app.thread.scheduleExit(); - } catch (Exception e) { - // Ignore exceptions. - } - } - // todo: For now we assume the application is not buggy - // or evil, and will quit as a result of our request. - // Eventually we need to drive this off of the death - // notification, and kill the process if it takes too long. - cleanUpApplicationRecordLocked(app, false, i); - i--; - //dump(); - } - } - - } - - int curMaxActivities = MAX_ACTIVITIES; - if (mAlwaysFinishActivities) { - curMaxActivities = 1; - } - - // Finally, if there are too many activities now running, try to - // finish as many as we can to get back down to the limit. - for ( i=0; - i<mMainStack.mLRUActivities.size() - && mMainStack.mLRUActivities.size() > curMaxActivities; - i++) { - final ActivityRecord r - = (ActivityRecord)mMainStack.mLRUActivities.get(i); - - // We can finish this one if we have its icicle saved and - // it is not persistent. - if ((r.haveState || !r.stateNotNeeded) && !r.visible - && r.stopped && !r.finishing) { - final int origSize = mMainStack.mLRUActivities.size(); - r.stack.destroyActivityLocked(r, true); - - // This will remove it from the LRU list, so keep - // our index at the same value. Note that this check to - // see if the size changes is just paranoia -- if - // something unexpected happens, we don't want to end up - // in an infinite loop. - if (origSize > mMainStack.mLRUActivities.size()) { - i--; - } - } - } + // Now update the oom adj for all processes. + updateOomAdjLocked(); } } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 090e26b..73ffafb 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -658,12 +658,12 @@ final class ActivityRecord extends IApplicationToken.Stub { public long getKeyDispatchingTimeout() { synchronized(service) { ActivityRecord r = getWaitingHistoryRecordLocked(); - if (r == null || r.app == null - || r.app.instrumentationClass == null) { - return ActivityManagerService.KEY_DISPATCHING_TIMEOUT; + if (r != null && r.app != null + && (r.app.instrumentationClass != null || r.app.usingWrapper)) { + return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT; } - - return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT; + + return ActivityManagerService.KEY_DISPATCHING_TIMEOUT; } } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index b1da69f..93d8164 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -52,6 +52,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -83,6 +84,8 @@ final class ActivityStack { static final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION; static final boolean DEBUG_TASKS = ActivityManagerService.DEBUG_TASKS; + static final boolean DEBUG_STATES = false; + static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS; // How long we wait until giving up on the last activity telling us it @@ -392,19 +395,25 @@ final class ActivityStack { } final int indexOfTokenLocked(IBinder token) { - int count = mHistory.size(); - - // convert the token to an entry in the history. - int index = -1; - for (int i=count-1; i>=0; i--) { - Object o = mHistory.get(i); - if (o == token) { - index = i; - break; - } + try { + ActivityRecord r = (ActivityRecord)token; + return mHistory.indexOf(r); + } catch (ClassCastException e) { + Slog.w(TAG, "Bad activity token: " + token, e); + return -1; } + } - return index; + final ActivityRecord isInStackLocked(IBinder token) { + try { + ActivityRecord r = (ActivityRecord)token; + if (mHistory.contains(r)) { + return r; + } + } catch (ClassCastException e) { + Slog.w(TAG, "Bad activity token: " + token, e); + } + return null; } private final boolean updateLRUListLocked(ActivityRecord r) { @@ -604,6 +613,8 @@ final class ActivityStack { // As part of the process of launching, ActivityThread also performs // a resume. r.state = ActivityState.RESUMED; + if (DEBUG_STATES) Slog.v(TAG, "Moving to RESUMED: " + r + + " (starting new instance)"); r.stopped = false; mResumedActivity = r; r.task.touchActiveTime(); @@ -617,6 +628,8 @@ final class ActivityStack { // should look like we asked it to pause+stop (but remain visible), // and it has done so and reported back the current icicle and // other state. + if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r + + " (starting in stopped state)"); r.state = ActivityState.STOPPED; r.stopped = true; } @@ -797,7 +810,8 @@ final class ActivityStack { resumeTopActivityLocked(null); return; } - if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev); + if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev); + else if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev); mResumedActivity = null; mPausingActivity = prev; mLastPausedActivity = prev; @@ -879,6 +893,8 @@ final class ActivityStack { r = mHistory.get(index); mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); if (mPausingActivity == r) { + if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r + + (timeout ? " (due to timeout)" : " (pause complete)")); r.state = ActivityState.PAUSED; completePauseLocked(); } else { @@ -891,6 +907,22 @@ final class ActivityStack { } } + final void activityStoppedLocked(ActivityRecord r, Bundle icicle, Bitmap thumbnail, + CharSequence description) { + r.icicle = icicle; + r.haveState = true; + r.updateThumbnail(thumbnail, description); + r.stopped = true; + if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r + " (stop complete)"); + r.state = ActivityState.STOPPED; + if (!r.finishing) { + if (r.configDestroy) { + destroyActivityLocked(r, true, false); + resumeTopActivityLocked(null); + } + } + } + private final void completePauseLocked() { ActivityRecord prev = mPausingActivity; if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev); @@ -914,7 +946,7 @@ final class ActivityStack { // instance right now, we need to first completely stop // the current instance before starting the new one. if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev); - destroyActivityLocked(prev, true); + destroyActivityLocked(prev, true, false); } else { mStoppingActivities.add(prev); if (mStoppingActivities.size() > 3) { @@ -1371,6 +1403,7 @@ final class ActivityStack { mService.updateCpuStats(); + if (DEBUG_STATES) Slog.v(TAG, "Moving to RESUMED: " + next + " (in existing)"); next.state = ActivityState.RESUMED; mResumedActivity = next; next.task.touchActiveTime(); @@ -1447,6 +1480,8 @@ final class ActivityStack { } catch (Exception e) { // Whoops, need to restart this activity! + if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to " + + lastState + ": " + next); next.state = lastState; mResumedActivity = lastResumedActivity; Slog.i(TAG, "Restarting because process died: " + next); @@ -2960,6 +2995,8 @@ final class ActivityStack { r.resumeKeyDispatchingLocked(); try { r.stopped = false; + if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r + + " (stop requested)"); r.state = ActivityState.STOPPING; if (DEBUG_VISBILITY) Slog.v( TAG, "Stopping visible=" + r.visible + " for " + r); @@ -2977,9 +3014,10 @@ final class ActivityStack { Slog.w(TAG, "Exception thrown during pause", e); // Just in case, assume it to be stopped. r.stopped = true; + if (DEBUG_STATES) Slog.v(TAG, "Stop failed; moving to STOPPED: " + r); r.state = ActivityState.STOPPED; if (r.configDestroy) { - destroyActivityLocked(r, true); + destroyActivityLocked(r, true, false); } } } @@ -3145,7 +3183,7 @@ final class ActivityStack { for (i=0; i<NF; i++) { ActivityRecord r = (ActivityRecord)finishes.get(i); synchronized (mService) { - destroyActivityLocked(r, true); + destroyActivityLocked(r, true, false); } } @@ -3340,6 +3378,8 @@ final class ActivityStack { checkReadyForSleepLocked(); } } + if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r + + " (finish requested)"); r.state = ActivityState.STOPPING; mService.updateOomAdjLocked(); return r; @@ -3353,6 +3393,7 @@ final class ActivityStack { mResumedActivity = null; } final ActivityState prevState = r.state; + if (DEBUG_STATES) Slog.v(TAG, "Moving to FINISHING: " + r); r.state = ActivityState.FINISHING; if (mode == FINISH_IMMEDIATELY @@ -3360,7 +3401,7 @@ final class ActivityStack { || prevState == ActivityState.INITIALIZING) { // If this activity is already stopped, we can just finish // it right now. - return destroyActivityLocked(r, true) ? null : r; + return destroyActivityLocked(r, true, true) ? null : r; } else { // Need to go through the full pause cycle to get this // activity into the stopped state and then finish it. @@ -3378,7 +3419,8 @@ final class ActivityStack { * processing going away, in which case there is no remaining client-side * state to destroy so only the cleanup here is needed. */ - final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices) { + final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, + boolean setState) { if (mResumedActivity == r) { mResumedActivity = null; } @@ -3389,6 +3431,11 @@ final class ActivityStack { r.configDestroy = false; r.frozenBeforeDestroy = false; + if (setState) { + if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (cleaning up)"); + r.state = ActivityState.DESTROYED; + } + // Make sure this record is no longer in the pending finishes list. // This could happen, for example, if we are trimming activities // down to the max limit while they are still waiting to finish. @@ -3428,6 +3475,8 @@ final class ActivityStack { r.makeFinishing(); mHistory.remove(r); r.takeFromHistory(); + if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + + " (removed from history)"); r.state = ActivityState.DESTROYED; mService.mWindowManager.removeAppToken(r); if (VALIDATE_TOKENS) { @@ -3453,6 +3502,22 @@ final class ActivityStack { } } + final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj) { + for (int i=mHistory.size()-1; i>=0; i--) { + ActivityRecord r = mHistory.get(i); + if (owner != null && r.app != owner) { + continue; + } + // We can destroy this one if we have its icicle saved and + // it is not in the process of pausing/stopping/finishing. + if (r.app != null && r.haveState && !r.visible && r.stopped && !r.finishing + && r.state != ActivityState.DESTROYING + && r.state != ActivityState.DESTROYED) { + destroyActivityLocked(r, true, oomAdj); + } + } + } + /** * Destroy the current CLIENT SIDE instance of an activity. This may be * called both when actually finishing an activity, or when performing @@ -3460,7 +3525,7 @@ final class ActivityStack { * but then create a new client-side object for this same HistoryRecord. */ final boolean destroyActivityLocked(ActivityRecord r, - boolean removeFromApp) { + boolean removeFromApp, boolean oomAdj) { if (DEBUG_SWITCH) Slog.v( TAG, "Removing activity: token=" + r + ", app=" + (r.app != null ? r.app.processName : "(null)")); @@ -3470,7 +3535,7 @@ final class ActivityStack { boolean removedFromHistory = false; - cleanUpActivityLocked(r, false); + cleanUpActivityLocked(r, false, false); final boolean hadApp = r.app != null; @@ -3488,7 +3553,7 @@ final class ActivityStack { if (r.app.activities.size() == 0) { // No longer have activities, so update location in // LRU list. - mService.updateLruProcessLocked(r.app, true, false); + mService.updateLruProcessLocked(r.app, oomAdj, false); } } @@ -3513,12 +3578,23 @@ final class ActivityStack { r.app = null; r.nowVisible = false; + // If the activity is finishing, we need to wait on removing it + // from the list to give it a chance to do its cleanup. During + // that time it may make calls back with its token so we need to + // be able to find it on the list and so we don't want to remove + // it from the list yet. Otherwise, we can just immediately put + // it in the destroyed state since we are not removing it from the + // list. if (r.finishing && !skipDestroy) { + if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYING: " + r + + " (destroy requested)"); r.state = ActivityState.DESTROYING; Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG); msg.obj = r; mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT); } else { + if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + + " (destroy skipped)"); r.state = ActivityState.DESTROYED; } } else { @@ -3527,6 +3603,8 @@ final class ActivityStack { removeActivityFromHistoryLocked(r); removedFromHistory = true; } else { + if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + + " (no app)"); r.state = ActivityState.DESTROYED; } } @@ -3919,7 +3997,7 @@ final class ActivityStack { if (r.app == null || r.app.thread == null) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Switch is destroying non-running " + r); - destroyActivityLocked(r, true); + destroyActivityLocked(r, true, false); } else if (r.state == ActivityState.PAUSING) { // A little annoying: we are waiting for this activity to // finish pausing. Let's not do anything now, but just diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 3968f66..99830f9 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -61,6 +61,7 @@ class ProcessRecord { int setAdj; // Last set OOM adjustment for this process int curSchedGroup; // Currently desired scheduling class int setSchedGroup; // Last set to background scheduling class + int trimMemoryLevel; // Last selected memory trimming level boolean keeping; // Actively running code so don't kill due to that? boolean setIsForeground; // Running foreground UI when last set? boolean foregroundServices; // Running any services that are foreground? @@ -78,6 +79,7 @@ class ProcessRecord { IInstrumentationWatcher instrumentationWatcher; // who is waiting Bundle instrumentationArguments;// as given to us ComponentName instrumentationResultClass;// copy of instrumentationClass + boolean usingWrapper; // Set to true when process was launched with a wrapper attached BroadcastRecord curReceiver;// receiver currently running in the app long lastWakeTime; // How long proc held wake lock at last check long lastCpuTime; // How long proc has run CPU at last check @@ -180,7 +182,8 @@ class ProcessRecord { pw.print(" cur="); pw.print(curAdj); pw.print(" set="); pw.println(setAdj); pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup); - pw.print(" setSchedGroup="); pw.println(setSchedGroup); + pw.print(" setSchedGroup="); pw.print(setSchedGroup); + pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground); pw.print(" foregroundServices="); pw.print(foregroundServices); pw.print(" forcingToForeground="); pw.println(forcingToForeground); @@ -304,8 +307,6 @@ class ProcessRecord { } void toShortString(StringBuilder sb) { - sb.append(Integer.toHexString(System.identityHashCode(this))); - sb.append(' '); sb.append(pid); sb.append(':'); sb.append(processName); @@ -319,6 +320,8 @@ class ProcessRecord { } StringBuilder sb = new StringBuilder(128); sb.append("ProcessRecord{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(' '); toShortString(sb); sb.append('}'); return stringName = sb.toString(); diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index 6e8f248..12c8ccf 100644 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -63,7 +63,7 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; // Current on-disk Parcel version - private static final int VERSION = 1005; + private static final int VERSION = 1006; private static final int CHECKIN_VERSION = 4; @@ -145,6 +145,8 @@ public final class UsageStatsService extends IUsageStats.Stub { final HashMap<String, TimeStats> mLaunchTimes = new HashMap<String, TimeStats>(); int mLaunchCount; + final HashMap<String, Long> mLastResumeTimes + = new HashMap<String, Long>(); long mUsageTime; long mPausedTime; long mResumedTime; @@ -160,20 +162,28 @@ public final class UsageStatsService extends IUsageStats.Stub { if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount + ", Usage time:" + mUsageTime); - final int N = in.readInt(); - if (localLOGV) Slog.v(TAG, "Reading comps: " + N); - for (int i=0; i<N; i++) { + final int numTimeStats = in.readInt(); + if (localLOGV) Slog.v(TAG, "Reading comps: " + numTimeStats); + for (int i=0; i<numTimeStats; i++) { String comp = in.readString(); if (localLOGV) Slog.v(TAG, "Component: " + comp); TimeStats times = new TimeStats(in); mLaunchTimes.put(comp, times); } + final int numResumeTimes = in.readInt(); + if (localLOGV) Slog.v(TAG, "Reading last resume times: " + numResumeTimes); + for (int i=0; i<numResumeTimes; i++) { + String comp = in.readString(); + if (localLOGV) Slog.v(TAG, "Component: " + comp); + mLastResumeTimes.put(comp, in.readLong()); + } } - - void updateResume(boolean launched) { + + void updateResume(String comp, boolean launched) { if (launched) { mLaunchCount ++; } + mLastResumeTimes.put(comp, System.currentTimeMillis()); mResumedTime = SystemClock.elapsedRealtime(); } @@ -203,20 +213,29 @@ public final class UsageStatsService extends IUsageStats.Stub { void writeToParcel(Parcel out) { out.writeInt(mLaunchCount); out.writeLong(mUsageTime); - final int N = mLaunchTimes.size(); - out.writeInt(N); - if (N > 0) { + final int numTimeStats = mLaunchTimes.size(); + out.writeInt(numTimeStats); + if (numTimeStats > 0) { for (Map.Entry<String, TimeStats> ent : mLaunchTimes.entrySet()) { out.writeString(ent.getKey()); TimeStats times = ent.getValue(); times.writeToParcel(out); } } + final int numResumeTimes = mLastResumeTimes.size(); + out.writeInt(numResumeTimes); + if (numResumeTimes > 0) { + for (Map.Entry<String, Long> ent : mLastResumeTimes.entrySet()) { + out.writeString(ent.getKey()); + out.writeLong(ent.getValue()); + } + } } void clear() { mLaunchTimes.clear(); mLaunchCount = 0; + mLastResumeTimes.clear(); mUsageTime = 0; } } @@ -546,7 +565,7 @@ public final class UsageStatsService extends IUsageStats.Stub { pus = new PkgUsageStatsExtended(); mStats.put(pkgName, pus); } - pus.updateResume(!samePackage); + pus.updateResume(mLastResumedComp, !samePackage); if (!sameComp) { pus.addLaunchCount(mLastResumedComp); } @@ -624,7 +643,8 @@ public final class UsageStatsService extends IUsageStats.Stub { if (pus == null) { return null; } - return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime); + return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime, + pus.mLastResumeTimes); } } @@ -641,7 +661,8 @@ public final class UsageStatsService extends IUsageStats.Stub { int i = 0; for (String key: keys) { PkgUsageStatsExtended pus = mStats.get(key); - retArr[i] = new PkgUsageStats(key, pus.mLaunchCount, pus.mUsageTime); + retArr[i] = new PkgUsageStats(key, pus.mLaunchCount, pus.mUsageTime, + pus.mLastResumeTimes); i++; } return retArr; diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 6bb7949..d7d4b03 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -221,8 +221,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } public void interfaceLinkStateChanged(String iface, boolean up) { - if (DEBUG) Log.d(TAG, "interfaceLinkStateChanged " + iface + ", " + up); - interfaceStatusChanged(iface, up); } private boolean isUsb(String iface) { diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 4fa3bda..c813d37 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -32,7 +32,6 @@ import android.location.LocationManager; import android.location.LocationProvider; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.SntpClient; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -51,6 +50,7 @@ import android.telephony.SmsMessage; import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; import android.util.Log; +import android.util.NtpTrustedTime; import android.util.SparseIntArray; import com.android.internal.app.IBatteryStats; @@ -61,7 +61,7 @@ import com.android.internal.telephony.Phone; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.StringBufferInputStream; +import java.io.StringReader; import java.util.ArrayList; import java.util.Date; import java.util.Map.Entry; @@ -235,13 +235,13 @@ public class GpsLocationProvider implements LocationProviderInterface { // properties loaded from PROPERTIES_FILE private Properties mProperties; - private String mNtpServer; private String mSuplServerHost; private int mSuplServerPort; private String mC2KServerHost; private int mC2KServerPort; private final Context mContext; + private final NtpTrustedTime mNtpTime; private final ILocationManager mLocationManager; private Location mLocation = new Location(LocationManager.GPS_PROVIDER); private Bundle mLocationExtras = new Bundle(); @@ -286,10 +286,6 @@ public class GpsLocationProvider implements LocationProviderInterface { // current setting - 5 minutes private static final long RETRY_INTERVAL = 5*60*1000; - // to avoid injecting bad NTP time, we reject any time fixes that differ from system time - // by more than 5 minutes. - private static final long MAX_NTP_SYSTEM_TIME_OFFSET = 5*60*1000; - private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() { public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException { if (listener == null) { @@ -378,6 +374,7 @@ public class GpsLocationProvider implements LocationProviderInterface { public GpsLocationProvider(Context context, ILocationManager locationManager) { mContext = context; + mNtpTime = NtpTrustedTime.getInstance(context); mLocationManager = locationManager; mNIHandler = new GpsNetInitiatedHandler(context); @@ -418,7 +415,6 @@ public class GpsLocationProvider implements LocationProviderInterface { FileInputStream stream = new FileInputStream(file); mProperties.load(stream); stream.close(); - mNtpServer = mProperties.getProperty("NTP_SERVER", null); mSuplServerHost = mProperties.getProperty("SUPL_HOST"); String portString = mProperties.getProperty("SUPL_PORT"); @@ -530,13 +526,18 @@ public class GpsLocationProvider implements LocationProviderInterface { } mInjectNtpTimePending = false; - SntpClient client = new SntpClient(); long delay; - if (client.requestTime(mNtpServer, 10000)) { - long time = client.getNtpTime(); - long timeReference = client.getNtpTimeReference(); - int certainty = (int)(client.getRoundTripTime()/2); + // force refresh NTP cache when outdated + if (mNtpTime.getCacheAge() >= NTP_INTERVAL) { + mNtpTime.forceRefresh(); + } + + // only update when NTP time is fresh + if (mNtpTime.getCacheAge() < NTP_INTERVAL) { + long time = mNtpTime.getCachedNtpTime(); + long timeReference = mNtpTime.getCachedNtpTimeReference(); + long certainty = mNtpTime.getCacheCertainty(); long now = System.currentTimeMillis(); Log.d(TAG, "NTP server returned: " @@ -545,7 +546,7 @@ public class GpsLocationProvider implements LocationProviderInterface { + " certainty: " + certainty + " system time offset: " + (time - now)); - native_inject_time(time, timeReference, certainty); + native_inject_time(time, timeReference, (int) certainty); delay = NTP_INTERVAL; } else { if (DEBUG) Log.d(TAG, "requestTime failed"); @@ -1395,7 +1396,7 @@ public class GpsLocationProvider implements LocationProviderInterface { Properties extraProp = new Properties(); try { - extraProp.load(new StringBufferInputStream(extras)); + extraProp.load(new StringReader(extras)); } catch (IOException e) { diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 2a17cbe..d30b66b 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -204,9 +204,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public NetworkPolicyManagerService(Context context, IActivityManager activityManager, IPowerManager powerManager, INetworkStatsService networkStats, INetworkManagementService networkManagement) { - // TODO: move to using cached NtpTrustedTime this(context, activityManager, powerManager, networkStats, networkManagement, - new NtpTrustedTime(), getSystemDir()); + NtpTrustedTime.getInstance(context), getSystemDir()); } private static File getSystemDir() { @@ -406,7 +405,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { final NetworkStats stats = mNetworkStats.getSummaryForNetwork( policy.template, start, end); - total = stats.rx[0] + stats.tx[0]; + final NetworkStats.Entry entry = stats.getValues(0, null); + total = entry.rxBytes + entry.txBytes; } catch (RemoteException e) { Slog.w(TAG, "problem reading summary for template " + policy.template); continue; @@ -606,7 +606,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long total; try { stats = mNetworkStats.getSummaryForNetwork(policy.template, start, end); - total = stats.rx[0] + stats.tx[0]; + final NetworkStats.Entry entry = stats.getValues(0, null); + total = entry.rxBytes + entry.txBytes; } catch (RemoteException e) { Slog.w(TAG, "problem reading summary for template " + policy.template); continue; diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index b4bd176..54e94db 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -27,7 +27,6 @@ import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_REMOVED; -import static android.provider.Settings.Secure.NETSTATS_ENABLED; import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION; import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY; import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD; @@ -71,7 +70,6 @@ import android.util.Slog; import android.util.TrustedTime; import com.android.internal.os.AtomicFile; -import com.android.server.NativeDaemonConnectorException; import com.google.android.collect.Maps; import com.google.android.collect.Sets; @@ -175,9 +173,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public NetworkStatsService( Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) { - // TODO: move to using cached NtpTrustedTime - this(context, networkManager, alarmManager, new NtpTrustedTime(), getSystemDir(), - new DefaultNetworkStatsSettings(context)); + this(context, networkManager, alarmManager, NtpTrustedTime.getInstance(context), + getSystemDir(), new DefaultNetworkStatsSettings(context)); } private static File getSystemDir() { @@ -316,22 +313,29 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); synchronized (mStatsLock) { - long rx = 0; - long tx = 0; - long[] networkTotal = new long[2]; + // use system clock to be externally consistent + final long now = System.currentTimeMillis(); + + final NetworkStats stats = new NetworkStats(end - start, 1); + final NetworkStats.Entry entry = new NetworkStats.Entry(); + NetworkStatsHistory.Entry historyEntry = null; // combine total from all interfaces that match template for (NetworkIdentitySet ident : mNetworkStats.keySet()) { if (templateMatches(template, ident)) { final NetworkStatsHistory history = mNetworkStats.get(ident); - networkTotal = history.getTotalData(start, end, networkTotal); - rx += networkTotal[0]; - tx += networkTotal[1]; + historyEntry = history.getValues(start, end, now, historyEntry); + + entry.iface = IFACE_ALL; + entry.uid = UID_ALL; + entry.tag = TAG_NONE; + entry.rxBytes = historyEntry.rxBytes; + entry.txBytes = historyEntry.txBytes; + + stats.combineValues(entry); } } - final NetworkStats stats = new NetworkStats(end - start, 1); - stats.addEntry(IFACE_ALL, UID_ALL, TAG_NONE, rx, tx); return stats; } } @@ -344,8 +348,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { synchronized (mStatsLock) { ensureUidStatsLoadedLocked(); + // use system clock to be externally consistent + final long now = System.currentTimeMillis(); + final NetworkStats stats = new NetworkStats(end - start, 24); - long[] total = new long[2]; + final NetworkStats.Entry entry = new NetworkStats.Entry(); + NetworkStatsHistory.Entry historyEntry = null; for (NetworkIdentitySet ident : mUidStats.keySet()) { if (templateMatches(template, ident)) { @@ -359,11 +367,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // other tags when requested. if (tag == TAG_NONE || includeTags) { final NetworkStatsHistory history = uidStats.valueAt(i); - total = history.getTotalData(start, end, total); - final long rx = total[0]; - final long tx = total[1]; - if (rx > 0 || tx > 0) { - stats.combineEntry(IFACE_ALL, uid, tag, rx, tx); + historyEntry = history.getValues(start, end, now, historyEntry); + + entry.iface = IFACE_ALL; + entry.uid = uid; + entry.tag = tag; + entry.rxBytes = historyEntry.rxBytes; + entry.txBytes = historyEntry.txBytes; + + if (entry.rxBytes > 0 || entry.txBytes > 0) { + stats.combineValues(entry); } } } @@ -418,6 +431,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // broadcast. final int uid = intent.getIntExtra(EXTRA_UID, 0); synchronized (mStatsLock) { + // TODO: perform one last stats poll for UID removeUidLocked(uid); } } @@ -515,10 +529,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStats persistDelta = computeStatsDelta( mLastPersistNetworkSnapshot, networkSnapshot); final long persistThreshold = mSettings.getPersistThreshold(); + + NetworkStats.Entry entry = null; for (String iface : persistDelta.getUniqueIfaces()) { final int index = persistDelta.findIndex(iface, UID_ALL, TAG_NONE); - if (forcePersist || persistDelta.rx[index] > persistThreshold - || persistDelta.tx[index] > persistThreshold) { + entry = persistDelta.getValues(index, entry); + if (forcePersist || entry.rxBytes > persistThreshold + || entry.txBytes > persistThreshold) { writeNetworkStatsLocked(); if (mUidStatsLoaded) { writeUidStatsLocked(); @@ -541,20 +558,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final HashSet<String> unknownIface = Sets.newHashSet(); final NetworkStats delta = computeStatsDelta(mLastNetworkSnapshot, networkSnapshot); - final long timeStart = currentTime - delta.elapsedRealtime; - for (int i = 0; i < delta.size; i++) { - final String iface = delta.iface[i]; - final NetworkIdentitySet ident = mActiveIfaces.get(iface); + final long timeStart = currentTime - delta.getElapsedRealtime(); + + NetworkStats.Entry entry = null; + for (int i = 0; i < delta.size(); i++) { + entry = delta.getValues(i, entry); + final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface); if (ident == null) { - unknownIface.add(iface); + unknownIface.add(entry.iface); continue; } - final long rx = delta.rx[i]; - final long tx = delta.tx[i]; - final NetworkStatsHistory history = findOrCreateNetworkStatsLocked(ident); - history.recordData(timeStart, currentTime, rx, tx); + history.recordData(timeStart, currentTime, entry.rxBytes, entry.txBytes); } // trim any history beyond max @@ -577,22 +593,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub { ensureUidStatsLoadedLocked(); final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot); - final long timeStart = currentTime - delta.elapsedRealtime; + final long timeStart = currentTime - delta.getElapsedRealtime(); - for (int i = 0; i < delta.size; i++) { - final String iface = delta.iface[i]; - final NetworkIdentitySet ident = mActiveIfaces.get(iface); + NetworkStats.Entry entry = null; + for (int i = 0; i < delta.size(); i++) { + entry = delta.getValues(i, entry); + final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface); if (ident == null) { continue; } - final int uid = delta.uid[i]; - final int tag = delta.tag[i]; - final long rx = delta.rx[i]; - final long tx = delta.tx[i]; - - final NetworkStatsHistory history = findOrCreateUidStatsLocked(ident, uid, tag); - history.recordData(timeStart, currentTime, rx, tx); + final NetworkStatsHistory history = findOrCreateUidStatsLocked( + ident, entry.uid, entry.tag); + history.recordData(timeStart, currentTime, entry.rxBytes, entry.txBytes); } // trim any history beyond max @@ -654,7 +667,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { NetworkStatsHistory updated = null; if (existing == null) { updated = new NetworkStatsHistory(bucketDuration, 10); - } else if (existing.bucketDuration != bucketDuration) { + } else if (existing.getBucketDuration() != bucketDuration) { updated = new NetworkStatsHistory( bucketDuration, estimateResizeBuckets(existing, bucketDuration)); updated.recordEntireHistory(existing); @@ -686,7 +699,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { NetworkStatsHistory updated = null; if (existing == null) { updated = new NetworkStatsHistory(bucketDuration, 10); - } else if (existing.bucketDuration != bucketDuration) { + } else if (existing.getBucketDuration() != bucketDuration) { updated = new NetworkStatsHistory( bucketDuration, estimateResizeBuckets(existing, bucketDuration)); updated.recordEntireHistory(existing); @@ -1006,7 +1019,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } private static int estimateResizeBuckets(NetworkStatsHistory existing, long newBucketDuration) { - return (int) (existing.bucketCount * existing.bucketDuration / newBucketDuration); + return (int) (existing.size() * existing.getBucketDuration() / newBucketDuration); } // @VisibleForTesting diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index ea5d26b..d6a15e6 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -778,16 +778,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSeparateProcesses = null; } - Installer installer = new Installer(); - // Little hacky thing to check if installd is here, to determine - // whether we are running on the simulator and thus need to take - // care of building the /data file structure ourself. - // (apparently the sim now has a working installer) - if (installer.ping() && Process.supportsProcesses()) { - mInstaller = installer; - } else { - mInstaller = null; - } + mInstaller = new Installer(); WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); Display d = wm.getDefaultDisplay(); @@ -806,17 +797,6 @@ public class PackageManagerService extends IPackageManager.Stub { mUserManager = new UserManager(mInstaller, mUserAppDataDir); - if (mInstaller == null) { - // Make sure these dirs exist, when we are running in - // the simulator. - // Make a wide-open directory for random misc stuff. - File miscDir = new File(dataDir, "misc"); - miscDir.mkdirs(); - mAppDataDir.mkdirs(); - mUserAppDataDir.mkdirs(); - mDrmAppPrivateInstallDir.mkdirs(); - } - readPermissions(); mRestoredSettings = mSettings.readLPw(); @@ -838,104 +818,102 @@ public class PackageManagerService extends IPackageManager.Stub { mFrameworkDir = new File(Environment.getRootDirectory(), "framework"); mDalvikCacheDir = new File(dataDir, "dalvik-cache"); - if (mInstaller != null) { - boolean didDexOpt = false; - - /** - * Out of paranoia, ensure that everything in the boot class - * path has been dexed. - */ - String bootClassPath = System.getProperty("java.boot.class.path"); - if (bootClassPath != null) { - String[] paths = splitString(bootClassPath, ':'); - for (int i=0; i<paths.length; i++) { - try { - if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) { - libFiles.add(paths[i]); - mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true); - didDexOpt = true; - } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Boot class path not found: " + paths[i]); - } catch (IOException e) { - Slog.w(TAG, "Exception reading boot class path: " + paths[i], e); + boolean didDexOpt = false; + + /** + * Out of paranoia, ensure that everything in the boot class + * path has been dexed. + */ + String bootClassPath = System.getProperty("java.boot.class.path"); + if (bootClassPath != null) { + String[] paths = splitString(bootClassPath, ':'); + for (int i=0; i<paths.length; i++) { + try { + if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) { + libFiles.add(paths[i]); + mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true); + didDexOpt = true; } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Boot class path not found: " + paths[i]); + } catch (IOException e) { + Slog.w(TAG, "Exception reading boot class path: " + paths[i], e); } - } else { - Slog.w(TAG, "No BOOTCLASSPATH found!"); } + } else { + Slog.w(TAG, "No BOOTCLASSPATH found!"); + } - /** - * Also ensure all external libraries have had dexopt run on them. - */ - if (mSharedLibraries.size() > 0) { - Iterator<String> libs = mSharedLibraries.values().iterator(); - while (libs.hasNext()) { - String lib = libs.next(); - try { - if (dalvik.system.DexFile.isDexOptNeeded(lib)) { - libFiles.add(lib); - mInstaller.dexopt(lib, Process.SYSTEM_UID, true); - didDexOpt = true; - } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Library not found: " + lib); - } catch (IOException e) { - Slog.w(TAG, "Exception reading library: " + lib, e); + /** + * Also ensure all external libraries have had dexopt run on them. + */ + if (mSharedLibraries.size() > 0) { + Iterator<String> libs = mSharedLibraries.values().iterator(); + while (libs.hasNext()) { + String lib = libs.next(); + try { + if (dalvik.system.DexFile.isDexOptNeeded(lib)) { + libFiles.add(lib); + mInstaller.dexopt(lib, Process.SYSTEM_UID, true); + didDexOpt = true; } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Library not found: " + lib); + } catch (IOException e) { + Slog.w(TAG, "Exception reading library: " + lib, e); } } + } - // Gross hack for now: we know this file doesn't contain any - // code, so don't dexopt it to avoid the resulting log spew. - libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk"); - - /** - * And there are a number of commands implemented in Java, which - * we currently need to do the dexopt on so that they can be - * run from a non-root shell. - */ - String[] frameworkFiles = mFrameworkDir.list(); - if (frameworkFiles != null) { - for (int i=0; i<frameworkFiles.length; i++) { - File libPath = new File(mFrameworkDir, frameworkFiles[i]); - String path = libPath.getPath(); - // Skip the file if we alrady did it. - if (libFiles.contains(path)) { - continue; - } - // Skip the file if it is not a type we want to dexopt. - if (!path.endsWith(".apk") && !path.endsWith(".jar")) { - continue; - } - try { - if (dalvik.system.DexFile.isDexOptNeeded(path)) { - mInstaller.dexopt(path, Process.SYSTEM_UID, true); - didDexOpt = true; - } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Jar not found: " + path); - } catch (IOException e) { - Slog.w(TAG, "Exception reading jar: " + path, e); + // Gross hack for now: we know this file doesn't contain any + // code, so don't dexopt it to avoid the resulting log spew. + libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk"); + + /** + * And there are a number of commands implemented in Java, which + * we currently need to do the dexopt on so that they can be + * run from a non-root shell. + */ + String[] frameworkFiles = mFrameworkDir.list(); + if (frameworkFiles != null) { + for (int i=0; i<frameworkFiles.length; i++) { + File libPath = new File(mFrameworkDir, frameworkFiles[i]); + String path = libPath.getPath(); + // Skip the file if we alrady did it. + if (libFiles.contains(path)) { + continue; + } + // Skip the file if it is not a type we want to dexopt. + if (!path.endsWith(".apk") && !path.endsWith(".jar")) { + continue; + } + try { + if (dalvik.system.DexFile.isDexOptNeeded(path)) { + mInstaller.dexopt(path, Process.SYSTEM_UID, true); + didDexOpt = true; } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Jar not found: " + path); + } catch (IOException e) { + Slog.w(TAG, "Exception reading jar: " + path, e); } } + } - if (didDexOpt) { - // If we had to do a dexopt of one of the previous - // things, then something on the system has changed. - // Consider this significant, and wipe away all other - // existing dexopt files to ensure we don't leave any - // dangling around. - String[] files = mDalvikCacheDir.list(); - if (files != null) { - for (int i=0; i<files.length; i++) { - String fn = files[i]; - if (fn.startsWith("data@app@") - || fn.startsWith("data@app-private@")) { - Slog.i(TAG, "Pruning dalvik file: " + fn); - (new File(mDalvikCacheDir, fn)).delete(); - } + if (didDexOpt) { + // If we had to do a dexopt of one of the previous + // things, then something on the system has changed. + // Consider this significant, and wipe away all other + // existing dexopt files to ensure we don't leave any + // dangling around. + String[] files = mDalvikCacheDir.list(); + if (files != null) { + for (int i=0; i<files.length; i++) { + String fn = files[i]; + if (fn.startsWith("data@app@") + || fn.startsWith("data@app-private@")) { + Slog.i(TAG, "Pruning dalvik file: " + fn); + (new File(mDalvikCacheDir, fn)).delete(); } } } @@ -965,11 +943,9 @@ public class PackageManagerService extends IPackageManager.Stub { scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); - if (mInstaller != null) { - if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands"); - mInstaller.moveFiles(); - } - + if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands"); + mInstaller.moveFiles(); + // Prune any system packages that no longer exist. Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { @@ -981,19 +957,12 @@ public class PackageManagerService extends IPackageManager.Stub { String msg = "System package " + ps.name + " no longer exists; wiping its data"; reportSettingsProblem(Log.WARN, msg); - if (mInstaller != null) { - mInstaller.remove(ps.name, 0); - mUserManager.removePackageForAllUsers(ps.name); - } + mInstaller.remove(ps.name, 0); + mUserManager.removePackageForAllUsers(ps.name); } } mAppInstallDir = new File(dataDir, "app"); - if (mInstaller == null) { - // Make sure these dirs exist, when we are running in - // the simulator. - mAppInstallDir.mkdirs(); // scanDirLI() assumes this dir exists - } //look for any incomplete package installations ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr(); //clean up list @@ -1067,19 +1036,12 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanupInstallFailedPackage(PackageSetting ps) { Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name); - if (mInstaller != null) { - int retCode = mInstaller.remove(ps.name, 0); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove app data directory for package: " - + ps.name + ", retcode=" + retCode); - } else { - mUserManager.removePackageForAllUsers(ps.name); - } + int retCode = mInstaller.remove(ps.name, 0); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove app data directory for package: " + + ps.name + ", retcode=" + retCode); } else { - //for emulator - PackageParser.Package pkg = mPackages.get(ps.name); - File dataDir = new File(pkg.applicationInfo.dataDir); - dataDir.delete(); + mUserManager.removePackageForAllUsers(ps.name); } if (ps.codePath != null) { if (!ps.codePath.delete()) { @@ -1562,12 +1524,10 @@ public class PackageManagerService extends IPackageManager.Stub { public void run() { mHandler.removeCallbacks(this); int retCode = -1; - if (mInstaller != null) { - retCode = mInstaller.freeCache(freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); - } - } //end if mInstaller + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Slog.w(TAG, "Couldn't clear application caches"); + } if (observer != null) { try { observer.onRemoveCompleted(null, (retCode >= 0)); @@ -1587,11 +1547,9 @@ public class PackageManagerService extends IPackageManager.Stub { public void run() { mHandler.removeCallbacks(this); int retCode = -1; - if (mInstaller != null) { - retCode = mInstaller.freeCache(freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); - } + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Slog.w(TAG, "Couldn't clear application caches"); } if(pi != null) { try { @@ -2850,7 +2808,7 @@ public class PackageManagerService extends IPackageManager.Stub { private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) { boolean performed = false; - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0 && mInstaller != null) { + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { String path = pkg.mScanPath; int ret = 0; try { @@ -3235,42 +3193,39 @@ public class PackageManagerService extends IPackageManager.Stub { mOutPermissions[1] = 0; FileUtils.getPermissions(dataPath.getPath(), mOutPermissions); - // If we have mismatched owners for the data path, we have a - // problem (unless we're running in the simulator.) - if (mOutPermissions[1] != pkg.applicationInfo.uid && Process.supportsProcesses()) { + // If we have mismatched owners for the data path, we have a problem. + if (mOutPermissions[1] != pkg.applicationInfo.uid) { boolean recovered = false; if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { // If this is a system app, we can at least delete its // current data so the application will still work. - if (mInstaller != null) { - int ret = mInstaller.remove(pkgName, 0); - if (ret >= 0) { - // TODO: Kill the processes first - // Remove the data directories for all users - mUserManager.removePackageForAllUsers(pkgName); - // Old data gone! - String msg = "System package " + pkg.packageName - + " has changed from uid: " - + mOutPermissions[1] + " to " - + pkg.applicationInfo.uid + "; old data erased"; + int ret = mInstaller.remove(pkgName, 0); + if (ret >= 0) { + // TODO: Kill the processes first + // Remove the data directories for all users + mUserManager.removePackageForAllUsers(pkgName); + // Old data gone! + String msg = "System package " + pkg.packageName + + " has changed from uid: " + + mOutPermissions[1] + " to " + + pkg.applicationInfo.uid + "; old data erased"; + reportSettingsProblem(Log.WARN, msg); + recovered = true; + + // And now re-install the app. + ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, + pkg.applicationInfo.uid); + if (ret == -1) { + // Ack should not happen! + msg = "System package " + pkg.packageName + + " could not have data directory re-created after delete."; reportSettingsProblem(Log.WARN, msg); - recovered = true; - - // And now re-install the app. - ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.uid); - if (ret == -1) { - // Ack should not happen! - msg = "System package " + pkg.packageName - + " could not have data directory re-created after delete."; - reportSettingsProblem(Log.WARN, msg); - mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - return null; - } - // Create data directories for all users - mUserManager.installPackageForAllUsers(pkgName, - pkg.applicationInfo.uid); + mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return null; } + // Create data directories for all users + mUserManager.installPackageForAllUsers(pkgName, + pkg.applicationInfo.uid); } if (!recovered) { mHasSystemUidErrors = true; @@ -3303,25 +3258,16 @@ public class PackageManagerService extends IPackageManager.Stub { Log.v(TAG, "Want this data dir: " + dataPath); } //invoke installer to do the actual installation - if (mInstaller != null) { - int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.uid); - if (ret < 0) { - // Error from installer - mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - return null; - } - // Create data directories for all users - mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); - } else { - dataPath.mkdirs(); - if (dataPath.exists()) { - FileUtils.setPermissions( - dataPath.toString(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, - pkg.applicationInfo.uid, pkg.applicationInfo.uid); - } + int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, + pkg.applicationInfo.uid); + if (ret < 0) { + // Error from installer + mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return null; } + // Create data directories for all users + mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); + if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { @@ -3352,65 +3298,62 @@ public class PackageManagerService extends IPackageManager.Stub { pkgSetting.uidError = uidError; } - // If we're running in the simulator, we don't need to unpack anything. - if (mInstaller != null) { - String path = scanFile.getPath(); - /* Note: We don't want to unpack the native binaries for - * system applications, unless they have been updated - * (the binaries are already under /system/lib). - * Also, don't unpack libs for apps on the external card - * since they should have their libraries in the ASEC - * container already. - * - * In other words, we're going to unpack the binaries - * only for non-system apps and system app upgrades. - */ - if (pkg.applicationInfo.nativeLibraryDir != null) { - try { - final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); - final String dataPathString = dataPath.getCanonicalFile().getPath(); + String path = scanFile.getPath(); + /* Note: We don't want to unpack the native binaries for + * system applications, unless they have been updated + * (the binaries are already under /system/lib). + * Also, don't unpack libs for apps on the external card + * since they should have their libraries in the ASEC + * container already. + * + * In other words, we're going to unpack the binaries + * only for non-system apps and system app upgrades. + */ + if (pkg.applicationInfo.nativeLibraryDir != null) { + try { + final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); + final String dataPathString = dataPath.getCanonicalFile().getPath(); - if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { - /* - * Upgrading from a previous version of the OS sometimes - * leaves native libraries in the /data/data/<app>/lib - * directory for system apps even when they shouldn't be. - * Recent changes in the JNI library search path - * necessitates we remove those to match previous behavior. - */ - if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) { - Log.i(TAG, "removed obsolete native libraries for system package " - + path); - } - } else if (nativeLibraryDir.getCanonicalFile().getParent() - .equals(dataPathString)) { - /* - * If this is an internal application or our - * nativeLibraryPath points to our data directory, unpack - * the libraries. The native library path pointing to the - * data directory for an application in an ASEC container - * can happen for older apps that existed before an OTA to - * Gingerbread. - */ - Slog.i(TAG, "Unpacking native libraries for " + path); - mInstaller.unlinkNativeLibraryDirectory(dataPathString); - NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir); - } else { - Slog.i(TAG, "Linking native library dir for " + path); - mInstaller.linkNativeLibraryDirectory(dataPathString, - pkg.applicationInfo.nativeLibraryDir); + if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { + /* + * Upgrading from a previous version of the OS sometimes + * leaves native libraries in the /data/data/<app>/lib + * directory for system apps even when they shouldn't be. + * Recent changes in the JNI library search path + * necessitates we remove those to match previous behavior. + */ + if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) { + Log.i(TAG, "removed obsolete native libraries for system package " + + path); } - } catch (IOException ioe) { - Log.e(TAG, "Unable to get canonical file " + ioe.toString()); + } else if (nativeLibraryDir.getCanonicalFile().getParent() + .equals(dataPathString)) { + /* + * If this is an internal application or our + * nativeLibraryPath points to our data directory, unpack + * the libraries. The native library path pointing to the + * data directory for an application in an ASEC container + * can happen for older apps that existed before an OTA to + * Gingerbread. + */ + Slog.i(TAG, "Unpacking native libraries for " + path); + mInstaller.unlinkNativeLibraryDirectory(dataPathString); + NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir); + } else { + Slog.i(TAG, "Linking native library dir for " + path); + mInstaller.linkNativeLibraryDirectory(dataPathString, + pkg.applicationInfo.nativeLibraryDir); } + } catch (IOException ioe) { + Log.e(TAG, "Unable to get canonical file " + ioe.toString()); } - pkg.mScanPath = path; + } + pkg.mScanPath = path; - if ((scanMode&SCAN_NO_DEX) == 0) { - if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) { - mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; - return null; - } + if ((scanMode&SCAN_NO_DEX) == 0) { + if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) { + mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; + return null; } } @@ -5434,7 +5377,7 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanUpResourcesLI() { String sourceDir = getCodePath(); - if (cleanUp() && mInstaller != null) { + if (cleanUp()) { int retCode = mInstaller.rmdex(sourceDir); if (retCode < 0) { Slog.w(TAG, "Couldn't remove dex file for package: " @@ -5662,14 +5605,12 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanUpResourcesLI() { String sourceFile = getCodePath(); // Remove dex file - if (mInstaller != null) { - int retCode = mInstaller.rmdex(sourceFile); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove dex file for package: " - + " at location " - + sourceFile.toString() + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion - } + int retCode = mInstaller.rmdex(sourceFile); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove dex file for package: " + + " at location " + + sourceFile.toString() + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion } cleanUp(); } @@ -6077,9 +6018,7 @@ public class PackageManagerService extends IPackageManager.Stub { } if((res.returnCode = setPermissionsLI(newPackage)) != PackageManager.INSTALL_SUCCEEDED) { - if (mInstaller != null) { - mInstaller.rmdex(newPackage.mScanPath); - } + mInstaller.rmdex(newPackage.mScanPath); return; } else { Log.d(TAG, "New package installed in " + newPackage.mPath); @@ -6207,15 +6146,8 @@ public class PackageManagerService extends IPackageManager.Stub { } finally { //TODO clean up the extracted public files } - if (mInstaller != null) { - retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath), - newPackage.applicationInfo.uid); - } else { - final int filePermissions = - FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP; - retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1, - newPackage.applicationInfo.uid); - } + retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath), + newPackage.applicationInfo.uid); } else { // The permissions on the resource file was set when it was copied for // non forward locked apps and apps on sdcard @@ -6478,25 +6410,14 @@ public class PackageManagerService extends IPackageManager.Stub { deletedPs = mSettings.mPackages.get(packageName); } if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { - if (mInstaller != null) { - int retCode = mInstaller.remove(packageName, 0); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove app data or cache directory for package: " - + packageName + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion - } else { - // TODO: Kill the processes first - mUserManager.removePackageForAllUsers(packageName); - } + int retCode = mInstaller.remove(packageName, 0); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove app data or cache directory for package: " + + packageName + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion } else { - // for simulator - File dataDir; - // reader - synchronized (mPackages) { - PackageParser.Package pkg = mPackages.get(packageName); - dataDir = new File(pkg.applicationInfo.dataDir); - } - dataDir.delete(); + // TODO: Kill the processes first + mUserManager.removePackageForAllUsers(packageName); } schedulePackageCleaning(packageName); } @@ -6745,13 +6666,11 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } } - if (mInstaller != null) { - int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove cache files for package: " - + packageName); - return false; - } + int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove cache files for package: " + + packageName); + return false; } return true; } @@ -6797,13 +6716,11 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } - if (mInstaller != null) { - int retCode = mInstaller.deleteCacheFiles(packageName); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove cache files for package: " - + packageName); - return false; - } + int retCode = mInstaller.deleteCacheFiles(packageName); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove cache files for package: " + + packageName); + return false; } return true; } @@ -6867,14 +6784,10 @@ public class PackageManagerService extends IPackageManager.Stub { publicSrcDir = applicationInfo.publicSourceDir; } } - if (mInstaller != null) { - int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, - asecPath, pStats); - if (res < 0) { - return false; - } else { - return true; - } + int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, + asecPath, pStats); + if (res < 0) { + return false; } return true; } diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index d645160..3139798 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -138,12 +138,9 @@ public class UsbDeviceManager { // create a thread for our Handler HandlerThread thread = new HandlerThread("UsbDeviceManager", - Process.THREAD_PRIORITY_BACKGROUND) { - protected void onLooperPrepared() { - mHandler = new UsbHandler(); - } - }; + Process.THREAD_PRIORITY_BACKGROUND); thread.start(); + mHandler = new UsbHandler(thread.getLooper()); } public void systemReady() { @@ -249,21 +246,21 @@ public class UsbDeviceManager { private static final int NOTIFICATION_INSTALLER = 3; private static final int NOTIFICATION_ADB = 4; - public UsbHandler() { + public UsbHandler(Looper looper) { + super(looper); try { + // persist.sys.usb.config should never be unset. But if it is, set it to "adb" + // so we have a chance of debugging what happened. + mDefaultFunctions = SystemProperties.get("persist.sys.usb.config", "adb"); // sanity check the sys.usb.config system property // this may be necessary if we crashed while switching USB configurations String config = SystemProperties.get("sys.usb.config", "none"); - if (config.equals("none")) { - String persistConfig = SystemProperties.get("persist.sys.usb.config", "none"); - Slog.w(TAG, "resetting config to persistent property: " + persistConfig); - SystemProperties.set("sys.usb.config", persistConfig); + if (!config.equals(mDefaultFunctions)) { + Slog.w(TAG, "resetting config to persistent property: " + mDefaultFunctions); + SystemProperties.set("sys.usb.config", mDefaultFunctions); } - // Read initial USB state - mCurrentFunctions = FileUtils.readTextFile( - new File(FUNCTIONS_PATH), 0, null).trim(); - mDefaultFunctions = mCurrentFunctions; + mCurrentFunctions = mDefaultFunctions; String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB); @@ -621,6 +618,16 @@ public class UsbDeviceManager { pw.println(" mConnected: " + mConnected); pw.println(" mConfigured: " + mConfigured); pw.println(" mCurrentAccessory: " + mCurrentAccessory); + try { + pw.println(" Kernel state: " + + FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim()); + pw.println(" Kernel function list: " + + FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim()); + pw.println(" Mass storage backing file: " + + FileUtils.readTextFile(new File(MASS_STORAGE_FILE_PATH), 0, null).trim()); + } catch (IOException e) { + pw.println("IOException: " + e); + } } } @@ -630,20 +637,20 @@ public class UsbDeviceManager { } /* opens the currently attached USB accessory */ - public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { - UsbAccessory currentAccessory = mHandler.getCurrentAccessory(); - if (currentAccessory == null) { - throw new IllegalArgumentException("no accessory attached"); - } - if (!currentAccessory.equals(accessory)) { - String error = accessory.toString() - + " does not match current accessory " - + currentAccessory; - throw new IllegalArgumentException(error); - } - mSettingsManager.checkPermission(accessory); - return nativeOpenAccessory(); + public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { + UsbAccessory currentAccessory = mHandler.getCurrentAccessory(); + if (currentAccessory == null) { + throw new IllegalArgumentException("no accessory attached"); + } + if (!currentAccessory.equals(accessory)) { + String error = accessory.toString() + + " does not match current accessory " + + currentAccessory; + throw new IllegalArgumentException(error); } + mSettingsManager.checkPermission(accessory); + return nativeOpenAccessory(); + } public void setCurrentFunction(String function, boolean makeDefault) { if (DEBUG) Slog.d(TAG, "setCurrentFunction(" + function + ") default: " + makeDefault); diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java index d3d9df4..bfa2b39 100644 --- a/services/java/com/android/server/wm/AppWindowToken.java +++ b/services/java/com/android/server/wm/AppWindowToken.java @@ -101,7 +101,7 @@ class AppWindowToken extends WindowToken { boolean firstWindowDrawn; // Input application handle used by the input dispatcher. - InputApplicationHandle mInputApplicationHandle; + final InputApplicationHandle mInputApplicationHandle; AppWindowToken(WindowManagerService _service, IApplicationToken _token) { super(_service, _token.asBinder(), diff --git a/services/java/com/android/server/wm/InputApplicationHandle.java b/services/java/com/android/server/wm/InputApplicationHandle.java index 64c8e7e..d78b1d9 100644 --- a/services/java/com/android/server/wm/InputApplicationHandle.java +++ b/services/java/com/android/server/wm/InputApplicationHandle.java @@ -32,6 +32,12 @@ public final class InputApplicationHandle { // The window manager's application window token. public final AppWindowToken appWindowToken; + // Application name. + public String name; + + // Dispatching timeout. + public long dispatchingTimeoutNanos; + private native void nativeDispose(); public InputApplicationHandle(AppWindowToken appWindowToken) { diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java index 65007f9..3133a19 100644 --- a/services/java/com/android/server/wm/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -25,7 +25,6 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.ContentObserver; import android.os.Environment; -import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; import android.os.SystemProperties; @@ -83,10 +82,10 @@ public class InputManager { private static native int nativeInjectInputEvent(InputEvent event, int injectorPid, int injectorUid, int syncMode, int timeoutMillis, int policyFlags); - private static native void nativeSetInputWindows(InputWindow[] windows); + private static native void nativeSetInputWindows(InputWindowHandle[] windowHandles); private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen); private static native void nativeSetSystemUiVisibility(int visibility); - private static native void nativeSetFocusedApplication(InputApplication application); + private static native void nativeSetFocusedApplication(InputApplicationHandle application); private static native InputDevice nativeGetInputDevice(int deviceId); private static native void nativeGetInputConfiguration(Configuration configuration); private static native int[] nativeGetInputDeviceIds(); @@ -372,11 +371,11 @@ public class InputManager { return nativeGetInputDeviceIds(); } - public void setInputWindows(InputWindow[] windows) { - nativeSetInputWindows(windows); + public void setInputWindows(InputWindowHandle[] windowHandles) { + nativeSetInputWindows(windowHandles); } - public void setFocusedApplication(InputApplication application) { + public void setFocusedApplication(InputApplicationHandle application) { nativeSetFocusedApplication(application); } diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java index 6806634..08a3560 100644 --- a/services/java/com/android/server/wm/InputMonitor.java +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -17,7 +17,6 @@ package com.android.server.wm; import android.graphics.Rect; -import android.os.Binder; import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -26,6 +25,7 @@ import android.view.KeyEvent; import android.view.WindowManager; import java.util.ArrayList; +import java.util.Arrays; final class InputMonitor { private final WindowManagerService mService; @@ -42,12 +42,14 @@ final class InputMonitor { // When true, need to call updateInputWindowsLw(). private boolean mUpdateInputWindowsNeeded = true; - // Temporary list of windows information to provide to the input dispatcher. - private InputWindowList mTempInputWindows = new InputWindowList(); - - // Temporary input application object to provide to the input dispatcher. - private InputApplication mTempInputApplication = new InputApplication(); - + // Fake handles for the drag surface, lazily initialized. + private InputApplicationHandle mDragApplicationHandle; + private InputWindowHandle mDragWindowHandle; + + // Array of window handles to provide to the input dispatcher. + private InputWindowHandle[] mInputWindowHandles; + private int mInputWindowHandleCount; + // Set to true when the first input device configuration change notification // is received to indicate that the input devices are ready. private final Object mInputDevicesReadyMonitor = new Object(); @@ -68,8 +70,10 @@ final class InputMonitor { synchronized (mService.mWindowMap) { WindowState windowState = (WindowState) inputWindowHandle.windowState; - Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState); - mService.removeWindowLocked(windowState.mSession, windowState); + if (windowState != null) { + Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState); + mService.removeWindowLocked(windowState.mSession, windowState); + } } } @@ -94,8 +98,11 @@ final class InputMonitor { if (appWindowToken == null && inputApplicationHandle != null) { appWindowToken = inputApplicationHandle.appWindowToken; - Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to application " - + appWindowToken.stringName); + if (appWindowToken != null) { + Slog.i(WindowManagerService.TAG, + "Input event dispatching timed out sending to application " + + appWindowToken.stringName); + } } if (appWindowToken != null && appWindowToken.appToken != null) { @@ -114,32 +121,59 @@ final class InputMonitor { return 0; // abort dispatching } - private void addDragInputWindowLw(InputWindowList windowList) { - final InputWindow inputWindow = windowList.add(); - inputWindow.inputChannel = mService.mDragState.mServerChannel; - inputWindow.name = "drag"; - inputWindow.layoutParamsFlags = 0; - inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; - inputWindow.dispatchingTimeoutNanos = WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; - inputWindow.visible = true; - inputWindow.canReceiveKeys = false; - inputWindow.hasFocus = true; - inputWindow.hasWallpaper = false; - inputWindow.paused = false; - inputWindow.layer = mService.mDragState.getDragLayerLw(); - inputWindow.ownerPid = Process.myPid(); - inputWindow.ownerUid = Process.myUid(); - inputWindow.inputFeatures = 0; - inputWindow.scaleFactor = 1.0f; + private void addDragInputWindowLw() { + if (mDragWindowHandle == null) { + mDragApplicationHandle = new InputApplicationHandle(null); + mDragApplicationHandle.name = "drag"; + mDragApplicationHandle.dispatchingTimeoutNanos = + WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + + mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null); + mDragWindowHandle.name = "drag"; + mDragWindowHandle.layoutParamsFlags = 0; + mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; + mDragWindowHandle.dispatchingTimeoutNanos = + WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mDragWindowHandle.visible = true; + mDragWindowHandle.canReceiveKeys = false; + mDragWindowHandle.hasFocus = true; + mDragWindowHandle.hasWallpaper = false; + mDragWindowHandle.paused = false; + mDragWindowHandle.ownerPid = Process.myPid(); + mDragWindowHandle.ownerUid = Process.myUid(); + mDragWindowHandle.inputFeatures = 0; + mDragWindowHandle.scaleFactor = 1.0f; + + // The drag window cannot receive new touches. + mDragWindowHandle.touchableRegion.setEmpty(); + } + + mDragWindowHandle.layer = mService.mDragState.getDragLayerLw(); // The drag window covers the entire display - inputWindow.frameLeft = 0; - inputWindow.frameTop = 0; - inputWindow.frameRight = mService.mDisplay.getRealWidth(); - inputWindow.frameBottom = mService.mDisplay.getRealHeight(); + mDragWindowHandle.frameLeft = 0; + mDragWindowHandle.frameTop = 0; + mDragWindowHandle.frameRight = mService.mDisplay.getRealWidth(); + mDragWindowHandle.frameBottom = mService.mDisplay.getRealHeight(); + + addInputWindowHandleLw(mDragWindowHandle); + } - // The drag window cannot receive new touches. - inputWindow.touchableRegion.setEmpty(); + private void addInputWindowHandleLw(InputWindowHandle windowHandle) { + if (mInputWindowHandles == null) { + mInputWindowHandles = new InputWindowHandle[16]; + } + if (mInputWindowHandleCount >= mInputWindowHandles.length) { + mInputWindowHandles = Arrays.copyOf(mInputWindowHandles, + mInputWindowHandleCount * 2); + } + mInputWindowHandles[mInputWindowHandleCount++] = windowHandle; + } + + private void clearInputWindowHandlesLw() { + while (mInputWindowHandleCount != 0) { + mInputWindowHandles[--mInputWindowHandleCount] = null; + } } public void setUpdateInputWindowsNeededLw() { @@ -154,7 +188,7 @@ final class InputMonitor { mUpdateInputWindowsNeeded = false; if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw"); - + // Populate the input window list with information about all of the windows that // could potentially receive input. // As an optimization, we could try to prune the list of windows but this turns @@ -168,7 +202,7 @@ final class InputMonitor { if (WindowManagerService.DEBUG_DRAG) { Log.d(WindowManagerService.TAG, "Inserting drag window"); } - addDragInputWindowLw(mTempInputWindows); + addDragInputWindowLw(); } final int N = windows.size(); @@ -194,48 +228,48 @@ final class InputMonitor { } // Add a window to our list of input windows. - final InputWindow inputWindow = mTempInputWindows.add(); - inputWindow.inputWindowHandle = child.mInputWindowHandle; - inputWindow.inputChannel = child.mInputChannel; - inputWindow.name = child.toString(); - inputWindow.layoutParamsFlags = flags; - inputWindow.layoutParamsType = type; - inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); - inputWindow.visible = isVisible; - inputWindow.canReceiveKeys = child.canReceiveKeys(); - inputWindow.hasFocus = hasFocus; - inputWindow.hasWallpaper = hasWallpaper; - inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false; - inputWindow.layer = child.mLayer; - inputWindow.ownerPid = child.mSession.mPid; - inputWindow.ownerUid = child.mSession.mUid; - inputWindow.inputFeatures = child.mAttrs.inputFeatures; + final InputWindowHandle inputWindowHandle = child.mInputWindowHandle; + inputWindowHandle.inputChannel = child.mInputChannel; + inputWindowHandle.name = child.toString(); + inputWindowHandle.layoutParamsFlags = flags; + inputWindowHandle.layoutParamsType = type; + inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); + inputWindowHandle.visible = isVisible; + inputWindowHandle.canReceiveKeys = child.canReceiveKeys(); + inputWindowHandle.hasFocus = hasFocus; + inputWindowHandle.hasWallpaper = hasWallpaper; + inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false; + inputWindowHandle.layer = child.mLayer; + inputWindowHandle.ownerPid = child.mSession.mPid; + inputWindowHandle.ownerUid = child.mSession.mUid; + inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; final Rect frame = child.mFrame; - inputWindow.frameLeft = frame.left; - inputWindow.frameTop = frame.top; - inputWindow.frameRight = frame.right; - inputWindow.frameBottom = frame.bottom; + inputWindowHandle.frameLeft = frame.left; + inputWindowHandle.frameTop = frame.top; + inputWindowHandle.frameRight = frame.right; + inputWindowHandle.frameBottom = frame.bottom; if (child.mGlobalScale != 1) { // If we are scaling the window, input coordinates need // to be inversely scaled to map from what is on screen // to what is actually being touched in the UI. - inputWindow.scaleFactor = 1.0f/child.mGlobalScale; + inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale; } else { - inputWindow.scaleFactor = 1; + inputWindowHandle.scaleFactor = 1; } - child.getTouchableRegion(inputWindow.touchableRegion); + child.getTouchableRegion(inputWindowHandle.touchableRegion); + + addInputWindowHandleLw(inputWindowHandle); } // Send windows to native code. - mService.mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray()); - + mService.mInputManager.setInputWindows(mInputWindowHandles); + // Clear the list in preparation for the next round. - // Also avoids keeping InputChannel objects referenced unnecessarily. - mTempInputWindows.clear(); - + clearInputWindowHandlesLw(); + if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw"); } @@ -329,14 +363,11 @@ final class InputMonitor { if (newApp == null) { mService.mInputManager.setFocusedApplication(null); } else { - mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle; - mTempInputApplication.name = newApp.toString(); - mTempInputApplication.dispatchingTimeoutNanos = - newApp.inputDispatchingTimeoutNanos; - - mService.mInputManager.setFocusedApplication(mTempInputApplication); + final InputApplicationHandle handle = newApp.mInputApplicationHandle; + handle.name = newApp.toString(); + handle.dispatchingTimeoutNanos = newApp.inputDispatchingTimeoutNanos; - mTempInputApplication.recycle(); + mService.mInputManager.setFocusedApplication(handle); } } diff --git a/services/java/com/android/server/wm/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java deleted file mode 100644 index 655d734..0000000 --- a/services/java/com/android/server/wm/InputWindow.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 com.android.server.wm; - -import android.graphics.Region; -import android.view.InputChannel; - -/** - * Describes input-related window properties for use by the input dispatcher. - * @hide - */ -public final class InputWindow { - // The window handle. - public InputWindowHandle inputWindowHandle; - - // The input channel associated with the window. - public InputChannel inputChannel; - - // The window name. - public String name; - - // Window layout params attributes. (WindowManager.LayoutParams) - public int layoutParamsFlags; - public int layoutParamsType; - - // Dispatching timeout. - public long dispatchingTimeoutNanos; - - // Window frame. - public int frameLeft; - public int frameTop; - public int frameRight; - public int frameBottom; - - // Global scaling factor applied to touch events when they are dispatched - // to the window - public float scaleFactor; - - // Window touchable region. - public final Region touchableRegion = new Region(); - - // Window is visible. - public boolean visible; - - // Window can receive keys. - public boolean canReceiveKeys; - - // Window has focus. - public boolean hasFocus; - - // Window has wallpaper. (window is the current wallpaper target) - public boolean hasWallpaper; - - // Input event dispatching is paused. - public boolean paused; - - // Window layer. - public int layer; - - // Id of process and user that owns the window. - public int ownerPid; - public int ownerUid; - - // Window input features. - public int inputFeatures; - - public void recycle() { - inputWindowHandle = null; - inputChannel = null; - } -} diff --git a/services/java/com/android/server/wm/InputWindowHandle.java b/services/java/com/android/server/wm/InputWindowHandle.java index cc508c6..abf68d9 100644 --- a/services/java/com/android/server/wm/InputWindowHandle.java +++ b/services/java/com/android/server/wm/InputWindowHandle.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import android.graphics.Region; +import android.view.InputChannel; import android.view.WindowManagerPolicy; /** @@ -35,6 +37,57 @@ public final class InputWindowHandle { // The window manager's window state. public final WindowManagerPolicy.WindowState windowState; + // The input channel associated with the window. + public InputChannel inputChannel; + + // The window name. + public String name; + + // Window layout params attributes. (WindowManager.LayoutParams) + public int layoutParamsFlags; + public int layoutParamsType; + + // Dispatching timeout. + public long dispatchingTimeoutNanos; + + // Window frame. + public int frameLeft; + public int frameTop; + public int frameRight; + public int frameBottom; + + // Global scaling factor applied to touch events when they are dispatched + // to the window + public float scaleFactor; + + // Window touchable region. + public final Region touchableRegion = new Region(); + + // Window is visible. + public boolean visible; + + // Window can receive keys. + public boolean canReceiveKeys; + + // Window has focus. + public boolean hasFocus; + + // Window has wallpaper. (window is the current wallpaper target) + public boolean hasWallpaper; + + // Input event dispatching is paused. + public boolean paused; + + // Window layer. + public int layer; + + // Id of process and user that owns the window. + public int ownerPid; + public int ownerUid; + + // Window input features. + public int inputFeatures; + private native void nativeDispose(); public InputWindowHandle(InputApplicationHandle inputApplicationHandle, diff --git a/services/java/com/android/server/wm/InputWindowList.java b/services/java/com/android/server/wm/InputWindowList.java deleted file mode 100644 index 6077337..0000000 --- a/services/java/com/android/server/wm/InputWindowList.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 com.android.server.wm; - - -/** - * A specialized list of window information objects backed by an array. - * - * This class is part of an InputManager optimization to avoid allocating objects and arrays - * unnecessarily. Internally, it keeps an array full of demand-allocated objects that it - * recycles each time the list is cleared. The used portion of the array is padded with a null. - * - * The contents of the list are intended to be Z-ordered from top to bottom. - * - * @hide - */ -public final class InputWindowList { - private InputWindow[] mArray; - private int mCount; - - /** - * Creates an empty list. - */ - public InputWindowList() { - mArray = new InputWindow[8]; - } - - /** - * Clears the list. - */ - public void clear() { - if (mCount == 0) { - return; - } - - int count = mCount; - mCount = 0; - mArray[count] = mArray[0]; - while (count > 0) { - count -= 1; - mArray[count].recycle(); - } - mArray[0] = null; - } - - /** - * Adds an uninitialized input window object to the list and returns it. - */ - public InputWindow add() { - if (mCount + 1 == mArray.length) { - InputWindow[] oldArray = mArray; - mArray = new InputWindow[oldArray.length * 2]; - System.arraycopy(oldArray, 0, mArray, 0, mCount); - } - - // Grab object from tail (after used section) if available. - InputWindow item = mArray[mCount + 1]; - if (item == null) { - item = new InputWindow(); - } - - mArray[mCount] = item; - mCount += 1; - mArray[mCount] = null; - return item; - } - - /** - * Gets the input window objects as a null-terminated array. - * @return The input window array. - */ - public InputWindow[] toNullTerminatedArray() { - return mArray; - } -}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index b370ec9..d298ff7 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -272,7 +272,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { float mSurfaceAlpha; // Input channel and input window handle used by the input dispatcher. - InputWindowHandle mInputWindowHandle; + final InputWindowHandle mInputWindowHandle; InputChannel mInputChannel; // Used to improve performance of toString() @@ -306,6 +306,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mIsFloatingLayer = false; mBaseLayer = 0; mSubLayer = 0; + mInputWindowHandle = null; return; } mDeathRecipient = deathRecipient; diff --git a/services/jni/Android.mk b/services/jni/Android.mk index f33920d..6fa5dfa 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -4,10 +4,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ com_android_server_AlarmManagerService.cpp \ com_android_server_BatteryService.cpp \ - com_android_server_InputApplication.cpp \ com_android_server_InputApplicationHandle.cpp \ com_android_server_InputManager.cpp \ - com_android_server_InputWindow.cpp \ com_android_server_InputWindowHandle.cpp \ com_android_server_LightsService.cpp \ com_android_server_PowerManagerService.cpp \ @@ -39,14 +37,6 @@ LOCAL_SHARED_LIBRARIES := \ libgui \ libusbhost -ifeq ($(TARGET_SIMULATOR),true) -ifeq ($(TARGET_OS),linux) -ifeq ($(TARGET_ARCH),x86) -LOCAL_LDLIBS += -lpthread -ldl -lrt -endif -endif -endif - ifeq ($(WITH_MALLOC_LEAK_CHECK),true) LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK endif diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp index c9a702a..e80dd04 100644 --- a/services/jni/com_android_server_AlarmManagerService.cpp +++ b/services/jni/com_android_server_AlarmManagerService.cpp @@ -32,17 +32,13 @@ #include <stdlib.h> #include <errno.h> #include <unistd.h> - -#ifdef HAVE_ANDROID_OS #include <linux/ioctl.h> #include <linux/android_alarm.h> -#endif namespace android { static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jobject obj, jint fd, jint minswest) { -#ifdef HAVE_ANDROID_OS struct timezone tz; tz.tz_minuteswest = minswest; @@ -57,30 +53,20 @@ static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jo } return 0; -#else - return -ENOSYS; -#endif } static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj) { -#ifdef HAVE_ANDROID_OS return open("/dev/alarm", O_RDWR); -#else - return -1; -#endif } static void android_server_AlarmManagerService_close(JNIEnv* env, jobject obj, jint fd) { -#ifdef HAVE_ANDROID_OS close(fd); -#endif } static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds) { -#ifdef HAVE_ANDROID_OS struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = nanoseconds; @@ -90,12 +76,10 @@ static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jin { LOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno)); } -#endif } static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject obj, jint fd) { -#ifdef HAVE_ANDROID_OS int result = 0; do @@ -110,7 +94,6 @@ static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject } return result; -#endif } static JNINativeMethod sMethods[] = { diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp index 98d0d92..b9f2c1f 100644 --- a/services/jni/com_android_server_BatteryService.cpp +++ b/services/jni/com_android_server_BatteryService.cpp @@ -32,10 +32,7 @@ #include <errno.h> #include <unistd.h> #include <dirent.h> - -#ifdef HAVE_ANDROID_OS #include <linux/ioctl.h> -#endif namespace android { diff --git a/services/jni/com_android_server_InputApplication.cpp b/services/jni/com_android_server_InputApplication.cpp deleted file mode 100644 index 1f80242..0000000 --- a/services/jni/com_android_server_InputApplication.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2011 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 "InputApplication" - -#include "JNIHelp.h" -#include "jni.h" -#include <android_runtime/AndroidRuntime.h> - -#include "com_android_server_InputApplication.h" -#include "com_android_server_InputApplicationHandle.h" - -namespace android { - -static struct { - jfieldID inputApplicationHandle; - jfieldID name; - jfieldID dispatchingTimeoutNanos; -} gInputApplicationClassInfo; - - -// --- Global functions --- - -void android_server_InputApplication_toNative( - JNIEnv* env, jobject inputApplicationObj, InputApplication* outInputApplication) { - jobject inputApplicationHandleObj = env->GetObjectField(inputApplicationObj, - gInputApplicationClassInfo.inputApplicationHandle); - if (inputApplicationHandleObj) { - outInputApplication->inputApplicationHandle = - android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj); - env->DeleteLocalRef(inputApplicationHandleObj); - } else { - outInputApplication->inputApplicationHandle = NULL; - } - - jstring nameObj = jstring(env->GetObjectField(inputApplicationObj, - gInputApplicationClassInfo.name)); - if (nameObj) { - const char* nameStr = env->GetStringUTFChars(nameObj, NULL); - outInputApplication->name.setTo(nameStr); - env->ReleaseStringUTFChars(nameObj, nameStr); - env->DeleteLocalRef(nameObj); - } else { - LOGE("InputApplication.name should not be null."); - outInputApplication->name.setTo("unknown"); - } - - outInputApplication->dispatchingTimeout = env->GetLongField(inputApplicationObj, - gInputApplicationClassInfo.dispatchingTimeoutNanos); -} - - -// --- JNI --- - -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); - -#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ - var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find field " fieldName); - -int register_android_server_InputApplication(JNIEnv* env) { - jclass clazz; - FIND_CLASS(clazz, "com/android/server/wm/InputApplication"); - - GET_FIELD_ID(gInputApplicationClassInfo.inputApplicationHandle, - clazz, - "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); - - GET_FIELD_ID(gInputApplicationClassInfo.name, clazz, - "name", "Ljava/lang/String;"); - - GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos, - clazz, - "dispatchingTimeoutNanos", "J"); - return 0; -} - -} /* namespace android */ diff --git a/services/jni/com_android_server_InputApplicationHandle.cpp b/services/jni/com_android_server_InputApplicationHandle.cpp index 9516964..7de67d9 100644 --- a/services/jni/com_android_server_InputApplicationHandle.cpp +++ b/services/jni/com_android_server_InputApplicationHandle.cpp @@ -27,6 +27,8 @@ namespace android { static struct { jfieldID ptr; + jfieldID name; + jfieldID dispatchingTimeoutNanos; } gInputApplicationHandleClassInfo; static Mutex gHandleMutex; @@ -47,6 +49,31 @@ jobject NativeInputApplicationHandle::getInputApplicationHandleObjLocalRef(JNIEn return env->NewLocalRef(mObjWeak); } +bool NativeInputApplicationHandle::update() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject obj = env->NewLocalRef(mObjWeak); + if (!obj) { + return false; + } + + jstring nameObj = jstring(env->GetObjectField(obj, + gInputApplicationHandleClassInfo.name)); + if (nameObj) { + const char* nameStr = env->GetStringUTFChars(nameObj, NULL); + name.setTo(nameStr); + env->ReleaseStringUTFChars(nameObj, nameStr); + env->DeleteLocalRef(nameObj); + } else { + name.setTo("<null>"); + } + + dispatchingTimeout = env->GetLongField(obj, + gInputApplicationHandleClassInfo.dispatchingTimeoutNanos); + + env->DeleteLocalRef(obj); + return true; +} + // --- Global functions --- @@ -113,6 +140,13 @@ int register_android_server_InputApplicationHandle(JNIEnv* env) { GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, clazz, "ptr", "I"); + GET_FIELD_ID(gInputApplicationHandleClassInfo.name, clazz, + "name", "Ljava/lang/String;"); + + GET_FIELD_ID(gInputApplicationHandleClassInfo.dispatchingTimeoutNanos, + clazz, + "dispatchingTimeoutNanos", "J"); + return 0; } diff --git a/services/jni/com_android_server_InputApplicationHandle.h b/services/jni/com_android_server_InputApplicationHandle.h index 9d18721..04cd9d6 100644 --- a/services/jni/com_android_server_InputApplicationHandle.h +++ b/services/jni/com_android_server_InputApplicationHandle.h @@ -31,6 +31,8 @@ public: jobject getInputApplicationHandleObjLocalRef(JNIEnv* env); + virtual bool update(); + private: jweak mObjWeak; }; diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 14a4109..de9c9d0 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -46,9 +46,7 @@ #include <android/graphics/GraphicsJNI.h> #include "com_android_server_PowerManagerService.h" -#include "com_android_server_InputApplication.h" #include "com_android_server_InputApplicationHandle.h" -#include "com_android_server_InputWindow.h" #include "com_android_server_InputWindowHandle.h" namespace android { @@ -175,8 +173,8 @@ public: const sp<InputWindowHandle>& inputWindowHandle, bool monitor); status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel); - void setInputWindows(JNIEnv* env, jobjectArray windowObjArray); - void setFocusedApplication(JNIEnv* env, jobject applicationObj); + void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray); + void setFocusedApplication(JNIEnv* env, jobject applicationHandleObj); void setInputDispatchMode(bool enabled, bool frozen); void setSystemUiVisibility(int32_t visibility); void setPointerSpeed(int32_t speed); @@ -582,31 +580,38 @@ bool NativeInputManager::isKeyRepeatEnabled() { return isScreenOn(); } -void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) { - Vector<InputWindow> windows; +void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) { + Vector<sp<InputWindowHandle> > windowHandles; - bool newPointerGesturesEnabled = true; - jsize length = env->GetArrayLength(windowObjArray); - for (jsize i = 0; i < length; i++) { - jobject windowObj = env->GetObjectArrayElement(windowObjArray, i); - if (! windowObj) { - break; // found null element indicating end of used portion of the array - } + if (windowHandleObjArray) { + jsize length = env->GetArrayLength(windowHandleObjArray); + for (jsize i = 0; i < length; i++) { + jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i); + if (! windowHandleObj) { + break; // found null element indicating end of used portion of the array + } - windows.push(); - InputWindow& window = windows.editTop(); - android_server_InputWindow_toNative(env, windowObj, &window); - if (window.inputChannel == NULL) { - windows.pop(); - } else if (window.hasFocus) { - if (window.inputFeatures & InputWindow::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES) { - newPointerGesturesEnabled = false; + sp<InputWindowHandle> windowHandle = + android_server_InputWindowHandle_getHandle(env, windowHandleObj); + if (windowHandle != NULL) { + windowHandles.push(windowHandle); } + env->DeleteLocalRef(windowHandleObj); } - env->DeleteLocalRef(windowObj); } - mInputManager->getDispatcher()->setInputWindows(windows); + mInputManager->getDispatcher()->setInputWindows(windowHandles); + + // Do this after the dispatcher has updated the window handle state. + bool newPointerGesturesEnabled = true; + size_t numWindows = windowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i); + if (windowHandle->hasFocus && (windowHandle->inputFeatures + & InputWindowHandle::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) { + newPointerGesturesEnabled = false; + } + } uint32_t changes = 0; { // acquire lock @@ -623,16 +628,10 @@ void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArra } } -void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) { - if (applicationObj) { - InputApplication application; - android_server_InputApplication_toNative(env, applicationObj, &application); - if (application.inputApplicationHandle != NULL) { - mInputManager->getDispatcher()->setFocusedApplication(&application); - return; - } - } - mInputManager->getDispatcher()->setFocusedApplication(NULL); +void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationHandleObj) { + sp<InputApplicationHandle> applicationHandle = + android_server_InputApplicationHandle_getHandle(env, applicationHandleObj); + mInputManager->getDispatcher()->setFocusedApplication(applicationHandle); } void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) { @@ -1137,21 +1136,21 @@ static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jcla } static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz, - jobjectArray windowObjArray) { + jobjectArray windowHandleObjArray) { if (checkInputManagerUnitialized(env)) { return; } - gNativeInputManager->setInputWindows(env, windowObjArray); + gNativeInputManager->setInputWindows(env, windowHandleObjArray); } static void android_server_InputManager_nativeSetFocusedApplication(JNIEnv* env, jclass clazz, - jobject applicationObj) { + jobject applicationHandleObj) { if (checkInputManagerUnitialized(env)) { return; } - gNativeInputManager->setFocusedApplication(env, applicationObj); + gNativeInputManager->setFocusedApplication(env, applicationHandleObj); } static void android_server_InputManager_nativeSetInputDispatchMode(JNIEnv* env, @@ -1313,9 +1312,9 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeSetInputFilterEnabled }, { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIIII)I", (void*) android_server_InputManager_nativeInjectInputEvent }, - { "nativeSetInputWindows", "([Lcom/android/server/wm/InputWindow;)V", + { "nativeSetInputWindows", "([Lcom/android/server/wm/InputWindowHandle;)V", (void*) android_server_InputManager_nativeSetInputWindows }, - { "nativeSetFocusedApplication", "(Lcom/android/server/wm/InputApplication;)V", + { "nativeSetFocusedApplication", "(Lcom/android/server/wm/InputApplicationHandle;)V", (void*) android_server_InputManager_nativeSetFocusedApplication }, { "nativeSetInputDispatchMode", "(ZZ)V", (void*) android_server_InputManager_nativeSetInputDispatchMode }, diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp deleted file mode 100644 index 0426f63..0000000 --- a/services/jni/com_android_server_InputWindow.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2011 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 "InputWindow" - -#include "JNIHelp.h" -#include "jni.h" -#include <android_runtime/AndroidRuntime.h> - -#include <android_view_InputChannel.h> -#include <android/graphics/Region.h> -#include "com_android_server_InputWindow.h" -#include "com_android_server_InputWindowHandle.h" - -namespace android { - -static struct { - jfieldID inputWindowHandle; - jfieldID inputChannel; - jfieldID name; - jfieldID layoutParamsFlags; - jfieldID layoutParamsType; - jfieldID dispatchingTimeoutNanos; - jfieldID frameLeft; - jfieldID frameTop; - jfieldID frameRight; - jfieldID frameBottom; - jfieldID scaleFactor; - jfieldID touchableRegion; - jfieldID visible; - jfieldID canReceiveKeys; - jfieldID hasFocus; - jfieldID hasWallpaper; - jfieldID paused; - jfieldID layer; - jfieldID ownerPid; - jfieldID ownerUid; - jfieldID inputFeatures; -} gInputWindowClassInfo; - - -// --- Global functions --- - -void android_server_InputWindow_toNative( - JNIEnv* env, jobject inputWindowObj, InputWindow* outInputWindow) { - jobject inputWindowHandleObj = env->GetObjectField(inputWindowObj, - gInputWindowClassInfo.inputWindowHandle); - if (inputWindowHandleObj) { - outInputWindow->inputWindowHandle = - android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj); - env->DeleteLocalRef(inputWindowHandleObj); - } else { - outInputWindow->inputWindowHandle = NULL; - } - - jobject inputChannelObj = env->GetObjectField(inputWindowObj, - gInputWindowClassInfo.inputChannel); - if (inputChannelObj) { - outInputWindow->inputChannel = - android_view_InputChannel_getInputChannel(env, inputChannelObj); - env->DeleteLocalRef(inputChannelObj); - } else { - outInputWindow->inputChannel = NULL; - } - - jstring nameObj = jstring(env->GetObjectField(inputWindowObj, - gInputWindowClassInfo.name)); - if (nameObj) { - const char* nameStr = env->GetStringUTFChars(nameObj, NULL); - outInputWindow->name.setTo(nameStr); - env->ReleaseStringUTFChars(nameObj, nameStr); - env->DeleteLocalRef(nameObj); - } else { - LOGE("InputWindow.name should not be null."); - outInputWindow->name.setTo("unknown"); - } - - outInputWindow->layoutParamsFlags = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.layoutParamsFlags); - outInputWindow->layoutParamsType = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.layoutParamsType); - outInputWindow->dispatchingTimeout = env->GetLongField(inputWindowObj, - gInputWindowClassInfo.dispatchingTimeoutNanos); - outInputWindow->frameLeft = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.frameLeft); - outInputWindow->frameTop = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.frameTop); - outInputWindow->frameRight = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.frameRight); - outInputWindow->frameBottom = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.frameBottom); - outInputWindow->scaleFactor = env->GetFloatField(inputWindowObj, - gInputWindowClassInfo.scaleFactor); - - jobject regionObj = env->GetObjectField(inputWindowObj, - gInputWindowClassInfo.touchableRegion); - if (regionObj) { - SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj); - outInputWindow->touchableRegion.set(*region); - env->DeleteLocalRef(regionObj); - } else { - outInputWindow->touchableRegion.setEmpty(); - } - - outInputWindow->visible = env->GetBooleanField(inputWindowObj, - gInputWindowClassInfo.visible); - outInputWindow->canReceiveKeys = env->GetBooleanField(inputWindowObj, - gInputWindowClassInfo.canReceiveKeys); - outInputWindow->hasFocus = env->GetBooleanField(inputWindowObj, - gInputWindowClassInfo.hasFocus); - outInputWindow->hasWallpaper = env->GetBooleanField(inputWindowObj, - gInputWindowClassInfo.hasWallpaper); - outInputWindow->paused = env->GetBooleanField(inputWindowObj, - gInputWindowClassInfo.paused); - outInputWindow->layer = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.layer); - outInputWindow->ownerPid = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.ownerPid); - outInputWindow->ownerUid = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.ownerUid); - outInputWindow->inputFeatures = env->GetIntField(inputWindowObj, - gInputWindowClassInfo.inputFeatures); -} - - -// --- JNI --- - -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); - -#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ - var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find field " fieldName); - -int register_android_server_InputWindow(JNIEnv* env) { - jclass clazz; - FIND_CLASS(clazz, "com/android/server/wm/InputWindow"); - - GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, clazz, - "inputWindowHandle", "Lcom/android/server/wm/InputWindowHandle;"); - - GET_FIELD_ID(gInputWindowClassInfo.inputChannel, clazz, - "inputChannel", "Landroid/view/InputChannel;"); - - GET_FIELD_ID(gInputWindowClassInfo.name, clazz, - "name", "Ljava/lang/String;"); - - GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, clazz, - "layoutParamsFlags", "I"); - - GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, clazz, - "layoutParamsType", "I"); - - GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, clazz, - "dispatchingTimeoutNanos", "J"); - - GET_FIELD_ID(gInputWindowClassInfo.frameLeft, clazz, - "frameLeft", "I"); - - GET_FIELD_ID(gInputWindowClassInfo.frameTop, clazz, - "frameTop", "I"); - - GET_FIELD_ID(gInputWindowClassInfo.frameRight, clazz, - "frameRight", "I"); - - GET_FIELD_ID(gInputWindowClassInfo.frameBottom, clazz, - "frameBottom", "I"); - - GET_FIELD_ID(gInputWindowClassInfo.scaleFactor, clazz, - "scaleFactor", "F"); - - GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, clazz, - "touchableRegion", "Landroid/graphics/Region;"); - - GET_FIELD_ID(gInputWindowClassInfo.visible, clazz, - "visible", "Z"); - - GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, clazz, - "canReceiveKeys", "Z"); - - GET_FIELD_ID(gInputWindowClassInfo.hasFocus, clazz, - "hasFocus", "Z"); - - GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, clazz, - "hasWallpaper", "Z"); - - GET_FIELD_ID(gInputWindowClassInfo.paused, clazz, - "paused", "Z"); - - GET_FIELD_ID(gInputWindowClassInfo.layer, clazz, - "layer", "I"); - - GET_FIELD_ID(gInputWindowClassInfo.ownerPid, clazz, - "ownerPid", "I"); - - GET_FIELD_ID(gInputWindowClassInfo.ownerUid, clazz, - "ownerUid", "I"); - - GET_FIELD_ID(gInputWindowClassInfo.inputFeatures, clazz, - "inputFeatures", "I"); - return 0; -} - -} /* namespace android */ diff --git a/services/jni/com_android_server_InputWindowHandle.cpp b/services/jni/com_android_server_InputWindowHandle.cpp index aaf679c..09be881 100644 --- a/services/jni/com_android_server_InputWindowHandle.cpp +++ b/services/jni/com_android_server_InputWindowHandle.cpp @@ -21,6 +21,9 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/threads.h> +#include <android_view_InputChannel.h> +#include <android/graphics/Region.h> + #include "com_android_server_InputWindowHandle.h" #include "com_android_server_InputApplicationHandle.h" @@ -29,6 +32,26 @@ namespace android { static struct { jfieldID ptr; jfieldID inputApplicationHandle; + jfieldID inputChannel; + jfieldID name; + jfieldID layoutParamsFlags; + jfieldID layoutParamsType; + jfieldID dispatchingTimeoutNanos; + jfieldID frameLeft; + jfieldID frameTop; + jfieldID frameRight; + jfieldID frameBottom; + jfieldID scaleFactor; + jfieldID touchableRegion; + jfieldID visible; + jfieldID canReceiveKeys; + jfieldID hasFocus; + jfieldID hasWallpaper; + jfieldID paused; + jfieldID layer; + jfieldID ownerPid; + jfieldID ownerUid; + jfieldID inputFeatures; } gInputWindowHandleClassInfo; static Mutex gHandleMutex; @@ -51,6 +74,83 @@ jobject NativeInputWindowHandle::getInputWindowHandleObjLocalRef(JNIEnv* env) { return env->NewLocalRef(mObjWeak); } +bool NativeInputWindowHandle::update() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject obj = env->NewLocalRef(mObjWeak); + if (!obj) { + return false; + } + + jobject inputChannelObj = env->GetObjectField(obj, + gInputWindowHandleClassInfo.inputChannel); + if (inputChannelObj) { + inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); + env->DeleteLocalRef(inputChannelObj); + } else { + inputChannel = NULL; + } + + jstring nameObj = jstring(env->GetObjectField(obj, + gInputWindowHandleClassInfo.name)); + if (nameObj) { + const char* nameStr = env->GetStringUTFChars(nameObj, NULL); + name.setTo(nameStr); + env->ReleaseStringUTFChars(nameObj, nameStr); + env->DeleteLocalRef(nameObj); + } else { + name.setTo("<null>"); + } + + layoutParamsFlags = env->GetIntField(obj, + gInputWindowHandleClassInfo.layoutParamsFlags); + layoutParamsType = env->GetIntField(obj, + gInputWindowHandleClassInfo.layoutParamsType); + dispatchingTimeout = env->GetLongField(obj, + gInputWindowHandleClassInfo.dispatchingTimeoutNanos); + frameLeft = env->GetIntField(obj, + gInputWindowHandleClassInfo.frameLeft); + frameTop = env->GetIntField(obj, + gInputWindowHandleClassInfo.frameTop); + frameRight = env->GetIntField(obj, + gInputWindowHandleClassInfo.frameRight); + frameBottom = env->GetIntField(obj, + gInputWindowHandleClassInfo.frameBottom); + scaleFactor = env->GetFloatField(obj, + gInputWindowHandleClassInfo.scaleFactor); + + jobject regionObj = env->GetObjectField(obj, + gInputWindowHandleClassInfo.touchableRegion); + if (regionObj) { + SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj); + touchableRegion.set(*region); + env->DeleteLocalRef(regionObj); + } else { + touchableRegion.setEmpty(); + } + + visible = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.visible); + canReceiveKeys = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.canReceiveKeys); + hasFocus = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.hasFocus); + hasWallpaper = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.hasWallpaper); + paused = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.paused); + layer = env->GetIntField(obj, + gInputWindowHandleClassInfo.layer); + ownerPid = env->GetIntField(obj, + gInputWindowHandleClassInfo.ownerPid); + ownerUid = env->GetIntField(obj, + gInputWindowHandleClassInfo.ownerUid); + inputFeatures = env->GetIntField(obj, + gInputWindowHandleClassInfo.inputFeatures); + + env->DeleteLocalRef(obj); + return true; +} + // --- Global functions --- @@ -127,6 +227,65 @@ int register_android_server_InputWindowHandle(JNIEnv* env) { clazz, "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); + GET_FIELD_ID(gInputWindowHandleClassInfo.inputChannel, clazz, + "inputChannel", "Landroid/view/InputChannel;"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.name, clazz, + "name", "Ljava/lang/String;"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.layoutParamsFlags, clazz, + "layoutParamsFlags", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.layoutParamsType, clazz, + "layoutParamsType", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.dispatchingTimeoutNanos, clazz, + "dispatchingTimeoutNanos", "J"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.frameLeft, clazz, + "frameLeft", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.frameTop, clazz, + "frameTop", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.frameRight, clazz, + "frameRight", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.frameBottom, clazz, + "frameBottom", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.scaleFactor, clazz, + "scaleFactor", "F"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegion, clazz, + "touchableRegion", "Landroid/graphics/Region;"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.visible, clazz, + "visible", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.canReceiveKeys, clazz, + "canReceiveKeys", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.hasFocus, clazz, + "hasFocus", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.hasWallpaper, clazz, + "hasWallpaper", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.paused, clazz, + "paused", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.layer, clazz, + "layer", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.ownerPid, clazz, + "ownerPid", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.ownerUid, clazz, + "ownerUid", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.inputFeatures, clazz, + "inputFeatures", "I"); return 0; } diff --git a/services/jni/com_android_server_InputWindowHandle.h b/services/jni/com_android_server_InputWindowHandle.h index 43f2a6b..913c3b1 100644 --- a/services/jni/com_android_server_InputWindowHandle.h +++ b/services/jni/com_android_server_InputWindowHandle.h @@ -32,6 +32,8 @@ public: jobject getInputWindowHandleObjLocalRef(JNIEnv* env); + virtual bool update(); + private: jweak mObjWeak; }; diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp index 9dff48b..4178039 100644 --- a/services/jni/onload.cpp +++ b/services/jni/onload.cpp @@ -22,9 +22,7 @@ namespace android { int register_android_server_AlarmManagerService(JNIEnv* env); int register_android_server_BatteryService(JNIEnv* env); -int register_android_server_InputApplication(JNIEnv* env); int register_android_server_InputApplicationHandle(JNIEnv* env); -int register_android_server_InputWindow(JNIEnv* env); int register_android_server_InputWindowHandle(JNIEnv* env); int register_android_server_InputManager(JNIEnv* env); int register_android_server_LightsService(JNIEnv* env); @@ -51,9 +49,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) LOG_ASSERT(env, "Could not retrieve the env!"); register_android_server_PowerManagerService(env); - register_android_server_InputApplication(env); register_android_server_InputApplicationHandle(env); - register_android_server_InputWindow(env); register_android_server_InputWindowHandle(env); register_android_server_InputManager(env); register_android_server_LightsService(env); diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk index ba3e6e5..6a302c0 100644 --- a/services/sensorservice/Android.mk +++ b/services/sensorservice/Android.mk @@ -16,13 +16,6 @@ LOCAL_SRC_FILES:= \ LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\" -# need "-lrt" on Linux simulator to pick up clock_gettime -ifeq ($(TARGET_SIMULATOR),true) - ifeq ($(HOST_OS),linux) - LOCAL_LDLIBS += -lrt -lpthread - endif -endif - LOCAL_SHARED_LIBRARIES := \ libcutils \ libhardware \ diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index c618263..f67c82e 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -26,13 +26,6 @@ ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) endif -# need "-lrt" on Linux simulator to pick up clock_gettime -ifeq ($(TARGET_SIMULATOR),true) - ifeq ($(HOST_OS),linux) - LOCAL_LDLIBS += -lrt -lpthread - endif -endif - LOCAL_SHARED_LIBRARIES := \ libcutils \ libhardware \ diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 33125c4..7bf3e0a 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -99,6 +99,31 @@ uint32_t DisplayHardware::getMaxViewportDims() const { mMaxViewportDims[0] : mMaxViewportDims[1]; } +static status_t selectConfigForPixelFormat( + EGLDisplay dpy, + EGLint const* attrs, + PixelFormat format, + EGLConfig* outConfig) +{ + EGLConfig config = NULL; + EGLint numConfigs = -1, n=0; + eglGetConfigs(dpy, NULL, 0, &numConfigs); + EGLConfig* const configs = new EGLConfig[numConfigs]; + eglChooseConfig(dpy, attrs, configs, numConfigs, &n); + for (int i=0 ; i<n ; i++) { + EGLint nativeVisualId = 0; + eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &nativeVisualId); + if (nativeVisualId>0 && format == nativeVisualId) { + *outConfig = configs[i]; + delete [] configs; + return NO_ERROR; + } + } + delete [] configs; + return NAME_NOT_FOUND; +} + + void DisplayHardware::init(uint32_t dpy) { mNativeWindow = new FramebufferNativeWindow(); @@ -108,6 +133,9 @@ void DisplayHardware::init(uint32_t dpy) exit(0); } + int format; + ANativeWindow const * const window = mNativeWindow.get(); + window->query(window, NATIVE_WINDOW_FORMAT, &format); mDpiX = mNativeWindow->xdpi; mDpiY = mNativeWindow->ydpi; mRefreshRate = fbDev->fps; @@ -116,11 +144,13 @@ void DisplayHardware::init(uint32_t dpy) EGLint numConfigs=0; EGLSurface surface; EGLContext context; + EGLBoolean result; + status_t err; // initialize EGL EGLint attribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_NONE, 0, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE, 0, EGL_NONE }; @@ -141,9 +171,8 @@ void DisplayHardware::init(uint32_t dpy) eglInitialize(display, NULL, NULL); eglGetConfigs(display, NULL, 0, &numConfigs); - EGLConfig config; - status_t err = EGLUtils::selectConfigForNativeWindow( - display, attribs, mNativeWindow.get(), &config); + EGLConfig config = NULL; + err = selectConfigForPixelFormat(display, attribs, format, &config); LOGE_IF(err, "couldn't find an EGLConfig matching the screen format"); EGLint r,g,b,a; @@ -224,7 +253,11 @@ void DisplayHardware::init(uint32_t dpy) * Gather OpenGL ES extensions */ - eglMakeCurrent(display, surface, surface, context); + result = eglMakeCurrent(display, surface, surface, context); + if (!result) { + LOGE("Couldn't create a working GLES context. check logs. exiting..."); + exit(0); + } GLExtensions& extensions(GLExtensions::getInstance()); extensions.initWithGLStrings( diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 35e29a6..f3b6c4d 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -188,22 +188,37 @@ void Layer::setGeometry(hwc_layer_t* hwcl) return; } + /* + * Transformations are applied in this order: + * 1) buffer orientation/flip/mirror + * 2) state transformation (window manager) + * 3) layer orientation (screen orientation) + * (NOTE: the matrices are multiplied in reverse order) + */ + + const Transform bufferOrientation(mCurrentTransform); + const Transform& stateTransform(s.transform); + const Transform layerOrientation(mOrientation); + + const Transform tr(layerOrientation * stateTransform * bufferOrientation); + + // this gives us only the "orientation" component of the transform + const uint32_t finalTransform = tr.getOrientation(); + // we can only handle simple transformation - if (mOrientation & Transform::ROT_INVALID) { + if (finalTransform & Transform::ROT_INVALID) { hwcl->flags = HWC_SKIP_LAYER; return; } - // FIXME: shouldn't we take the state's transform into account here? - - Transform tr(Transform(mOrientation) * Transform(mCurrentTransform)); - hwcl->transform = tr.getOrientation(); + hwcl->transform = finalTransform; if (!isOpaque()) { hwcl->blending = mPremultipliedAlpha ? HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE; } + // scaling is already applied in mTransformedBounds hwcl->displayFrame.left = mTransformedBounds.left; hwcl->displayFrame.top = mTransformedBounds.top; hwcl->displayFrame.right = mTransformedBounds.right; diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index bcd8c83..c86c659 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -228,13 +228,18 @@ void LayerBase::validateVisibility(const Transform& planeTransform) const Layer::State& s(drawingState()); const Transform tr(planeTransform * s.transform); const bool transformed = tr.transformed(); - + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t hw_h = hw.getHeight(); + uint32_t w = s.w; uint32_t h = s.h; tr.transform(mVertices[0], 0, 0); tr.transform(mVertices[1], 0, h); tr.transform(mVertices[2], w, h); tr.transform(mVertices[3], w, 0); + for (size_t i=0 ; i<4 ; i++) + mVertices[i][1] = hw_h - mVertices[i][1]; + if (UNLIKELY(transformed)) { // NOTE: here we could also punt if we have too many rectangles // in the transparent region diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index cccab4a..b0881a4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -276,7 +276,8 @@ status_t SurfaceFlinger::readyToRun() glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrthof(0, w, h, 0, 0, 1); + // put the origin in the left-bottom corner + glOrthof(0, w, 0, h, 0, 1); // l=0, r=w ; b=0, t=h mReadyToRunBarrier.open(); @@ -1791,7 +1792,7 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() } GLfloat vtx[8]; - const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} }; + const GLfloat texCoords[4][2] = { {0,1}, {0,1-v}, {u,1-v}, {u,1} }; glBindTexture(GL_TEXTURE_2D, tname); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -1800,6 +1801,22 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vtx); + /* + * Texture coordinate mapping + * + * u + * 1 +----------+---+ + * | | | | image is inverted + * | V | | w.r.t. the texture + * 1-v +----------+ | coordinates + * | | + * | | + * | | + * 0 +--------------+ + * 0 1 + * + */ + class s_curve_interpolator { const float nbFrames, s, v; public: @@ -1824,10 +1841,10 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() const GLfloat h = hw_h - (hw_h * v); const GLfloat x = (hw_w - w) * 0.5f; const GLfloat y = (hw_h - h) * 0.5f; - vtx[0] = x; vtx[1] = y + h; - vtx[2] = x; vtx[3] = y; - vtx[4] = x + w; vtx[5] = y; - vtx[6] = x + w; vtx[7] = y + h; + vtx[0] = x; vtx[1] = y; + vtx[2] = x; vtx[3] = y + h; + vtx[4] = x + w; vtx[5] = y + h; + vtx[6] = x + w; vtx[7] = y; } }; @@ -1842,15 +1859,20 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() const GLfloat h = 1.0f; const GLfloat x = (hw_w - w) * 0.5f; const GLfloat y = (hw_h - h) * 0.5f; - vtx[0] = x; vtx[1] = y + h; - vtx[2] = x; vtx[3] = y; - vtx[4] = x + w; vtx[5] = y; - vtx[6] = x + w; vtx[7] = y + h; + vtx[0] = x; vtx[1] = y; + vtx[2] = x; vtx[3] = y + h; + vtx[4] = x + w; vtx[5] = y + h; + vtx[6] = x + w; vtx[7] = y; } }; // the full animation is 24 frames - const int nbFrames = 12; + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.electron_frames", value, "24"); + int nbFrames = (atoi(value) + 1) >> 1; + if (nbFrames <= 0) // just in case + nbFrames = 24; + s_curve_interpolator itr(nbFrames, 7.5f); s_curve_interpolator itg(nbFrames, 8.0f); s_curve_interpolator itb(nbFrames, 8.5f); @@ -2225,10 +2247,11 @@ status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy, // invert everything, b/c glReadPixel() below will invert the FB glViewport(0, 0, sw, sh); glScissor(0, 0, sw, sh); + glEnable(GL_SCISSOR_TEST); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); - glOrthof(0, hw_w, 0, hw_h, 0, 1); + glOrthof(0, hw_w, hw_h, 0, 0, 1); glMatrixMode(GL_MODELVIEW); // redraw the screen entirely... @@ -2244,6 +2267,7 @@ status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy, } // XXX: this is needed on tegra + glEnable(GL_SCISSOR_TEST); glScissor(0, 0, sw, sh); // check for errors and return screen capture diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp index 0467a14..4cedcbf 100644 --- a/services/surfaceflinger/Transform.cpp +++ b/services/surfaceflinger/Transform.cpp @@ -308,6 +308,7 @@ uint32_t Transform::type() const scale = true; } } else { + // there is a skew component and/or a non 90 degrees rotation flags = ROT_INVALID; } diff --git a/services/tests/servicestests/res/raw/net_dev_typical b/services/tests/servicestests/res/raw/net_dev_typical new file mode 100644 index 0000000..290bf03 --- /dev/null +++ b/services/tests/servicestests/res/raw/net_dev_typical @@ -0,0 +1,8 @@ +Inter-| Receive | Transmit + face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed + lo: 8308 116 0 0 0 0 0 0 8308 116 0 0 0 0 0 0 +rmnet0: 1507570 2205 0 0 0 0 0 0 489339 2237 0 0 0 0 0 0 + ifb0: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 + ifb1: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 + sit0: 0 0 0 0 0 0 0 0 0 0 148 0 0 0 0 0 +ip6tnl0: 0 0 0 0 0 0 0 0 0 0 151 151 0 0 0 0 diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java index ac7cb5a..56ef995 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; import static com.android.server.NetworkManagementSocketTagger.kernelToTag; import static com.android.server.NetworkManagementSocketTagger.tagToKernel; @@ -25,9 +27,11 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import com.android.frameworks.servicestests.R; +import com.google.common.io.Files; import java.io.File; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.InputStream; import java.io.OutputStream; @@ -46,14 +50,23 @@ public class NetworkManagementServiceTest extends AndroidTestCase { public void setUp() throws Exception { super.setUp(); - mTestProc = getContext().getFilesDir(); - mService = NetworkManagementService.createForTest(mContext, mTestProc); + final File canonicalFilesDir = getContext().getFilesDir().getCanonicalFile(); + mTestProc = new File(canonicalFilesDir, "proc"); + if (mTestProc.exists()) { + Files.deleteRecursively(mTestProc); + } + + mService = NetworkManagementService.createForTest(mContext, mTestProc, true); } @Override public void tearDown() throws Exception { mService = null; + if (mTestProc.exists()) { + Files.deleteRecursively(mTestProc); + } + super.tearDown(); } @@ -61,7 +74,7 @@ public class NetworkManagementServiceTest extends AndroidTestCase { stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats")); final NetworkStats stats = mService.getNetworkStatsDetail(); - assertEquals(31, stats.size); + assertEquals(31, stats.size()); assertStatsEntry(stats, "wlan0", 0, 0, 14615L, 4270L); assertStatsEntry(stats, "wlan0", 10004, 0, 333821L, 53558L); assertStatsEntry(stats, "wlan0", 10004, 1947740890, 18725L, 1066L); @@ -73,11 +86,37 @@ public class NetworkManagementServiceTest extends AndroidTestCase { stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats")); final NetworkStats stats = mService.getNetworkStatsDetail(); - assertEquals(2, stats.size); + assertEquals(2, stats.size()); assertStatsEntry(stats, "test0", 1000, 0, 1024L, 2048L); assertStatsEntry(stats, "test0", 1000, 0xF00D, 512L, 512L); } + public void testNetworkStatsSummary() throws Exception { + stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev")); + + final NetworkStats stats = mService.getNetworkStatsSummary(); + assertEquals(6, stats.size()); + assertStatsEntry(stats, "lo", UID_ALL, TAG_NONE, 8308L, 8308L); + assertStatsEntry(stats, "rmnet0", UID_ALL, TAG_NONE, 1507570L, 489339L); + assertStatsEntry(stats, "ifb0", UID_ALL, TAG_NONE, 52454L, 0L); + assertStatsEntry(stats, "ifb1", UID_ALL, TAG_NONE, 52454L, 0L); + assertStatsEntry(stats, "sit0", UID_ALL, TAG_NONE, 0L, 0L); + assertStatsEntry(stats, "ip6tnl0", UID_ALL, TAG_NONE, 0L, 0L); + } + + public void testNetworkStatsSummaryDown() throws Exception { + stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev")); + stageLong(1024L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_bytes")); + stageLong(128L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_packets")); + stageLong(2048L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_bytes")); + stageLong(256L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_packets")); + + final NetworkStats stats = mService.getNetworkStatsSummary(); + assertEquals(7, stats.size()); + assertStatsEntry(stats, "rmnet0", UID_ALL, TAG_NONE, 1507570L, 489339L); + assertStatsEntry(stats, "wlan0", UID_ALL, TAG_NONE, 1024L, 2048L); + } + public void testKernelTags() throws Exception { assertEquals("0", tagToKernel(0x0)); assertEquals("214748364800", tagToKernel(0x32)); @@ -90,7 +129,6 @@ public class NetworkManagementServiceTest extends AndroidTestCase { assertEquals(2147483647, kernelToTag("0x7fffffff00000000")); assertEquals(0, kernelToTag("0x0000000000000000")); assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000")); - } /** @@ -111,11 +149,23 @@ public class NetworkManagementServiceTest extends AndroidTestCase { } } + private void stageLong(long value, File file) throws Exception { + new File(file.getParent()).mkdirs(); + FileWriter out = null; + try { + out = new FileWriter(file); + out.write(Long.toString(value)); + } finally { + IoUtils.closeQuietly(out); + } + } + private static void assertStatsEntry( - NetworkStats stats, String iface, int uid, int tag, long rx, long tx) { + NetworkStats stats, String iface, int uid, int tag, long rxBytes, long txBytes) { final int i = stats.findIndex(iface, uid, tag); - assertEquals(rx, stats.rx[i]); - assertEquals(tx, stats.tx[i]); + final NetworkStats.Entry entry = stats.getValues(i, null); + assertEquals(rxBytes, entry.rxBytes); + assertEquals(txBytes, entry.txBytes); } } diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index b4ac987..33fd355 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -447,7 +447,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { // pretend that 512 bytes total have happened stats = new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 256L, 256L); + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 256L, 2L, 256L, 2L); expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10)) .andReturn(stats).atLeastOnce(); diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index f2c28bb..ac74063 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -168,7 +168,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 2048L)); + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 1L, 2048L, 2L)); expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); replay(); @@ -184,7 +184,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 4096L, 8192L)); + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 4096L, 4L, 8192L, 8L)); expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); replay(); @@ -219,10 +219,10 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 2048L)); + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 8L, 2048L, 16L)); expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 2) - .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 512L, 256L) - .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 128L)); + .addValues(TEST_IFACE, UID_RED, TAG_NONE, 512L, 4L, 256L, 2L) + .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 1L, 128L, 1L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); @@ -264,7 +264,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase { public void testStatsBucketResize() throws Exception { long elapsedRealtime = 0; NetworkStatsHistory history = null; - long[] total = null; assertStatsFilesExist(false); @@ -284,7 +283,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 512L, 512L)); + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 512L, 4L, 512L, 4L)); expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); replay(); @@ -292,11 +291,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase { // verify service recorded history history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null)); - total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); - assertEquals(512L, total[0]); - assertEquals(512L, total[1]); - assertEquals(HOUR_IN_MILLIS, history.bucketDuration); - assertEquals(2, history.bucketCount); + assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L); + assertEquals(HOUR_IN_MILLIS, history.getBucketDuration()); + assertEquals(2, history.size()); verifyAndReset(); // now change bucket duration setting and trigger another poll with @@ -311,11 +308,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase { // verify identical stats, but spread across 4 buckets now history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null)); - total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); - assertEquals(512L, total[0]); - assertEquals(512L, total[1]); - assertEquals(30 * MINUTE_IN_MILLIS, history.bucketDuration); - assertEquals(4, history.bucketCount); + assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L); + assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration()); + assertEquals(4, history.size()); verifyAndReset(); } @@ -338,11 +333,11 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 2048L, 512L)); + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 2048L, 16L, 512L, 4L)); expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 3) - .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 1536L, 512L) - .addEntry(TEST_IFACE, UID_RED, 0xF00D, 512L, 512L) - .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 512L, 0L)); + .addValues(TEST_IFACE, UID_RED, TAG_NONE, 1536L, 12L, 512L, 4L) + .addValues(TEST_IFACE, UID_RED, 0xF00D, 512L, 4L, 512L, 4L) + .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 512L, 4L, 0L, 0L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); @@ -373,9 +368,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 128L, 1024L)); + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 128L, 1L, 1024L, 8L)); expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 1024L)); + .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 1L, 1024L, 8L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); @@ -412,11 +407,11 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectTime(TEST_START + elapsedRealtime); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 4128L, 544L)); + .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 4128L, 258L, 544L, 34L)); expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 16L, 16L) - .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 4096L, 512L) - .addEntry(TEST_IFACE, UID_GREEN, TAG_NONE, 16L, 16L)); + .addValues(TEST_IFACE, UID_RED, TAG_NONE, 16L, 1L, 16L, 1L) + .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 4096L, 258L, 512L, 32L) + .addValues(TEST_IFACE, UID_GREEN, TAG_NONE, 16L, 1L, 16L, 1L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); @@ -468,8 +463,8 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime)); expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 1024L, 1024L) - .addEntry(TEST_IFACE, UID_RED, 0xF00D, 512L, 512L)); + .addValues(TEST_IFACE, UID_RED, TAG_NONE, 1024L, 8L, 1024L, 8L) + .addValues(TEST_IFACE, UID_RED, 0xF00D, 512L, 4L, 512L, 4L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); @@ -497,7 +492,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime)); expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 512L, 256L)); + .addValues(TEST_IFACE, UID_RED, TAG_NONE, 512L, 4L, 256L, 2L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); @@ -548,9 +543,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime)); expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 50L, 50L) - .addEntry(TEST_IFACE, UID_RED, 0xF00D, 10L, 10L) - .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 1024L, 512L)); + .addValues(TEST_IFACE, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L) + .addValues(TEST_IFACE, UID_RED, 0xF00D, 10L, 1L, 10L, 1L) + .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); @@ -566,7 +561,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime)); expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1) - .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 2048L, 1024L)); + .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); @@ -574,33 +569,29 @@ public class NetworkStatsServiceTest extends AndroidTestCase { // first verify entire history present NetworkStats stats = mService.getSummaryForAllUid( sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertEquals(3, stats.size); - assertStatsEntry(stats, 0, IFACE_ALL, UID_RED, TAG_NONE, 50L, 50L); - assertStatsEntry(stats, 1, IFACE_ALL, UID_RED, 0xF00D, 10L, 10L); - assertStatsEntry(stats, 2, IFACE_ALL, UID_BLUE, TAG_NONE, 2048L, 1024L); + assertEquals(3, stats.size()); + assertValues(stats, 0, IFACE_ALL, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L); + assertValues(stats, 1, IFACE_ALL, UID_RED, 0xF00D, 10L, 1L, 10L, 1L); + assertValues(stats, 2, IFACE_ALL, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L); // now verify that recent history only contains one uid final long currentTime = TEST_START + elapsedRealtime; stats = mService.getSummaryForAllUid( sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true); - assertEquals(1, stats.size); - assertStatsEntry(stats, 0, IFACE_ALL, UID_BLUE, TAG_NONE, 1024L, 512L); + assertEquals(1, stats.size()); + assertValues(stats, 0, IFACE_ALL, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L); verifyAndReset(); } - private void assertNetworkTotal(NetworkTemplate template, long rx, long tx) { + private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long txBytes) { final NetworkStatsHistory history = mService.getHistoryForNetwork(template); - final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); - assertEquals(rx, total[0]); - assertEquals(tx, total[1]); + assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, txBytes); } - private void assertUidTotal(NetworkTemplate template, int uid, long rx, long tx) { + private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long txBytes) { final NetworkStatsHistory history = mService.getHistoryForUid(template, uid, TAG_NONE); - final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); - assertEquals(rx, total[0]); - assertEquals(tx, total[1]); + assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, txBytes); } private void expectSystemReady() throws Exception { @@ -660,13 +651,24 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } } - private static void assertStatsEntry( - NetworkStats stats, int i, String iface, int uid, int tag, long rx, long tx) { - assertEquals(iface, stats.iface[i]); - assertEquals(uid, stats.uid[i]); - assertEquals(tag, stats.tag[i]); - assertEquals(rx, stats.rx[i]); - assertEquals(tx, stats.tx[i]); + private static void assertValues(NetworkStats stats, int i, String iface, int uid, int tag, + long rxBytes, long rxPackets, long txBytes, long txPackets) { + final NetworkStats.Entry entry = stats.getValues(i, null); + assertEquals(iface, entry.iface); + assertEquals(uid, entry.uid); + assertEquals(tag, entry.tag); + assertEquals(rxBytes, entry.rxBytes); + // TODO: enable testing packet counts once stored in history +// assertEquals(rxPackets, entry.rxPackets); + assertEquals(txBytes, entry.txBytes); +// assertEquals(txPackets, entry.txPackets); + } + + private static void assertValues( + NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) { + final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); } private static NetworkState buildWifiState() { diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java index 2f275c3..50c18f0 100644 --- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java @@ -289,7 +289,7 @@ public class ThrottleServiceTest extends AndroidTestCase { public void expectGetInterfaceCounter(long rx, long tx) throws Exception { // TODO: provide elapsedRealtime mock to match TimeAuthority final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); - stats.addEntry(TEST_IFACE, NetworkStats.UID_ALL, NetworkStats.TAG_NONE, rx, tx); + stats.addValues(TEST_IFACE, NetworkStats.UID_ALL, NetworkStats.TAG_NONE, rx, 0L, tx, 0L); expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats).atLeastOnce(); } diff --git a/telephony/java/com/android/internal/telephony/DataCallState.java b/telephony/java/com/android/internal/telephony/DataCallState.java index fba3184..a69ce8b 100644 --- a/telephony/java/com/android/internal/telephony/DataCallState.java +++ b/telephony/java/com/android/internal/telephony/DataCallState.java @@ -52,7 +52,7 @@ public class DataCallState { /** * Class returned by onSetupConnectionCompleted. */ - protected enum SetupResult { + public enum SetupResult { SUCCESS, ERR_BadCommand, ERR_UnacceptableParameter, diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java index 5c84fdc..1bba8e3 100644 --- a/telephony/java/com/android/internal/telephony/DataConnection.java +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -17,30 +17,25 @@ package com.android.internal.telephony; +import com.android.internal.telephony.DataCallState.SetupResult; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import android.app.PendingIntent; -import android.net.LinkAddress; import android.net.LinkCapabilities; import android.net.LinkProperties; -import android.net.NetworkUtils; +import android.net.LinkProperties.CompareAddressesResult; import android.net.ProxyProperties; import android.os.AsyncResult; -import android.os.Bundle; import android.os.Message; -import android.os.Parcel; -import android.os.Parcelable; import android.os.SystemProperties; import android.text.TextUtils; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; /** * {@hide} @@ -497,8 +492,7 @@ public abstract class DataConnection extends StateMachine { } else { if (DBG) log("onSetupConnectionCompleted received DataCallState: " + response); cid = response.cid; - // set link properties based on data call response - result = setLinkProperties(response, mLinkProperties); + result = updateLinkProperty(response).setupResult; } return result; @@ -527,48 +521,41 @@ public abstract class DataConnection extends StateMachine { return response.setLinkProperties(lp, okToUseSystemPropertyDns); } - private DataConnectionAc.LinkPropertyChangeAction updateLinkProperty( - DataCallState newState) { - DataConnectionAc.LinkPropertyChangeAction changed = - DataConnectionAc.LinkPropertyChangeAction.NONE; + public static class UpdateLinkPropertyResult { + public DataCallState.SetupResult setupResult = DataCallState.SetupResult.SUCCESS; + public LinkProperties oldLp; + public LinkProperties newLp; + public UpdateLinkPropertyResult(LinkProperties curLp) { + oldLp = curLp; + newLp = curLp; + } + } - if (newState == null) return changed; + private UpdateLinkPropertyResult updateLinkProperty(DataCallState newState) { + UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties); - DataCallState.SetupResult result; - LinkProperties newLp = new LinkProperties(); + if (newState == null) return result; + + DataCallState.SetupResult setupResult; + result.newLp = new LinkProperties(); // set link properties based on data call response - result = setLinkProperties(newState, newLp); - if (result != DataCallState.SetupResult.SUCCESS) { - if (DBG) log("UpdateLinkProperty failed : " + result); - return changed; + result.setupResult = setLinkProperties(newState, result.newLp); + if (result.setupResult != DataCallState.SetupResult.SUCCESS) { + if (DBG) log("updateLinkProperty failed : " + result.setupResult); + return result; } // copy HTTP proxy as it is not part DataCallState. - newLp.setHttpProxy(mLinkProperties.getHttpProxy()); - - if (DBG) log("old LP=" + mLinkProperties); - if (DBG) log("new LP=" + newLp); - - // Check consistency of link address. Currently we expect - // only one "global" address is assigned per each IP type. - Collection<LinkAddress> oLinks = mLinkProperties.getLinkAddresses(); - Collection<LinkAddress> nLinks = newLp.getLinkAddresses(); - for (LinkAddress oldLink : oLinks) { - for (LinkAddress newLink : nLinks) { - if ((NetworkUtils.addressTypeMatches(oldLink.getAddress(), - newLink.getAddress())) && - (oldLink.equals(newLink) == false)) { - return DataConnectionAc.LinkPropertyChangeAction.RESET; - } - } - } + result.newLp.setHttpProxy(mLinkProperties.getHttpProxy()); - if (mLinkProperties == null || !mLinkProperties.equals(newLp)) { - mLinkProperties = newLp; - changed = DataConnectionAc.LinkPropertyChangeAction.CHANGED; + if (DBG && (! result.oldLp.equals(result.newLp))) { + if (DBG) log("updateLinkProperty old != new"); + if (VDBG) log("updateLinkProperty old LP=" + result.oldLp); + if (VDBG) log("updateLinkProperty new LP=" + result.newLp); } + mLinkProperties = result.newLp; - return changed; + return result; } /** @@ -643,14 +630,15 @@ public abstract class DataConnection extends StateMachine { } case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: { DataCallState newState = (DataCallState) msg.obj; - DataConnectionAc.LinkPropertyChangeAction action = updateLinkProperty(newState); + UpdateLinkPropertyResult result = + updateLinkProperty(newState); if (VDBG) { - log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE action=" - + action + " newState=" + newState); + log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE result=" + + result + " newState=" + newState); } mAc.replyToMessage(msg, DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, - action.ordinal()); + result); break; } case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: { @@ -688,7 +676,7 @@ public abstract class DataConnection extends StateMachine { case DataConnectionAc.REQ_GET_APNCONTEXT_LIST: { if (VDBG) log("REQ_GET_APNCONTEXT_LIST num in list=" + mApnList.size()); mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNCONTEXT_LIST, - new ArrayList(mApnList)); + new ArrayList<ApnContext>(mApnList)); break; } case DataConnectionAc.REQ_SET_RECONNECT_INTENT: { diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java index 309dbed..9e185e5 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java @@ -16,12 +16,14 @@ package com.android.internal.telephony; +import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; import android.app.PendingIntent; import android.net.LinkCapabilities; import android.net.LinkProperties; +import android.net.LinkProperties.CompareAddressesResult; import android.net.ProxyProperties; import android.os.Message; @@ -310,18 +312,18 @@ public class DataConnectionAc extends AsyncChannel { if (DBG) log("reqUpdateLinkPropertiesDataCallState"); } - public LinkPropertyChangeAction rspUpdateLinkPropertiesDataCallState(Message response) { - LinkPropertyChangeAction retVal = LinkPropertyChangeAction.fromInt(response.arg1); - if (DBG) log("rspUpdateLinkPropertiesState=" + retVal); + public UpdateLinkPropertyResult rspUpdateLinkPropertiesDataCallState(Message response) { + UpdateLinkPropertyResult retVal = (UpdateLinkPropertyResult)response.obj; + if (DBG) log("rspUpdateLinkPropertiesState: retVal=" + retVal); return retVal; } /** * Update link properties in the data connection * - * @return true if link property has been updated. false otherwise. + * @return the removed and added addresses. */ - public LinkPropertyChangeAction updateLinkPropertiesDataCallStateSync(DataCallState newState) { + public UpdateLinkPropertyResult updateLinkPropertiesDataCallStateSync(DataCallState newState) { Message response = sendMessageSynchronously(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState); if ((response != null) && @@ -329,7 +331,7 @@ public class DataConnectionAc extends AsyncChannel { return rspUpdateLinkPropertiesDataCallState(response); } else { log("getLinkProperties error response=" + response); - return LinkPropertyChangeAction.NONE; + return new UpdateLinkPropertyResult(new LinkProperties()); } } diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index 977b412..4f8b525 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -18,10 +18,12 @@ package com.android.internal.telephony; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.database.ContentObserver; import android.net.LinkCapabilities; import android.net.LinkProperties; import android.net.NetworkInfo; @@ -334,6 +336,31 @@ public abstract class DataConnectionTracker extends Handler { } }; + private final DataRoamingSettingObserver mDataRoamingSettingObserver; + + private class DataRoamingSettingObserver extends ContentObserver { + public DataRoamingSettingObserver(Handler handler) { + super(handler); + } + + public void register(Context context) { + final ContentResolver resolver = context.getContentResolver(); + resolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.DATA_ROAMING), false, this); + } + + public void unregister(Context context) { + final ContentResolver resolver = context.getContentResolver(); + resolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + // already running on mPhone handler thread + handleDataOnRoamingChange(); + } + } + protected boolean isDataSetupCompleteOk(AsyncResult ar) { if (ar.exception != null) { if (DBG) log("isDataSetupCompleteOk return false, ar.result=" + ar.result); @@ -398,6 +425,10 @@ public abstract class DataConnectionTracker extends Handler { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()); mAutoAttachOnCreation = sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false); + + // watch for changes to Settings.Secure.DATA_ROAMING + mDataRoamingSettingObserver = new DataRoamingSettingObserver(mPhone); + mDataRoamingSettingObserver.register(mPhone.getContext()); } public void dispose() { @@ -407,6 +438,7 @@ public abstract class DataConnectionTracker extends Handler { mDataConnectionAsyncChannels.clear(); mIsDisposed = true; mPhone.getContext().unregisterReceiver(this.mIntentReceiver); + mDataRoamingSettingObserver.unregister(mPhone.getContext()); } protected void broadcastMessenger() { @@ -461,32 +493,38 @@ public abstract class DataConnectionTracker extends Handler { return result; } - //The data roaming setting is now located in the shared preferences. - // See if the requested preference value is the same as that stored in - // the shared values. If it is not, then update it. + /** + * Modify {@link Settings.Secure#DATA_ROAMING} value. + */ public void setDataOnRoamingEnabled(boolean enabled) { if (getDataOnRoamingEnabled() != enabled) { - Settings.Secure.putInt(mPhone.getContext().getContentResolver(), - Settings.Secure.DATA_ROAMING, enabled ? 1 : 0); - if (mPhone.getServiceState().getRoaming()) { - if (enabled) { - resetAllRetryCounts(); - } - sendMessage(obtainMessage(EVENT_ROAMING_ON)); - } + final ContentResolver resolver = mPhone.getContext().getContentResolver(); + Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0); + // will trigger handleDataOnRoamingChange() through observer } } - // Retrieve the data roaming setting from the shared preferences. + /** + * Return current {@link Settings.Secure#DATA_ROAMING} value. + */ public boolean getDataOnRoamingEnabled() { try { - return Settings.Secure.getInt( - mPhone.getContext().getContentResolver(), Settings.Secure.DATA_ROAMING) > 0; + final ContentResolver resolver = mPhone.getContext().getContentResolver(); + return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING) != 0; } catch (SettingNotFoundException snfe) { return false; } } + private void handleDataOnRoamingChange() { + if (mPhone.getServiceState().getRoaming()) { + if (getDataOnRoamingEnabled()) { + resetAllRetryCounts(); + } + sendMessage(obtainMessage(EVENT_ROAMING_ON)); + } + } + // abstract methods protected abstract String getActionIntentReconnectAlarm(); protected abstract void startNetStatPoll(); diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 48c5318..4b02e8e 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -1430,6 +1430,11 @@ public interface Phone { String getMeid(); /** + * Retrieves IMEI for phones. Returns null if IMEI is not set. + */ + String getImei(); + + /** * Retrieves the PhoneSubInfo of the Phone */ public PhoneSubInfo getPhoneSubInfo(); diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index c2212db..b5bfc76f 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -685,6 +685,10 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getMeid(); } + public String getImei() { + return mActivePhone.getImei(); + } + public PhoneSubInfo getPhoneSubInfo(){ return mActivePhone.getPhoneSubInfo(); } diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java index 4309309..60cf9b7 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java +++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java @@ -166,4 +166,11 @@ public interface TelephonyProperties * the value of config_sms_capable */ static final String PROPERTY_SMS_SEND = "telephony.sms.send"; + + /** + * Set to true to indicate a test CSIM card is used in the device. + * This property is for testing purpose only. This should not be defined + * in commercial configuration. + */ + static final String PROPERTY_TEST_CSIM = "persist.radio.test-csim"; } diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java index a31b704..0d9d27d 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java @@ -136,6 +136,11 @@ public class CDMALTEPhone extends CDMAPhone { } @Override + public String getImei() { + return mImei; + } + + @Override protected void log(String s) { if (DBG) Log.d(LOG_TAG, "[CDMALTEPhone] " + s); diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 8a60b5a..286515e 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -122,6 +122,8 @@ public class CDMAPhone extends PhoneBase { //keep track of if phone is in emergency callback mode private boolean mIsPhoneInEcmState; private Registrant mEcmExitRespRegistrant; + protected String mImei; + protected String mImeiSv; private String mEsn; private String mMeid; // string to define how the carrier specifies its own ota sp number @@ -489,6 +491,11 @@ public class CDMAPhone extends PhoneBase { return mSST.getImsi(); } + public String getImei() { + Log.e(LOG_TAG, "IMEI is not available in CDMA"); + return null; + } + public boolean canConference() { Log.e(LOG_TAG, "canConference: not possible in CDMA"); return false; @@ -987,6 +994,8 @@ public class CDMAPhone extends PhoneBase { break; } String[] respId = (String[])ar.result; + mImei = respId[0]; + mImeiSv = respId[1]; mEsn = respId[2]; mMeid = respId[3]; } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java index 459cf87..0d551aa 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java @@ -39,7 +39,6 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { CDMALTEPhone mCdmaLtePhone; private ServiceState mLteSS; // The last LTE state from Voice Registration - private String mCurrentSpn = null; public CdmaLteServiceStateTracker(CDMALTEPhone phone) { super(phone); @@ -345,6 +344,18 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { ss.setOperatorAlphaLong(eriText); } + if (cm.getSimState().isSIMReady()) { + // SIM is found on the device. If ERI roaming is OFF, use operator name + // from CSIM record. + boolean showSpn = + ((CdmaLteUiccRecords)phone.mIccRecords).getCsimSpnDisplayCondition(); + int iconIndex = ss.getCdmaEriIconIndex(); + + if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF)) { + ss.setOperatorAlphaLong(phone.mIccRecords.getServiceProviderName()); + } + } + String operatorNumeric; phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA, @@ -468,43 +479,6 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } @Override - protected void updateSpnDisplay() { - // mOperatorAlphaLong contains the ERI text - String plmn = ss.getOperatorAlphaLong(); - - boolean showSpn = false; - String spn = null; - if (cm.getSimState().isSIMReady()) { - // SIM is found on the device. Read the operator name from the card. - showSpn = ((CdmaLteUiccRecords)phone.mIccRecords).getCsimSpnDisplayCondition(); - spn = phone.mIccRecords.getServiceProviderName(); - - // double check we are not printing identicall test - if (TextUtils.equals(plmn, spn)) showSpn = false; - } - - if (!TextUtils.equals(plmn, mCurPlmn) || - !TextUtils.equals(spn, mCurrentSpn)) { - boolean showPlmn = plmn != null; - if (DBG) { - log(String.format("updateSpnDisplay: changed sending intent" + - " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s'", - showPlmn, plmn, showSpn, spn)); - } - Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn); - intent.putExtra(Intents.EXTRA_SPN, spn); - intent.putExtra(Intents.EXTRA_SHOW_PLMN, showPlmn); - intent.putExtra(Intents.EXTRA_PLMN, plmn); - phone.getContext().sendStickyBroadcast(intent); - } - - mCurPlmn = plmn; - mCurrentSpn = spn; - } - - @Override protected void log(String s) { Log.d(LOG_TAG, "[CdmaLteSST] " + s); } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java index 10515f7..fc6abad 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java @@ -16,6 +16,7 @@ package com.android.internal.telephony.cdma; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.IccCardApplication.AppType; import com.android.internal.telephony.IccFileHandler; @@ -447,6 +448,12 @@ public final class CdmaLteUiccRecords extends SIMRecords { // to determine if the SIM is provisioned. Otherwise, // consider the SIM is provisioned. (for case of ordinal // USIM only UICC.) + // If PROPERTY_TEST_CSIM is defined, bypess provision check + // and consider the SIM is provisioned. + if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) { + return true; + } + if (phone.mIccCard.isApplicationOnIcc(AppType.APPTYPE_CSIM) && ((mMdn == null) || (mMin == null))) { return false; diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index d357eac..1db9860 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -855,6 +855,10 @@ public class GSMPhone extends PhoneBase { return mImeiSv; } + public String getImei() { + return mImei; + } + public String getEsn() { Log.e(LOG_TAG, "[GSMPhone] getEsn() is a CDMA method"); return "0"; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java index 9695344..1f24b58 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java @@ -76,8 +76,6 @@ public class GsmDataConnection extends DataConnection { + "' APN: '" + mApn.apn + "' proxy: '" + mApn.proxy + "' port: '" + mApn.port); - setHttpProxy (mApn.proxy, mApn.port); - createTime = -1; lastFailTime = -1; lastFailCause = FailCause.NONE; @@ -152,38 +150,6 @@ public class GsmDataConnection extends DataConnection { Log.d(LOG_TAG, "[" + getName() + "] " + s); } - private void setHttpProxy(String httpProxy, String httpPort) { - - if (DBG) log("set http proxy for" - + "' APN: '" + mActiveApnType - + "' proxy: '" + mApn.proxy + "' port: '" + mApn.port); - if(TextUtils.equals(mActiveApnType, Phone.APN_TYPE_DEFAULT)) { - if (httpProxy == null || httpProxy.length() == 0) { - phone.setSystemProperty("net.gprs.http-proxy", null); - return; - } - - if (httpPort == null || httpPort.length() == 0) { - httpPort = "8080"; // Default to port 8080 - } - - phone.setSystemProperty("net.gprs.http-proxy", - "http://" + httpProxy + ":" + httpPort + "/"); - } else { - if (httpProxy == null || httpProxy.length() == 0) { - phone.setSystemProperty("net.gprs.http-proxy." + mActiveApnType, null); - return; - } - - if (httpPort == null || httpPort.length() == 0) { - httpPort = "8080"; // Default to port 8080 - } - - phone.setSystemProperty("net.gprs.http-proxy." + mActiveApnType, - "http://" + httpProxy + ":" + httpPort + "/"); - } - } - private boolean isIpAddress(String address) { if (address == null) return false; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index fe57d0d..bf964b7 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -26,6 +26,9 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.database.Cursor; import android.net.ConnectivityManager; +import android.net.LinkAddress; +import android.net.LinkProperties.CompareAddressesResult; +import android.net.NetworkUtils; import android.net.ProxyProperties; import android.net.TrafficStats; import android.net.Uri; @@ -53,6 +56,7 @@ import com.android.internal.telephony.ApnContext; import com.android.internal.telephony.ApnSetting; import com.android.internal.telephony.DataCallState; import com.android.internal.telephony.DataConnection; +import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult; import com.android.internal.telephony.DataConnectionAc; import com.android.internal.telephony.DataConnectionTracker; import com.android.internal.telephony.Phone; @@ -1037,7 +1041,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { /** * @param dcacs Collection of DataConnectionAc reported from RIL. - * @return List of ApnContext whihc is connected, but does not present in + * @return List of ApnContext which is connected, but is not present in * data connection list reported from RIL. */ private List<ApnContext> findApnContextToClean(Collection<DataConnectionAc> dcacs) { @@ -1055,10 +1059,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } } if (!found) { - // ApnContext does not have dcan reorted in data call list. + // ApnContext does not have dcac reported in data call list. + // Fetch all the ApnContexts that map to this dcac which are in + // INITING state too. if (DBG) log("onDataStateChanged(ar): Connected apn not found in the list (" + apnContext.toString() + ")"); - list.add(apnContext); + if (apnContext.getDataConnectionAc() != null) { + list.addAll(apnContext.getDataConnectionAc().getApnListSync()); + } else { + list.add(apnContext); + } } } } @@ -1085,99 +1095,118 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size()); // Create a hash map to store the dataCallState of each DataConnectionAc - // TODO: Depends on how frequent the DATA_CALL_LIST got updated, - // may cache response to reduce comparison. - HashMap<DataCallState, DataConnectionAc> response; - response = new HashMap<DataCallState, DataConnectionAc>(); + HashMap<DataCallState, DataConnectionAc> dataCallStateToDcac; + dataCallStateToDcac = new HashMap<DataCallState, DataConnectionAc>(); for (DataCallState dataCallState : dataCallStates) { DataConnectionAc dcac = findDataConnectionAcByCid(dataCallState.cid); - if (dcac != null) response.put(dataCallState, dcac); + if (dcac != null) dataCallStateToDcac.put(dataCallState, dcac); } - // step1: Find a list of "connected" APN which does not have reference to - // calls listed in the Data Call List. - List<ApnContext> apnsToClear = findApnContextToClean(response.values()); + // A list of apns to cleanup, those that aren't in the list we know we have to cleanup + List<ApnContext> apnsToCleanup = findApnContextToClean(dataCallStateToDcac.values()); - // step2: Check status of each calls in Data Call List. - // Collect list of ApnContext associated with the data call if the link - // has to be cleared. + // Find which connections have changed state and send a notification or cleanup for (DataCallState newState : dataCallStates) { - DataConnectionAc dcac = response.get(newState); + DataConnectionAc dcac = dataCallStateToDcac.get(newState); - // no associated DataConnection found. Ignore. - if (dcac == null) continue; + if (dcac == null) { + loge("onDataStateChanged(ar): No associated DataConnection ignore"); + continue; + } + // The list of apn's associated with this DataConnection Collection<ApnContext> apns = dcac.getApnListSync(); - // filter out ApnContext with "Connected" state. + // Find which ApnContexts of this DC are in the "Connected/Connecting" state. ArrayList<ApnContext> connectedApns = new ArrayList<ApnContext>(); for (ApnContext apnContext : apns) { - if (apnContext.getState() == State.CONNECTED) { + if (apnContext.getState() == State.CONNECTED || + apnContext.getState() == State.CONNECTING || + apnContext.getState() == State.INITING) { connectedApns.add(apnContext); } } - - // No "Connected" ApnContext associated with this CID. Ignore. - if (connectedApns.isEmpty()) { - continue; - } - - if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid - + " newState=" + newState.toString()); - if (newState.active != 0) { - boolean resetConnection; - switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) { - case NONE: - if (DBG) log("onDataStateChanged(ar): Found but no change, skip"); - resetConnection = false; - break; - case CHANGED: - for (ApnContext apnContext : connectedApns) { - if (DBG) log("onDataStateChanged(ar): Found and changed, notify (" + - apnContext.toString() + ")"); - mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED, - apnContext.getApnType()); + if (connectedApns.size() == 0) { + if (DBG) log("onDataStateChanged(ar): no connected apns"); + } else { + // Determine if the connection/apnContext should be cleaned up + // or just a notification should be sent out. + if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid + + " newState=" + newState.toString()); + if (newState.active == 0) { + if (DBG) { + log("onDataStateChanged(ar): inactive, cleanup apns=" + connectedApns); } - // Temporary hack, at this time a transition from CDMA -> Global - // fails so we'll hope for the best and not reset the connection. - // @see bug/4455071 - if (SystemProperties.getBoolean("telephony.ignore-state-changes", - true)) { - log("onDataStateChanged(ar): STOPSHIP don't reset, continue"); - resetConnection = false; + apnsToCleanup.addAll(connectedApns); + } else { + // Its active so update the DataConnections link properties + UpdateLinkPropertyResult result = + dcac.updateLinkPropertiesDataCallStateSync(newState); + if (result.oldLp.equals(result.newLp)) { + if (DBG) log("onDataStateChanged(ar): no change"); } else { - // Things changed so reset connection, when hack is removed - // this is the normal path. - log("onDataStateChanged(ar): changed so resetting connection"); - resetConnection = true; + if (result.oldLp.isIdenticalInterfaceName(result.newLp)) { + if (! result.oldLp.isIdenticalDnses(result.newLp) || + ! result.oldLp.isIdenticalRoutes(result.newLp) || + ! result.oldLp.isIdenticalHttpProxy(result.newLp) || + ! result.oldLp.isIdenticalAddresses(result.newLp)) { + // If the same address type was removed and added we need to cleanup + CompareAddressesResult car = + result.oldLp.compareAddresses(result.newLp); + boolean needToClean = false; + for (LinkAddress added : car.added) { + for (LinkAddress removed : car.removed) { + if (NetworkUtils.addressTypeMatches(removed.getAddress(), + added.getAddress())) { + needToClean = true; + break; + } + } + } + if (needToClean) { + if (DBG) { + log("onDataStateChanged(ar): addr change, cleanup apns=" + + connectedApns); + } + apnsToCleanup.addAll(connectedApns); + } else { + if (DBG) log("onDataStateChanged(ar): simple change"); + for (ApnContext apnContext : connectedApns) { + mPhone.notifyDataConnection( + Phone.REASON_LINK_PROPERTIES_CHANGED, + apnContext.getApnType()); + } + } + } else { + if (DBG) { + log("onDataStateChanged(ar): no changes"); + } + } + } else { + if (DBG) { + log("onDataStateChanged(ar): interface change, cleanup apns=" + + connectedApns); + } + apnsToCleanup.addAll(connectedApns); + } } - break; - case RESET: - default: - if (DBG) log("onDataStateChanged(ar): an error, reset connection"); - resetConnection = true; - break; } - if (resetConnection == false) continue; } - - if (DBG) log("onDataStateChanged(ar): reset connection."); - - apnsToClear.addAll(connectedApns); } - // step3: Clear apn connection if applicable. - if (!apnsToClear.isEmpty()) { + if (apnsToCleanup.size() != 0) { // Add an event log when the network drops PDP int cid = getCellLocationId(); EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid, TelephonyManager.getDefault().getNetworkType()); } - for (ApnContext apnContext : apnsToClear) { + // Cleanup those dropped connections + for (ApnContext apnContext : apnsToCleanup) { cleanUpConnection(true, apnContext); } + if (DBG) log("onDataStateChanged(ar): X"); } diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java index 9dfc015..5c4b446 100755 --- a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java +++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java @@ -264,6 +264,10 @@ abstract class SipPhoneBase extends PhoneBase { return null; } + public String getImei() { + return null; + } + public String getEsn() { Log.e(LOG_TAG, "[SipPhone] getEsn() is a CDMA method"); return "0"; diff --git a/tests/BiDiTests/res/layout/textview_drawables_ltr.xml b/tests/BiDiTests/res/layout/textview_drawables_ltr.xml new file mode 100644 index 0000000..88b13b7 --- /dev/null +++ b/tests/BiDiTests/res/layout/textview_drawables_ltr.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/textview_drawables_ltr" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layoutDirection="ltr"> + + <LinearLayout android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableLeft="@drawable/start" + android:drawableRight="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableStart="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableLeft="@drawable/end" + android:drawableStart="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableRight="@drawable/start" + android:drawableStart="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableLeft="@drawable/end" + android:drawableRight="@drawable/start" + android:drawableStart="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableRight="@drawable/end" + android:drawableStart="@drawable/start" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableLeft="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:id="@+id/textview_error" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawablePadding="3dip" + /> + + </LinearLayout> + +</FrameLayout> diff --git a/tests/BiDiTests/res/layout/textview_drawables_rtl.xml b/tests/BiDiTests/res/layout/textview_drawables_rtl.xml new file mode 100644 index 0000000..7f47d5d --- /dev/null +++ b/tests/BiDiTests/res/layout/textview_drawables_rtl.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/textview_drawables_rtl" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layoutDirection="rtl"> + + <LinearLayout android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableLeft="@drawable/end" + android:drawableRight="@drawable/start" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableStart="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableLeft="@drawable/start" + android:drawableStart="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableRight="@drawable/end" + android:drawableStart="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableLeft="@drawable/start" + android:drawableRight="@drawable/end" + android:drawableStart="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableLeft="@drawable/end" + android:drawableStart="@drawable/start" + android:drawablePadding="3dip" + /> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawableRight="@drawable/start" + android:drawableEnd="@drawable/end" + android:drawablePadding="3dip" + /> + + <TextView android:id="@+id/textview_error" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:drawablePadding="3dip" + /> + + </LinearLayout> + +</FrameLayout> diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java index c033879..7002c41 100644 --- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java @@ -134,6 +134,9 @@ public class BiDiTestActivity extends Activity { addItem(result, "TextDirection LTR", BiDiTestTextViewDirectionLtr.class, R.id.textview_direction_ltr); addItem(result, "TextDirection RTL", BiDiTestTextViewDirectionRtl.class, R.id.textview_direction_rtl); + addItem(result, "TextView Drawables LTR", BiDiTestTextViewDrawablesLtr.class, R.id.textview_drawables_ltr); + addItem(result, "TextView Drawables RTL", BiDiTestTextViewDrawablesRtl.class, R.id.textview_drawables_rtl); + return result; } diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDrawablesLtr.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDrawablesLtr.java new file mode 100644 index 0000000..a65d92d --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDrawablesLtr.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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.bidi; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class BiDiTestTextViewDrawablesLtr extends Fragment { + + private View currentView; + private TextView textViewError; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + currentView = inflater.inflate(R.layout.textview_drawables_ltr, container, false); + return currentView; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + textViewError = (TextView) currentView.findViewById(R.id.textview_error); + textViewError.setError("Error!!"); + } +} diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDrawablesRtl.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDrawablesRtl.java new file mode 100644 index 0000000..7b7e812 --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDrawablesRtl.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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.bidi; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class BiDiTestTextViewDrawablesRtl extends Fragment { + + private View currentView; + private TextView textViewError; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + currentView = inflater.inflate(R.layout.textview_drawables_rtl, container, false); + return currentView; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + textViewError = (TextView) currentView.findViewById(R.id.textview_error); + textViewError.setError("Error!!"); + } +} diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java index b9bf526..af5006f 100644 --- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java @@ -38,20 +38,20 @@ public class Activity2 extends Activity { vg.setUseDefaultMargins(true); vg.setAlignmentMode(ALIGN_BOUNDS); - Group row1 = new Group(1, CENTER); - Group row2 = new Group(2, CENTER); - Group row3 = new Group(3, BASELINE); - Group row4 = new Group(4, BASELINE); - Group row5 = new Group(5, FILL); - Group row6 = new Group(6, CENTER); - Group row7 = new Group(7, CENTER); + Spec row1 = spec(0, CENTER); + Spec row2 = spec(1, CENTER); + Spec row3 = spec(2, BASELINE); + Spec row4 = spec(3, BASELINE); + Spec row5 = spec(4, FILL, CAN_STRETCH); + Spec row6 = spec(5, CENTER); + Spec row7 = spec(6, CENTER); - Group col1a = new Group(1, 4, CENTER); - Group col1b = new Group(1, 4, LEFT); - Group col1c = new Group(1, RIGHT); - Group col2 = new Group(2, LEFT); - Group col3 = new Group(3, FILL); - Group col4 = new Group(4, FILL); + Spec col1a = spec(0, 4, CENTER); + Spec col1b = spec(0, 4, LEFT); + Spec col1c = spec(0, RIGHT); + Spec col2 = spec(1, LEFT); + Spec col3 = spec(2, FILL, CAN_STRETCH); + Spec col4 = spec(3, FILL); { TextView v = new TextView(context); @@ -96,10 +96,7 @@ public class Activity2 extends Activity { { Space v = new Space(context); { - LayoutParams lp = new LayoutParams(row5, col3); - lp.columnGroup.flexibility = CAN_STRETCH; - lp.rowGroup.flexibility = CAN_STRETCH; - vg.addView(v, lp); + vg.addView(v, new LayoutParams(row5, col3)); } } { diff --git a/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java b/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java index 505c83d..b1c4486 100755 --- a/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java +++ b/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java @@ -21,7 +21,6 @@ import android.content.Context; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; import android.widget.Button; import android.widget.EditText; import android.widget.GridLayout; @@ -84,9 +83,7 @@ public class AlignmentTest extends Activity { Alignment va = VERTICAL_ALIGNMENTS[i]; for (int j = 0; j < HORIZONTAL_ALIGNMENTS.length; j++) { Alignment ha = HORIZONTAL_ALIGNMENTS[j]; - Group rowGroup = new Group(i, va); - Group colGroup = new Group(j, ha); - LayoutParams layoutParams = new LayoutParams(rowGroup, colGroup); + LayoutParams layoutParams = new LayoutParams(spec(i, va), spec(j, ha)); String name = VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j]; ViewFactory factory = FACTORIES[(i + j) % FACTORIES.length]; container.addView(factory.create(name, 20), layoutParams); diff --git a/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java b/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java index c5681e2..4ce449a 100644 --- a/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java +++ b/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java @@ -33,9 +33,9 @@ public class GridLayoutTest extends AbstractLayoutTest { int va = VERTICAL_ALIGNMENTS[i]; for (int j = 0; j < HORIZONTAL_ALIGNMENTS.length; j++) { int ha = HORIZONTAL_ALIGNMENTS[j]; - GridLayout.Group rowGroup = new GridLayout.Group(UNDEFINED, null); - GridLayout.Group colGroup = new GridLayout.Group(UNDEFINED, null); - GridLayout.LayoutParams lp = new GridLayout.LayoutParams(rowGroup, colGroup); + Spec rowSpec = spec(UNDEFINED, null); + Spec colSpec = spec(UNDEFINED, null); + GridLayout.LayoutParams lp = new GridLayout.LayoutParams(rowSpec, colSpec); //GridLayout.LayoutParams lp = new GridLayout.LayoutParams(); lp.setGravity(va | ha); View v = create(context, VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20); diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index cb8084d..32a6a65 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -382,6 +382,15 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + + <activity + android:name="MoreNinePatchesActivity" + android:label="_9patch2"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> <activity android:name="QuickRejectActivity" diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_bg_holo_dark.9.png b/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_bg_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..089704e --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_bg_holo_dark.9.png diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_primary_holo_dark.9.png b/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_primary_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..385dbc4 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_primary_holo_dark.9.png diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_secondary_holo_dark.9.png b/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_secondary_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..f1510b2 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_secondary_holo_dark.9.png diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_primary_holo.9.png b/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_primary_holo.9.png Binary files differnew file mode 100644 index 0000000..4208c6f --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_primary_holo.9.png diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_secondary_holo.9.png b/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_secondary_holo.9.png Binary files differnew file mode 100644 index 0000000..b25fb2f --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_secondary_holo.9.png diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_dark.9.png b/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..25129c6 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_dark.9.png diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_light.9.png b/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_light.9.png Binary files differnew file mode 100644 index 0000000..1505e0e --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_light.9.png diff --git a/tests/HwAccelerationTest/res/drawable/progress_vertical_holo_dark.xml b/tests/HwAccelerationTest/res/drawable/progress_vertical_holo_dark.xml new file mode 100644 index 0000000..9eb54b7 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable/progress_vertical_holo_dark.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:id="@android:id/background" + android:drawable="@drawable/progress_vertical_bg_holo_dark" /> + + <item android:id="@android:id/secondaryProgress"> + <scale android:scaleHeight="100%" android:scaleGravity="bottom" + android:drawable="@drawable/progress_vertical_secondary_holo_dark" /> + </item> + + <item android:id="@android:id/progress"> + <scale android:scaleHeight="100%" android:scaleGravity="bottom" + android:drawable="@drawable/progress_vertical_primary_holo_dark" /> + </item> + +</layer-list> diff --git a/tests/HwAccelerationTest/res/drawable/scrubber_progress_vertical_holo_dark.xml b/tests/HwAccelerationTest/res/drawable/scrubber_progress_vertical_holo_dark.xml new file mode 100644 index 0000000..0cc56bf --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable/scrubber_progress_vertical_holo_dark.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@android:id/background" + android:drawable="@drawable/scrubber_vertical_track_holo_dark" /> + <item android:id="@android:id/secondaryProgress"> + <scale android:scaleHeight="100%" android:scaleGravity="bottom" + android:drawable="@drawable/scrubber_vertical_secondary_holo" /> + </item> + <item android:id="@android:id/progress"> + <scale android:scaleHeight="100%" android:scaleGravity="bottom" + android:drawable="@drawable/scrubber_vertical_primary_holo" /> + </item> +</layer-list> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java index e1ca756..949589f 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java @@ -39,7 +39,10 @@ import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import javax.microedition.khronos.opengles.GL; - +import java.io.BufferedOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; @@ -57,6 +60,25 @@ public class GLTextureViewActivity extends Activity implements TextureView.Surfa mTextureView = new TextureView(this); mTextureView.setSurfaceTextureListener(this); + mTextureView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Bitmap b = mTextureView.getBitmap(800, 800); + BufferedOutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream("/sdcard/out.png")); + b.compress(Bitmap.CompressFormat.PNG, 100, out); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } finally { + if (out != null) try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + }); setContentView(mTextureView, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, @@ -77,7 +99,7 @@ public class GLTextureViewActivity extends Activity implements TextureView.Surfa animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - ((View) mTextureView.getParent()).invalidate(); + mTextureView.invalidate(); } }); animator.start(); diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MoreNinePatchesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MoreNinePatchesActivity.java new file mode 100644 index 0000000..0c42387 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MoreNinePatchesActivity.java @@ -0,0 +1,77 @@ +/* + * 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 com.android.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; + +@SuppressWarnings({"UnusedDeclaration"}) +public class MoreNinePatchesActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FrameLayout layout = new FrameLayout(this); + PatchView b = new PatchView (this); + b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, Gravity.CENTER)); + layout.addView(b); + layout.setBackgroundColor(0xffffffff); + + setContentView(layout); + } + + private class PatchView extends View { + private final Drawable mDrawable1; + private final Drawable mDrawable2; + private final Drawable mDrawable3; + + private PatchView(Context context) { + super(context); + Resources res = context.getResources(); + mDrawable1 = res.getDrawable(R.drawable.progress_vertical_holo_dark); + mDrawable2 = res.getDrawable(R.drawable.scrubber_progress_vertical_holo_dark); + mDrawable3 = res.getDrawable(R.drawable.scrubber_vertical_primary_holo); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.translate(100, 100); + mDrawable1.setBounds(0, 0, 33, 120); + mDrawable1.setLevel(5000); + mDrawable1.draw(canvas); + + canvas.translate(20, 0); + mDrawable2.setBounds(0, 0, 33, 120); + mDrawable2.setLevel(5000); + mDrawable2.draw(canvas); + + canvas.translate(20, 0); + mDrawable3.setBounds(0, 0, 33, 120); + mDrawable3.draw(canvas); + } + } +} diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java index c857ded..634e7e3 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java @@ -16,7 +16,6 @@ package com.android.test.hwui; -import android.animation.AnimatorSet; import android.app.Activity; import android.graphics.SurfaceTexture; import android.hardware.Camera; @@ -34,7 +33,6 @@ public class TextureViewActivity extends Activity implements TextureView.Surface private Camera mCamera; private TextureView mTextureView; private FrameLayout mContent; - private AnimatorSet mAnimatorSet; @Override protected void onCreate(Bundle savedInstanceState) { @@ -53,7 +51,6 @@ public class TextureViewActivity extends Activity implements TextureView.Surface @Override public void onClick(View v) { if (mAdded) { - if (mAnimatorSet != null) mAnimatorSet.cancel(); mContent.removeView(mTextureView); } else { mContent.addView(mTextureView); @@ -62,7 +59,9 @@ public class TextureViewActivity extends Activity implements TextureView.Surface } }); - mContent.addView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER)); + mContent.addView(mTextureView, new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, + Gravity.CENTER)); mContent.addView(button, new FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM)); @@ -72,6 +71,9 @@ public class TextureViewActivity extends Activity implements TextureView.Surface @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mCamera = Camera.open(); + Camera.Size previewSize = mCamera.getParameters().getPreviewSize(); + mTextureView.setLayoutParams(new FrameLayout.LayoutParams( + previewSize.width, previewSize.height, Gravity.CENTER)); try { mCamera.setPreviewTexture(surface); @@ -82,20 +84,6 @@ public class TextureViewActivity extends Activity implements TextureView.Surface mCamera.startPreview(); mTextureView.setCameraDistance(5000); - -// ObjectAnimator rotationY = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f); -// rotationY.setRepeatMode(ObjectAnimator.REVERSE); -// rotationY.setRepeatCount(ObjectAnimator.INFINITE); -// rotationY.setDuration(4000); - -// ObjectAnimator alpha = ObjectAnimator.ofFloat(mTextureView, "alpha", 1.0f, 0.0f); -// alpha.setRepeatMode(ObjectAnimator.REVERSE); -// alpha.setRepeatCount(ObjectAnimator.INFINITE); -// alpha.setDuration(4000); - -// mAnimatorSet = new AnimatorSet(); -// mAnimatorSet.play(alpha).with(rotationY); -// mAnimatorSet.start(); } @Override diff --git a/tests/RenderScriptTests/FBOTest/Android.mk b/tests/RenderScriptTests/FBOTest/Android.mk index 55525c4..1df7b26 100644 --- a/tests/RenderScriptTests/FBOTest/Android.mk +++ b/tests/RenderScriptTests/FBOTest/Android.mk @@ -14,8 +14,6 @@ # limitations under the License. # -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -26,5 +24,3 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-fil LOCAL_PACKAGE_NAME := FBOTest include $(BUILD_PACKAGE) - -endif diff --git a/tests/RenderScriptTests/ImageProcessing/Android.mk b/tests/RenderScriptTests/ImageProcessing/Android.mk index 7fa30d0..507cc92 100644 --- a/tests/RenderScriptTests/ImageProcessing/Android.mk +++ b/tests/RenderScriptTests/ImageProcessing/Android.mk @@ -14,8 +14,6 @@ # limitations under the License. # -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -28,5 +26,3 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) \ LOCAL_PACKAGE_NAME := ImageProcessing include $(BUILD_PACKAGE) - -endif diff --git a/tests/RenderScriptTests/ModelViewer/Android.mk b/tests/RenderScriptTests/ModelViewer/Android.mk index efe77d7..1d9bacf 100644 --- a/tests/RenderScriptTests/ModelViewer/Android.mk +++ b/tests/RenderScriptTests/ModelViewer/Android.mk @@ -14,8 +14,6 @@ # limitations under the License. # -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -27,5 +25,3 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-fil LOCAL_PACKAGE_NAME := ModelViewer include $(BUILD_PACKAGE) - -endif diff --git a/tests/RenderScriptTests/PerfTest/Android.mk b/tests/RenderScriptTests/PerfTest/Android.mk index 757b3b1..0d1e7d2 100644 --- a/tests/RenderScriptTests/PerfTest/Android.mk +++ b/tests/RenderScriptTests/PerfTest/Android.mk @@ -14,8 +14,6 @@ # limitations under the License. # -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -29,5 +27,3 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-fil LOCAL_PACKAGE_NAME := PerfTest include $(BUILD_PACKAGE) - -endif diff --git a/tests/RenderScriptTests/tests/Android.mk b/tests/RenderScriptTests/tests/Android.mk index 6c992d5..880b80f 100644 --- a/tests/RenderScriptTests/tests/Android.mk +++ b/tests/RenderScriptTests/tests/Android.mk @@ -14,8 +14,6 @@ # limitations under the License. # -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -26,5 +24,3 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-fil LOCAL_PACKAGE_NAME := RSTest include $(BUILD_PACKAGE) - -endif diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.png Binary files differnew file mode 100644 index 0000000..ac5a97b --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.png diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.png Binary files differnew file mode 100644 index 0000000..a90dc9b --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.png diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.png Binary files differnew file mode 100644 index 0000000..cb3c433 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png Binary files differindex 4bcd2be..5ab09f0 100644 --- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png Binary files differindex cfeba3e..62ca427 100644 --- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png Binary files differindex 1d97e05..ff698fb 100644 --- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.png Binary files differnew file mode 100644 index 0000000..4cb305d --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.png diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.png Binary files differnew file mode 100644 index 0000000..31d35c8 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.png diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.png Binary files differnew file mode 100644 index 0000000..f0cc341 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.png diff --git a/tools/layoutlib/bridge/src/android/view/Display_Delegate.java b/tools/layoutlib/bridge/src/android/view/Display_Delegate.java new file mode 100644 index 0000000..83f9cc2 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/view/Display_Delegate.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2011 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.view; + +import com.android.layoutlib.bridge.android.BridgeWindowManager; +import com.android.layoutlib.bridge.impl.RenderAction; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import android.os.RemoteException; + +/** + * Delegate used to provide new implementation of a select few methods of {@link Display} + * + * Through the layoutlib_create tool, the original methods of Display have been replaced + * by calls to methods of the same name in this delegate class. + * + */ +public class Display_Delegate { + + // ---- Overridden methods ---- + + @LayoutlibDelegate + public static IWindowManager getWindowManager() { + return RenderAction.getCurrentContext().getIWindowManager(); + } + + // ---- Native methods ---- + + @LayoutlibDelegate + /*package*/ static int getDisplayCount() { + return 1; + } + + @LayoutlibDelegate + /** @hide Returns the actual screen size, not including any decor. */ + /*package*/ static int getRealWidth(Display theDisplay) { + // always dynamically query for the current window manager + return RenderAction.getCurrentContext().getIWindowManager().getMetrics().widthPixels; + } + + @LayoutlibDelegate + /** @hide Returns the actual screen size, not including any decor. */ + /*package*/ static int getRealHeight(Display theDisplay) { + // always dynamically query for the current window manager + return RenderAction.getCurrentContext().getIWindowManager().getMetrics().heightPixels; + } + + @LayoutlibDelegate + /** @hide special for when we are faking the screen size. */ + /*package*/ static int getRawWidth(Display theDisplay) { + // same as real since we're not faking compatibility mode. + return getRealWidth(theDisplay); + } + + @LayoutlibDelegate + /** @hide special for when we are faking the screen size. */ + /*package*/ static int getRawHeight(Display theDisplay) { + // same as real since we're not faking compatibility mode. + return getRealHeight(theDisplay); + } + + @LayoutlibDelegate + /*package*/ static int getOrientation(Display theDisplay) { + try { + // always dynamically query for the current window manager + return getWindowManager().getRotation(); + } catch (RemoteException e) { + // this will never been thrown since this is not a true RPC. + } + + return Surface.ROTATION_0; + } + + @LayoutlibDelegate + /*package*/ static void nativeClassInit() { + // not needed for now. + } + + @LayoutlibDelegate + /*package*/ static void init(Display theDisplay, int display) { + // always dynamically query for the current window manager + BridgeWindowManager wm = RenderAction.getCurrentContext().getIWindowManager(); + theDisplay.mDensity = wm.getMetrics().density; + theDisplay.mDpiX = wm.getMetrics().xdpi; + theDisplay.mDpiY = wm.getMetrics().ydpi; + } +} diff --git a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java index 251c053..1fd7836 100644 --- a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java +++ b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java @@ -16,8 +16,11 @@ package android.view.accessibility; +import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; import android.content.pm.ServiceInfo; +import android.view.IWindow; +import android.view.View; import java.util.Collections; import java.util.List; @@ -38,6 +41,19 @@ public final class AccessibilityManager { private static AccessibilityManager sInstance = new AccessibilityManager(); /** + * Listener for the accessibility state. + */ + public interface AccessibilityStateChangeListener { + + /** + * Called back on change in the accessibility state. + * + * @param enabled Whether accessibility is enabled. + */ + public void onAccessibilityStateChanged(boolean enabled); + } + + /** * Get an AccessibilityManager instance (create one if necessary). * * @hide @@ -92,4 +108,30 @@ public final class AccessibilityManager { List<ServiceInfo> services = null; return Collections.unmodifiableList(services); } + + public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { + // normal implementation does this in some case, so let's do the same + // (unmodifiableList wrapped around null). + List<AccessibilityServiceInfo> services = null; + return Collections.unmodifiableList(services); + } + + public boolean addAccessibilityStateChangeListener( + AccessibilityStateChangeListener listener) { + return true; + } + + public boolean removeAccessibilityStateChangeListener( + AccessibilityStateChangeListener listener) { + return true; + } + + public int addAccessibilityInteractionConnection(IWindow windowToken, + IAccessibilityInteractionConnection connection) { + return View.NO_ID; + } + + public void removeAccessibilityInteractionConnection(IWindow windowToken) { + } + } diff --git a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java new file mode 100644 index 0000000..ec7a67e --- /dev/null +++ b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011 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.view.inputmethod; + +import com.android.layoutlib.bridge.android.BridgeIInputMethodManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import android.os.Looper; + + +/** + * Delegate used to provide new implementation of a select few methods of {@link InputMethodManager} + * + * Through the layoutlib_create tool, the original methods of InputMethodManager have been replaced + * by calls to methods of the same name in this delegate class. + * + */ +public class InputMethodManager_Delegate { + + // ---- Overridden methods ---- + + @LayoutlibDelegate + /*package*/ static InputMethodManager getInstance(Looper mainLooper) { + synchronized (InputMethodManager.mInstanceSync) { + if (InputMethodManager.mInstance != null) { + return InputMethodManager.mInstance; + } + + InputMethodManager.mInstance = new InputMethodManager(new BridgeIInputMethodManager(), + mainLooper); + } + return InputMethodManager.mInstance; + + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 47fa68e..69e0de9 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -64,6 +64,7 @@ import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.LayoutInflater; +import android.view.Surface; import android.view.View; import android.view.ViewGroup; @@ -91,7 +92,11 @@ public final class BridgeContext extends Activity { private final Object mProjectKey; private final DisplayMetrics mMetrics; private final RenderResources mRenderResources; + private final Configuration mConfig; private final ApplicationInfo mApplicationInfo; + private final IProjectCallback mProjectCallback; + + private final BridgeWindowManager mIWindowManager; private final Map<Object, Map<String, String>> mDefaultPropMaps = new IdentityHashMap<Object, Map<String,String>>(); @@ -105,7 +110,6 @@ public final class BridgeContext extends Activity { private Map<int[], Map<Integer, TypedArray>> mTypedArrayCache; private BridgeInflater mBridgeInflater; - private final IProjectCallback mProjectCallback; private BridgeContentResolver mContentResolver; private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<BridgeXmlBlockParser>(); @@ -113,28 +117,25 @@ public final class BridgeContext extends Activity { /** * @param projectKey An Object identifying the project. This is used for the cache mechanism. * @param metrics the {@link DisplayMetrics}. - * @param themeName The name of the theme to use. - * @param projectResources the resources of the project. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the - * map contains (String, {@link }) pairs where the key is the resource name, - * and the value is the resource value. - * @param frameworkResources the framework resources. The map contains (String, map) pairs - * where the string is the type of the resource reference used in the layout file, and the map - * contains (String, {@link ResourceValue}) pairs where the key is the resource name, and the - * value is the resource value. - * @param styleInheritanceMap + * @param renderResources the configured resources (both framework and projects) for this + * render. * @param projectCallback + * @param config the Configuration object for this render. * @param targetSdkVersion the targetSdkVersion of the application. */ public BridgeContext(Object projectKey, DisplayMetrics metrics, RenderResources renderResources, IProjectCallback projectCallback, + Configuration config, int targetSdkVersion) { mProjectKey = projectKey; mMetrics = metrics; mProjectCallback = projectCallback; mRenderResources = renderResources; + mConfig = config; + + mIWindowManager = new BridgeWindowManager(mConfig, metrics, Surface.ROTATION_0); mFragments.mCurState = Fragment.CREATED; mFragments.mActivity = this; @@ -151,13 +152,12 @@ public final class BridgeContext extends Activity { */ public void initResources() { AssetManager assetManager = AssetManager.getSystem(); - Configuration config = new Configuration(); mSystemResources = BridgeResources.initSystem( this, assetManager, mMetrics, - config, + mConfig, mProjectCallback); mTheme = mSystemResources.newTheme(); } @@ -197,6 +197,10 @@ public final class BridgeContext extends Activity { return mRenderResources; } + public BridgeWindowManager getIWindowManager() { + return mIWindowManager; + } + public Map<String, String> getDefaultPropMap(Object key) { return mDefaultPropMaps.get(key); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java new file mode 100644 index 0000000..1394c32 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2011 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.layoutlib.bridge.android; + +import com.android.internal.view.IInputContext; +import com.android.internal.view.IInputMethodClient; +import com.android.internal.view.IInputMethodManager; +import com.android.internal.view.InputBindResult; + +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.text.style.SuggestionSpan; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodSubtype; + +import java.util.List; + +/** + * Basic implementation of IInputMethodManager that does nothing. + * + */ +public class BridgeIInputMethodManager implements IInputMethodManager { + + public void addClient(IInputMethodClient arg0, IInputContext arg1, int arg2, int arg3) + throws RemoteException { + // TODO Auto-generated method stub + + } + + public void finishInput(IInputMethodClient arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public InputMethodSubtype getCurrentInputMethodSubtype() throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public List<InputMethodInfo> getEnabledInputMethodList() throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo arg0, + boolean arg1) throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public List<InputMethodInfo> getInputMethodList() throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public InputMethodSubtype getLastInputMethodSubtype() throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public List getShortcutInputMethodsAndSubtypes() throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public void hideMySoftInput(IBinder arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public boolean hideSoftInput(IInputMethodClient arg0, int arg1, ResultReceiver arg2) + throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean notifySuggestionPicked(SuggestionSpan arg0, String arg1, int arg2) + throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public void registerSuggestionSpansForNotification(SuggestionSpan[] arg0) + throws RemoteException { + // TODO Auto-generated method stub + + } + + public void removeClient(IInputMethodClient arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public boolean setAdditionalInputMethodSubtypes(IBinder arg0, InputMethodSubtype[] arg1) + throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean setCurrentInputMethodSubtype(InputMethodSubtype arg0) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public void setImeWindowStatus(IBinder arg0, int arg1, int arg2) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setInputMethod(IBinder arg0, String arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setInputMethodAndSubtype(IBinder arg0, String arg1, InputMethodSubtype arg2) + throws RemoteException { + // TODO Auto-generated method stub + + } + + public boolean setInputMethodEnabled(String arg0, boolean arg1) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public void showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient arg0, String arg1) + throws RemoteException { + // TODO Auto-generated method stub + + } + + public void showInputMethodPickerFromClient(IInputMethodClient arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void showMySoftInput(IBinder arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public boolean showSoftInput(IInputMethodClient arg0, int arg1, ResultReceiver arg2) + throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public InputBindResult startInput(IInputMethodClient arg0, IInputContext arg1, EditorInfo arg2, + boolean arg3, boolean arg4) throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public boolean switchToLastInputMethod(IBinder arg0) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public void updateStatusIcon(IBinder arg0, String arg1, int arg2) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void windowGainedFocus(IInputMethodClient arg0, IBinder arg1, boolean arg2, + boolean arg3, int arg4, boolean arg5, int arg6) throws RemoteException { + // TODO Auto-generated method stub + + } + + public IBinder asBinder() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java index 260cdc8..fc2f2f8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java @@ -131,6 +131,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public CharSequence getText(int index) { + if (index < 0 || index >= mResourceData.length) { + return null; + } + if (mResourceData[index] != null) { // FIXME: handle styled strings! return mResourceData[index].getValue(); @@ -149,6 +153,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public String getString(int index) { + if (index < 0 || index >= mResourceData.length) { + return null; + } + if (mResourceData[index] != null) { return mResourceData[index].getValue(); } @@ -166,6 +174,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public boolean getBoolean(int index, boolean defValue) { + if (index < 0 || index >= mResourceData.length) { + return defValue; + } + if (mResourceData[index] == null) { return defValue; } @@ -188,6 +200,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public int getInt(int index, int defValue) { + if (index < 0 || index >= mResourceData.length) { + return defValue; + } + if (mResourceData[index] == null) { return defValue; } @@ -252,6 +268,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public float getFloat(int index, float defValue) { + if (index < 0 || index >= mResourceData.length) { + return defValue; + } + if (mResourceData[index] == null) { return defValue; } @@ -287,6 +307,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public int getColor(int index, int defValue) { + if (index < 0 || index >= mResourceData.length) { + return defValue; + } + if (mResourceData[index] == null) { return defValue; } @@ -311,6 +335,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public ColorStateList getColorStateList(int index) { + if (index < 0 || index >= mResourceData.length) { + return null; + } + if (mResourceData[index] == null) { return null; } @@ -395,6 +423,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public float getDimension(int index, float defValue) { + if (index < 0 || index >= mResourceData.length) { + return defValue; + } + if (mResourceData[index] == null) { return defValue; } @@ -568,6 +600,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public float getFraction(int index, int base, int pbase, float defValue) { + if (index < 0 || index >= mResourceData.length) { + return defValue; + } + if (mResourceData[index] == null) { return defValue; } @@ -607,6 +643,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public int getResourceId(int index, int defValue) { + if (index < 0 || index >= mResourceData.length) { + return defValue; + } + // get the Resource for this index ResourceValue resValue = mResourceData[index]; @@ -718,6 +758,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public Drawable getDrawable(int index) { + if (index < 0 || index >= mResourceData.length) { + return null; + } + if (mResourceData[index] == null) { return null; } @@ -744,6 +788,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public CharSequence[] getTextArray(int index) { + if (index < 0 || index >= mResourceData.length) { + return null; + } + if (mResourceData[index] == null) { return null; } @@ -776,6 +824,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public boolean getValue(int index, TypedValue outValue) { + if (index < 0 || index >= mResourceData.length) { + return false; + } + if (mResourceData[index] == null) { return false; } @@ -795,6 +847,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public boolean hasValue(int index) { + if (index < 0 || index >= mResourceData.length) { + return false; + } + return mResourceData[index] != null; } @@ -811,6 +867,10 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public TypedValue peekValue(int index) { + if (index < 0 || index >= mResourceData.length) { + return null; + } + if (getValue(index, mValue)) { return mValue; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java new file mode 100644 index 0000000..13cd9ec --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2011 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.layoutlib.bridge.android; + +import com.android.internal.view.IInputContext; +import com.android.internal.view.IInputMethodClient; + +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.Display_Delegate; +import android.view.IApplicationToken; +import android.view.IOnKeyguardExitResult; +import android.view.IRotationWatcher; +import android.view.IWindowManager; +import android.view.IWindowSession; +import android.view.InputChannel; +import android.view.InputDevice; +import android.view.InputEvent; +import android.view.KeyEvent; +import android.view.MotionEvent; + +import java.util.List; + +/** + * Basic implementation of {@link IWindowManager} so that {@link Display} (and + * {@link Display_Delegate}) can return a valid instance. + */ +public class BridgeWindowManager implements IWindowManager { + + private final Configuration mConfig; + private final DisplayMetrics mMetrics; + private final int mRotation; + + public BridgeWindowManager(Configuration config, DisplayMetrics metrics, int rotation) { + mConfig = config; + mMetrics = metrics; + mRotation = rotation; + } + + // custom API. + + public DisplayMetrics getMetrics() { + return mMetrics; + } + + // ---- implementation of IWindowManager that we care about ---- + + public int getRotation() throws RemoteException { + return mRotation; + } + + public int getMaximumSizeDimension() throws RemoteException { + return 0; + } + + public void getDisplaySize(Point arg0) throws RemoteException { + } + + // ---- unused implementation of IWindowManager ---- + + public boolean canStatusBarHide() throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, boolean arg4) + throws RemoteException { + // TODO Auto-generated method stub + + } + + public void addWindowToken(IBinder arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void clearForcedDisplaySize() throws RemoteException { + // TODO Auto-generated method stub + + } + + public void closeSystemDialogs(String arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void disableKeyguard(IBinder arg0, String arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void executeAppTransition() throws RemoteException { + // TODO Auto-generated method stub + + } + + public void exitKeyguardSecurely(IOnKeyguardExitResult arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void freezeRotation() throws RemoteException { + // TODO Auto-generated method stub + + } + + public float getAnimationScale(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public float[] getAnimationScales() throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public int getAppOrientation(IApplicationToken arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public int getDPadKeycodeState(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public int getDPadScancodeState(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + + public InputDevice getInputDevice(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public int[] getInputDeviceIds() throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public int getKeycodeState(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public int getKeycodeStateForDevice(int arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + + public int getPendingAppTransition() throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + + public int getScancodeState(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public int getScancodeStateForDevice(int arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public int getSwitchState(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public int getSwitchStateForDevice(int arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public int getTrackballKeycodeState(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public int getTrackballScancodeState(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public boolean hasKeys(int[] arg0, boolean[] arg1) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean inKeyguardRestrictedInputMode() throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean injectInputEventNoWait(InputEvent arg0) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean injectKeyEvent(KeyEvent arg0, boolean arg1) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean injectPointerEvent(MotionEvent arg0, boolean arg1) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean injectTrackballEvent(MotionEvent arg0, boolean arg1) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean inputMethodClientHasFocus(IInputMethodClient arg0) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean isKeyguardLocked() throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean isKeyguardSecure() throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public boolean isViewServerRunning() throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public InputChannel monitorInput(String arg0) throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public void moveAppToken(int arg0, IBinder arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void moveAppTokensToBottom(List<IBinder> arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void moveAppTokensToTop(List<IBinder> arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public IWindowSession openSession(IInputMethodClient arg0, IInputContext arg1) + throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public void overridePendingAppTransition(String arg0, int arg1, int arg2) + throws RemoteException { + // TODO Auto-generated method stub + + } + + public void pauseKeyDispatching(IBinder arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void prepareAppTransition(int arg0, boolean arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void reenableKeyguard(IBinder arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void removeAppToken(IBinder arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void removeWindowToken(IBinder arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void resumeKeyDispatching(IBinder arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public Bitmap screenshotApplications(IBinder arg0, int arg1, int arg2) throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public void setAnimationScale(int arg0, float arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setAnimationScales(float[] arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setAppOrientation(IApplicationToken arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setAppStartingWindow(IBinder arg0, String arg1, int arg2, CompatibilityInfo arg3, + CharSequence arg4, int arg5, int arg6, int arg7, IBinder arg8, boolean arg9) + throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setAppVisibility(IBinder arg0, boolean arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setAppWillBeHidden(IBinder arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setEventDispatching(boolean arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setFocusedApp(IBinder arg0, boolean arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setForcedDisplaySize(int arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setInTouchMode(boolean arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setNewConfiguration(Configuration arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setPointerSpeed(int arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setRotation(int arg0, boolean arg1, int arg2) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void setStrictModeVisualIndicatorPreference(String arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void showStrictModeViolation(boolean arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void startAppFreezingScreen(IBinder arg0, int arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public boolean startViewServer(int arg0) throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public void statusBarVisibilityChanged(int arg0) throws RemoteException { + // TODO Auto-generated method stub + + } + + public void stopAppFreezingScreen(IBinder arg0, boolean arg1) throws RemoteException { + // TODO Auto-generated method stub + + } + + public boolean stopViewServer() throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + public void thawRotation() throws RemoteException { + // TODO Auto-generated method stub + + } + + public Configuration updateOrientationFromAppTokens(Configuration arg0, IBinder arg1) + throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + public int watchRotation(IRotationWatcher arg0) throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + public IBinder asBinder() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java index 6194f5d..d40222f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java @@ -27,10 +27,16 @@ import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.RenderResources.FrameworkResourceIdProvider; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.resources.Density; import com.android.resources.ResourceType; +import com.android.resources.ScreenSize; +import android.content.res.Configuration; import android.os.HandlerThread_Delegate; +import android.os.Looper; import android.util.DisplayMetrics; +import android.view.ViewConfiguration; +import android.view.inputmethod.InputMethodManager; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -94,24 +100,29 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso // setup the display Metrics. DisplayMetrics metrics = new DisplayMetrics(); metrics.densityDpi = mParams.getDensity().getDpiValue(); - metrics.density = metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT; - metrics.scaledDensity = metrics.density; - metrics.widthPixels = mParams.getScreenWidth(); - metrics.heightPixels = mParams.getScreenHeight(); - metrics.xdpi = mParams.getXdpi(); - metrics.ydpi = mParams.getYdpi(); + + metrics.density = metrics.noncompatDensity = + metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT; + + metrics.scaledDensity = metrics.noncompatScaledDensity = metrics.density; + + metrics.widthPixels = metrics.noncompatWidthPixels = mParams.getScreenWidth(); + metrics.heightPixels = metrics.noncompatHeightPixels = mParams.getScreenHeight(); + metrics.xdpi = metrics.noncompatXdpi = mParams.getXdpi(); + metrics.ydpi = metrics.noncompatYdpi = mParams.getYdpi(); RenderResources resources = mParams.getResources(); // build the context mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources, - mParams.getProjectCallback(), mParams.getTargetSdkVersion()); + mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion()); setUp(); return SUCCESS.createResult(); } + /** * Prepares the scene for action. * <p> @@ -215,6 +226,9 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso mContext.initResources(); sCurrentContext = mContext; + // create an InputMethodManager + InputMethodManager.getInstance(Looper.myLooper()); + LayoutLog currentLog = mParams.getLog(); Bridge.setLog(currentLog); mContext.getRenderResources().setFrameworkResourceIdProvider(this); @@ -233,6 +247,12 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso // quit HandlerThread created during this session. HandlerThread_Delegate.cleanUp(sCurrentContext); + // clear the stored ViewConfiguration since the map is per density and not per context. + ViewConfiguration.sConfigurations.clear(); + + // remove the InputMethodManager + InputMethodManager.mInstance = null; + sCurrentContext = null; Bridge.setLog(null); @@ -281,6 +301,50 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso } } + private Configuration getConfiguration() { + Configuration config = new Configuration(); + + ScreenSize screenSize = mParams.getConfigScreenSize(); + if (screenSize != null) { + switch (screenSize) { + case SMALL: + config.screenLayout |= Configuration.SCREENLAYOUT_SIZE_SMALL; + break; + case NORMAL: + config.screenLayout |= Configuration.SCREENLAYOUT_SIZE_NORMAL; + break; + case LARGE: + config.screenLayout |= Configuration.SCREENLAYOUT_SIZE_LARGE; + break; + case XLARGE: + config.screenLayout |= Configuration.SCREENLAYOUT_SIZE_XLARGE; + break; + } + } + + Density density = mParams.getDensity(); + if (density == null) { + density = Density.MEDIUM; + } + + config.screenWidthDp = mParams.getScreenWidth() / density.getDpiValue(); + config.screenHeightDp = mParams.getScreenHeight() / density.getDpiValue(); + if (config.screenHeightDp < config.screenWidthDp) { + config.smallestScreenWidthDp = config.screenHeightDp; + } else { + config.smallestScreenWidthDp = config.screenWidthDp; + } + + // never run in compat mode: + config.compatScreenWidthDp = config.screenWidthDp; + config.compatScreenHeightDp = config.screenHeightDp; + + // TODO: fill in more config info. + + return config; + } + + // --- FrameworkResourceIdProvider methods @Override diff --git a/tools/layoutlib/bridge/tests/.classpath b/tools/layoutlib/bridge/tests/.classpath index 9cc2433..027bc67 100644 --- a/tools/layoutlib/bridge/tests/.classpath +++ b/tools/layoutlib/bridge/tests/.classpath @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="res"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/layoutlib_bridge"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/> diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 233f72e..df7e04f 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -103,9 +103,11 @@ public final class CreateInfo implements ICreateInfo { "android.os.Handler#sendMessageAtTime", "android.os.HandlerThread#run", "android.os.Build#getString", + "android.view.Display#getWindowManager", "android.view.LayoutInflater#rInflate", "android.view.LayoutInflater#parseInclude", "android.view.View#isInEditMode", + "android.view.inputmethod.InputMethodManager#getInstance", "com.android.internal.util.XmlUtils#convertValueToInt", // TODO: comment out once DelegateClass is working }; @@ -154,6 +156,7 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.Xfermode", "android.os.SystemClock", "android.util.FloatMath", + "android.view.Display", "libcore.icu.ICU", }; diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java index c1affa6..fcdbd2c 100644 --- a/voip/java/android/net/sip/SipAudioCall.java +++ b/voip/java/android/net/sip/SipAudioCall.java @@ -57,6 +57,7 @@ public class SipAudioCall { private static final boolean RELEASE_SOCKET = true; private static final boolean DONT_RELEASE_SOCKET = false; private static final int SESSION_TIMEOUT = 5; // in seconds + private static final int TRANSFER_TIMEOUT = 15; // in seconds /** Listener for events relating to a SIP call, such as when a call is being * recieved ("on ringing") or a call is outgoing ("on calling"). @@ -537,10 +538,14 @@ public class SipAudioCall { Log.v(TAG, "onCallTransferring mSipSession:" + mSipSession + " newSession:" + newSession); mTransferringSession = newSession; - // session changing request try { - String answer = createAnswer(sessionDescription).encode(); - newSession.answerCall(answer, SESSION_TIMEOUT); + if (sessionDescription == null) { + newSession.makeCall(newSession.getPeerProfile(), + createOffer().encode(), TRANSFER_TIMEOUT); + } else { + String answer = createAnswer(sessionDescription).encode(); + newSession.answerCall(answer, SESSION_TIMEOUT); + } } catch (Throwable e) { Log.e(TAG, "onCallTransferring()", e); newSession.endCall(); diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java index c031bc1..dc628e0 100644 --- a/voip/java/com/android/server/sip/SipHelper.java +++ b/voip/java/com/android/server/sip/SipHelper.java @@ -19,6 +19,9 @@ package com.android.server.sip; import gov.nist.javax.sip.SipStackExt; import gov.nist.javax.sip.clientauthutils.AccountManager; import gov.nist.javax.sip.clientauthutils.AuthenticationHelper; +import gov.nist.javax.sip.header.extensions.ReferencesHeader; +import gov.nist.javax.sip.header.extensions.ReferredByHeader; +import gov.nist.javax.sip.header.extensions.ReplacesHeader; import android.net.sip.SipProfile; import android.util.Log; @@ -284,14 +287,18 @@ class SipHelper { } public ClientTransaction sendInvite(SipProfile caller, SipProfile callee, - String sessionDescription, String tag) - throws SipException { + String sessionDescription, String tag, ReferredByHeader referredBy, + String replaces) throws SipException { try { Request request = createRequest(Request.INVITE, caller, callee, tag); + if (referredBy != null) request.addHeader(referredBy); + if (replaces != null) { + request.addHeader(mHeaderFactory.createHeader( + ReplacesHeader.NAME, replaces)); + } request.setContent(sessionDescription, mHeaderFactory.createContentTypeHeader( "application", "sdp")); - ClientTransaction clientTransaction = mSipProvider.getNewClientTransaction(request); if (DEBUG) Log.d(TAG, "send INVITE: " + request); @@ -455,6 +462,25 @@ class SipHelper { } } + public void sendReferNotify(Dialog dialog, String content) + throws SipException { + try { + Request request = dialog.createRequest(Request.NOTIFY); + request.addHeader(mHeaderFactory.createSubscriptionStateHeader( + "active;expires=60")); + // set content here + request.setContent(content, + mHeaderFactory.createContentTypeHeader( + "message", "sipfrag")); + request.addHeader(mHeaderFactory.createEventHeader( + ReferencesHeader.REFER)); + if (DEBUG) Log.d(TAG, "send NOTIFY: " + request); + dialog.sendRequest(mSipProvider.getNewClientTransaction(request)); + } catch (ParseException e) { + throw new SipException("sendReferNotify()", e); + } + } + public void sendInviteRequestTerminated(Request inviteRequest, ServerTransaction inviteTransaction) throws SipException { try { diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java index 4e44402..48d9b17 100644 --- a/voip/java/com/android/server/sip/SipSessionGroup.java +++ b/voip/java/com/android/server/sip/SipSessionGroup.java @@ -18,12 +18,15 @@ package com.android.server.sip; import gov.nist.javax.sip.clientauthutils.AccountManager; import gov.nist.javax.sip.clientauthutils.UserCredentials; -import gov.nist.javax.sip.header.SIPHeaderNames; import gov.nist.javax.sip.header.ProxyAuthenticate; +import gov.nist.javax.sip.header.ReferTo; +import gov.nist.javax.sip.header.SIPHeaderNames; +import gov.nist.javax.sip.header.StatusLine; import gov.nist.javax.sip.header.WWWAuthenticate; import gov.nist.javax.sip.header.extensions.ReferredByHeader; import gov.nist.javax.sip.header.extensions.ReplacesHeader; import gov.nist.javax.sip.message.SIPMessage; +import gov.nist.javax.sip.message.SIPResponse; import android.net.sip.ISipSession; import android.net.sip.ISipSessionListener; @@ -71,12 +74,15 @@ import javax.sip.address.SipURI; import javax.sip.header.CSeqHeader; import javax.sip.header.ExpiresHeader; import javax.sip.header.FromHeader; +import javax.sip.header.HeaderAddress; import javax.sip.header.MinExpiresHeader; +import javax.sip.header.ReferToHeader; import javax.sip.header.ViaHeader; import javax.sip.message.Message; import javax.sip.message.Request; import javax.sip.message.Response; + /** * Manages {@link ISipSession}'s for a SIP account. */ @@ -390,25 +396,26 @@ class SipSessionGroup implements SipListener { } } + private SipSessionImpl createNewSession(RequestEvent event, + ISipSessionListener listener, ServerTransaction transaction, + int newState) throws SipException { + SipSessionImpl newSession = new SipSessionImpl(listener); + newSession.mServerTransaction = transaction; + newSession.mState = newState; + newSession.mDialog = newSession.mServerTransaction.getDialog(); + newSession.mInviteReceived = event; + newSession.mPeerProfile = createPeerProfile((HeaderAddress) + event.getRequest().getHeader(FromHeader.NAME)); + newSession.mPeerSessionDescription = + extractContent(event.getRequest()); + return newSession; + } + private class SipSessionCallReceiverImpl extends SipSessionImpl { public SipSessionCallReceiverImpl(ISipSessionListener listener) { super(listener); } - private SipSessionImpl createNewSession(RequestEvent event, - ISipSessionListener listener, ServerTransaction transaction) - throws SipException { - SipSessionImpl newSession = new SipSessionImpl(listener); - newSession.mServerTransaction = transaction; - newSession.mState = SipSession.State.INCOMING_CALL; - newSession.mDialog = newSession.mServerTransaction.getDialog(); - newSession.mInviteReceived = event; - newSession.mPeerProfile = createPeerProfile(event.getRequest()); - newSession.mPeerSessionDescription = - extractContent(event.getRequest()); - return newSession; - } - private int processInviteWithReplaces(RequestEvent event, ReplacesHeader replaces) { String callId = replaces.getCallId(); @@ -452,7 +459,8 @@ class SipSessionGroup implements SipListener { // got INVITE w/ replaces request. newSession = createNewSession(event, replacedSession.mProxy.getListener(), - mSipHelper.getServerTransaction(event)); + mSipHelper.getServerTransaction(event), + SipSession.State.INCOMING_CALL); newSession.mProxy.onCallTransferring(newSession, newSession.mPeerSessionDescription); } else { @@ -461,7 +469,8 @@ class SipSessionGroup implements SipListener { } else { // New Incoming call. newSession = createNewSession(event, mProxy, - mSipHelper.sendRinging(event, generateTag())); + mSipHelper.sendRinging(event, generateTag()), + SipSession.State.INCOMING_CALL); mProxy.onRinging(newSession, newSession.mPeerProfile, newSession.mPeerSessionDescription); } @@ -507,6 +516,11 @@ class SipSessionGroup implements SipListener { private SipSessionImpl mKeepAliveSession; + // the following three members are used for handling refer request. + SipSessionImpl mReferSession; + ReferredByHeader mReferredBy; + String mReplaces; + // lightweight timer class SessionTimer { private boolean mRunning = true; @@ -556,6 +570,9 @@ class SipSessionGroup implements SipListener { mInviteReceived = null; mPeerSessionDescription = null; mAuthenticationRetryCount = 0; + mReferSession = null; + mReferredBy = null; + mReplaces = null; if (mDialog != null) mDialog.delete(); mDialog = null; @@ -969,15 +986,26 @@ class SipSessionGroup implements SipListener { return (proxyAuth == null) ? null : proxyAuth.getNonce(); } + private String getResponseString(int statusCode) { + StatusLine statusLine = new StatusLine(); + statusLine.setStatusCode(statusCode); + statusLine.setReasonPhrase(SIPResponse.getReasonPhrase(statusCode)); + return statusLine.encode(); + } + private boolean readyForCall(EventObject evt) throws SipException { // expect MakeCallCommand, RegisterCommand, DEREGISTER if (evt instanceof MakeCallCommand) { mState = SipSession.State.OUTGOING_CALL; MakeCallCommand cmd = (MakeCallCommand) evt; mPeerProfile = cmd.getPeerProfile(); - mClientTransaction = mSipHelper.sendInvite(mLocalProfile, - mPeerProfile, cmd.getSessionDescription(), - generateTag()); + if (mReferSession != null) { + mSipHelper.sendReferNotify(mReferSession.mDialog, + getResponseString(Response.TRYING)); + } + mClientTransaction = mSipHelper.sendInvite( + mLocalProfile, mPeerProfile, cmd.getSessionDescription(), + generateTag(), mReferredBy, mReplaces); mDialog = mClientTransaction.getDialog(); addSipSession(this); startSessionTimer(cmd.getTimeout()); @@ -1072,6 +1100,12 @@ class SipSessionGroup implements SipListener { } return true; case Response.OK: + if (mReferSession != null) { + mSipHelper.sendReferNotify(mReferSession.mDialog, + getResponseString(Response.OK)); + // since we don't need to remember the session anymore. + mReferSession = null; + } mSipHelper.sendInviteAck(event, mDialog); mPeerSessionDescription = extractContent(response); establishCall(true); @@ -1087,6 +1121,10 @@ class SipSessionGroup implements SipListener { // rfc3261#section-14.1; re-schedule invite return true; default: + if (mReferSession != null) { + mSipHelper.sendReferNotify(mReferSession.mDialog, + getResponseString(Response.SERVICE_UNAVAILABLE)); + } if (statusCode >= 400) { // error: an ack is sent automatically by the stack onError(response); @@ -1155,6 +1193,38 @@ class SipSessionGroup implements SipListener { return false; } + private boolean processReferRequest(RequestEvent event) + throws SipException { + try { + ReferToHeader referto = (ReferToHeader) event.getRequest() + .getHeader(ReferTo.NAME); + Address address = referto.getAddress(); + SipURI uri = (SipURI) address.getURI(); + String replacesHeader = uri.getHeader(ReplacesHeader.NAME); + String username = uri.getUser(); + if (username == null) { + mSipHelper.sendResponse(event, Response.BAD_REQUEST); + return false; + } + // send notify accepted + mSipHelper.sendResponse(event, Response.ACCEPTED); + SipSessionImpl newSession = createNewSession(event, + this.mProxy.getListener(), + mSipHelper.getServerTransaction(event), + SipSession.State.READY_TO_CALL); + newSession.mReferSession = this; + newSession.mReferredBy = (ReferredByHeader) event.getRequest() + .getHeader(ReferredByHeader.NAME); + newSession.mReplaces = replacesHeader; + newSession.mPeerProfile = createPeerProfile(referto); + newSession.mProxy.onCallTransferring(newSession, + null); + return true; + } catch (IllegalArgumentException e) { + throw new SipException("createPeerProfile()", e); + } + } + private boolean inCall(EventObject evt) throws SipException { // expect END_CALL cmd, BYE request, hold call (MakeCallCommand) // OK retransmission is handled in SipStack @@ -1175,6 +1245,8 @@ class SipSessionGroup implements SipListener { mSipHelper.sendResponse((RequestEvent) evt, Response.OK); endCallNormally(); return true; + } else if (isRequestEvent(Request.REFER, evt)) { + return processReferRequest((RequestEvent) evt); } else if (evt instanceof MakeCallCommand) { // to change call mState = SipSession.State.OUTGOING_CALL; @@ -1182,6 +1254,8 @@ class SipSessionGroup implements SipListener { ((MakeCallCommand) evt).getSessionDescription()); startSessionTimer(((MakeCallCommand) evt).getTimeout()); return true; + } else if (evt instanceof ResponseEvent) { + if (expectResponse(Request.NOTIFY, evt)) return true; } return false; } @@ -1558,12 +1632,10 @@ class SipSessionGroup implements SipListener { return false; } - private static SipProfile createPeerProfile(Request request) + private static SipProfile createPeerProfile(HeaderAddress header) throws SipException { try { - FromHeader fromHeader = - (FromHeader) request.getHeader(FromHeader.NAME); - Address address = fromHeader.getAddress(); + Address address = header.getAddress(); SipURI uri = (SipURI) address.getURI(); String username = uri.getUser(); if (username == null) username = ANONYMOUS; |
