diff options
234 files changed, 3989 insertions, 5811 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 d507940..f3dda78 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 @@ -1121,6 +1126,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 @@ -14377,7 +14392,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 @@ -14999,6 +15014,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"; @@ -15741,6 +16007,7 @@ package android.provider { protected static abstract interface ContactsContract.GroupsColumns { 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"; @@ -15868,6 +16135,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"; @@ -22474,7 +22742,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); @@ -26151,7 +26419,7 @@ 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 resetResolvedLayoutDirection(); method public void setAllCaps(boolean); method public final void setAutoLinkMask(int); method public void setCompoundDrawablePadding(int); 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/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/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/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..ee04729 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4093,11 +4093,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 +4100,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 +4141,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/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/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/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 9d40c42..a4c66e4 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -43,17 +43,41 @@ public class NetworkStats implements Parcelable { /** {@link #tag} value for without tag. */ public static final int TAG_NONE = 0; + // TODO: move public fields to Entry accessors, then undeprecate + // TODO: refactor rx/tx to rxBytes/txBytes + /** * {@link SystemClock#elapsedRealtime()} timestamp when this data was * generated. */ + @Deprecated public final long elapsedRealtime; + @Deprecated public int size; + @Deprecated public String[] iface; + @Deprecated public int[] uid; + @Deprecated public int[] tag; + @Deprecated public long[] rx; + @Deprecated + public long[] rxPackets; + @Deprecated public long[] tx; + @Deprecated + public 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 NetworkStats(long elapsedRealtime, int initialSize) { this.elapsedRealtime = elapsedRealtime; @@ -62,7 +86,9 @@ public class NetworkStats implements Parcelable { this.uid = new int[initialSize]; this.tag = new int[initialSize]; this.rx = new long[initialSize]; + this.rxPackets = new long[initialSize]; this.tx = new long[initialSize]; + this.txPackets = new long[initialSize]; } public NetworkStats(Parcel parcel) { @@ -72,38 +98,82 @@ public class NetworkStats implements Parcelable { uid = parcel.createIntArray(); tag = parcel.createIntArray(); rx = parcel.createLongArray(); + rxPackets = parcel.createLongArray(); tx = parcel.createLongArray(); + txPackets = parcel.createLongArray(); } /** * Add new stats entry with given values. */ public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) { + final Entry entry = new Entry(); + entry.iface = iface; + entry.uid = uid; + entry.tag = tag; + entry.rxBytes = rx; + entry.txBytes = tx; + return addValues(entry); + } + + /** + * Add new stats entry, copying from given {@link Entry}. The {@link Entry} + * object can be recycled across multiple calls. + */ + 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); + rx = Arrays.copyOf(rx, newLength); + rxPackets = Arrays.copyOf(rxPackets, newLength); + tx = Arrays.copyOf(tx, 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; + rx[size] = entry.rxBytes; + rxPackets[size] = entry.rxPackets; + tx[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 = rx[i]; + entry.rxPackets = rxPackets[i]; + entry.txBytes = tx[i]; + entry.txPackets = txPackets[i]; + return entry; + } + + public long getElapsedRealtime() { + return elapsedRealtime; + } + + public int size() { + return size; + } + + /** * 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) { + // TODO: extent to accept rxPackets/txPackets final int i = findIndex(iface, uid, tag); if (i == -1) { // only create new entry when positive contribution @@ -199,30 +269,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 = rx[i]; + entry.rxPackets = rxPackets[i]; + entry.txBytes = tx[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 = rx[i] - value.rx[j]; + entry.rxPackets = rxPackets[i] - value.rxPackets[j]; + entry.txBytes = tx[i] - value.tx[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 +316,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(rx[i]); + pw.print(" rxPackets="); pw.print(rxPackets[i]); + pw.print(" txBytes="); pw.print(tx[i]); + pw.print(" txPackets="); pw.println(txPackets[i]); } } @@ -265,7 +348,9 @@ public class NetworkStats implements Parcelable { dest.writeIntArray(uid); dest.writeIntArray(tag); dest.writeLongArray(rx); + dest.writeLongArray(rxPackets); dest.writeLongArray(tx); + dest.writeLongArray(txPackets); } public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { 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/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..5b1f563 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -266,84 +266,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 +375,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 +390,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 +411,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 +432,6 @@ public class Process { throw new ZygoteStartFailedEx(ex); } - - return pid; } /** @@ -500,20 +443,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 +479,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 +511,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 +673,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 +797,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..60900e1 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) { @@ -171,9 +185,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 +208,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..ec67683 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -135,14 +135,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 +263,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> @@ -1881,13 +1871,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 +2204,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 +2215,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 +2226,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> @@ -2537,7 +2553,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); @@ -3814,27 +3829,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. @@ -6213,6 +6207,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 @@ -6338,6 +6344,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> 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/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..b23e3ce 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); } } diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index c7603ee..8ef4295 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -384,11 +384,16 @@ class AudioPlaybackHandler { } count += written; } + + 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 @@ -416,6 +421,8 @@ class AudioPlaybackHandler { 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); 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..caf02ef 100644 --- a/core/java/android/speech/tts/SynthesisMessageParams.java +++ b/core/java/android/speech/tts/SynthesisMessageParams.java @@ -30,6 +30,7 @@ final class SynthesisMessageParams extends MessageParams { final int mChannelCount; final float mVolume; final float mPan; + final EventLogger mLogger; public volatile AudioTrack mAudioTrack; @@ -38,7 +39,7 @@ final class SynthesisMessageParams extends MessageParams { 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,6 +48,7 @@ final class SynthesisMessageParams extends MessageParams { mChannelCount = channelCount; mVolume = volume; mPan = pan; + mLogger = logger; // initially null. mAudioTrack = null; 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/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/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 188970c..011e44c 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -128,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. @@ -289,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; @@ -311,8 +326,16 @@ 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; @@ -326,11 +349,6 @@ public abstract class HardwareRenderer { 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) { @@ -342,7 +360,7 @@ public abstract class HardwareRenderer { * Indicates whether this renderer instance can track and update dirty regions. */ boolean hasDirtyRegions() { - return mDirtyRegions; + return mDirtyRegionsEnabled; } /** @@ -479,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"); @@ -500,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())); @@ -566,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(); @@ -620,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); } @@ -645,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 @@ -706,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)) { 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/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/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 2145edd..c652e55 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -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/TextView.java b/core/java/android/widget/TextView.java index 1e63e26..766b520 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5571,8 +5571,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 || @@ -6212,6 +6212,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, @@ -6230,28 +6239,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) { @@ -6293,20 +6285,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(); 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/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index b86eb13..9c06d69 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,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter { // Give a reasonable anchor to nested submenus. setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton); } + + setCallback(mPopupPresenterCallback); } @Override @@ -489,6 +551,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..56128d4 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,47 @@ 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; + 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/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..a4edbc5 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -25,8 +25,8 @@ 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.ContextMenu.ContextMenuInfo; import android.view.KeyCharacterMap; @@ -38,7 +38,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 +48,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 +255,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; } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index cffbb4e..4ecc828 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -18,6 +18,7 @@ package com.android.internal.view.menu; import android.content.Context; import android.content.res.Resources; +import android.os.Parcelable; import android.util.DisplayMetrics; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -296,4 +297,18 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On 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..595753a 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(); } @@ -1301,5 +1302,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/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_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_Process.cpp b/core/jni/android_util_Process.cpp index 0960b25..d1ba2d1 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; } @@ -370,11 +368,6 @@ jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint uid) #endif } -jboolean android_os_Process_supportsProcesses(JNIEnv* env, jobject clazz) -{ - return ProcessState::self()->supportsProcesses(); -} - static int pid_compare(const void* v1, const void* v2) { //LOGI("Compare %d vs %d\n", *((const jint*)v1), *((const jint*)v2)); @@ -878,7 +871,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/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 index 7603a31..c05ba1a 100644 --- a/core/res/res/layout/activity_chooser_list_footer.xml +++ b/core/res/res/layout/activity_chooser_list_footer.xml @@ -23,13 +23,11 @@ android:gravity="center" android:orientation="vertical"> - <ImageView + <View 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" /> + android:background="@android:color/holo_blue_light" /> <TextView android:id="@+id/title" diff --git a/core/res/res/layout/activity_chooser_list_header.xml b/core/res/res/layout/activity_chooser_list_header.xml index 867014b..0fb256f 100644 --- a/core/res/res/layout/activity_chooser_list_header.xml +++ b/core/res/res/layout/activity_chooser_list_header.xml @@ -32,12 +32,10 @@ android:duplicateParentState="true" android:singleLine="true" /> - <ImageView + <View 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/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/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 39de054..9c2133f 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. --> @@ -5303,6 +5318,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/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 3c23add..f7701f2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1781,6 +1781,12 @@ <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" /> @@ -1809,4 +1815,15 @@ <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" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 50f8df7..70c204e 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> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 8a8f81a..a5cd6e3 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1356,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"> @@ -1455,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"> 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/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 059503c..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 --> 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/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java index 3cb64c7..82345e2 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -44,20 +44,20 @@ public class NetworkStatsTest extends TestCase { public void testAddEntryGrow() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 2); - assertEquals(0, stats.size); + assertEquals(0, stats.size()); assertEquals(2, stats.iface.length); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 2L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L); - assertEquals(2, stats.size); + assertEquals(2, stats.size()); assertEquals(2, stats.iface.length); 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); - assertEquals(5, stats.size); + assertEquals(5, stats.size()); assertTrue(stats.iface.length >= 5); assertEquals(1L, stats.rx[0]); 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/core/tests/coretests/src/android/widget/TextViewTestActivity.java b/core/tests/coretests/src/android/widget/TextViewTestActivity.java new file mode 100644 index 0000000..1bb4d24 --- /dev/null +++ b/core/tests/coretests/src/android/widget/TextViewTestActivity.java @@ -0,0 +1,30 @@ +/* + * 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.widget; + +import android.app.Activity; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; + +public class TextViewTestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.textview_test); + } +} 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/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/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/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/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/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/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 2d4e10d..7264ac4 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,19 @@ bool ProcessState::becomeContextManager(context_check_func checkFunc, void* user AutoMutex _l(mLock); mBinderContextCheckFunc = checkFunc; mBinderContextUserData = userData; - if (mDriverFD >= 0) { - int dummy = 0; + + int dummy = 0; #if defined(HAVE_ANDROID_OS) - status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); + status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); #else - status_t result = INVALID_OPERATION; + 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. + 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,10 +300,6 @@ 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); @@ -386,9 +360,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/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/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 06e3b29..a349121 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -673,11 +673,11 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) float x = rect.left; float y = rect.top; - bool simpleTransform = mSnapshot->transform->isPureTranslate(); - - if (simpleTransform && + bool simpleTransform = mSnapshot->transform->isPureTranslate() && layer->getWidth() == (uint32_t) rect.getWidth() && - layer->getHeight() == (uint32_t) rect.getHeight()) { + 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); 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/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp index 8120864..62eb24e 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); } 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/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/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/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..e8ddd2d 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,37 +835,31 @@ public class MediaScanner } } - 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(mFilesUri, values); + } + if (result != null) { rowId = ContentUris.parseId(result); entry.mRowId = rowId; } } else { // updated file - result = ContentUris.withAppendedId(tableUri, rowId); + result = ContentUris.withAppendedId(mFilesUri, rowId); // path should never change, and we want to avoid replacing mixed cased paths // with squashed lower case paths values.remove(MediaStore.MediaColumns.DATA); @@ -854,7 +878,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 +900,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); } @@ -885,19 +909,19 @@ public class MediaScanner if (notifications && !mDefaultNotificationSet) { if (TextUtils.isEmpty(mDefaultNotificationFilename) || doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) { - setSettingIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId); + setSettingIfNotSet(Settings.System.NOTIFICATION_SOUND, mFilesUri, rowId); mDefaultNotificationSet = true; } } else if (ringtones && !mDefaultRingtoneSet) { if (TextUtils.isEmpty(mDefaultRingtoneFilename) || doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) { - setSettingIfNotSet(Settings.System.RINGTONE, tableUri, rowId); + setSettingIfNotSet(Settings.System.RINGTONE, mFilesUri, rowId); mDefaultRingtoneSet = true; } } else if (alarms && !mDefaultAlarmSet) { if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) || doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) { - setSettingIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId); + setSettingIfNotSet(Settings.System.ALARM_ALERT, mFilesUri, rowId); mDefaultAlarmSet = true; } } @@ -1152,10 +1176,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 +1185,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/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_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp index aaf85c3..446b630 100644 --- a/media/jni/android_mtp_MtpServer.cpp +++ b/media/jni/android_mtp_MtpServer.cpp @@ -48,6 +48,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; @@ -228,12 +229,14 @@ android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage) 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); + MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, + reserveSpace, removable, maxFileSize); thread->addStorage(storage); env->ReleaseStringUTFChars(path, pathStr); env->ReleaseStringUTFChars(description, descriptionStr); @@ -312,6 +315,11 @@ int register_android_mtp_MtpServer(JNIEnv *env) LOGE("Can't find MtpStorage.mRemovable"); return -1; } + field_MtpStorage_maxFileSize = env->GetFieldID(clazz, "mMaxFileSize", "J"); + if (field_MtpStorage_maxFileSize == NULL) { + LOGE("Can't find MtpStorage.mMaxFileSize"); + return -1; + } clazz_MtpStorage = (jclass)env->NewGlobalRef(clazz); clazz = env->FindClass("android/mtp/MtpServer"); 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/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/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/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/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index ed8149a..c7e7ced 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -158,12 +158,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 +461,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 +539,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 +590,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 +600,7 @@ void CameraSource::releaseCamera() { mCamera->unlock(); } mCamera.clear(); + IPCThreadState::self()->restoreCallingIdentity(token); } if (mCameraRecordingProxy != 0) { mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier); diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index f075699..eaad2c3 100644 --- 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; } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 1ac2c1f..06363ee 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1295,13 +1295,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; @@ -1312,8 +1305,14 @@ status_t OMXCodec::setupAVCEncoderParameters(const sp<MetaData>& meta) { h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(profileLevel.mLevel); 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; diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index bc04e8c..9085f10 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -871,6 +871,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/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/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..ab5f4bd --- /dev/null +++ b/opengl/tests/EGLTest/Android.mk @@ -0,0 +1,41 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +ifneq ($(TARGET_SIMULATOR),true) + +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) + +endif + +# 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/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/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/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 10b9083..da9b55c 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -3154,11 +3154,9 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { mLastHoverWindow = NULL; } - mWindows.clear(); - // Loop over new windows and rebuild the necessary window pointers for // tracking focus and touch. - mWindows.appendVector(inputWindows); + mWindows = inputWindows; size_t numWindows = mWindows.size(); for (size_t i = 0; i < numWindows; i++) { @@ -4560,8 +4558,7 @@ 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, 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..786f2fa 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -224,6 +224,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 +234,7 @@ class BackupManagerService extends IBackupManager.Stub { pkgInfo = _pkg; pmToken = _pmToken; needFullBackup = _needFullBackup; + filterSet = null; } RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, @@ -243,6 +245,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 +418,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; } @@ -3020,6 +3034,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 +3048,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 +3056,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 +3076,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 +3096,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 +4307,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..41450d2 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -1602,8 +1602,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 + ", NetworkUtils.RESET_ALL_ADDRESSES)"); + } + NetworkUtils.resetConnections(iface, NetworkUtils.RESET_ALL_ADDRESSES); } } } 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/MountService.java b/services/java/com/android/server/MountService.java index 54e5432..2e54c99 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; diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index da1bf83..829df39 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() { @@ -930,13 +939,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(); + + // 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")); - 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); + stats.addValues(entry); + } + + if (DBG) Slog.d(TAG, "recorded stale stats from " + xtIfaces); } return stats; @@ -1063,13 +1127,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 +1147,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 +1181,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 +1272,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 +1307,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..24d4dd3 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(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 29cccb6..8501163 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -208,6 +208,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 +508,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. */ @@ -1959,8 +1956,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 +1970,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 +1989,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 +2002,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. @@ -3605,9 +3596,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; } @@ -4041,8 +4029,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()) { @@ -4301,8 +4288,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 +4432,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); @@ -5527,8 +5513,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 +5952,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); @@ -6584,13 +6570,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 +7933,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; @@ -13127,74 +13098,72 @@ public final class ActivityManagerService extends ActivityManagerNative 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.lastCpuTime = app.curCpuTime; + } - app.setRawAdj = app.curRawAdj; + 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); + app.setAdj = adj; + } else { + success = false; } - 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; + } + 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); + } } - } - 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(); + if (false) { + 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) { } } } 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/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 3968f66..da83e7d 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -78,6 +78,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 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..d23d0f4 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() { diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index b4bd176..b6834f6 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() { 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..86b3d36 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -251,19 +251,18 @@ public class UsbDeviceManager { public UsbHandler() { 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 +620,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 +639,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/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/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/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/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..df5898b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -1055,10 +1055,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); + } } } } @@ -1110,10 +1116,12 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { Collection<ApnContext> apns = dcac.getApnListSync(); - // filter out ApnContext with "Connected" state. + // filter out ApnContext with "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); } } 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 |