diff options
164 files changed, 6182 insertions, 1298 deletions
diff --git a/Android.mk b/Android.mk index 335fb732b214..3b0fc599ed17 100644 --- a/Android.mk +++ b/Android.mk @@ -158,6 +158,11 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/os/IResultReceiver.aidl \ core/java/com/android/internal/statusbar/IStatusBar.aidl \ core/java/com/android/internal/statusbar/IStatusBarService.aidl \ + core/java/com/android/internal/textservice/ISpellCheckerService.aidl \ + core/java/com/android/internal/textservice/ISpellCheckerSession.aidl \ + core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \ + core/java/com/android/internal/textservice/ITextServicesManager.aidl \ + core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \ core/java/com/android/internal/view/IInputContext.aidl \ core/java/com/android/internal/view/IInputContextCallback.aidl \ core/java/com/android/internal/view/IInputMethod.aidl \ @@ -266,6 +271,11 @@ aidl_files := \ frameworks/base/core/java/android/view/Surface.aidl \ frameworks/base/core/java/android/view/WindowManager.aidl \ frameworks/base/core/java/android/widget/RemoteViews.aidl \ + frameworks/base/core/java/com/android/internal/textservice/ISpellCheckerService.aidl \ + frameworks/base/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl \ + frameworks/base/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \ + frameworks/base/core/java/com/android/internal/textservice/ITextServicesManager.aidl \ + frameworks/base/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \ frameworks/base/core/java/com/android/internal/view/IInputContext.aidl \ frameworks/base/core/java/com/android/internal/view/IInputMethod.aidl \ frameworks/base/core/java/com/android/internal/view/IInputMethodCallback.aidl \ diff --git a/api/current.txt b/api/current.txt index d821c8e75089..2514efdb4910 100644 --- a/api/current.txt +++ b/api/current.txt @@ -21,6 +21,7 @@ package android { field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; + field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER"; field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH"; field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN"; @@ -184,14 +185,14 @@ package android { public static final class R.attr { ctor public R.attr(); field public static final int absListViewStyle = 16842858; // 0x101006a - field public static final int accessibilityEventTypes = 16843648; // 0x1010380 - field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 - field public static final int accessibilityFlags = 16843652; // 0x1010384 + field public static final int accessibilityEventTypes = 16843647; // 0x101037f + field public static final int accessibilityFeedbackType = 16843649; // 0x1010381 + field public static final int accessibilityFlags = 16843651; // 0x1010383 field public static final int accountPreferences = 16843423; // 0x101029f field public static final int accountType = 16843407; // 0x101028f field public static final int action = 16842797; // 0x101002d field public static final int actionBarSize = 16843499; // 0x10102eb - field public static final int actionBarSplitStyle = 16843670; // 0x1010396 + field public static final int actionBarSplitStyle = 16843669; // 0x1010395 field public static final int actionBarStyle = 16843470; // 0x10102ce field public static final int actionBarTabBarStyle = 16843508; // 0x10102f4 field public static final int actionBarTabStyle = 16843507; // 0x10102f3 @@ -207,10 +208,10 @@ package android { field public static final int actionModeCopyDrawable = 16843538; // 0x1010312 field public static final int actionModeCutDrawable = 16843537; // 0x1010311 field public static final int actionModePasteDrawable = 16843539; // 0x1010313 - field public static final int actionModeSelectAllDrawable = 16843646; // 0x101037e - field public static final int actionModeStyle = 16843682; // 0x10103a2 + field public static final int actionModeSelectAllDrawable = 16843645; // 0x101037d + field public static final int actionModeStyle = 16843681; // 0x10103a1 field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6 - field public static final int actionProviderClass = 16843671; // 0x1010397 + field public static final int actionProviderClass = 16843670; // 0x1010396 field public static final int actionViewClass = 16843516; // 0x10102fc field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba @@ -222,7 +223,7 @@ package android { field public static final int alertDialogIcon = 16843605; // 0x1010355 field public static final int alertDialogStyle = 16842845; // 0x101005d field public static final int alertDialogTheme = 16843529; // 0x1010309 - field public static final int alignmentMode = 16843640; // 0x1010378 + field public static final int alignmentMode = 16843639; // 0x1010377 field public static final int allContactsName = 16843468; // 0x10102cc field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 @@ -256,8 +257,8 @@ package android { field public static final int background = 16842964; // 0x10100d4 field public static final int backgroundDimAmount = 16842802; // 0x1010032 field public static final int backgroundDimEnabled = 16843295; // 0x101021f - field public static final int backgroundSplit = 16843673; // 0x1010399 - field public static final int backgroundStacked = 16843672; // 0x1010398 + field public static final int backgroundSplit = 16843672; // 0x1010398 + field public static final int backgroundStacked = 16843671; // 0x1010397 field public static final int backupAgent = 16843391; // 0x101027f field public static final int baseline = 16843548; // 0x101031c field public static final int baselineAlignBottom = 16843042; // 0x1010122 @@ -266,7 +267,7 @@ package android { field public static final int borderlessButtonStyle = 16843563; // 0x101032b field public static final int bottom = 16843184; // 0x10101b0 field public static final int bottomBright = 16842957; // 0x10100cd - field public static final int bottomChevronDrawable = 16843659; // 0x101038b + field public static final int bottomChevronDrawable = 16843658; // 0x101038a field public static final int bottomDark = 16842953; // 0x10100c9 field public static final int bottomLeftRadius = 16843179; // 0x10101ab field public static final int bottomMedium = 16842958; // 0x10100ce @@ -285,7 +286,7 @@ package android { field public static final int cacheColorHint = 16843009; // 0x1010101 field public static final int calendarViewShown = 16843596; // 0x101034c field public static final int calendarViewStyle = 16843613; // 0x101035d - field public static final int canRetrieveWindowContent = 16843653; // 0x1010385 + field public static final int canRetrieveWindowContent = 16843652; // 0x1010384 field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230 field public static final deprecated int capitalize = 16843113; // 0x1010169 field public static final int centerBright = 16842956; // 0x10100cc @@ -314,18 +315,18 @@ 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 = 16843678; // 0x101039e + field public static final int colorActivatedHighlight = 16843677; // 0x101039d field public static final int colorBackground = 16842801; // 0x1010031 field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab - field public static final int colorFocusedHighlight = 16843677; // 0x101039d + field public static final int colorFocusedHighlight = 16843676; // 0x101039c field public static final int colorForeground = 16842800; // 0x1010030 field public static final int colorForegroundInverse = 16843270; // 0x1010206 - field public static final int colorLongPressedHighlight = 16843676; // 0x101039c - field public static final int colorMultiSelectHighlight = 16843679; // 0x101039f - field public static final int colorPressedHighlight = 16843675; // 0x101039b - field public static final int columnCount = 16843637; // 0x1010375 + field public static final int colorLongPressedHighlight = 16843675; // 0x101039b + field public static final int colorMultiSelectHighlight = 16843678; // 0x101039e + field public static final int colorPressedHighlight = 16843674; // 0x101039a + field public static final int columnCount = 16843636; // 0x1010374 field public static final int columnDelay = 16843215; // 0x10101cf - field public static final int columnOrderPreserved = 16843638; // 0x1010376 + field public static final int columnOrderPreserved = 16843637; // 0x1010375 field public static final int columnWidth = 16843031; // 0x1010117 field public static final int compatibleWidthLimitDp = 16843621; // 0x1010365 field public static final int completionHint = 16843122; // 0x1010172 @@ -379,11 +380,11 @@ package android { field public static final int drawSelectorOnTop = 16843004; // 0x10100fc field public static final int drawable = 16843161; // 0x1010199 field public static final int drawableBottom = 16843118; // 0x101016e - field public static final int drawableEnd = 16843681; // 0x10103a1 + field public static final int drawableEnd = 16843680; // 0x10103a0 field public static final int drawableLeft = 16843119; // 0x101016f field public static final int drawablePadding = 16843121; // 0x1010171 field public static final int drawableRight = 16843120; // 0x1010170 - field public static final int drawableStart = 16843680; // 0x10103a0 + field public static final int drawableStart = 16843679; // 0x101039f field public static final int drawableTop = 16843117; // 0x101016d field public static final int drawingCacheQuality = 16842984; // 0x10100e8 field public static final int dropDownAnchor = 16843363; // 0x1010263 @@ -440,7 +441,7 @@ package android { field public static final int fastScrollTextColor = 16843609; // 0x1010359 field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336 field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339 - field public static final int feedbackCount = 16843665; // 0x1010391 + field public static final int feedbackCount = 16843664; // 0x1010390 field public static final int fillAfter = 16843197; // 0x10101bd field public static final int fillBefore = 16843196; // 0x10101bc field public static final int fillEnabled = 16843343; // 0x101024f @@ -493,7 +494,7 @@ package android { field public static final int hand_hour = 16843011; // 0x1010103 field public static final int hand_minute = 16843012; // 0x1010104 field public static final int handle = 16843354; // 0x101025a - field public static final int handleDrawable = 16843655; // 0x1010387 + field public static final int handleDrawable = 16843654; // 0x1010386 field public static final int handleProfiling = 16842786; // 0x1010022 field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e field public static final int hardwareAccelerated = 16843475; // 0x10102d3 @@ -502,12 +503,12 @@ package android { field public static final int headerDividersEnabled = 16843310; // 0x101022e field public static final int height = 16843093; // 0x1010155 field public static final int hint = 16843088; // 0x1010150 - field public static final int hitRadius = 16843662; // 0x101038e + field public static final int hitRadius = 16843661; // 0x101038d field public static final int homeAsUpIndicator = 16843531; // 0x101030b field public static final int homeLayout = 16843549; // 0x101031d field public static final int horizontalDivider = 16843053; // 0x101012d field public static final int horizontalGap = 16843327; // 0x101023f - field public static final int horizontalOffset = 16843667; // 0x1010393 + field public static final int horizontalOffset = 16843666; // 0x1010392 field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353 field public static final int horizontalSpacing = 16843028; // 0x1010114 field public static final int host = 16842792; // 0x1010028 @@ -553,7 +554,7 @@ package android { field public static final int installLocation = 16843447; // 0x10102b7 field public static final int interpolator = 16843073; // 0x1010141 field public static final int isAlwaysSyncable = 16843571; // 0x1010333 - field public static final int isAuxiliary = 16843647; // 0x101037f + field public static final int isAuxiliary = 16843646; // 0x101037e field public static final int isDefault = 16843297; // 0x1010221 field public static final int isIndicator = 16843079; // 0x1010147 field public static final int isModifier = 16843334; // 0x1010246 @@ -606,8 +607,8 @@ package android { field public static final int layout_centerInParent = 16843151; // 0x101018f field public static final int layout_centerVertical = 16843153; // 0x1010191 field public static final int layout_column = 16843084; // 0x101014c - field public static final int layout_columnFlexibility = 16843645; // 0x101037d - field public static final int layout_columnSpan = 16843644; // 0x101037c + field public static final int layout_columnFlexibility = 16843644; // 0x101037c + field public static final int layout_columnSpan = 16843643; // 0x101037b field public static final int layout_gravity = 16842931; // 0x10100b3 field public static final int layout_height = 16842997; // 0x10100f5 field public static final int layout_margin = 16842998; // 0x10100f6 @@ -615,9 +616,9 @@ package android { field public static final int layout_marginLeft = 16842999; // 0x10100f7 field public static final int layout_marginRight = 16843001; // 0x10100f9 field public static final int layout_marginTop = 16843000; // 0x10100f8 - field public static final int layout_row = 16843641; // 0x1010379 - field public static final int layout_rowFlexibility = 16843643; // 0x101037b - field public static final int layout_rowSpan = 16843642; // 0x101037a + field public static final int layout_row = 16843640; // 0x1010378 + field public static final int layout_rowFlexibility = 16843642; // 0x101037a + field public static final int layout_rowSpan = 16843641; // 0x1010379 field public static final int layout_scale = 16843155; // 0x1010193 field public static final int layout_span = 16843085; // 0x101014d field public static final int layout_toLeftOf = 16843138; // 0x1010182 @@ -627,7 +628,7 @@ package android { field public static final int layout_x = 16843135; // 0x101017f field public static final int layout_y = 16843136; // 0x1010180 field public static final int left = 16843181; // 0x10101ad - field public static final int leftChevronDrawable = 16843656; // 0x1010388 + field public static final int leftChevronDrawable = 16843655; // 0x1010387 field public static final int lineSpacingExtra = 16843287; // 0x1010217 field public static final int lineSpacingMultiplier = 16843288; // 0x1010218 field public static final int lines = 16843092; // 0x1010154 @@ -639,8 +640,8 @@ package android { field public static final int listDividerAlertDialog = 16843525; // 0x1010305 field public static final int listPopupWindowStyle = 16843519; // 0x10102ff field public static final int listPreferredItemHeight = 16842829; // 0x101004d - field public static final int listPreferredItemHeightLarge = 16843668; // 0x1010394 - field public static final int listPreferredItemHeightSmall = 16843669; // 0x1010395 + field public static final int listPreferredItemHeightLarge = 16843667; // 0x1010393 + field public static final int listPreferredItemHeightSmall = 16843668; // 0x1010394 field public static final int listSelector = 16843003; // 0x10100fb field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208 field public static final int listViewStyle = 16842868; // 0x1010074 @@ -671,8 +672,8 @@ package android { field public static final int minHeight = 16843072; // 0x1010140 field public static final int minLevel = 16843185; // 0x10101b1 field public static final int minLines = 16843094; // 0x1010156 - field public static final int minResizeHeight = 16843684; // 0x10103a4 - field public static final int minResizeWidth = 16843683; // 0x10103a3 + field public static final int minResizeHeight = 16843683; // 0x10103a3 + field public static final int minResizeWidth = 16843682; // 0x10103a2 field public static final int minSdkVersion = 16843276; // 0x101020c field public static final int minWidth = 16843071; // 0x101013f field public static final int mode = 16843134; // 0x101017e @@ -688,7 +689,7 @@ package android { field public static final int nextFocusUp = 16842979; // 0x10100e3 field public static final int noHistory = 16843309; // 0x101022d field public static final int normalScreens = 16843397; // 0x1010285 - field public static final int notificationTimeout = 16843651; // 0x1010383 + field public static final int notificationTimeout = 16843650; // 0x1010382 field public static final int numColumns = 16843032; // 0x1010118 field public static final int numStars = 16843076; // 0x1010144 field public static final deprecated int numeric = 16843109; // 0x1010165 @@ -702,11 +703,11 @@ package android { field public static final int orderingFromXml = 16843239; // 0x10101e7 field public static final int orientation = 16842948; // 0x10100c4 field public static final int outAnimation = 16843128; // 0x1010178 - field public static final int outerRadius = 16843661; // 0x101038d + field public static final int outerRadius = 16843660; // 0x101038c field public static final int overScrollFooter = 16843459; // 0x10102c3 field public static final int overScrollHeader = 16843458; // 0x10102c2 field public static final int overScrollMode = 16843457; // 0x10102c1 - field public static final int packageNames = 16843649; // 0x1010381 + field public static final int packageNames = 16843648; // 0x1010380 field public static final int padding = 16842965; // 0x10100d5 field public static final int paddingBottom = 16842969; // 0x10100d9 field public static final int paddingLeft = 16842966; // 0x10100d6 @@ -791,17 +792,17 @@ package android { field public static final int restoreAnyVersion = 16843450; // 0x10102ba field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d field public static final int right = 16843183; // 0x10101af - field public static final int rightChevronDrawable = 16843657; // 0x1010389 + field public static final int rightChevronDrawable = 16843656; // 0x1010388 field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093 field public static final int ringtoneType = 16843257; // 0x10101f9 field public static final int rotation = 16843558; // 0x1010326 field public static final int rotationX = 16843559; // 0x1010327 field public static final int rotationY = 16843560; // 0x1010328 - field public static final int rowCount = 16843635; // 0x1010373 + field public static final int rowCount = 16843634; // 0x1010372 field public static final int rowDelay = 16843216; // 0x10101d0 field public static final int rowEdgeFlags = 16843329; // 0x1010241 field public static final int rowHeight = 16843058; // 0x1010132 - field public static final int rowOrderPreserved = 16843636; // 0x1010374 + field public static final int rowOrderPreserved = 16843635; // 0x1010373 field public static final int saveEnabled = 16842983; // 0x10100e7 field public static final int scaleGravity = 16843262; // 0x10101fe field public static final int scaleHeight = 16843261; // 0x10101fd @@ -867,7 +868,7 @@ package android { field public static final int smallIcon = 16843422; // 0x101029e field public static final int smallScreens = 16843396; // 0x1010284 field public static final int smoothScrollbar = 16843313; // 0x1010231 - field public static final int snapMargin = 16843664; // 0x1010390 + field public static final int snapMargin = 16843663; // 0x101038f field public static final int soundEffectsEnabled = 16843285; // 0x1010215 field public static final int spacing = 16843027; // 0x1010113 field public static final int spinnerDropDownItemStyle = 16842887; // 0x1010087 @@ -915,7 +916,7 @@ package android { field public static final int subtitleTextStyle = 16843513; // 0x10102f9 field public static final int suggestActionMsg = 16843228; // 0x10101dc field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd - field public static final int suggestionsEnabled = 16843634; // 0x1010372 + field public static final int suggestionsEnabled = 16843633; // 0x1010371 field public static final int summary = 16843241; // 0x10101e9 field public static final int summaryColumn = 16843426; // 0x10102a2 field public static final int summaryOff = 16843248; // 0x10101f0 @@ -932,7 +933,7 @@ package android { field public static final int tag = 16842961; // 0x10100d1 field public static final int targetActivity = 16843266; // 0x1010202 field public static final int targetClass = 16842799; // 0x101002f - field public static final int targetDrawables = 16843654; // 0x1010386 + field public static final int targetDrawables = 16843653; // 0x1010385 field public static final int targetPackage = 16842785; // 0x1010021 field public static final int targetSdkVersion = 16843376; // 0x1010270 field public static final int taskAffinity = 16842770; // 0x1010012 @@ -947,7 +948,7 @@ package android { field public static final int tension = 16843370; // 0x101026a field public static final int testOnly = 16843378; // 0x1010272 field public static final int text = 16843087; // 0x101014f - field public static final int textAllCaps = 16843674; // 0x101039a + field public static final int textAllCaps = 16843673; // 0x1010399 field public static final int textAppearance = 16842804; // 0x1010034 field public static final int textAppearanceButton = 16843271; // 0x1010207 field public static final int textAppearanceInverse = 16842805; // 0x1010035 @@ -988,9 +989,8 @@ package android { field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314 field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f field public static final int textEditSidePasteWindowLayout = 16843614; // 0x101035e - field public static final int textEditSuggestionItemLayout = 16843633; // 0x1010371 - field public static final int textEditSuggestionsBottomWindowLayout = 16843631; // 0x101036f - field public static final int textEditSuggestionsTopWindowLayout = 16843632; // 0x1010370 + field public static final int textEditSuggestionItemLayout = 16843632; // 0x1010370 + field public static final int textEditSuggestionsWindowLayout = 16843631; // 0x101036f field public static final int textFilterEnabled = 16843007; // 0x10100ff field public static final int textIsSelectable = 16843542; // 0x1010316 field public static final int textOff = 16843045; // 0x1010125 @@ -1023,7 +1023,7 @@ package android { field public static final int toYScale = 16843205; // 0x10101c5 field public static final int top = 16843182; // 0x10101ae field public static final int topBright = 16842955; // 0x10100cb - field public static final int topChevronDrawable = 16843658; // 0x101038a + field public static final int topChevronDrawable = 16843657; // 0x1010389 field public static final int topDark = 16842951; // 0x10100c7 field public static final int topLeftRadius = 16843177; // 0x10101a9 field public static final int topOffset = 16843352; // 0x1010258 @@ -1039,7 +1039,7 @@ package android { field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344 field public static final int unselectedAlpha = 16843278; // 0x101020e field public static final int updatePeriodMillis = 16843344; // 0x1010250 - field public static final int useDefaultMargins = 16843639; // 0x1010377 + field public static final int useDefaultMargins = 16843638; // 0x1010376 field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310 field public static final int useLevel = 16843167; // 0x101019f field public static final int userVisible = 16843409; // 0x1010291 @@ -1053,10 +1053,10 @@ package android { field public static final int verticalCorrection = 16843322; // 0x101023a field public static final int verticalDivider = 16843054; // 0x101012e field public static final int verticalGap = 16843328; // 0x1010240 - field public static final int verticalOffset = 16843666; // 0x1010392 + field public static final int verticalOffset = 16843665; // 0x1010391 field public static final int verticalScrollbarPosition = 16843572; // 0x1010334 field public static final int verticalSpacing = 16843029; // 0x1010115 - field public static final int vibrationDuration = 16843663; // 0x101038f + field public static final int vibrationDuration = 16843662; // 0x101038e field public static final int visibility = 16842972; // 0x10100dc field public static final int visible = 16843156; // 0x1010194 field public static final int vmSafeMode = 16843448; // 0x10102b8 @@ -1073,7 +1073,7 @@ package android { field public static final int wallpaperIntraOpenExitAnimation = 16843416; // 0x1010298 field public static final int wallpaperOpenEnterAnimation = 16843411; // 0x1010293 field public static final int wallpaperOpenExitAnimation = 16843412; // 0x1010294 - field public static final int waveDrawable = 16843660; // 0x101038c + field public static final int waveDrawable = 16843659; // 0x101038b field public static final int webTextViewStyle = 16843449; // 0x10102b9 field public static final int webViewStyle = 16842885; // 0x1010085 field public static final int weekDayTextAppearance = 16843592; // 0x1010348 @@ -4784,6 +4784,7 @@ package android.content { field public static final java.lang.String SENSOR_SERVICE = "sensor"; field public static final java.lang.String STORAGE_SERVICE = "storage"; field public static final java.lang.String TELEPHONY_SERVICE = "phone"; + field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices"; field public static final java.lang.String UI_MODE_SERVICE = "uimode"; field public static final java.lang.String USB_SERVICE = "usb"; field public static final java.lang.String VIBRATOR_SERVICE = "vibrator"; @@ -10405,8 +10406,8 @@ package android.media { method public void setAudioEncodingBitRate(int); method public void setAudioSamplingRate(int); method public void setAudioSource(int) throws java.lang.IllegalStateException; - method public void setAuxiliaryOutputFile(java.io.FileDescriptor); - method public void setAuxiliaryOutputFile(java.lang.String); + method public deprecated void setAuxiliaryOutputFile(java.io.FileDescriptor); + method public deprecated void setAuxiliaryOutputFile(java.lang.String); method public void setCamera(android.hardware.Camera); method public void setCaptureRate(double); method public void setLocation(float, float); @@ -17942,6 +17943,31 @@ package android.security { } +package android.service.textservice { + + public abstract class SpellCheckerService extends android.app.Service { + ctor public SpellCheckerService(); + method public void cancel(); + method public abstract android.view.textservice.SuggestionsInfo getSuggestions(android.view.textservice.TextInfo, int, java.lang.String); + method public android.view.textservice.SuggestionsInfo[] getSuggestionsMultiple(android.view.textservice.TextInfo[], java.lang.String, int, boolean); + method public final android.os.IBinder onBind(android.content.Intent); + field public static final java.lang.String SERVICE_INTERFACE; + } + + public class SpellCheckerSession { + method public void close(); + method public android.view.textservice.SpellCheckerInfo getSpellChecker(); + method public void getSuggestions(android.view.textservice.TextInfo, int); + method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean); + method public boolean isSessionDisconnected(); + } + + public static abstract interface SpellCheckerSession.SpellCheckerSessionListener { + method public abstract void onGetSuggestions(android.view.textservice.SuggestionsInfo[]); + } + +} + package android.service.wallpaper { public abstract class WallpaperService extends android.app.Service { @@ -21005,6 +21031,11 @@ package android.view { method public void onPrepareSubMenu(android.view.SubMenu); } + public abstract interface CollapsibleActionView { + method public abstract void onActionViewCollapsed(); + method public abstract void onActionViewExpanded(); + } + public abstract interface ContextMenu implements android.view.Menu { method public abstract void clearHeader(); method public abstract android.view.ContextMenu setHeaderIcon(int); @@ -22715,10 +22746,8 @@ package android.view { ctor public ViewDebug(); method public static void dumpCapturedView(java.lang.String, java.lang.Object); method public static void startHierarchyTracing(java.lang.String, android.view.View); - method public static void startLooperProfiling(java.io.File); method public static void startRecyclerTracing(java.lang.String, android.view.View); method public static void stopHierarchyTracing(); - method public static void stopLooperProfiling(); method public static void stopRecyclerTracing(); method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...); method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType); @@ -24019,6 +24048,52 @@ package android.view.inputmethod { } +package android.view.textservice { + + public final class SpellCheckerInfo implements android.os.Parcelable { + method public int describeContents(); + method public android.content.ComponentName getComponent(); + method public java.lang.String getId(); + method public java.lang.String getPackageName(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public final class SuggestionsInfo implements android.os.Parcelable { + ctor public SuggestionsInfo(int, java.lang.String[]); + ctor public SuggestionsInfo(int, java.lang.String[], int, int); + ctor public SuggestionsInfo(android.os.Parcel); + method public int describeContents(); + method public int getCookie(); + method public int getSequence(); + method public java.lang.String getSuggestionAt(int); + method public int getSuggestionsAttributes(); + method public int getSuggestionsCount(); + method public void setCookieAndSequence(int, int); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int RESULT_ATTR_IN_THE_DICTIONARY = 1; // 0x1 + field public static final int RESULT_ATTR_LOOKS_TYPO = 2; // 0x2 + } + + public final class TextInfo implements android.os.Parcelable { + ctor public TextInfo(java.lang.String); + ctor public TextInfo(java.lang.String, int, int); + ctor public TextInfo(android.os.Parcel); + method public int describeContents(); + method public int getCookie(); + method public int getSequence(); + method public java.lang.String getText(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public final class TextServicesManager { + method public android.service.textservice.SpellCheckerSession newSpellCheckerSession(java.util.Locale, android.service.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean); + } + +} + package android.webkit { public final deprecated class CacheManager { diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 3fb17362f4c1..6dfa12be9592 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -468,10 +468,16 @@ public class Am { String profileFile = null; boolean start = false; boolean wall = false; + int profileType = 0; String process = null; String cmd = nextArgRequired(); + if ("looper".equals(cmd)) { + cmd = nextArgRequired(); + profileType = 1; + } + if ("start".equals(cmd)) { start = true; wall = "--wall".equals(nextOption()); @@ -516,7 +522,7 @@ public class Am { } else if (start) { //removeWallOption(); } - if (!mAm.profileControl(process, start, profileFile, fd)) { + if (!mAm.profileControl(process, start, profileFile, fd, profileType)) { wall = false; throw new AndroidException("PROFILE FAILED on process " + process); } @@ -1076,8 +1082,8 @@ public class Am { " am broadcast <INTENT>\n" + " am instrument [-r] [-e <NAME> <VALUE>] [-p] [-w]\n" + " [--no-window-animation] <COMPONENT>\n" + - " am profile start <PROCESS> <FILE>\n" + - " am profile stop <PROCESS>\n" + + " am profile [looper] start <PROCESS> <FILE>\n" + + " am profile [looper] stop <PROCESS>\n" + " am dumpheap [flags] <PROCESS> <FILE>\n" + " am monitor [--gdb <port>]\n" + " am screen-compat [on|off] <PACKAGE>\n" + diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 2a731a3999fc..b7cd829563c9 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1104,10 +1104,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); String process = data.readString(); boolean start = data.readInt() != 0; + int profileType = data.readInt(); String path = data.readString(); ParcelFileDescriptor fd = data.readInt() != 0 ? data.readFileDescriptor() : null; - boolean res = profileControl(process, start, path, fd); + boolean res = profileControl(process, start, path, fd, profileType); reply.writeNoException(); reply.writeInt(res ? 1 : 0); return true; @@ -2888,13 +2889,14 @@ class ActivityManagerProxy implements IActivityManager } public boolean profileControl(String process, boolean start, - String path, ParcelFileDescriptor fd) throws RemoteException + String path, ParcelFileDescriptor fd, int profileType) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeString(process); data.writeInt(start ? 1 : 0); + data.writeInt(profileType); data.writeString(path); if (fd != null) { data.writeInt(1); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f6cd866d4e34..9bbbd6c76753 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -676,11 +676,12 @@ public final class ActivityThread { queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token); } - public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) { + public void profilerControl(boolean start, String path, ParcelFileDescriptor fd, + int profileType) { ProfilerControlData pcd = new ProfilerControlData(); pcd.path = path; pcd.fd = fd; - queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0); + queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0, profileType); } public void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) { @@ -954,7 +955,7 @@ public final class ActivityThread { } public void scheduleTrimMemory(int level) { - queueOrSendMessage(H.TRIM_MEMORY, level); + queueOrSendMessage(H.TRIM_MEMORY, null, level); } } @@ -1148,7 +1149,7 @@ public final class ActivityThread { handleActivityConfigurationChanged((IBinder)msg.obj); break; case PROFILER_CONTROL: - handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj); + handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj, msg.arg2); break; case CREATE_BACKUP_AGENT: handleCreateBackupAgent((CreateBackupAgentData)msg.obj); @@ -1184,8 +1185,10 @@ public final class ActivityThread { break; case UPDATE_PACKAGE_COMPATIBILITY_INFO: handleUpdatePackageCompatibilityInfo((UpdateCompatibilityData)msg.obj); + break; case TRIM_MEMORY: handleTrimMemory(msg.arg1); + break; } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what); } @@ -3469,11 +3472,18 @@ public final class ActivityThread { performConfigurationChanged(r.activity, mCompatConfiguration); } - final void handleProfilerControl(boolean start, ProfilerControlData pcd) { + final void handleProfilerControl(boolean start, ProfilerControlData pcd, int profileType) { if (start) { try { - Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(), - 8 * 1024 * 1024, 0); + switch (profileType) { + case 1: + ViewDebug.startLooperProfiling(pcd.path, pcd.fd.getFileDescriptor()); + break; + default: + Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(), + 8 * 1024 * 1024, 0); + break; + } } catch (RuntimeException e) { Slog.w(TAG, "Profiling failed on path " + pcd.path + " -- can the process access this path?"); @@ -3485,7 +3495,15 @@ public final class ActivityThread { } } } else { - Debug.stopMethodTracing(); + switch (profileType) { + case 1: + ViewDebug.stopLooperProfiling(); + break; + default: + Debug.stopMethodTracing(); + break; + + } } } diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 942f245bebf8..9a5b527d7c5b 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -376,10 +376,11 @@ public abstract class ApplicationThreadNative extends Binder { data.enforceInterface(IApplicationThread.descriptor); boolean start = data.readInt() != 0; + int profileType = data.readInt(); String path = data.readString(); ParcelFileDescriptor fd = data.readInt() != 0 ? data.readFileDescriptor() : null; - profilerControl(start, path, fd); + profilerControl(start, path, fd, profileType); return true; } @@ -936,10 +937,11 @@ class ApplicationThreadProxy implements IApplicationThread { } public void profilerControl(boolean start, String path, - ParcelFileDescriptor fd) throws RemoteException { + ParcelFileDescriptor fd, int profileType) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeInt(start ? 1 : 0); + data.writeInt(profileType); data.writeString(path); if (fd != null) { data.writeInt(1); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index d2323e7defec..6289730cb56e 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -83,6 +83,7 @@ import android.view.ContextThemeWrapper; import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.InputMethodManager; +import android.view.textservice.TextServicesManager; import android.accounts.AccountManager; import android.accounts.IAccountManager; import android.app.admin.DevicePolicyManager; @@ -320,6 +321,11 @@ class ContextImpl extends Context { return InputMethodManager.getInstance(ctx); }}); + registerService(TEXT_SERVICES_MANAGER_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return TextServicesManager.getInstance(); + }}); + registerService(KEYGUARD_SERVICE, new ServiceFetcher() { public Object getService(ContextImpl ctx) { // TODO: why isn't this caching it? It wasn't diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index c82c9ecf7a8f..789d3a692775 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1695,6 +1695,7 @@ final class FragmentManagerImpl extends FragmentManager { public void dispatchDestroy() { mDestroyed = true; + execPendingActions(); moveToState(Fragment.INITIALIZING, false); mActivity = null; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 93c821c04469..64d77e80750c 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -284,7 +284,7 @@ public interface IActivityManager extends IInterface { // Turn on/off profiling in a particular process. public boolean profileControl(String process, boolean start, - String path, ParcelFileDescriptor fd) throws RemoteException; + String path, ParcelFileDescriptor fd, int profileType) throws RemoteException; public boolean shutdown(int timeout) throws RemoteException; diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 9de0bf4ffa0e..d0607d09040d 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -105,7 +105,7 @@ public interface IApplicationThread extends IInterface { throws RemoteException; void scheduleLowMemory() throws RemoteException; void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException; - void profilerControl(boolean start, String path, ParcelFileDescriptor fd) + void profilerControl(boolean start, String path, ParcelFileDescriptor fd, int profileType) throws RemoteException; void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) throws RemoteException; diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 019652c024d9..1ef99a1e6845 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -388,6 +388,10 @@ public class AppWidgetManager { TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics); info.minHeight = TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics); + info.minResizeWidth = + TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics); + info.minResizeHeight = + TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics); } return providers; } @@ -411,6 +415,10 @@ public class AppWidgetManager { TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics); info.minHeight = TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics); + info.minResizeWidth = + TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics); + info.minResizeHeight = + TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics); } return info; } diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java index b8c5b028b165..9c352d58dfb2 100644 --- a/core/java/android/appwidget/AppWidgetProviderInfo.java +++ b/core/java/android/appwidget/AppWidgetProviderInfo.java @@ -187,6 +187,8 @@ public class AppWidgetProviderInfo implements Parcelable { } this.minWidth = in.readInt(); this.minHeight = in.readInt(); + this.minResizeWidth = in.readInt(); + this.minResizeHeight = in.readInt(); this.updatePeriodMillis = in.readInt(); this.initialLayout = in.readInt(); if (0 != in.readInt()) { @@ -208,6 +210,8 @@ public class AppWidgetProviderInfo implements Parcelable { } out.writeInt(this.minWidth); out.writeInt(this.minHeight); + out.writeInt(this.minResizeWidth); + out.writeInt(this.minResizeHeight); out.writeInt(this.updatePeriodMillis); out.writeInt(this.initialLayout); if (this.configure != null) { diff --git a/core/java/android/content/ComponentCallbacks.java b/core/java/android/content/ComponentCallbacks.java index 92b98fd81b39..37cc1416ed81 100644 --- a/core/java/android/content/ComponentCallbacks.java +++ b/core/java/android/content/ComponentCallbacks.java @@ -56,11 +56,8 @@ public interface ComponentCallbacks { static final int TRIM_MEMORY_COMPLETE = 80; /** @hide */ - static final int TRIM_MEMORY_MODERATE = 60; + static final int TRIM_MEMORY_MODERATE = 50; /** @hide */ - static final int TRIM_MEMORY_BACKGROUND = 40; - - /** @hide */ - static final int TRIM_MEMORY_INVISIBLE = 20; + static final int TRIM_MEMORY_BACKGROUND = 20; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fed6d815c9f2..0a2253c8dc75 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1612,6 +1612,15 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a + * {@link android.view.textservice.TextServicesManager} for accessing + * text services. + * + * @see #getSystemService + */ + public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices"; + + /** + * Use with {@link #getSystemService} to retrieve a * {@link android.appwidget.AppWidgetManager} for accessing AppWidgets. * * @hide diff --git a/core/java/android/inputmethodservice/ExtractEditLayout.java b/core/java/android/inputmethodservice/ExtractEditLayout.java index eafff49528f5..5cfa998857b7 100644 --- a/core/java/android/inputmethodservice/ExtractEditLayout.java +++ b/core/java/android/inputmethodservice/ExtractEditLayout.java @@ -172,7 +172,10 @@ public class ExtractEditLayout extends LinearLayout { @Override public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { - return mCallback.onActionItemClicked(this, item); + if (mCallback != null) { + return mCallback.onActionItemClicked(this, item); + } + return false; } @Override diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index ce6f697be6be..a564d9771f7b 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -653,6 +653,17 @@ public class ConnectivityManager { } } + /** + * {@hide} + */ + public int setUsbTethering(boolean enable) { + try { + return mService.setUsbTethering(enable); + } catch (RemoteException e) { + return TETHER_ERROR_SERVICE_UNAVAIL; + } + } + /** {@hide} */ public static final int TETHER_ERROR_NO_ERROR = 0; /** {@hide} */ diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index a866436ed77d..b035c51d3da5 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -103,6 +103,10 @@ public class EthernetDataTracker implements NetworkStateTracker { public void interfaceRemoved(String iface) { mTracker.interfaceRemoved(iface); } + + public void limitReached(String limitName, String iface) { + // Ignored. + } } private EthernetDataTracker() { diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d95fc8de70c3..b1d99a4abe42 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -88,6 +88,8 @@ interface IConnectivityManager String[] getTetherableBluetoothRegexs(); + int setUsbTethering(boolean enable); + void requestNetworkTransitionWakelock(in String forWhom); void reportInetCondition(int networkType, int percentage); diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl index 4436e6e444ee..a97f2030a146 100644 --- a/core/java/android/net/INetworkManagementEventObserver.aidl +++ b/core/java/android/net/INetworkManagementEventObserver.aidl @@ -52,4 +52,14 @@ interface INetworkManagementEventObserver { * @param iface The interface. */ void interfaceRemoved(String iface); + + /** + * A networking quota limit has been reached. The quota might not + * be specific to an interface. + * + * @param limitName The name of the limit that triggered. + * @param iface The interface on which the limit was detected. + */ + void limitReached(String limitName, String iface); + } diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 9826becf3180..132f3bad2d26 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -58,8 +58,8 @@ public class LinkProperties implements Parcelable { private ProxyProperties mHttpProxy; public static class CompareResult<T> { - public ArrayList<T> removed = new ArrayList<T>(); - public ArrayList<T> added = new ArrayList<T>(); + public Collection<T> removed = new ArrayList<T>(); + public Collection<T> added = new ArrayList<T>(); @Override public String toString() { diff --git a/core/java/android/net/VpnBuilder.java b/core/java/android/net/VpnBuilder.java new file mode 100644 index 000000000000..458252345152 --- /dev/null +++ b/core/java/android/net/VpnBuilder.java @@ -0,0 +1,413 @@ +/* + * 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.net; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; + +import com.android.internal.net.VpnConfig; + +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.DatagramSocket; +import java.net.Socket; +import java.util.ArrayList; + +/** + * VpnBuilder is a framework which enables applications to build their + * own VPN solutions. In general, it creates a virtual network interface, + * configures addresses and routing rules, and returns a file descriptor + * to the application. Each read from the descriptor retrieves an outgoing + * packet which was routed to the interface. Each write to the descriptor + * injects an incoming packet just like it was received from the interface. + * The framework is running on Internet Protocol (IP), so packets are + * always started with IP headers. The application then completes a VPN + * connection by processing and exchanging packets with a remote server + * over a secured tunnel. + * + * <p>Letting applications intercept packets raises huge security concerns. + * Besides, a VPN application can easily break the network, and two of them + * may conflict with each other. The framework takes several actions to + * address these issues. Here are some key points: + * <ul> + * <li>User action is required to create a VPN connection.</li> + * <li>There can be only one VPN connection running at the same time. The + * existing interface is deactivated when a new one is created.</li> + * <li>A system-managed notification is shown during the lifetime of a + * VPN connection.</li> + * <li>A system-managed dialog gives the information of the current VPN + * connection. It also provides a button to disconnect.</li> + * <li>The network is restored automatically when the file descriptor is + * closed. It also covers the cases when a VPN application is crashed + * or killed by the system.</li> + * </ul> + * + * <p>There are two primary methods in this class: {@link #prepare} and + * {@link #establish}. The former deals with the user action and stops + * the existing VPN connection created by another application. The latter + * creates a VPN interface using the parameters supplied to this builder. + * An application must call {@link #prepare} to grant the right to create + * an interface, and it can be revoked at any time by another application. + * The application got revoked is notified by an {@link #ACTION_VPN_REVOKED} + * broadcast. Here are the general steps to create a VPN connection: + * <ol> + * <li>When the user press the button to connect, call {@link #prepare} + * and launch the intent if necessary.</li> + * <li>Register a receiver for {@link #ACTION_VPN_REVOKED} broadcasts. + * <li>Connect to the remote server and negotiate the network parameters + * of the VPN connection.</li> + * <li>Use those parameters to configure a VpnBuilder and create a VPN + * interface by calling {@link #establish}.</li> + * <li>Start processing packets between the returned file descriptor and + * the VPN tunnel.</li> + * <li>When an {@link #ACTION_VPN_REVOKED} broadcast is received, the + * interface is already deactivated by the framework. Close the file + * descriptor and shut down the VPN tunnel gracefully. + * </ol> + * Methods in this class can be used in activities and services. However, + * the intent returned from {@link #prepare} must be launched from an + * activity. The broadcast receiver can be registered at any time, but doing + * it before calling {@link #establish} effectively avoids race conditions. + * + * <p class="note">Using this class requires + * {@link android.Manifest.permission#VPN} permission. + * @hide + */ +public class VpnBuilder { + + /** + * Broadcast intent action indicating that the VPN application has been + * revoked. This can be only received by the target application on the + * receiver explicitly registered using {@link Context#registerReceiver}. + * + * <p>This is a protected intent that can only be sent by the system. + */ + public static final String ACTION_VPN_REVOKED = VpnConfig.ACTION_VPN_REVOKED; + + /** + * Use IConnectivityManager instead since those methods are hidden and + * not available in ConnectivityManager. + */ + private static IConnectivityManager getService() { + return IConnectivityManager.Stub.asInterface( + ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); + } + + /** + * Prepare to establish a VPN connection. This method returns {@code null} + * if the VPN application is already prepared. Otherwise, it returns an + * {@link Intent} to a system activity. The application should launch the + * activity using {@link Activity#startActivityForResult} to get itself + * prepared. The activity may pop up a dialog to require user action, and + * the result will come back to the application through its + * {@link Activity#onActivityResult}. The application becomes prepared if + * the result is {@link Activity#RESULT_OK}, and it is granted to create a + * VPN interface by calling {@link #establish}. + * + * <p>Only one application can be granted at the same time. The right + * is revoked when another application is granted. The application + * losing the right will be notified by an {@link #ACTION_VPN_REVOKED} + * broadcast, and its VPN interface will be deactivated by the system. + * The application should then notify the remote server and disconnect + * gracefully. Unless the application becomes prepared again, subsequent + * calls to {@link #establish} will return {@code null}. + * + * @see #establish + * @see #ACTION_VPN_REVOKED + */ + public static Intent prepare(Context context) { + try { + if (getService().prepareVpn(context.getPackageName(), null)) { + return null; + } + } catch (RemoteException e) { + // ignore + } + return VpnConfig.getIntentForConfirmation(); + } + + private VpnConfig mConfig = new VpnConfig(); + private StringBuilder mAddresses = new StringBuilder(); + private StringBuilder mRoutes = new StringBuilder(); + + /** + * Set the name of this session. It will be displayed in system-managed + * dialogs and notifications. This is recommended not required. + */ + public VpnBuilder setSession(String session) { + mConfig.session = session; + return this; + } + + /** + * Set the {@link PendingIntent} to an activity for users to configure + * the VPN connection. If it is not set, the button to configure will + * not be shown in system-managed dialogs. + */ + public VpnBuilder setConfigureIntent(PendingIntent intent) { + mConfig.configureIntent = intent; + return this; + } + + /** + * Set the maximum transmission unit (MTU) of the VPN interface. If it + * is not set, the default value in the operating system will be used. + * + * @throws IllegalArgumentException if the value is not positive. + */ + public VpnBuilder setMtu(int mtu) { + if (mtu <= 0) { + throw new IllegalArgumentException("Bad mtu"); + } + mConfig.mtu = mtu; + return this; + } + + /** + * Private method to validate address and prefixLength. + */ + private static void check(InetAddress address, int prefixLength) { + if (address.isLoopbackAddress()) { + throw new IllegalArgumentException("Bad address"); + } + if (address instanceof Inet4Address) { + if (prefixLength < 0 || prefixLength > 32) { + throw new IllegalArgumentException("Bad prefixLength"); + } + } else if (address instanceof Inet6Address) { + if (prefixLength < 0 || prefixLength > 128) { + throw new IllegalArgumentException("Bad prefixLength"); + } + } else { + throw new IllegalArgumentException("Unsupported family"); + } + } + + /** + * Convenience method to add a network address to the VPN interface + * using a numeric address string. See {@link InetAddress} for the + * definitions of numeric address formats. + * + * @throws IllegalArgumentException if the address is invalid. + * @see #addAddress(InetAddress, int) + */ + public VpnBuilder addAddress(String address, int prefixLength) { + return addAddress(InetAddress.parseNumericAddress(address), prefixLength); + } + + /** + * Add a network address to the VPN interface. Both IPv4 and IPv6 + * addresses are supported. At least one address must be set before + * calling {@link #establish}. + * + * @throws IllegalArgumentException if the address is invalid. + */ + public VpnBuilder addAddress(InetAddress address, int prefixLength) { + check(address, prefixLength); + + if (address.isAnyLocalAddress()) { + throw new IllegalArgumentException("Bad address"); + } + + mAddresses.append(String.format(" %s/%d", address.getHostAddress(), prefixLength)); + return this; + } + + /** + * Convenience method to add a network route to the VPN interface + * using a numeric address string. See {@link InetAddress} for the + * definitions of numeric address formats. + * + * @see #addRoute(InetAddress, int) + * @throws IllegalArgumentException if the route is invalid. + */ + public VpnBuilder addRoute(String address, int prefixLength) { + return addRoute(InetAddress.parseNumericAddress(address), prefixLength); + } + + /** + * Add a network route to the VPN interface. Both IPv4 and IPv6 + * routes are supported. + * + * @throws IllegalArgumentException if the route is invalid. + */ + public VpnBuilder addRoute(InetAddress address, int prefixLength) { + check(address, prefixLength); + + int offset = prefixLength / 8; + byte[] bytes = address.getAddress(); + if (offset < bytes.length) { + if ((byte)(bytes[offset] << (prefixLength % 8)) != 0) { + throw new IllegalArgumentException("Bad address"); + } + while (++offset < bytes.length) { + if (bytes[offset] != 0) { + throw new IllegalArgumentException("Bad address"); + } + } + } + + mRoutes.append(String.format(" %s/%d", address.getHostAddress(), prefixLength)); + return this; + } + + /** + * Convenience method to add a DNS server to the VPN connection + * using a numeric address string. See {@link InetAddress} for the + * definitions of numeric address formats. + * + * @throws IllegalArgumentException if the address is invalid. + * @see #addDnsServer(InetAddress) + */ + public VpnBuilder addDnsServer(String address) { + return addDnsServer(InetAddress.parseNumericAddress(address)); + } + + /** + * Add a DNS server to the VPN connection. Both IPv4 and IPv6 + * addresses are supported. If none is set, the DNS servers of + * the default network will be used. + * + * @throws IllegalArgumentException if the address is invalid. + */ + public VpnBuilder addDnsServer(InetAddress address) { + if (address.isLoopbackAddress() || address.isAnyLocalAddress()) { + throw new IllegalArgumentException("Bad address"); + } + if (mConfig.dnsServers == null) { + mConfig.dnsServers = new ArrayList<String>(); + } + mConfig.dnsServers.add(address.getHostAddress()); + return this; + } + + /** + * Add a search domain to the DNS resolver. + */ + public VpnBuilder addSearchDomain(String domain) { + if (mConfig.searchDomains == null) { + mConfig.searchDomains = new ArrayList<String>(); + } + mConfig.searchDomains.add(domain); + return this; + } + + /** + * Create a VPN interface using the parameters supplied to this builder. + * The interface works on IP packets, and a file descriptor is returned + * for the application to access them. Each read retrieves an outgoing + * packet which was routed to the interface. Each write injects an + * incoming packet just like it was received from the interface. The file + * descriptor is put into non-blocking mode by default to avoid blocking + * Java threads. To use the file descriptor completely in native space, + * see {@link ParcelFileDescriptor#detachFd()}. The application MUST + * close the file descriptor when the VPN connection is terminated. The + * VPN interface will be removed and the network will be restored by the + * framework automatically. + * + * <p>To avoid conflicts, there can be only one active VPN interface at + * the same time. Usually network parameters are never changed during the + * lifetime of a VPN connection. It is also common for an application to + * create a new file descriptor after closing the previous one. However, + * it is rare but not impossible to have two interfaces while performing a + * seamless handover. In this case, the old interface will be deactivated + * when the new one is configured successfully. Both file descriptors are + * valid but now outgoing packets will be routed to the new interface. + * Therefore, after draining the old file descriptor, the application MUST + * close it and start using the new file descriptor. If the new interface + * cannot be created, the existing interface and its file descriptor remain + * untouched. + * + * <p>An exception will be thrown if the interface cannot be created for + * any reason. However, this method returns {@code null} if the application + * is not prepared or is revoked by another application. This helps solve + * possible race conditions while handling {@link #ACTION_VPN_REVOKED} + * broadcasts. + * + * @return {@link ParcelFileDescriptor} of the VPN interface, or + * {@code null} if the application is not prepared. + * @throws IllegalArgumentException if a parameter is not accepted by the + * operating system. + * @throws IllegalStateException if a parameter cannot be applied by the + * operating system. + * @see #prepare + */ + public ParcelFileDescriptor establish() { + mConfig.addresses = mAddresses.toString(); + mConfig.routes = mRoutes.toString(); + + try { + return getService().establishVpn(mConfig); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + } + + /** + * Protect a socket from VPN connections. The socket will be bound to the + * current default network interface, so its traffic will not be forwarded + * through VPN. This method is useful if some connections need to be kept + * outside of VPN. For example, a VPN tunnel should protect itself if its + * destination is covered by VPN routes. Otherwise its outgoing packets + * will be sent back to the VPN interface and cause an infinite loop. + * + * <p>The socket is NOT closed by this method. + * + * @return {@code true} on success. + */ + public static boolean protect(int socket) { + ParcelFileDescriptor dup = null; + try { + dup = ParcelFileDescriptor.fromFd(socket); + return getService().protectVpn(dup); + } catch (Exception e) { + return false; + } finally { + try { + dup.close(); + } catch (Exception e) { + // ignore + } + } + } + + /** + * Protect a {@link Socket} from VPN connections. + * + * @return {@code true} on success. + * @see #protect(int) + */ + public static boolean protect(Socket socket) { + return protect(socket.getFileDescriptor$().getInt$()); + } + + /** + * Protect a {@link DatagramSocket} from VPN connections. + * + * @return {@code true} on success. + * @see #protect(int) + */ + public static boolean protect(DatagramSocket socket) { + return protect(socket.getFileDescriptor$().getInt$()); + } +} diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index cd39d5cc6dee..bc372447d63e 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -361,7 +361,8 @@ public class Handler { /** * Remove any pending posts of Runnable <var>r</var> with Object - * <var>token</var> that are in the message queue. + * <var>token</var> that are in the message queue. If <var>token</var> is null, + * all callbacks will be removed. */ public final void removeCallbacks(Runnable r, Object token) { @@ -517,7 +518,8 @@ public class Handler { /** * Remove any pending posts of messages with code 'what' and whose obj is - * 'object' that are in the message queue. + * 'object' that are in the message queue. If <var>token</var> is null, + * all messages will be removed. */ public final void removeMessages(int what, Object object) { mQueue.removeMessages(this, what, object, true); @@ -525,7 +527,8 @@ public class Handler { /** * Remove any pending posts of callbacks and sent messages whose - * <var>obj</var> is <var>token</var>. + * <var>obj</var> is <var>token</var>. If <var>token</var> is null, + * all callbacks and messages will be removed. */ public final void removeCallbacksAndMessages(Object token) { mQueue.removeCallbacksAndMessages(this, token); diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index c90de176896b..78c9010af8fd 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -563,6 +563,12 @@ public abstract class PreferenceActivity extends ListActivity implements // Single pane, showing just a prefs fragment. findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE); mPrefsContainer.setVisibility(View.VISIBLE); + if (initialTitle != 0) { + CharSequence initialTitleStr = getText(initialTitle); + CharSequence initialShortTitleStr = initialShortTitle != 0 + ? getText(initialShortTitle) : null; + showBreadCrumbs(initialTitleStr, initialShortTitleStr); + } } else if (mHeaders.size() > 0) { setListAdapter(new HeaderAdapter(this, mHeaders)); if (!mSinglePane) { @@ -1093,6 +1099,10 @@ public abstract class PreferenceActivity extends ListActivity implements } else { getListView().clearChoices(); } + showBreadCrumbs(header); + } + + void showBreadCrumbs(Header header) { if (header != null) { CharSequence title = header.getBreadCrumbTitle(getResources()); if (title == null) title = header.getTitle(getResources()); diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index c2998916add4..4a719ec7b9a1 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1439,6 +1439,13 @@ public final class ContactsContract { CONTENT_URI, "strequent"); /** + * The content:// style URI for showing frequently contacted person listing. + * @hide + */ + public static final Uri CONTENT_FREQUENT_URI = Uri.withAppendedPath( + CONTENT_URI, "frequent"); + + /** * The content:// style URI used for "type-to-filter" functionality on the * {@link #CONTENT_STREQUENT_URI} URI. The filter string will be used to match * various parts of the contact name. The filter argument should be passed diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index cb87e94164bb..1cd46dedd2d2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -16,8 +16,6 @@ package android.provider; - - import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.SearchManager; @@ -48,7 +46,6 @@ import java.net.URISyntaxException; import java.util.HashMap; import java.util.HashSet; - /** * The Settings provider contains global system-level device preferences. */ @@ -3737,6 +3734,15 @@ public final class Settings { */ public static final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service"; + + /** + * The {@link ComponentName} string of the service to be used as the spell checker + * service which is one of the services managed by the text service manager. + * + * @hide + */ + public static final String SPELL_CHECKER_SERVICE = "spell_checker_service"; + /** * What happens when the user presses the Power button while in-call * and the screen is on.<br/> diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index ab569a1dea89..d68d8ba1e5b9 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -527,10 +527,19 @@ public class BluetoothService extends IBluetooth.Stub { break; case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY: address = (String)msg.obj; - if (address != null) { + if (address == null) return; + int attempt = mBondState.getAttempt(address); + + // Try only if attemps are in progress and cap it 2 attempts + // The 2 attempts cap is a fail safe if the stack returns + // an incorrect error code for bonding failures and if the pin + // is entered wrongly twice we should abort. + if (attempt > 0 && attempt <= 2) { + mBondState.attempt(address); createBond(address); return; } + if (attempt > 0) mBondState.clearPinAttempts(address); break; } } @@ -741,7 +750,6 @@ public class BluetoothService extends IBluetooth.Stub { BluetoothDevice.BOND_NONE, result); return; } - mBondState.attempt(address); } /*package*/ BluetoothDevice getRemoteDevice(String address) { diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java new file mode 100644 index 000000000000..6ac99cab0ebf --- /dev/null +++ b/core/java/android/service/textservice/SpellCheckerService.java @@ -0,0 +1,140 @@ +/* + * 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.service.textservice; + +import com.android.internal.textservice.ISpellCheckerService; +import com.android.internal.textservice.ISpellCheckerSession; +import com.android.internal.textservice.ISpellCheckerSessionListener; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.view.textservice.SuggestionsInfo; +import android.view.textservice.TextInfo; + +import java.lang.ref.WeakReference; + +/** + * SpellCheckerService provides an abstract base class for a spell checker. + * This class combines a service to the system with the spell checker service interface that + * spell checker must implement. + */ +public abstract class SpellCheckerService extends Service { + private static final String TAG = SpellCheckerService.class.getSimpleName(); + public static final String SERVICE_INTERFACE = SpellCheckerService.class.getName(); + + private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this); + + /** + * Get suggestions for specified text in TextInfo. + * This function will run on the incoming IPC thread. So, this is not called on the main thread, + * but will be called in series on another thread. + * @param textInfo the text metadata + * @param suggestionsLimit the number of limit of suggestions returned + * @param locale the locale for getting suggestions + * @return SuggestionInfo which contains suggestions for textInfo + */ + public abstract SuggestionsInfo getSuggestions( + TextInfo textInfo, int suggestionsLimit, String locale); + + /** + * A batch process of onGetSuggestions. + * This function will run on the incoming IPC thread. So, this is not called on the main thread, + * but will be called in series on another thread. + * @param textInfos an array of the text metadata + * @param locale the locale for getting suggestions + * @param suggestionsLimit the number of limit of suggestions returned + * @param sequentialWords true if textInfos can be treated as sequential words. + * @return an array of SuggestionInfo of onGetSuggestions + */ + public SuggestionsInfo[] getSuggestionsMultiple( + TextInfo[] textInfos, String locale, int suggestionsLimit, boolean sequentialWords) { + final int length = textInfos.length; + final SuggestionsInfo[] retval = new SuggestionsInfo[length]; + for (int i = 0; i < length; ++i) { + retval[i] = getSuggestions(textInfos[i], suggestionsLimit, locale); + retval[i].setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence()); + } + return retval; + } + + /** + * Request to abort all tasks executed in SpellChecker. + * This function will run on the incoming IPC thread. So, this is not called on the main thread, + * but will be called in series on another thread. + */ + public void cancel() {} + + /** + * Implement to return the implementation of the internal spell checker + * service interface. Subclasses should not override. + */ + @Override + public final IBinder onBind(final Intent intent) { + return mBinder; + } + + private static class SpellCheckerSessionImpl extends ISpellCheckerSession.Stub { + private final WeakReference<SpellCheckerService> mInternalServiceRef; + private final String mLocale; + private final ISpellCheckerSessionListener mListener; + + public SpellCheckerSessionImpl( + SpellCheckerService service, String locale, ISpellCheckerSessionListener listener) { + mInternalServiceRef = new WeakReference<SpellCheckerService>(service); + mLocale = locale; + mListener = listener; + } + + @Override + public void getSuggestionsMultiple( + TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { + final SpellCheckerService service = mInternalServiceRef.get(); + if (service == null) return; + try { + mListener.onGetSuggestions( + service.getSuggestionsMultiple(textInfos, mLocale, + suggestionsLimit, sequentialWords)); + } catch (RemoteException e) { + } + } + + @Override + public void cancel() { + final SpellCheckerService service = mInternalServiceRef.get(); + if (service == null) return; + service.cancel(); + } + } + + private static class SpellCheckerServiceBinder extends ISpellCheckerService.Stub { + private final WeakReference<SpellCheckerService> mInternalServiceRef; + + public SpellCheckerServiceBinder(SpellCheckerService service) { + mInternalServiceRef = new WeakReference<SpellCheckerService>(service); + } + + @Override + public ISpellCheckerSession getISpellCheckerSession( + String locale, ISpellCheckerSessionListener listener) { + final SpellCheckerService service = mInternalServiceRef.get(); + if (service == null) return null; + return new SpellCheckerSessionImpl(service, locale, listener); + } + } +} diff --git a/core/java/android/service/textservice/SpellCheckerSession.java b/core/java/android/service/textservice/SpellCheckerSession.java new file mode 100644 index 000000000000..400454da40f9 --- /dev/null +++ b/core/java/android/service/textservice/SpellCheckerSession.java @@ -0,0 +1,284 @@ +/* + * 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.service.textservice; + +import com.android.internal.textservice.ISpellCheckerSession; +import com.android.internal.textservice.ISpellCheckerSessionListener; +import com.android.internal.textservice.ITextServicesManager; +import com.android.internal.textservice.ITextServicesSessionListener; + +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; +import android.view.textservice.SpellCheckerInfo; +import android.view.textservice.SuggestionsInfo; +import android.view.textservice.TextInfo; + +import java.util.LinkedList; +import java.util.Locale; +import java.util.Queue; + +/** + * The SpellCheckerSession interface provides the per client functionality of SpellCheckerService. + */ +public class SpellCheckerSession { + private static final String TAG = SpellCheckerSession.class.getSimpleName(); + private static final boolean DBG = false; + + private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1; + + private final InternalListener mInternalListener; + private final ITextServicesManager mTextServicesManager; + private final SpellCheckerInfo mSpellCheckerInfo; + private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl; + + private boolean mIsUsed; + private SpellCheckerSessionListener mSpellCheckerSessionListener; + + /** Handler that will execute the main tasks */ + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_ON_GET_SUGGESTION_MULTIPLE: + handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj); + break; + } + } + }; + + /** + * Constructor + * @hide + */ + public SpellCheckerSession( + SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener) { + if (info == null || listener == null || tsm == null) { + throw new NullPointerException(); + } + mSpellCheckerInfo = info; + mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler); + mInternalListener = new InternalListener(); + mTextServicesManager = tsm; + mIsUsed = true; + mSpellCheckerSessionListener = listener; + } + + /** + * @return true if the connection to a text service of this session is disconnected and not + * alive. + */ + public boolean isSessionDisconnected() { + return mSpellCheckerSessionListenerImpl.isDisconnected(); + } + + /** + * Get the spell checker service info this spell checker session has. + * @return SpellCheckerInfo for the specified locale. + */ + public SpellCheckerInfo getSpellChecker() { + return mSpellCheckerInfo; + } + + /** + * Finish this session and allow TextServicesManagerService to disconnect the bound spell + * checker. + */ + public void close() { + mIsUsed = false; + try { + mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl); + } catch (RemoteException e) { + // do nothing + } + } + + /** + * Get candidate strings for a substring of the specified text. + * @param textInfo text metadata for a spell checker + * @param suggestionsLimit the number of limit of suggestions returned + */ + public void getSuggestions(TextInfo textInfo, int suggestionsLimit) { + getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false); + } + + /** + * A batch process of getSuggestions + * @param textInfos an array of text metadata for a spell checker + * @param suggestionsLimit the number of limit of suggestions returned + * @param sequentialWords true if textInfos can be treated as sequential words. + */ + public void getSuggestions( + TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { + // TODO: Handle multiple words suggestions by using WordBreakIterator + mSpellCheckerSessionListenerImpl.getSuggestionsMultiple( + textInfos, suggestionsLimit, sequentialWords); + } + + private void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionInfos) { + mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos); + } + + private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub { + private static final int TASK_CANCEL = 1; + private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2; + private final Queue<SpellCheckerParams> mPendingTasks = + new LinkedList<SpellCheckerParams>(); + private final Handler mHandler; + + private boolean mOpened; + private ISpellCheckerSession mISpellCheckerSession; + + public SpellCheckerSessionListenerImpl(Handler handler) { + mOpened = false; + mHandler = handler; + } + + private static class SpellCheckerParams { + public final int mWhat; + public final TextInfo[] mTextInfos; + public final int mSuggestionsLimit; + public final boolean mSequentialWords; + public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit, + boolean sequentialWords) { + mWhat = what; + mTextInfos = textInfos; + mSuggestionsLimit = suggestionsLimit; + mSequentialWords = sequentialWords; + } + } + + private void processTask(SpellCheckerParams scp) { + switch (scp.mWhat) { + case TASK_CANCEL: + processCancel(); + break; + case TASK_GET_SUGGESTIONS_MULTIPLE: + processGetSuggestionsMultiple(scp); + break; + } + } + + public synchronized void onServiceConnected(ISpellCheckerSession session) { + mISpellCheckerSession = session; + mOpened = true; + if (DBG) + Log.d(TAG, "onServiceConnected - Success"); + while (!mPendingTasks.isEmpty()) { + processTask(mPendingTasks.poll()); + } + } + + public void getSuggestionsMultiple( + TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { + processOrEnqueueTask( + new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos, + suggestionsLimit, sequentialWords)); + } + + public boolean isDisconnected() { + return mOpened && mISpellCheckerSession == null; + } + + public boolean checkOpenConnection() { + if (mISpellCheckerSession != null) { + return true; + } + Log.e(TAG, "not connected to the spellchecker service."); + return false; + } + + private void processOrEnqueueTask(SpellCheckerParams scp) { + if (mISpellCheckerSession == null) { + mPendingTasks.offer(scp); + } else { + processTask(scp); + } + } + + private void processCancel() { + if (!checkOpenConnection()) { + return; + } + try { + mISpellCheckerSession.cancel(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel " + e); + } + } + + private void processGetSuggestionsMultiple(SpellCheckerParams scp) { + if (!checkOpenConnection()) { + return; + } + try { + mISpellCheckerSession.getSuggestionsMultiple( + scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get suggestions " + e); + } + } + + @Override + public void onGetSuggestions(SuggestionsInfo[] results) { + mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results)); + } + } + + /** + * Callback for getting results from text services + */ + public interface SpellCheckerSessionListener { + /** + * Callback for "getSuggestions" + * @param results an array of results of getSuggestions + */ + public void onGetSuggestions(SuggestionsInfo[] results); + } + + private class InternalListener extends ITextServicesSessionListener.Stub { + @Override + public void onServiceConnected(ISpellCheckerSession session) { + mSpellCheckerSessionListenerImpl.onServiceConnected(session); + } + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + if (mIsUsed) { + Log.e(TAG, "SpellCheckerSession was not finished properly." + + "You should call finishShession() when you finished to use a spell checker."); + close(); + } + } + + /** + * @hide + */ + public ITextServicesSessionListener getTextServicesSessionListener() { + return mInternalListener; + } + + /** + * @hide + */ + public ISpellCheckerSessionListener getSpellCheckerSessionListener() { + return mSpellCheckerSessionListenerImpl; + } +} diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java index f1393721cb41..f2a86c969368 100644 --- a/core/java/android/util/JsonReader.java +++ b/core/java/android/util/JsonReader.java @@ -740,8 +740,8 @@ public final class JsonReader implements Closeable { limit += total; // if this is the first read, consume an optional byte order mark (BOM) if it exists - if (bufferStartLine == 1 && bufferStartColumn == 1 - && limit > 0 && buffer[0] == '\ufeff') { + if (bufferStartLine == 1 && bufferStartColumn == 1 + && limit > 0 && buffer[0] == '\ufeff') { pos++; bufferStartColumn--; } @@ -852,7 +852,7 @@ public final class JsonReader implements Closeable { private boolean skipTo(String toFind) throws IOException { outer: - for (; pos + toFind.length() < limit || fillBuffer(toFind.length()); pos++) { + for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) { for (int c = 0; c < toFind.length(); c++) { if (buffer[pos + c] != toFind.charAt(c)) { continue outer; diff --git a/core/java/android/view/CollapsibleActionView.java b/core/java/android/view/CollapsibleActionView.java new file mode 100644 index 000000000000..ab2365eb744c --- /dev/null +++ b/core/java/android/view/CollapsibleActionView.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.view.MenuItem.OnActionExpandListener; + +/** + * When a {@link View} implements this interface it will receive callbacks + * when expanded or collapsed as an action view alongside the optional, + * app-specified callbacks to {@link OnActionExpandListener}. + * + * <p>See {@link MenuItem} for more information about action views. + * See {@link android.app.ActionBar} for more information about the action bar. + */ +public interface CollapsibleActionView { + /** + * Called when this view is expanded as an action view. + * See {@link MenuItem#expandActionView()}. + */ + public void onActionViewExpanded(); + + /** + * Called when this view is collapsed as an action view. + * See {@link MenuItem#collapseActionView()}. + */ + public void onActionViewCollapsed(); +} diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index 4484d59a3ce4..f4c0249ab5a9 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -41,15 +41,6 @@ public abstract class DisplayList { abstract void end(); /** - * Indicates whether this display list can be replayed or not. - * - * @return True if the display list can be replayed, false otherwise. - * - * @see android.view.HardwareCanvas#drawDisplayList(DisplayList) - */ - abstract boolean isReady(); - - /** * Invalidates the display list, indicating that it should be repopulated * with new drawing commands prior to being used again. Calling this method * causes calls to {@link #isValid()} to return <code>false</code>. diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 80244bbe9244..d22fa6e501ec 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -51,6 +51,7 @@ class GLES20Canvas extends HardwareCanvas { // The native renderer will be destroyed when this object dies. // DO NOT overwrite this reference once it is set. + @SuppressWarnings("unused") private CanvasFinalizer mFinalizer; private int mWidth; @@ -97,12 +98,8 @@ class GLES20Canvas extends HardwareCanvas { protected GLES20Canvas(boolean record, boolean translucent) { mOpaque = !translucent; - setupRenderer(record); - } - - protected void setupRenderer(boolean record) { if (record) { - mRenderer = nGetDisplayListRenderer(mRenderer); + mRenderer = nCreateDisplayListRenderer(); } else { mRenderer = nCreateRenderer(); } @@ -114,43 +111,31 @@ class GLES20Canvas extends HardwareCanvas { if (mRenderer == 0) { throw new IllegalStateException("Could not create GLES20Canvas renderer"); } else { - mFinalizer = CanvasFinalizer.getFinalizer(mFinalizer, mRenderer); + mFinalizer = new CanvasFinalizer(mRenderer); } } + protected void resetDisplayListRenderer() { + nResetDisplayListRenderer(mRenderer); + } + private static native int nCreateRenderer(); private static native int nCreateLayerRenderer(int layer); - private static native int nGetDisplayListRenderer(int renderer); + private static native int nCreateDisplayListRenderer(); + private static native void nResetDisplayListRenderer(int renderer); private static native void nDestroyRenderer(int renderer); - private static class CanvasFinalizer { - int mRenderer; - - // Factory method returns new instance if old one is null, or old instance - // otherwise, destroying native renderer along the way as necessary - static CanvasFinalizer getFinalizer(CanvasFinalizer oldFinalizer, int renderer) { - if (oldFinalizer == null) { - return new CanvasFinalizer(renderer); - } - oldFinalizer.replaceNativeObject(renderer); - return oldFinalizer; - } + private static final class CanvasFinalizer { + private final int mRenderer; - private CanvasFinalizer(int renderer) { + public CanvasFinalizer(int renderer) { mRenderer = renderer; } - private void replaceNativeObject(int newRenderer) { - if (mRenderer != 0 && newRenderer != mRenderer) { - nDestroyRenderer(mRenderer); - } - mRenderer = newRenderer; - } - @Override protected void finalize() throws Throwable { try { - replaceNativeObject(0); + nDestroyRenderer(mRenderer); } finally { super.finalize(); } @@ -322,11 +307,11 @@ class GLES20Canvas extends HardwareCanvas { // Display list /////////////////////////////////////////////////////////////////////////// - int getDisplayList() { - return nGetDisplayList(mRenderer); + int getDisplayList(int displayList) { + return nGetDisplayList(mRenderer, displayList); } - private static native int nGetDisplayList(int renderer); + private static native int nGetDisplayList(int renderer, int displayList); static void destroyDisplayList(int displayList) { nDestroyDisplayList(displayList); @@ -337,7 +322,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) { return nDrawDisplayList(mRenderer, - ((GLES20DisplayList) displayList).mNativeDisplayList, width, height, dirty); + ((GLES20DisplayList) displayList).getNativeDisplayList(), width, height, dirty); } private static native boolean nDrawDisplayList(int renderer, int displayList, @@ -345,7 +330,7 @@ class GLES20Canvas extends HardwareCanvas { @Override void outputDisplayList(DisplayList displayList) { - nOutputDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList); + nOutputDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList()); } private static native void nOutputDisplayList(int renderer, int displayList); diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index aeff31f8a193..9e649cea63dc 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -16,52 +16,50 @@ package android.view; -import java.lang.ref.WeakReference; +import android.graphics.Bitmap; + +import java.util.ArrayList; /** * An implementation of display list for OpenGL ES 2.0. */ class GLES20DisplayList extends DisplayList { - private GLES20Canvas mCanvas; - - private boolean mStarted = false; - private boolean mRecorded = false; - private boolean mValid = false; + // These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long + // as the DisplayList is alive. The Bitmaps are populated by the GLES20RecordingCanvas. + final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5); - int mNativeDisplayList; - WeakReference<View> hostView; + private GLES20RecordingCanvas mCanvas; + private boolean mValid; // The native display list will be destroyed when this object dies. // DO NOT overwrite this reference once it is set. - @SuppressWarnings("unused") private DisplayListFinalizer mFinalizer; - public GLES20DisplayList(View view) { - hostView = new WeakReference<View>(view); + int getNativeDisplayList() { + if (!mValid || mFinalizer == null) { + throw new IllegalStateException("The display list is not valid."); + } + return mFinalizer.mNativeDisplayList; } @Override HardwareCanvas start() { - if (mStarted) { - throw new IllegalStateException("Recording has already started"); - } - if (mCanvas != null) { - ((GLES20RecordingCanvas) mCanvas).reset(); - } else { - mCanvas = new GLES20RecordingCanvas(true); + throw new IllegalStateException("Recording has already started"); } - mStarted = true; - mRecorded = false; - mValid = true; + mValid = false; + mCanvas = GLES20RecordingCanvas.obtain(this); + mCanvas.start(); return mCanvas; } @Override void invalidate() { - mStarted = false; - mRecorded = false; + if (mCanvas != null) { + mCanvas.recycle(); + mCanvas = null; + } mValid = false; } @@ -73,48 +71,28 @@ class GLES20DisplayList extends DisplayList { @Override void end() { if (mCanvas != null) { - mStarted = false; - mRecorded = true; - - mNativeDisplayList = mCanvas.getDisplayList(); - mFinalizer = DisplayListFinalizer.getFinalizer(mFinalizer, mNativeDisplayList); + if (mFinalizer != null) { + mCanvas.end(mFinalizer.mNativeDisplayList); + } else { + mFinalizer = new DisplayListFinalizer(mCanvas.end(0)); + } + mCanvas.recycle(); + mCanvas = null; + mValid = true; } } - @Override - boolean isReady() { - return !mStarted && mRecorded; - } - private static class DisplayListFinalizer { - int mNativeDisplayList; - - // Factory method returns new instance if old one is null, or old instance - // otherwise, destroying native display list along the way as necessary - static DisplayListFinalizer getFinalizer(DisplayListFinalizer oldFinalizer, - int nativeDisplayList) { - if (oldFinalizer == null) { - return new DisplayListFinalizer(nativeDisplayList); - } - oldFinalizer.replaceNativeObject(nativeDisplayList); - return oldFinalizer; - } + final int mNativeDisplayList; - private DisplayListFinalizer(int nativeDisplayList) { + public DisplayListFinalizer(int nativeDisplayList) { mNativeDisplayList = nativeDisplayList; } - private void replaceNativeObject(int newNativeDisplayList) { - if (mNativeDisplayList != 0 && mNativeDisplayList != newNativeDisplayList) { - GLES20Canvas.destroyDisplayList(mNativeDisplayList); - } - mNativeDisplayList = newNativeDisplayList; - } - @Override protected void finalize() throws Throwable { try { - replaceNativeObject(0); + GLES20Canvas.destroyDisplayList(mNativeDisplayList); } finally { super.finalize(); } diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java index ec94fe70ae91..078222be6fe9 100644 --- a/core/java/android/view/GLES20RecordingCanvas.java +++ b/core/java/android/view/GLES20RecordingCanvas.java @@ -24,8 +24,10 @@ import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; - -import java.util.ArrayList; +import android.util.Pool; +import android.util.Poolable; +import android.util.PoolableManager; +import android.util.Pools; /** * An implementation of a GL canvas that records drawing operations. @@ -33,62 +35,94 @@ import java.util.ArrayList; * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while * the DisplayList is still holding a native reference to the memory. */ -class GLES20RecordingCanvas extends GLES20Canvas { - // These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long - // as the DisplayList is alive. - @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"}) - private final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5); +class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20RecordingCanvas> { + // The recording canvas pool should be large enough to handle a deeply nested + // view hierarchy because display lists are generated recursively. + private static final int POOL_LIMIT = 50; + + private static final Pool<GLES20RecordingCanvas> sPool = Pools.synchronizedPool( + Pools.finitePool(new PoolableManager<GLES20RecordingCanvas>() { + public GLES20RecordingCanvas newInstance() { + return new GLES20RecordingCanvas(); + } + @Override + public void onAcquired(GLES20RecordingCanvas element) { + } + @Override + public void onReleased(GLES20RecordingCanvas element) { + } + }, POOL_LIMIT)); + + private GLES20RecordingCanvas mNextPoolable; + private boolean mIsPooled; + + private GLES20DisplayList mDisplayList; - GLES20RecordingCanvas(boolean translucent) { - super(true, translucent); + private GLES20RecordingCanvas() { + super(true /*record*/, true /*translucent*/); + } + + static GLES20RecordingCanvas obtain(GLES20DisplayList displayList) { + GLES20RecordingCanvas canvas = sPool.acquire(); + canvas.mDisplayList = displayList; + return canvas; + } + + void recycle() { + mDisplayList = null; + resetDisplayListRenderer(); + sPool.release(this); + } + + void start() { + mDisplayList.mBitmaps.clear(); + } + + int end(int nativeDisplayList) { + return getDisplayList(nativeDisplayList); } private void recordShaderBitmap(Paint paint) { if (paint != null) { final Shader shader = paint.getShader(); if (shader instanceof BitmapShader) { - mBitmaps.add(((BitmapShader) shader).mBitmap); + mDisplayList.mBitmaps.add(((BitmapShader) shader).mBitmap); } } } - void reset() { - mBitmaps.clear(); - setupRenderer(true); - } - @Override public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) { super.drawPatch(bitmap, chunks, dst, paint); - mBitmaps.add(bitmap); + mDisplayList.mBitmaps.add(bitmap); // Shaders in the Paint are ignored when drawing a Bitmap } @Override public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { super.drawBitmap(bitmap, left, top, paint); - mBitmaps.add(bitmap); + mDisplayList.mBitmaps.add(bitmap); // Shaders in the Paint are ignored when drawing a Bitmap } @Override public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { super.drawBitmap(bitmap, matrix, paint); - mBitmaps.add(bitmap); + mDisplayList.mBitmaps.add(bitmap); // Shaders in the Paint are ignored when drawing a Bitmap } @Override public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { super.drawBitmap(bitmap, src, dst, paint); - mBitmaps.add(bitmap); + mDisplayList.mBitmaps.add(bitmap); // Shaders in the Paint are ignored when drawing a Bitmap } @Override public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) { super.drawBitmap(bitmap, src, dst, paint); - mBitmaps.add(bitmap); + mDisplayList.mBitmaps.add(bitmap); // Shaders in the Paint are ignored when drawing a Bitmap } @@ -111,7 +145,7 @@ class GLES20RecordingCanvas extends GLES20Canvas { int vertOffset, int[] colors, int colorOffset, Paint paint) { super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, paint); - mBitmaps.add(bitmap); + mDisplayList.mBitmaps.add(bitmap); // Shaders in the Paint are ignored when drawing a Bitmap } @@ -270,4 +304,24 @@ class GLES20RecordingCanvas extends GLES20Canvas { colorOffset, indices, indexOffset, indexCount, paint); recordShaderBitmap(paint); } + + @Override + public GLES20RecordingCanvas getNextPoolable() { + return mNextPoolable; + } + + @Override + public void setNextPoolable(GLES20RecordingCanvas element) { + mNextPoolable = element; + } + + @Override + public boolean isPooled() { + return mIsPooled; + } + + @Override + public void setPooled(boolean isPooled) { + mIsPooled = isPooled; + } } diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index b865b5007200..503b54bd437d 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -189,7 +189,7 @@ public abstract class HardwareRenderer { * * @return A new display list. */ - abstract DisplayList createDisplayList(View v); + abstract DisplayList createDisplayList(); /** * Creates a new hardware layer. A hardware layer built by calling this @@ -852,8 +852,8 @@ public abstract class HardwareRenderer { } @Override - DisplayList createDisplayList(View v) { - return new GLES20DisplayList(v); + DisplayList createDisplayList() { + return new GLES20DisplayList(); } @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0108ecf17b6d..c68b01cac538 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9099,7 +9099,10 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED; } - private void resolvePadding() { + /** + * @hide + */ + protected void resolvePadding() { // If the user specified the absolute padding (either with android:padding or // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise // use the default padding or the padding from the background drawable @@ -9830,7 +9833,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit // we copy in child display lists into ours in drawChild() mRecreateDisplayList = true; if (mDisplayList == null) { - mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(this); + mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(); // If we're creating a new display list, make sure our parent gets invalidated // since they will need to recreate their display list to account for this // new child display list. @@ -12025,12 +12028,13 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit mPrivateFlags |= FORCE_LAYOUT; mPrivateFlags |= INVALIDATED; - if (mLayoutParams != null && mParent != null) { - mLayoutParams.resolveWithDirection(getResolvedLayoutDirection()); - } - - if (mParent != null && !mParent.isLayoutRequested()) { - mParent.requestLayout(); + if (mParent != null) { + if (mLayoutParams != null) { + mLayoutParams.resolveWithDirection(getResolvedLayoutDirection()); + } + if (!mParent.isLayoutRequested()) { + mParent.requestLayout(); + } } } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 3798c9d0a0eb..4acf48c6b316 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -25,6 +25,7 @@ import android.os.Debug; import android.os.Environment; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SystemClock; import android.util.DisplayMetrics; @@ -36,7 +37,7 @@ import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; -import java.io.FileNotFoundException; +import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; @@ -50,6 +51,9 @@ import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; @@ -426,22 +430,22 @@ public class ViewDebug { * and obtain the traces. Both methods must be invoked on the * same thread. * - * @param traceFile The path where to write the looper traces - * - * @see #stopLooperProfiling() + * @hide */ - public static void startLooperProfiling(File traceFile) { + public static void startLooperProfiling(String path, FileDescriptor fileDescriptor) { if (sLooperProfilerStorage.get() == null) { - LooperProfiler profiler = new LooperProfiler(traceFile); + LooperProfiler profiler = new LooperProfiler(path, fileDescriptor); sLooperProfilerStorage.set(profiler); Looper.myLooper().setMessageLogging(profiler); } - } + } /** * Stops profiling the looper associated with the current thread. * - * @see #startLooperProfiling(java.io.File) + * @see #startLooperProfiling(String, java.io.FileDescriptor) + * + * @hide */ public static void stopLooperProfiling() { LooperProfiler profiler = sLooperProfilerStorage.get(); @@ -453,21 +457,33 @@ public class ViewDebug { } private static class LooperProfiler implements Looper.Profiler, Printer { - private static final int LOOPER_PROFILER_VERSION = 1; - private static final String LOG_TAG = "LooperProfiler"; + private static final int TRACE_VERSION_NUMBER = 3; + private static final int ACTION_EXIT_METHOD = 0x1; + private static final int HEADER_SIZE = 32; + private static final String HEADER_MAGIC = "SLOW"; + private static final short HEADER_RECORD_SIZE = (short) 14; + private final long mTraceWallStart; private final long mTraceThreadStart; private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512); - private final File mTraceFile; - private final HashMap<String, Short> mTraceNames = new HashMap<String, Short>(32); - private short mTraceId = 0; + private final HashMap<String, Integer> mTraceNames = new HashMap<String, Integer>(32); + private int mTraceId = 0; - LooperProfiler(File traceFile) { - mTraceFile = traceFile; + private final String mPath; + private ParcelFileDescriptor mFileDescriptor; + + LooperProfiler(String path, FileDescriptor fileDescriptor) { + mPath = path; + try { + mFileDescriptor = ParcelFileDescriptor.dup(fileDescriptor); + } catch (IOException e) { + Log.e(LOG_TAG, "Could not write trace file " + mPath, e); + throw new RuntimeException(e); + } mTraceWallStart = SystemClock.currentTimeMicro(); mTraceThreadStart = SystemClock.currentThreadTimeMicro(); } @@ -490,11 +506,11 @@ public class ViewDebug { mTraces.add(entry); } - private short getTraceId(Message message) { + private int getTraceId(Message message) { String name = message.getTarget().getMessageName(message); - Short traceId = mTraceNames.get(name); + Integer traceId = mTraceNames.get(name); if (traceId == null) { - traceId = mTraceId++; + traceId = mTraceId++ << 4; mTraceNames.put(name, traceId); } return traceId; @@ -507,62 +523,135 @@ public class ViewDebug { public void run() { saveTraces(); } - }, "LooperProfiler[" + mTraceFile + "]").start(); + }, "LooperProfiler[" + mPath + "]").start(); } private void saveTraces() { - FileOutputStream fos; - try { - fos = new FileOutputStream(mTraceFile); - } catch (FileNotFoundException e) { - Log.e(LOG_TAG, "Could not open trace file: " + mTraceFile); - return; - } - + FileOutputStream fos = new FileOutputStream(mFileDescriptor.getFileDescriptor()); DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos)); try { - out.writeInt(LOOPER_PROFILER_VERSION); - out.writeLong(mTraceWallStart); - out.writeLong(mTraceThreadStart); + writeHeader(out, mTraceWallStart, mTraceNames, mTraces); + out.flush(); - out.writeInt(mTraceNames.size()); - for (Map.Entry<String, Short> entry : mTraceNames.entrySet()) { - saveTraceName(entry.getKey(), entry.getValue(), out); - } + writeTraces(fos, out.size(), mTraceWallStart, mTraceThreadStart, mTraces); - out.writeInt(mTraces.size()); - for (Entry entry : mTraces) { - saveTrace(entry, out); - } - - Log.d(LOG_TAG, "Looper traces ready: " + mTraceFile); + Log.d(LOG_TAG, "Looper traces ready: " + mPath); } catch (IOException e) { - Log.e(LOG_TAG, "Could not write trace file: ", e); + Log.e(LOG_TAG, "Could not write trace file " + mPath, e); } finally { try { out.close(); } catch (IOException e) { - // Ignore + Log.e(LOG_TAG, "Could not write trace file " + mPath, e); + } + try { + mFileDescriptor.close(); + } catch (IOException e) { + Log.e(LOG_TAG, "Could not write trace file " + mPath, e); } } } - - private void saveTraceName(String name, short id, DataOutputStream out) throws IOException { - out.writeShort(id); - out.writeUTF(name); + + private static void writeTraces(FileOutputStream out, long offset, long wallStart, + long threadStart, ArrayList<Entry> entries) throws IOException { + + FileChannel channel = out.getChannel(); + + // Header + ByteBuffer buffer = ByteBuffer.allocateDirect(HEADER_SIZE); + buffer.put(HEADER_MAGIC.getBytes()); + buffer = buffer.order(ByteOrder.LITTLE_ENDIAN); + buffer.putShort((short) TRACE_VERSION_NUMBER); // version + buffer.putShort((short) HEADER_SIZE); // offset to data + buffer.putLong(wallStart); // start time in usec + buffer.putShort(HEADER_RECORD_SIZE); // size of a record in bytes + // padding to 32 bytes + for (int i = 0; i < HEADER_SIZE - 18; i++) { + buffer.put((byte) 0); + } + + buffer.flip(); + channel.position(offset).write(buffer); + + buffer = ByteBuffer.allocateDirect(14).order(ByteOrder.LITTLE_ENDIAN); + for (Entry entry : entries) { + buffer.putShort((short) 1); // we simulate only one thread + buffer.putInt(entry.traceId); // entering method + buffer.putInt((int) (entry.threadStart - threadStart)); + buffer.putInt((int) (entry.wallStart - wallStart)); + + buffer.flip(); + channel.write(buffer); + buffer.clear(); + + buffer.putShort((short) 1); + buffer.putInt(entry.traceId | ACTION_EXIT_METHOD); // exiting method + buffer.putInt((int) (entry.threadStart + entry.threadTime - threadStart)); + buffer.putInt((int) (entry.wallStart + entry.wallTime - wallStart)); + + buffer.flip(); + channel.write(buffer); + buffer.clear(); + } + + channel.close(); } + + private static void writeHeader(DataOutputStream out, long start, + HashMap<String, Integer> names, ArrayList<Entry> entries) throws IOException { + + Entry last = entries.get(entries.size() - 1); + long wallTotal = (last.wallStart + last.wallTime) - start; + + startSection("version", out); + addValue(null, Integer.toString(TRACE_VERSION_NUMBER), out); + addValue("data-file-overflow", "false", out); + addValue("clock", "dual", out); + addValue("elapsed-time-usec", Long.toString(wallTotal), out); + addValue("num-method-calls", Integer.toString(entries.size()), out); + addValue("clock-call-overhead-nsec", "1", out); + addValue("vm", "dalvik", out); + + startSection("threads", out); + addThreadId(1, "main", out); + + startSection("methods", out); + addMethods(names, out); + + startSection("end", out); + } + + private static void addMethods(HashMap<String, Integer> names, DataOutputStream out) + throws IOException { + + for (Map.Entry<String, Integer> name : names.entrySet()) { + out.writeBytes(String.format("0x%08x\tEventQueue\t%s\t()V\tLooper\t-2\n", + name.getValue(), name.getKey())); + } + } + + private static void addThreadId(int id, String name, DataOutputStream out) + throws IOException { - private void saveTrace(Entry entry, DataOutputStream out) throws IOException { - out.writeShort(entry.traceId); - out.writeLong(entry.wallStart); - out.writeLong(entry.wallTime); - out.writeLong(entry.threadStart); - out.writeLong(entry.threadTime); + out.writeBytes(Integer.toString(id) + '\t' + name + '\n'); + } + + private static void addValue(String name, String value, DataOutputStream out) + throws IOException { + + if (name != null) { + out.writeBytes(name + "="); + } + out.writeBytes(value + '\n'); + } + + private static void startSection(String name, DataOutputStream out) throws IOException { + out.writeBytes("*" + name + '\n'); } static class Entry { - short traceId; + int traceId; long wallStart; long wallTime; long threadStart; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 92a8ce7b033a..6f909713e4c1 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2854,7 +2854,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // display lists to render, force an invalidate to allow the animation to // continue drawing another frame invalidate(true); - if (a instanceof AlphaAnimation) { + if (a.hasAlpha()) { // alpha animations should cause the child to recreate its display list child.invalidate(true); } diff --git a/core/java/android/view/animation/AlphaAnimation.java b/core/java/android/view/animation/AlphaAnimation.java index 651fe458ab4b..c4d9afcc15c6 100644 --- a/core/java/android/view/animation/AlphaAnimation.java +++ b/core/java/android/view/animation/AlphaAnimation.java @@ -78,4 +78,12 @@ public class AlphaAnimation extends Animation { public boolean willChangeBounds() { return false; } + + /** + * @hide + */ + @Override + public boolean hasAlpha() { + return true; + } } diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index 87c759c48853..b7dfabcbed0f 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -1001,6 +1001,15 @@ public abstract class Animation implements Cloneable { } /** + * Return true if this animation changes the view's alpha property. + * + * @hide + */ + public boolean hasAlpha() { + return false; + } + + /** * Utility class to parse a string description of a size. */ protected static class Description { diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java index 873ce53ff315..4f2542b730de 100644 --- a/core/java/android/view/animation/AnimationSet.java +++ b/core/java/android/view/animation/AnimationSet.java @@ -43,6 +43,8 @@ public class AnimationSet extends Animation { private static final int PROPERTY_CHANGE_BOUNDS_MASK = 0x80; private int mFlags = 0; + private boolean mDirty; + private boolean mHasAlpha; private ArrayList<Animation> mAnimations = new ArrayList<Animation>(); @@ -138,6 +140,28 @@ public class AnimationSet extends Animation { } /** + * @hide + */ + @Override + public boolean hasAlpha() { + if (mDirty) { + mDirty = mHasAlpha = false; + + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + + for (int i = 0; i < count; i++) { + if (animations.get(i).hasAlpha()) { + mHasAlpha = true; + break; + } + } + } + + return mHasAlpha; + } + + /** * <p>Sets the duration of every child animation.</p> * * @param durationMillis the duration of the animation, in milliseconds, for @@ -175,6 +199,8 @@ public class AnimationSet extends Animation { mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration()); mDuration = mLastEnd - mStartOffset; } + + mDirty = true; } /** diff --git a/core/java/android/view/textservice/SpellCheckerInfo.aidl b/core/java/android/view/textservice/SpellCheckerInfo.aidl new file mode 100644 index 000000000000..eb5dfcc01c88 --- /dev/null +++ b/core/java/android/view/textservice/SpellCheckerInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textservice; + +parcelable SpellCheckerInfo; diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java new file mode 100644 index 000000000000..1205adfaceb3 --- /dev/null +++ b/core/java/android/view/textservice/SpellCheckerInfo.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.view.textservice; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class is used to specify meta information of an spell checker. + */ +public final class SpellCheckerInfo implements Parcelable { + private final ResolveInfo mService; + private final String mId; + + /** + * Constructor. + * @hide + */ + public SpellCheckerInfo(Context context, ResolveInfo service) { + mService = service; + ServiceInfo si = service.serviceInfo; + mId = new ComponentName(si.packageName, si.name).flattenToShortString(); + } + + /** + * Constructor. + * @hide + */ + public SpellCheckerInfo(Parcel source) { + mId = source.readString(); + mService = ResolveInfo.CREATOR.createFromParcel(source); + } + + /** + * Return a unique ID for this spell checker. The ID is generated from + * the package and class name implementing the method. + */ + public String getId() { + return mId; + } + + + /** + * Return the component of the service that implements. + */ + public ComponentName getComponent() { + return new ComponentName( + mService.serviceInfo.packageName, mService.serviceInfo.name); + } + + /** + * Return the .apk package that implements this input method. + */ + public String getPackageName() { + return mService.serviceInfo.packageName; + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mId); + mService.writeToParcel(dest, flags); + } + + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator<SpellCheckerInfo> CREATOR + = new Parcelable.Creator<SpellCheckerInfo>() { + @Override + public SpellCheckerInfo createFromParcel(Parcel source) { + return new SpellCheckerInfo(source); + } + + @Override + public SpellCheckerInfo[] newArray(int size) { + return new SpellCheckerInfo[size]; + } + }; + + /** + * Used to make this class parcelable. + */ + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/view/textservice/SuggestionsInfo.aidl b/core/java/android/view/textservice/SuggestionsInfo.aidl new file mode 100644 index 000000000000..66e20d24ce1c --- /dev/null +++ b/core/java/android/view/textservice/SuggestionsInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textservice; + +parcelable SuggestionsInfo; diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java new file mode 100644 index 000000000000..3332f1e262ee --- /dev/null +++ b/core/java/android/view/textservice/SuggestionsInfo.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.view.textservice; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class contains a metadata of suggestions from the text service + */ +public final class SuggestionsInfo implements Parcelable { + private static final String[] EMPTY = new String[0]; + + /** + * Flag of the attributes of the suggestions that can be obtained by + * {@link #getSuggestionsAttributes}: this tells that the requested word was found + * in the dictionary in the text service. + */ + public static final int RESULT_ATTR_IN_THE_DICTIONARY = 0x0001; + /** + * Flag of the attributes of the suggestions that can be obtained by + * {@link #getSuggestionsAttributes}: this tells that the text service thinks the requested + * word looks a typo. + */ + public static final int RESULT_ATTR_LOOKS_TYPO = 0x0002; + private final int mSuggestionsAttributes; + private final String[] mSuggestions; + private final boolean mSuggestionsAvailable; + private int mCookie; + private int mSequence; + + /** + * Constructor. + * @param suggestionsAttributes from the text service + * @param suggestions from the text service + */ + public SuggestionsInfo(int suggestionsAttributes, String[] suggestions) { + mSuggestionsAttributes = suggestionsAttributes; + if (suggestions == null) { + mSuggestions = EMPTY; + mSuggestionsAvailable = false; + } else { + mSuggestions = suggestions; + mSuggestionsAvailable = true; + } + mCookie = 0; + mSequence = 0; + } + + /** + * Constructor. + * @param suggestionsAttributes from the text service + * @param suggestions from the text service + * @param cookie the cookie of the input TextInfo + * @param sequence the cookie of the input TextInfo + */ + public SuggestionsInfo( + int suggestionsAttributes, String[] suggestions, int cookie, int sequence) { + if (suggestions == null) { + mSuggestions = EMPTY; + mSuggestionsAvailable = false; + } else { + mSuggestions = suggestions; + mSuggestionsAvailable = true; + } + mSuggestionsAttributes = suggestionsAttributes; + mCookie = cookie; + mSequence = sequence; + } + + public SuggestionsInfo(Parcel source) { + mSuggestionsAttributes = source.readInt(); + mSuggestions = source.readStringArray(); + mCookie = source.readInt(); + mSequence = source.readInt(); + mSuggestionsAvailable = source.readInt() == 1; + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSuggestionsAttributes); + dest.writeStringArray(mSuggestions); + dest.writeInt(mCookie); + dest.writeInt(mSequence); + dest.writeInt(mSuggestionsAvailable ? 1 : 0); + } + + /** + * Set the cookie and the sequence of SuggestionsInfo which are set to TextInfo from a client + * application + * @param cookie the cookie of an input TextInfo + * @param sequence the cookie of an input TextInfo + */ + public void setCookieAndSequence(int cookie, int sequence) { + mCookie = cookie; + mSequence = sequence; + } + + /** + * @return the cookie which may be set by a client application + */ + public int getCookie() { + return mCookie; + } + + /** + * @return the sequence which may be set by a client application + */ + public int getSequence() { + return mSequence; + } + + /** + * @return the attributes of suggestions. This includes whether the spell checker has the word + * in its dictionary or not and whether the spell checker has confident suggestions for the + * word or not. + */ + public int getSuggestionsAttributes() { + return mSuggestionsAttributes; + } + + /** + * @return the count of the suggestions. If there's no suggestions at all, this method returns + * -1. Even if this method returns 0, it doesn't necessarily mean that there are no suggestions + * for the requested word. For instance, the caller could have been asked to limit the maximum + * number of suggestions returned. + */ + public int getSuggestionsCount() { + if (!mSuggestionsAvailable) { + return -1; + } + return mSuggestions.length; + } + + /** + * @param i the id of suggestions + * @return the suggestion at the specified id + */ + public String getSuggestionAt(int i) { + return mSuggestions[i]; + } + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator<SuggestionsInfo> CREATOR + = new Parcelable.Creator<SuggestionsInfo>() { + @Override + public SuggestionsInfo createFromParcel(Parcel source) { + return new SuggestionsInfo(source); + } + + @Override + public SuggestionsInfo[] newArray(int size) { + return new SuggestionsInfo[size]; + } + }; + + /** + * Used to make this class parcelable. + */ + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/view/textservice/TextInfo.aidl b/core/java/android/view/textservice/TextInfo.aidl new file mode 100644 index 000000000000..d231d7638a75 --- /dev/null +++ b/core/java/android/view/textservice/TextInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textservice; + +parcelable TextInfo; diff --git a/core/java/android/view/textservice/TextInfo.java b/core/java/android/view/textservice/TextInfo.java new file mode 100644 index 000000000000..b534eb0be607 --- /dev/null +++ b/core/java/android/view/textservice/TextInfo.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.view.textservice; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +/** + * This class contains a metadata of the input of TextService + */ +public final class TextInfo implements Parcelable { + private final String mText; + private final int mCookie; + private final int mSequence; + + /** + * Constructor. + * @param text the text which will be input to TextService + */ + public TextInfo(String text) { + this(text, 0, 0); + } + + /** + * Constructor. + * @param text the text which will be input to TextService + * @param cookie the cookie for this TextInfo + * @param sequence the sequence number for this TextInfo + */ + public TextInfo(String text, int cookie, int sequence) { + if (TextUtils.isEmpty(text)) { + throw new IllegalArgumentException(text); + } + mText = text; + mCookie = cookie; + mSequence = sequence; + } + + public TextInfo(Parcel source) { + mText = source.readString(); + mCookie = source.readInt(); + mSequence = source.readInt(); + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mText); + dest.writeInt(mCookie); + dest.writeInt(mSequence); + } + + /** + * @return the text which is an input of a text service + */ + public String getText() { + return mText; + } + + /** + * @return the cookie of TextInfo + */ + public int getCookie() { + return mCookie; + } + + /** + * @return the sequence of TextInfo + */ + public int getSequence() { + return mSequence; + } + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator<TextInfo> CREATOR + = new Parcelable.Creator<TextInfo>() { + @Override + public TextInfo createFromParcel(Parcel source) { + return new TextInfo(source); + } + + @Override + public TextInfo[] newArray(int size) { + return new TextInfo[size]; + } + }; + + /** + * Used to make this class parcelable. + */ + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java new file mode 100644 index 000000000000..97494168a281 --- /dev/null +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.view.textservice; + +import com.android.internal.textservice.ITextServicesManager; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.view.textservice.SpellCheckerInfo; +import android.service.textservice.SpellCheckerSession; +import android.service.textservice.SpellCheckerSession.SpellCheckerSessionListener; + +import java.util.Locale; + +/** + * System API to the overall text services, which arbitrates interaction between applications + * and text services. You can retrieve an instance of this interface with + * {@link Context#getSystemService(String) Context.getSystemService()}. + * + * The user can change the current text services in Settings. And also applications can specify + * the target text services. + */ +public final class TextServicesManager { + private static final String TAG = TextServicesManager.class.getSimpleName(); + + private static TextServicesManager sInstance; + private static ITextServicesManager sService; + + private TextServicesManager() { + if (sService == null) { + IBinder b = ServiceManager.getService(Context.TEXT_SERVICES_MANAGER_SERVICE); + sService = ITextServicesManager.Stub.asInterface(b); + } + } + + /** + * Retrieve the global TextServicesManager instance, creating it if it doesn't already exist. + * @hide + */ + public static TextServicesManager getInstance() { + synchronized (TextServicesManager.class) { + if (sInstance != null) { + return sInstance; + } + sInstance = new TextServicesManager(); + } + return sInstance; + } + + /** + * Get a spell checker session for the specified spell checker + * @param locale the locale for the spell checker + * @param listener a spell checker session lister for getting results from a spell checker. + * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled + * languages in settings will be returned. + * @return the spell checker session of the spell checker + */ + // TODO: Add a method to get enabled spell checkers. + // TODO: Handle referToSpellCheckerLanguageSettings + public SpellCheckerSession newSpellCheckerSession(Locale locale, + SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) { + if (locale == null || listener == null) { + throw new NullPointerException(); + } + final SpellCheckerInfo info; + try { + info = sService.getCurrentSpellChecker(locale.toString()); + } catch (RemoteException e) { + return null; + } + if (info == null) { + return null; + } + final SpellCheckerSession session = new SpellCheckerSession(info, sService, listener); + try { + sService.getSpellCheckerService( + info, locale.toString(), session.getTextServicesSessionListener(), + session.getSpellCheckerSessionListener()); + } catch (RemoteException e) { + return null; + } + return session; + } +} diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 73db03e44f56..6a3b2ffefae1 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -9119,20 +9119,12 @@ public class WebView extends AbsoluteLayout return nativeTileProfilingNumTilesInFrame(frame); } /** @hide only used by profiling tests */ - public int tileProfilingGetX(int frame, int tile) { - return nativeTileProfilingGetX(frame, tile); + public int tileProfilingGetInt(int frame, int tile, String key) { + return nativeTileProfilingGetInt(frame, tile, key); } /** @hide only used by profiling tests */ - public int tileProfilingGetY(int frame, int tile) { - return nativeTileProfilingGetY(frame, tile); - } - /** @hide only used by profiling tests */ - public boolean tileProfilingGetReady(int frame, int tile) { - return nativeTileProfilingGetReady(frame, tile); - } - /** @hide only used by profiling tests */ - public int tileProfilingGetLevel(int frame, int tile) { - return nativeTileProfilingGetLevel(frame, tile); + public float tileProfilingGetFloat(int frame, int tile, String key) { + return nativeTileProfilingGetFloat(frame, tile, key); } private native int nativeCacheHitFramePointer(); @@ -9262,10 +9254,8 @@ public class WebView extends AbsoluteLayout private native void nativeTileProfilingClear(); private native int nativeTileProfilingNumFrames(); private native int nativeTileProfilingNumTilesInFrame(int frame); - private native int nativeTileProfilingGetX(int frame, int tile); - private native int nativeTileProfilingGetY(int frame, int tile); - private native boolean nativeTileProfilingGetReady(int frame, int tile); - private native int nativeTileProfilingGetLevel(int frame, int tile); + private native int nativeTileProfilingGetInt(int frame, int tile, String key); + private native float nativeTileProfilingGetFloat(int frame, int tile, String key); // Never call this version except by updateCachedTextfield(String) - // we always want to pass in our generation number. private native void nativeUpdateCachedTextfield(String updatedText, diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index d7a2526003e7..8d8023bb0dd3 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1069,6 +1069,15 @@ public final class WebViewCore { + " arg1=" + msg.arg1 + " arg2=" + msg.arg2 + " obj=" + msg.obj); } + if (mWebView == null + && msg.what != EventHub.RESUME_TIMERS + && msg.what != EventHub.PAUSE_TIMERS) { + if (DebugFlags.WEB_VIEW_CORE) { + Log.v(LOGTAG, "Rejecting message " + msg.what + + " because we are destroyed"); + } + return; + } switch (msg.what) { case WEBKIT_DRAW: webkitDraw(); @@ -1757,30 +1766,17 @@ public final class WebViewCore { } /** - * Removes pending messages and trigger a DESTROY message to send to - * WebCore. + * Sends a DESTROY message to WebCore. * Called from UI thread. */ void destroy() { - // We don't want anyone to post a message between removing pending - // messages and sending the destroy message. synchronized (mEventHub) { - // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to - // be preserved even the WebView is destroyed. - // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS - boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS); - boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS); - mEventHub.removeMessages(); + // Do not call removeMessages as then we risk removing PAUSE_TIMERS + // or RESUME_TIMERS messages, which we must still handle as they + // are per process. DESTROY will instead trigger a white list in + // mEventHub, skipping any remaining messages in the queue mEventHub.sendMessageAtFrontOfQueue( Message.obtain(null, EventHub.DESTROY)); - if (hasPause) { - mEventHub.sendMessageAtFrontOfQueue( - Message.obtain(null, EventHub.PAUSE_TIMERS)); - } - if (hasResume) { - mEventHub.sendMessageAtFrontOfQueue( - Message.obtain(null, EventHub.RESUME_TIMERS)); - } mEventHub.blockMessages(); } } @@ -2113,13 +2109,17 @@ public final class WebViewCore { // called from JNI or WebView thread /* package */ void contentDraw() { - // don't update the Picture until we have an initial width and finish - // the first layout - if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { - return; - } - // only fire an event if this is our first request synchronized (this) { + if (mWebView == null || mBrowserFrame == null) { + // We were destroyed + return; + } + // don't update the Picture until we have an initial width and finish + // the first layout + if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { + return; + } + // only fire an event if this is our first request if (mDrawIsScheduled) return; mDrawIsScheduled = true; if (mDrawIsPaused) return; diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 8f8c1d069b9d..b7c1687debef 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -4638,9 +4638,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te childrenTop += getVerticalFadingEdgeLength(); } } - // Don't ever focus a disabled item. - if (!mAdapter.isEnabled(i)) continue; - if (top >= childrenTop) { // Found a view whose top is fully visisble selectedPos = firstPosition + i; diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index 49616cc1ef18..f3a6da7780bb 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -39,8 +39,9 @@ public class CheckedTextView extends TextView implements Checkable { private boolean mChecked; private int mCheckMarkResource; private Drawable mCheckMarkDrawable; - private int mBasePaddingRight; + private int mBasePadding; private int mCheckMarkWidth; + private boolean mNeedRequestlayout; private static final int[] CHECKED_STATE_SET = { R.attr.state_checked @@ -123,6 +124,7 @@ public class CheckedTextView extends TextView implements Checkable { mCheckMarkDrawable.setCallback(null); unscheduleDrawable(mCheckMarkDrawable); } + mNeedRequestlayout = (d != mCheckMarkDrawable); if (d != null) { d.setCallback(this); d.setVisible(getVisibility() == VISIBLE, false); @@ -130,19 +132,35 @@ public class CheckedTextView extends TextView implements Checkable { setMinHeight(d.getIntrinsicHeight()); mCheckMarkWidth = d.getIntrinsicWidth(); - mUserPaddingRight = mCheckMarkWidth + mBasePaddingRight; d.setState(getDrawableState()); } else { - mUserPaddingRight = mBasePaddingRight; + mCheckMarkWidth = 0; } mCheckMarkDrawable = d; - requestLayout(); + // Do padding resolution. This will call setPadding() and do a requestLayout() if needed. + resolvePadding(); + } + + /** + * @hide + */ + @Override + protected void resolvePadding() { + super.resolvePadding(); + int newPadding = (mCheckMarkDrawable != null) ? + mCheckMarkWidth + mBasePadding : mBasePadding; + mNeedRequestlayout |= (mPaddingRight != newPadding); + mPaddingRight = newPadding; + if (mNeedRequestlayout) { + requestLayout(); + mNeedRequestlayout = false; + } } @Override public void setPadding(int left, int top, int right, int bottom) { super.setPadding(left, top, right, bottom); - mBasePaddingRight = mUserPaddingRight; + mBasePadding = mPaddingRight; } @Override @@ -167,9 +185,9 @@ public class CheckedTextView extends TextView implements Checkable { int right = getWidth(); checkMarkDrawable.setBounds( - right - mUserPaddingRight, + right - mPaddingRight, y, - right - mUserPaddingRight + mCheckMarkWidth, + right - mPaddingRight + mCheckMarkWidth, y + height); checkMarkDrawable.draw(canvas); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 108ac335fe7f..a7324b085f9b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -329,7 +329,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout; private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout; - private int mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout; + private int mTextEditSuggestionsWindowLayout; private int mTextEditSuggestionItemLayout; private SuggestionsPopupWindow mSuggestionsPopupWindow; private SuggestionRangeSpan mSuggestionRangeSpan; @@ -830,12 +830,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTextEditSideNoPasteWindowLayout = a.getResourceId(attr, 0); break; - case com.android.internal.R.styleable.TextView_textEditSuggestionsBottomWindowLayout: - mTextEditSuggestionsBottomWindowLayout = a.getResourceId(attr, 0); - break; - - case com.android.internal.R.styleable.TextView_textEditSuggestionsTopWindowLayout: - mTextEditSuggestionsTopWindowLayout = a.getResourceId(attr, 0); + case com.android.internal.R.styleable.TextView_textEditSuggestionsWindowLayout: + mTextEditSuggestionsWindowLayout = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.TextView_textEditSuggestionItemLayout: @@ -8785,9 +8781,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private static final int MAX_NUMBER_SUGGESTIONS = 5; private static final int NO_SUGGESTIONS = -1; private final PopupWindow mContainer; - private final ViewGroup[] mSuggestionViews = new ViewGroup[2]; - private final int[] mSuggestionViewLayouts = new int[] { - mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout}; + private ViewGroup mSuggestionViewGroup; private WordIterator mSuggestionWordIterator; private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan[0]; @@ -8809,12 +8803,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int suggestionIndex; // the index of the suggestion inside suggestionSpan } - private ViewGroup getViewGroup(boolean under) { - final int viewIndex = under ? 0 : 1; - ViewGroup viewGroup = mSuggestionViews[viewIndex]; - - if (viewGroup == null) { - final int layout = mSuggestionViewLayouts[viewIndex]; + private void initSuggestionViewGroup() { + if (mSuggestionViewGroup == null) { LayoutInflater inflater = (LayoutInflater) TextView.this.mContext. getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -8823,19 +8813,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener "Unable to create TextEdit suggestion window inflater"); } - View view = inflater.inflate(layout, null); + View view = inflater.inflate(mTextEditSuggestionsWindowLayout, null); if (! (view instanceof ViewGroup)) { throw new IllegalArgumentException( "Inflated TextEdit suggestion window is not a ViewGroup: " + view); } - viewGroup = (ViewGroup) view; + mSuggestionViewGroup = (ViewGroup) view; // Inflate the suggestion items once and for all. for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) { - View childView = inflater.inflate(mTextEditSuggestionItemLayout, viewGroup, - false); + View childView = inflater.inflate(mTextEditSuggestionItemLayout, + mSuggestionViewGroup, false); if (! (childView instanceof TextView)) { throw new IllegalArgumentException( @@ -8843,14 +8833,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } childView.setTag(new SuggestionInfo()); - viewGroup.addView(childView); + mSuggestionViewGroup.addView(childView); childView.setOnClickListener(this); } - mSuggestionViews[viewIndex] = viewGroup; + mContainer.setContentView(mSuggestionViewGroup); } - - return viewGroup; } public void show() { @@ -8861,8 +8849,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class); final int nbSpans = suggestionSpans.length; - ViewGroup viewGroup = getViewGroup(true); - mContainer.setContentView(viewGroup); + initSuggestionViewGroup(); int totalNbSuggestions = 0; int spanUnionStart = mText.length(); @@ -8878,7 +8865,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener String[] suggestions = suggestionSpan.getSuggestions(); int nbSuggestions = suggestions.length; for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) { - TextView textView = (TextView) viewGroup.getChildAt(totalNbSuggestions); + TextView textView = (TextView) mSuggestionViewGroup.getChildAt( + totalNbSuggestions); textView.setText(suggestions[suggestionIndex]); SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag(); suggestionInfo.spanStart = spanStart; @@ -8897,7 +8885,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (totalNbSuggestions == 0) { // TODO Replace by final text, use a dedicated layout, add a fade out timer... - TextView textView = (TextView) viewGroup.getChildAt(0); + TextView textView = (TextView) mSuggestionViewGroup.getChildAt(0); textView.setText("No suggestions available"); SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag(); suggestionInfo.spanStart = NO_SUGGESTIONS; @@ -8908,17 +8896,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); for (int i = 0; i < totalNbSuggestions; i++) { - final TextView textView = (TextView) viewGroup.getChildAt(i); + final TextView textView = (TextView) mSuggestionViewGroup.getChildAt(i); highlightTextDifferences(textView, spanUnionStart, spanUnionEnd); } } - for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) { - viewGroup.getChildAt(i).setVisibility(i < totalNbSuggestions ? VISIBLE : GONE); + for (int i = 0; i < totalNbSuggestions; i++) { + mSuggestionViewGroup.getChildAt(i).setVisibility(VISIBLE); + } + for (int i = totalNbSuggestions; i < MAX_NUMBER_SUGGESTIONS; i++) { + mSuggestionViewGroup.getChildAt(i).setVisibility(GONE); } - final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); - viewGroup.measure(size, size); + final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); + final int screenWidth = displayMetrics.widthPixels; + final int screenHeight = displayMetrics.heightPixels; + mSuggestionViewGroup.measure( + View.MeasureSpec.makeMeasureSpec(screenWidth, View.MeasureSpec.AT_MOST), + View.MeasureSpec.makeMeasureSpec(screenHeight, View.MeasureSpec.AT_MOST)); positionAtCursor(); } @@ -9171,23 +9166,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Vertical clipping if (coords[1] + height > screenHeight) { - // Try to position above current line instead - // TODO use top layout instead, reverse suggestion order, - // try full screen vertical down if it still does not fit. TBD with designers. - - // Update dimensions from new view - contentView = mContainer.getContentView(); - width = contentView.getMeasuredWidth(); - height = contentView.getMeasuredHeight(); - - final int lineTop = mLayout.getLineTop(line); - final int lineHeight = lineBottom - lineTop; - coords[1] -= height + lineHeight; + coords[1] = screenHeight - height; } // Horizontal clipping - coords[0] = Math.max(0, coords[0]); coords[0] = Math.min(displayMetrics.widthPixels - width, coords[0]); + coords[0] = Math.max(0, coords[0]); mContainer.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]); } diff --git a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl new file mode 100644 index 000000000000..ff0049276bce --- /dev/null +++ b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl @@ -0,0 +1,29 @@ +/* + * 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.internal.textservice; + +import com.android.internal.textservice.ISpellCheckerSession; +import com.android.internal.textservice.ISpellCheckerSessionListener; + +/** + * Public interface to the global spell checker. + * @hide + */ +interface ISpellCheckerService { + ISpellCheckerSession getISpellCheckerSession( + String locale, ISpellCheckerSessionListener listener); +} diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl new file mode 100644 index 000000000000..79e43510c00a --- /dev/null +++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl @@ -0,0 +1,28 @@ +/* + * 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.internal.textservice; + +import android.view.textservice.TextInfo; + +/** + * @hide + */ +oneway interface ISpellCheckerSession { + void getSuggestionsMultiple( + in TextInfo[] textInfos, int suggestionsLimit, boolean multipleWords); + void cancel(); +} diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl new file mode 100644 index 000000000000..796b06eb06d6 --- /dev/null +++ b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.internal.textservice; + +import android.view.textservice.SuggestionsInfo; + +/** + * @hide + */ +oneway interface ISpellCheckerSessionListener { + void onGetSuggestions(in SuggestionsInfo[] results); +} diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl new file mode 100644 index 000000000000..ad0c1ff3f816 --- /dev/null +++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.textservice; + +import com.android.internal.textservice.ISpellCheckerSessionListener; +import com.android.internal.textservice.ITextServicesSessionListener; + +import android.content.ComponentName; +import android.view.textservice.SpellCheckerInfo; + +/** + * Interface to the text service manager. + * @hide + */ +interface ITextServicesManager { + SpellCheckerInfo getCurrentSpellChecker(String locale); + oneway void getSpellCheckerService(in SpellCheckerInfo info, in String locale, + in ITextServicesSessionListener tsListener, + in ISpellCheckerSessionListener scListener); + oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener); +} diff --git a/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl b/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl new file mode 100644 index 000000000000..ecb6cd0f85d1 --- /dev/null +++ b/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl @@ -0,0 +1,29 @@ +/* + * 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.internal.textservice; + +import com.android.internal.textservice.ISpellCheckerSession; + +import android.view.textservice.SpellCheckerInfo; + +/** + * Interface to the text service session. + * @hide + */ +interface ITextServicesSessionListener { + oneway void onServiceConnected(in ISpellCheckerSession spellCheckerSession); +} diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index 164d5811c31e..159b3da440bd 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -50,6 +50,8 @@ public class MenuBuilder implements Menu { private static final String LOGTAG = "MenuBuilder"; private static final String PRESENTER_KEY = "android:menu:presenters"; + private static final String ACTION_VIEW_STATES_KEY = "android:menu:actionviewstates"; + private static final String EXPANDED_ACTION_VIEW_ID = "android:menu:expandedactionview"; private static final int[] sCategoryToOrder = new int[] { 1, /* No category */ @@ -308,6 +310,67 @@ public class MenuBuilder implements Menu { dispatchRestoreInstanceState(state); } + public void saveActionViewStates(Bundle outStates) { + SparseArray<Parcelable> viewStates = null; + + final int itemCount = size(); + for (int i = 0; i < itemCount; i++) { + final MenuItem item = getItem(i); + final View v = item.getActionView(); + if (v != null && v.getId() != View.NO_ID) { + if (viewStates == null) { + viewStates = new SparseArray<Parcelable>(); + } + v.saveHierarchyState(viewStates); + if (item.isActionViewExpanded()) { + outStates.putInt(EXPANDED_ACTION_VIEW_ID, item.getItemId()); + } + } + if (item.hasSubMenu()) { + final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); + subMenu.saveActionViewStates(outStates); + } + } + + if (viewStates != null) { + outStates.putSparseParcelableArray(getActionViewStatesKey(), viewStates); + } + } + + public void restoreActionViewStates(Bundle states) { + if (states == null) { + return; + } + + SparseArray<Parcelable> viewStates = states.getSparseParcelableArray( + getActionViewStatesKey()); + + final int itemCount = size(); + for (int i = 0; i < itemCount; i++) { + final MenuItem item = getItem(i); + final View v = item.getActionView(); + if (v != null && v.getId() != View.NO_ID) { + v.restoreHierarchyState(viewStates); + } + if (item.hasSubMenu()) { + final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); + subMenu.restoreActionViewStates(states); + } + } + + final int expandedId = states.getInt(EXPANDED_ACTION_VIEW_ID); + if (expandedId > 0) { + MenuItem itemToExpand = findItem(expandedId); + if (itemToExpand != null) { + itemToExpand.expandActionView(); + } + } + } + + protected String getActionViewStatesKey() { + return ACTION_VIEW_STATES_KEY; + } + public void setCallback(Callback cb) { mCallback = cb; } diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java index 541d10120dd6..b0a002d2ce8d 100644 --- a/core/java/com/android/internal/view/menu/MenuItemImpl.java +++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java @@ -553,6 +553,9 @@ public final class MenuItemImpl implements MenuItem { public MenuItem setActionView(View view) { mActionView = view; mActionProvider = null; + if (view != null && view.getId() == View.NO_ID && mId > 0) { + view.setId(mId); + } mMenu.onItemActionRequestChanged(this); return this; } diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java index fb1cd5e81ef7..92acf8cda8f5 100644 --- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java +++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java @@ -121,4 +121,13 @@ public class SubMenuBuilder extends MenuBuilder implements SubMenu { public boolean collapseItemActionView(MenuItemImpl item) { return mParentMenu.collapseItemActionView(item); } + + @Override + public String getActionViewStatesKey() { + final int itemId = mItem != null ? mItem.getItemId() : 0; + if (itemId == 0) { + return null; + } + return super.getActionViewStatesKey() + ":" + itemId; + } } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index e03858b4af13..09262e01284a 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -42,6 +42,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; +import android.view.CollapsibleActionView; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; @@ -285,8 +286,11 @@ public class ActionBarView extends AbsActionBarView { public void setSplitActionBar(boolean splitActionBar) { if (mSplitActionBar != splitActionBar) { if (mMenuView != null) { + final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); + if (oldParent != null) { + oldParent.removeView(mMenuView); + } if (splitActionBar) { - removeView(mMenuView); if (mSplitView != null) { mSplitView.addView(mMenuView); } @@ -332,7 +336,10 @@ public class ActionBarView extends AbsActionBarView { MenuBuilder builder = (MenuBuilder) menu; mOptionsMenu = builder; if (mMenuView != null) { - removeView(mMenuView); + final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); + if (oldParent != null) { + oldParent.removeView(mMenuView); + } } if (mActionMenuPresenter == null) { mActionMenuPresenter = new ActionMenuPresenter(); @@ -351,6 +358,10 @@ public class ActionBarView extends AbsActionBarView { builder.addMenuPresenter(mActionMenuPresenter); builder.addMenuPresenter(mExpandedMenuPresenter); menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); + final ViewGroup oldParent = (ViewGroup) menuView.getParent(); + if (oldParent != null && oldParent != this) { + oldParent.removeView(menuView); + } addView(menuView, layoutParams); } else { mActionMenuPresenter.setExpandedActionViewsExclusive(false); @@ -365,6 +376,10 @@ public class ActionBarView extends AbsActionBarView { builder.addMenuPresenter(mExpandedMenuPresenter); menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); if (mSplitView != null) { + final ViewGroup oldParent = (ViewGroup) menuView.getParent(); + if (oldParent != null && oldParent != mSplitView) { + oldParent.removeView(menuView); + } mSplitView.addView(menuView, layoutParams); } else { // We'll add this later if we missed it this time. @@ -1304,6 +1319,10 @@ public class ActionBarView extends AbsActionBarView { if (mCustomNavView != null) mCustomNavView.setVisibility(GONE); requestLayout(); item.setActionViewExpanded(true); + + if (mExpandedActionView instanceof CollapsibleActionView) { + ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded(); + } return true; } @@ -1330,11 +1349,16 @@ public class ActionBarView extends AbsActionBarView { if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { mCustomNavView.setVisibility(VISIBLE); } + View collapsedView = mExpandedActionView; mExpandedActionView = null; mExpandedHomeLayout.setIcon(null); mCurrentExpandedItem = null; requestLayout(); item.setActionViewExpanded(false); + + if (collapsedView instanceof CollapsibleActionView) { + ((CollapsibleActionView) collapsedView).onActionViewCollapsed(); + } return true; } diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index e930c5c37638..0c81634a4a08 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -315,26 +315,56 @@ static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject) return doBooleanCommand("OK", "DRIVER STOP"); } -static jboolean android_net_wifi_startPacketFiltering(JNIEnv* env, jobject) +/* + Multicast filtering rules work as follows: + + The driver can filter multicast (v4 and/or v6) and broadcast packets when in + a power optimized mode (typically when screen goes off). + + In order to prevent the driver from filtering the multicast/broadcast packets, we have to + add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective + + DRIVER RXFILTER-ADD Num + where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6 + + and DRIVER RXFILTER-START + + In order to stop the usage of these rules, we do + + DRIVER RXFILTER-STOP + DRIVER RXFILTER-REMOVE Num + where Num is as described for RXFILTER-ADD + + The SETSUSPENDOPT driver command overrides the filtering rules +*/ + +static jboolean android_net_wifi_startMultiV4Filtering(JNIEnv* env, jobject) { - return doBooleanCommand("OK", "DRIVER RXFILTER-ADD 0") - && doBooleanCommand("OK", "DRIVER RXFILTER-ADD 1") - && doBooleanCommand("OK", "DRIVER RXFILTER-ADD 3") + return doBooleanCommand("OK", "DRIVER RXFILTER-STOP") + && doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 2") && doBooleanCommand("OK", "DRIVER RXFILTER-START"); } -static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject) +static jboolean android_net_wifi_stopMultiV4Filtering(JNIEnv* env, jobject) { - jboolean result = doBooleanCommand("OK", "DRIVER RXFILTER-STOP"); - if (result) { - (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 3"); - (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 1"); - (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 0"); - } + return doBooleanCommand("OK", "DRIVER RXFILTER-ADD 2") + && doBooleanCommand("OK", "DRIVER RXFILTER-START"); +} - return result; +static jboolean android_net_wifi_startMultiV6Filtering(JNIEnv* env, jobject) +{ + return doBooleanCommand("OK", "DRIVER RXFILTER-STOP") + && doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 3") + && doBooleanCommand("OK", "DRIVER RXFILTER-START"); } +static jboolean android_net_wifi_stopMultiV6Filtering(JNIEnv* env, jobject) +{ + return doBooleanCommand("OK", "DRIVER RXFILTER-ADD 3") + && doBooleanCommand("OK", "DRIVER RXFILTER-START"); +} + + static jint android_net_wifi_getRssiHelper(const char *cmd) { char reply[BUF_SIZE]; @@ -545,8 +575,10 @@ static JNINativeMethod gWifiMethods[] = { { "setScanModeCommand", "(Z)Z", (void*) android_net_wifi_setScanModeCommand }, { "startDriverCommand", "()Z", (void*) android_net_wifi_startDriverCommand }, { "stopDriverCommand", "()Z", (void*) android_net_wifi_stopDriverCommand }, - { "startPacketFiltering", "()Z", (void*) android_net_wifi_startPacketFiltering }, - { "stopPacketFiltering", "()Z", (void*) android_net_wifi_stopPacketFiltering }, + { "startFilteringMulticastV4Packets", "()Z", (void*) android_net_wifi_startMultiV4Filtering}, + { "stopFilteringMulticastV4Packets", "()Z", (void*) android_net_wifi_stopMultiV4Filtering}, + { "startFilteringMulticastV6Packets", "()Z", (void*) android_net_wifi_startMultiV6Filtering}, + { "stopFilteringMulticastV6Packets", "()Z", (void*) android_net_wifi_stopMultiV6Filtering}, { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand }, { "getPowerModeCommand", "()I", (void*) android_net_wifi_getPowerModeCommand }, { "setBandCommand", "(I)Z", (void*) android_net_wifi_setBandCommand}, diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index b0c2f2c2da07..b06de9d1b91e 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -576,18 +576,18 @@ static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject clazz, // ---------------------------------------------------------------------------- static DisplayList* android_view_GLES20Canvas_getDisplayList(JNIEnv* env, - jobject clazz, DisplayListRenderer* renderer) { - return renderer->getDisplayList(); + jobject clazz, DisplayListRenderer* renderer, DisplayList* displayList) { + return renderer->getDisplayList(displayList); +} + +static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env, + jobject clazz) { + return new DisplayListRenderer; } -static OpenGLRenderer* android_view_GLES20Canvas_getDisplayListRenderer(JNIEnv* env, +static void android_view_GLES20Canvas_resetDisplayListRenderer(JNIEnv* env, jobject clazz, DisplayListRenderer* renderer) { - if (renderer == NULL) { - renderer = new DisplayListRenderer; - } else { - renderer->reset(); - } - return renderer; + renderer->reset(); } static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env, @@ -812,9 +812,10 @@ static JNINativeMethod gMethods[] = { { "nGetClipBounds", "(ILandroid/graphics/Rect;)Z", (void*) android_view_GLES20Canvas_getClipBounds }, - { "nGetDisplayList", "(I)I", (void*) android_view_GLES20Canvas_getDisplayList }, + { "nGetDisplayList", "(II)I", (void*) android_view_GLES20Canvas_getDisplayList }, { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList }, - { "nGetDisplayListRenderer", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListRenderer }, + { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer }, + { "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer }, { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;)Z", (void*) android_view_GLES20Canvas_drawDisplayList }, { "nOutputDisplayList", "(II)V", (void*) android_view_GLES20Canvas_outputDisplayList }, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 103a32691735..91003d181b9c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1120,6 +1120,13 @@ android:description="@string/permdesc_bindInputMethod" android:protectionLevel="signature" /> + <!-- Must be required by a TextService (e.g. SpellCheckerService) + to ensure that only the system can bind to it. --> + <permission android:name="android.permission.BIND_TEXT_SERVICE" + android:label="@string/permlab_bindTextService" + android:description="@string/permdesc_bindTextService" + android:protectionLevel="signature" /> + <!-- Must be required by a {@link android.service.wallpaper.WallpaperService}, to ensure that only the system can bind to it. --> <permission android:name="android.permission.BIND_WALLPAPER" @@ -1197,7 +1204,7 @@ <permission android:name="android.permission.READ_FRAME_BUFFER" android:label="@string/permlab_readFrameBuffer" android:description="@string/permdesc_readFrameBuffer" - android:protectionLevel="signature" /> + android:protectionLevel="signatureOrSystem" /> <!-- Required to be able to disable the device (very dangerous!). --> <permission android:name="android.permission.BRICK" diff --git a/core/res/res/anim/screen_rotate_minus_90_enter.xml b/core/res/res/anim/screen_rotate_minus_90_enter.xml index 30518e02784b..61aa72a3b307 100644 --- a/core/res/res/anim/screen_rotate_minus_90_enter.xml +++ b/core/res/res/anim/screen_rotate_minus_90_enter.xml @@ -19,11 +19,6 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <scale android:fromXScale="100%p" android:toXScale="100%" - android:fromYScale="100%p" android:toYScale="100%" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:duration="@android:integer/config_mediumAnimTime" /> <rotate android:fromDegrees="-90" android:toDegrees="0" android:pivotX="50%" android:pivotY="50%" android:interpolator="@interpolator/decelerate_quint" diff --git a/core/res/res/anim/screen_rotate_plus_90_enter.xml b/core/res/res/anim/screen_rotate_plus_90_enter.xml index 20943c853578..53b0ccd43f05 100644 --- a/core/res/res/anim/screen_rotate_plus_90_enter.xml +++ b/core/res/res/anim/screen_rotate_plus_90_enter.xml @@ -19,11 +19,6 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <scale android:fromXScale="100%p" android:toXScale="100%" - android:fromYScale="100%p" android:toYScale="100%" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:duration="@android:integer/config_mediumAnimTime" /> <rotate android:fromDegrees="90" android:toDegrees="0" android:pivotX="50%" android:pivotY="50%" android:interpolator="@interpolator/decelerate_quint" diff --git a/core/res/res/drawable-hdpi/text_edit_suggestions_top_window.9.png b/core/res/res/drawable-hdpi/text_edit_suggestions_top_window.9.png Binary files differdeleted file mode 100644 index ff6b34a7ace4..000000000000 --- a/core/res/res/drawable-hdpi/text_edit_suggestions_top_window.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/text_edit_suggestions_bottom_window.9.png b/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.png Binary files differindex c97514f3ca99..c97514f3ca99 100644 --- a/core/res/res/drawable-hdpi/text_edit_suggestions_bottom_window.9.png +++ b/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.png diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png Binary files differdeleted file mode 100644 index 41886eb378a5..000000000000 --- a/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.png Binary files differindex 88be6e1f96f4..88be6e1f96f4 100644 --- a/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png +++ b/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.png diff --git a/core/res/res/layout/text_edit_suggestion_item.xml b/core/res/res/layout/text_edit_suggestion_item.xml index ef537d9efa93..082c5ec031c0 100644 --- a/core/res/res/layout/text_edit_suggestion_item.xml +++ b/core/res/res/layout/text_edit_suggestion_item.xml @@ -22,6 +22,8 @@ android:paddingTop="8dip" android:paddingBottom="8dip" android:layout_gravity="left|center_vertical" + android:singleLine="true" + android:ellipsize="marquee" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@android:color/dim_foreground_light" /> diff --git a/core/res/res/layout/text_edit_suggestions_bottom_window.xml b/core/res/res/layout/text_edit_suggestions_bottom_window.xml deleted file mode 100644 index 588bfbd9a321..000000000000 --- a/core/res/res/layout/text_edit_suggestions_bottom_window.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" - android:background="@android:drawable/text_edit_suggestions_bottom_window"> - -</LinearLayout> diff --git a/core/res/res/layout/text_edit_suggestions_top_window.xml b/core/res/res/layout/text_edit_suggestions_window.xml index 67faa37c32d5..824025e606f4 100644 --- a/core/res/res/layout/text_edit_suggestions_top_window.xml +++ b/core/res/res/layout/text_edit_suggestions_window.xml @@ -18,6 +18,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" - android:background="@android:drawable/text_edit_suggestions_top_window"> + android:background="@android:drawable/text_edit_suggestions_window"> </LinearLayout> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 082284a5f337..7d7aea920012 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -713,10 +713,7 @@ <!-- Layout of a the view that is used to create the text suggestions popup window in an EditText. This window will be displayed below the text line. --> - <attr name="textEditSuggestionsBottomWindowLayout" format="reference" /> - <!-- Same as textEditSuggestionsBottomWindowLayout, but used when the popup is displayed - above the current line of text instead of below. --> - <attr name="textEditSuggestionsTopWindowLayout" format="reference" /> + <attr name="textEditSuggestionsWindowLayout" format="reference" /> <!-- Layout of the TextView item that will populate the suggestion popup window. --> <attr name="textEditSuggestionItemLayout" format="reference" /> @@ -3082,10 +3079,7 @@ <!-- Layout of a the view that is used to create the text suggestions popup window in an EditText. This window will be displayed below the text line. --> - <attr name="textEditSuggestionsBottomWindowLayout" /> - <!-- Same as textEditSuggestionsBottomWindowLayout, but used when the popup is displayed - above the current line of text instead of below. --> - <attr name="textEditSuggestionsTopWindowLayout" /> + <attr name="textEditSuggestionsWindowLayout" /> <!-- Layout of the TextView item that will populate the suggestion popup window. --> <attr name="textEditSuggestionItemLayout" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 75f0c4e78f1d..b2b7025b398d 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1715,8 +1715,7 @@ <public type="attr" name="switchPreferenceStyle" /> <public type="attr" name="textSuggestionsWindowStyle" /> - <public type="attr" name="textEditSuggestionsBottomWindowLayout" /> - <public type="attr" name="textEditSuggestionsTopWindowLayout" /> + <public type="attr" name="textEditSuggestionsWindowLayout" /> <public type="attr" name="textEditSuggestionItemLayout" /> <public type="attr" name="suggestionsEnabled" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 509ee6996246..feac38d6d695 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -705,6 +705,12 @@ interface of an input method. Should never be needed for normal applications.</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_bindTextService">bind to a text service</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_bindTextService">Allows the holder to bind to the top-level + interface of a text service(e.g. SpellCheckerService). Should never be needed for normal applications.</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_bindWallpaper">bind to a wallpaper</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_bindWallpaper">Allows the holder to bind to the top-level @@ -2675,12 +2681,14 @@ <!-- USB_STORAGE_ERROR dialog ok button--> <string name="dlg_ok">OK</string> - <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in MTP mode. This is the title --> + <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MTP mode. This is the title --> <string name="usb_mtp_notification_title">Connected as a media device</string> - <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in PTP mode. This is the title --> + <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in PTP mode. This is the title --> <string name="usb_ptp_notification_title">Connected as a camera</string> - <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in mass storage mode (for installer CD image). This is the title --> + <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in mass storage mode (for installer CD image). This is the title --> <string name="usb_cd_installer_notification_title">Connected as an installer</string> + <!-- USB_PREFERENCES: Notification for when a USB accessory is attached. This is the title --> + <string name="usb_accessory_notification_title">Connected to a USB accessory</string> <!-- See USB_PREFERENCES. This is the message. --> <string name="usb_notification_message">Touch for other USB options</string> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index d647467a59fc..9b6c4424e7f0 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -423,8 +423,7 @@ <item name="android:textEditNoPasteWindowLayout">?android:attr/textEditNoPasteWindowLayout</item> <item name="android:textEditSidePasteWindowLayout">?android:attr/textEditSidePasteWindowLayout</item> <item name="android:textEditSideNoPasteWindowLayout">?android:attr/textEditSideNoPasteWindowLayout</item> - <item name="android:textEditSuggestionsBottomWindowLayout">?android:attr/textEditSuggestionsBottomWindowLayout</item> - <item name="android:textEditSuggestionsTopWindowLayout">?android:attr/textEditSuggestionsTopWindowLayout</item> + <item name="android:textEditSuggestionsWindowLayout">?android:attr/textEditSuggestionsWindowLayout</item> <item name="android:textEditSuggestionItemLayout">?android:attr/textEditSuggestionItemLayout</item> <item name="android:textCursorDrawable">?android:attr/textCursorDrawable</item> </style> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 90f3602073c0..93ccfe30412a 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -190,8 +190,7 @@ <item name="textEditSidePasteWindowLayout">@android:layout/text_edit_side_paste_window</item> <item name="textEditSideNoPasteWindowLayout">@android:layout/text_edit_side_no_paste_window</item> <item name="textSuggestionsWindowStyle">@android:style/Widget.TextSuggestions</item> - <item name="textEditSuggestionsBottomWindowLayout">@android:layout/text_edit_suggestions_bottom_window</item> - <item name="textEditSuggestionsTopWindowLayout">@android:layout/text_edit_suggestions_top_window</item> + <item name="textEditSuggestionsWindowLayout">@android:layout/text_edit_suggestions_window</item> <item name="textEditSuggestionItemLayout">@android:layout/text_edit_suggestion_item</item> <item name="textCursorDrawable">@null</item> diff --git a/docs/html/guide/developing/tools/adb.jd b/docs/html/guide/developing/tools/adb.jd index 78d12effd8b7..d32cf6684eda 100644 --- a/docs/html/guide/developing/tools/adb.jd +++ b/docs/html/guide/developing/tools/adb.jd @@ -280,7 +280,7 @@ instance, see <a href="{@docRoot}guide/developing/building/index.html">Building <td>Run PPP over USB. <ul> <li><code><tty></code> — the tty for PPP stream. For example <code>dev:/dev/omap_csmi_ttyl</code>. </li> -<li><code>[parm]... </code> &mdash zero or more PPP/PPPD options, such as <code>defaultroute</code>, <code>local</code>, <code>notty</code>, etc.</li></ul> +<li><code>[parm]... </code> — zero or more PPP/PPPD options, such as <code>defaultroute</code>, <code>local</code>, <code>notty</code>, etc.</li></ul> <p>Note that you should not automatically start a PPP connection. </p></td> <td></td> diff --git a/docs/html/guide/topics/intents/intents-filters.jd b/docs/html/guide/topics/intents/intents-filters.jd index 59052143523e..3f9455391a86 100644 --- a/docs/html/guide/topics/intents/intents-filters.jd +++ b/docs/html/guide/topics/intents/intents-filters.jd @@ -927,7 +927,7 @@ as described by its two intent filters: <p> The first, primary, purpose of this activity is to enable the user to -interact with a single note &mdash to either {@code VIEW} the note or +interact with a single note — to either {@code VIEW} the note or {@code EDIT} it. (The {@code EDIT_NOTE} category is a synonym for {@code EDIT}.) The intent would contain the URI for data matching the MIME type <code>vnd.android.cursor.item/vnd.google.note</code> — diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd index 34862121ae82..743832c6e763 100644 --- a/docs/html/guide/topics/manifest/activity-element.jd +++ b/docs/html/guide/topics/manifest/activity-element.jd @@ -710,7 +710,7 @@ the soft keyboard.</li> The setting must be one of the values listed in the following table, or a combination of one "{@code state...}" value plus one "{@code adjust...}" value. Setting multiple values in either group — multiple -"{@code state...}" values, for example &mdash has undefined results. +"{@code state...}" values, for example — has undefined results. Individual values are separated by a vertical bar ({@code |}). For example: </p> @@ -801,4 +801,4 @@ Level 3.</dd> <dt>see also:</dt> <dd><code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> <br/><code><a href="{@docRoot}guide/topics/manifest/activity-alias-element.html"><activity-alias></a></code></dd> -</dl>
\ No newline at end of file +</dl> diff --git a/docs/html/guide/topics/providers/content-providers.jd b/docs/html/guide/topics/providers/content-providers.jd index 2a84c263f652..513886afbe23 100644 --- a/docs/html/guide/topics/providers/content-providers.jd +++ b/docs/html/guide/topics/providers/content-providers.jd @@ -277,7 +277,7 @@ are returned. All the content providers that come with the platform define constants for their columns. For example, the {@link android.provider.Contacts.Phones android.provider.Contacts.Phones} class defines constants for the names of the columns in the phone table illustrated -earlier &mdash {@code _ID}, {@code NUMBER}, {@code NUMBER_KEY}, {@code NAME}, +earlier — {@code _ID}, {@code NUMBER}, {@code NUMBER_KEY}, {@code NAME}, and so on.</li> <li><p>A filter detailing which rows to return, formatted as an SQL {@code WHERE} diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index 4a85faf7dd2e..3476bd5aa646 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -504,6 +504,7 @@ nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, RsContext con, jint alloc, void* ptr = bitmap.getPixels(); rsAllocationCopyToBitmap(con, (RsAllocation)alloc, ptr, bitmap.getSize()); bitmap.unlockPixels(); + bitmap.notifyPixelsChanged(); } static void ReleaseBitmapCallback(void *bmp) diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h index bc630ae4ee57..1eda64669599 100644 --- a/include/gui/ISurfaceTexture.h +++ b/include/gui/ISurfaceTexture.h @@ -51,7 +51,7 @@ protected: // the given slot index, and the client is expected to mirror the // slot->buffer mapping so that it's not necessary to transfer a // GraphicBuffer for every dequeue operation. - virtual sp<GraphicBuffer> requestBuffer(int slot) = 0; + virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0; // setBufferCount sets the number of buffer slots available. Calling this // will also cause all buffer slots to be emptied. The caller should empty @@ -94,12 +94,6 @@ protected: virtual status_t setTransform(uint32_t transform) = 0; virtual status_t setScalingMode(int mode) = 0; - // getAllocator retrieves the binder object that must be referenced as long - // as the GraphicBuffers dequeued from this ISurfaceTexture are referenced. - // Holding this binder reference prevents SurfaceFlinger from freeing the - // buffers before the client is done with them. - virtual sp<IBinder> getAllocator() = 0; - // query retrieves some information for this surface // 'what' tokens allowed are that of android_natives.h virtual int query(int what, int* value) = 0; diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 945f4bcd6890..134c208f4d26 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -69,7 +69,7 @@ public: // SurfaceTexture object (i.e. they are not owned by the client). virtual status_t setBufferCount(int bufferCount); - virtual sp<GraphicBuffer> requestBuffer(int buf); + virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); // dequeueBuffer gets the next buffer slot index for the client to use. If a // buffer slot is available then that slot index is written to the location @@ -190,6 +190,17 @@ public: // getCurrentScalingMode returns the scaling mode of the current buffer uint32_t getCurrentScalingMode() const; + // abandon frees all the buffers and puts the SurfaceTexture into the + // 'abandoned' state. Once put in this state the SurfaceTexture can never + // leave it. When in the 'abandoned' state, all methods of the + // ISurfaceTexture interface will fail with the NO_INIT error. + // + // Note that while calling this method causes all the buffers to be freed + // from the perspective of the the SurfaceTexture, if there are additional + // references on the buffers (e.g. if a buffer is referenced by a client or + // by OpenGL ES as a texture) then those buffer will remain allocated. + void abandon(); + // dump our state in a String void dump(String8& result) const; void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; @@ -343,8 +354,7 @@ private: // mCurrentTextureBuf is the graphic buffer of the current texture. It's // possible that this buffer is not associated with any buffer slot, so we - // must track it separately in order to properly use - // IGraphicBufferAlloc::freeAllGraphicBuffersExcept. + // must track it separately in order to support the getCurrentBuffer method. sp<GraphicBuffer> mCurrentTextureBuf; // mCurrentCrop is the crop rectangle that applies to the current texture. @@ -412,6 +422,13 @@ private: typedef Vector<int> Fifo; Fifo mQueue; + // mAbandoned indicates that the SurfaceTexture will no longer be used to + // consume images buffers pushed to it using the ISurfaceTexture interface. + // It is initialized to false, and set to true in the abandon method. A + // SurfaceTexture that has been abandoned will return the NO_INIT error from + // all ISurfaceTexture methods capable of returning an error. + bool mAbandoned; + // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h index 829d8abf7057..56f029f0c254 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -106,10 +106,6 @@ private: // interactions with the server using this interface. sp<ISurfaceTexture> mSurfaceTexture; - // mAllocator is the binder object that is referenced to prevent the - // dequeued buffers from being freed prematurely. - sp<IBinder> mAllocator; - // mSlots stores the buffers that have been allocated for each buffer slot. // It is initialized to null pointers, and gets filled in with the result of // ISurfaceTexture::requestBuffer when the client dequeues a buffer from a diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h index a73267d92587..007aea6c0362 100644 --- a/include/media/IMediaRecorder.h +++ b/include/media/IMediaRecorder.h @@ -26,6 +26,7 @@ class Surface; class ICamera; class ICameraRecordingProxy; class IMediaRecorderClient; +class ISurfaceTexture; class IMediaRecorder: public IInterface { @@ -55,6 +56,7 @@ public: virtual status_t init() = 0; virtual status_t close() = 0; virtual status_t release() = 0; + virtual sp<ISurfaceTexture> querySurfaceMediaSource() = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h index 1c08969a0d5c..ef799f5cff26 100644 --- a/include/media/MediaRecorderBase.h +++ b/include/media/MediaRecorderBase.h @@ -26,6 +26,7 @@ namespace android { class ICameraRecordingProxy; class Surface; +class ISurfaceTexture; struct MediaRecorderBase { MediaRecorderBase() {} @@ -54,6 +55,7 @@ struct MediaRecorderBase { virtual status_t reset() = 0; virtual status_t getMaxAmplitude(int *max) = 0; virtual status_t dump(int fd, const Vector<String16>& args) const = 0; + virtual sp<ISurfaceTexture> querySurfaceMediaSource() const = 0; private: MediaRecorderBase(const MediaRecorderBase &); diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index af12d3c59c1f..72d3736296d1 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -31,12 +31,15 @@ class Surface; class IMediaRecorder; class ICamera; class ICameraRecordingProxy; +class ISurfaceTexture; +class SurfaceTextureClient; typedef void (*media_completion_f)(status_t status, void *cookie); enum video_source { VIDEO_SOURCE_DEFAULT = 0, VIDEO_SOURCE_CAMERA = 1, + VIDEO_SOURCE_GRALLOC_BUFFER = 2, VIDEO_SOURCE_LIST_END // must be last - used to validate audio source type }; @@ -226,6 +229,7 @@ public: status_t close(); status_t release(); void notify(int msg, int ext1, int ext2); + sp<ISurfaceTexture> querySurfaceMediaSourceFromMediaServer(); private: void doCleanUp(); @@ -233,6 +237,12 @@ private: sp<IMediaRecorder> mMediaRecorder; sp<MediaRecorderListener> mListener; + + // Reference toISurfaceTexture + // for encoding GL Frames. That is useful only when the + // video source is set to VIDEO_SOURCE_GRALLOC_BUFFER + sp<ISurfaceTexture> mSurfaceMediaSource; + media_recorder_states mCurrentState; bool mIsAudioSourceSet; bool mIsVideoSourceSet; diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h index 48d1464336a1..713af92860ef 100644 --- a/include/media/stagefright/DataSource.h +++ b/include/media/stagefright/DataSource.h @@ -20,6 +20,7 @@ #include <sys/types.h> +#include <media/stagefright/MediaErrors.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/List.h> @@ -61,6 +62,10 @@ public: return 0; } + virtual status_t reconnectAtOffset(off64_t offset) { + return ERROR_UNSUPPORTED; + } + //////////////////////////////////////////////////////////////////////////// bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta); diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h index 946a0aaa8f6c..32eed3f79c6c 100644 --- a/include/media/stagefright/HardwareAPI.h +++ b/include/media/stagefright/HardwareAPI.h @@ -99,6 +99,13 @@ struct GetAndroidNativeBufferUsageParams { OMX_U32 nUsage; // OUT }; +// An enum OMX_COLOR_FormatAndroidOpaque to indicate an opaque colorformat +// is declared in media/stagefright/openmax/OMX_IVCommon.h +// This will inform the encoder that the actual +// colorformat will be relayed by the GRalloc Buffers. +// OMX_COLOR_FormatAndroidOpaque = 0x7F000001, + + } // namespace android extern android::OMXPluginBase *createOMXPlugin(); diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h index 37dbcd8eae84..3818e63ff4e9 100644 --- a/include/media/stagefright/MediaSource.h +++ b/include/media/stagefright/MediaSource.h @@ -29,7 +29,7 @@ namespace android { class MediaBuffer; class MetaData; -struct MediaSource : public RefBase { +struct MediaSource : public virtual RefBase { MediaSource(); // To be called before any other methods on this object, except diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h new file mode 100644 index 000000000000..56bd9c315005 --- /dev/null +++ b/include/media/stagefright/SurfaceMediaSource.h @@ -0,0 +1,350 @@ +/* + * 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. + */ + +#ifndef ANDROID_GUI_SURFACEMEDIASOURCE_H +#define ANDROID_GUI_SURFACEMEDIASOURCE_H + +#include <gui/ISurfaceTexture.h> + +#include <utils/threads.h> +#include <utils/Vector.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MediaBuffer.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class IGraphicBufferAlloc; +class String8; +class GraphicBuffer; + +class SurfaceMediaSource : public BnSurfaceTexture, public MediaSource, + public MediaBufferObserver { +public: + enum { MIN_UNDEQUEUED_BUFFERS = 3 }; + enum { + MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1, + MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + }; + enum { NUM_BUFFER_SLOTS = 32 }; + enum { NO_CONNECTED_API = 0 }; + + struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called from queueBuffer() is the FIFO is + // empty. You can use SurfaceMediaSource::getQueuedCount() to + // figure out if there are more frames waiting. + // This is called without any lock held can be called concurrently by + // multiple threads. + virtual void onFrameAvailable() = 0; + }; + + SurfaceMediaSource(uint32_t bufW, uint32_t bufH); + + virtual ~SurfaceMediaSource(); + + + // For the MediaSource interface for use by StageFrightRecorder: + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + virtual sp<MetaData> getFormat(); + + // Get / Set the frame rate used for encoding. Default fps = 30 + status_t setFrameRate(int32_t fps) ; + int32_t getFrameRate( ) const; + + // The call for the StageFrightRecorder to tell us that + // it is done using the MediaBuffer data so that its state + // can be set to FREE for dequeuing + virtual void signalBufferReturned(MediaBuffer* buffer); + // end of MediaSource interface + + uint32_t getBufferCount( ) const { return mBufferCount;} + + + // setBufferCount updates the number of available buffer slots. After + // calling this all buffer slots are both unallocated and owned by the + // SurfaceMediaSource object (i.e. they are not owned by the client). + virtual status_t setBufferCount(int bufferCount); + + virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); + + // dequeueBuffer gets the next buffer slot index for the client to use. If a + // buffer slot is available then that slot index is written to the location + // pointed to by the buf argument and a status of OK is returned. If no + // slot is available then a status of -EBUSY is returned and buf is + // unmodified. + virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage); + + // queueBuffer returns a filled buffer to the SurfaceMediaSource. In addition, a + // timestamp must be provided for the buffer. The timestamp is in + // nanoseconds, and must be monotonically increasing. Its other semantics + // (zero point, etc) are client-dependent and should be documented by the + // client. + virtual status_t queueBuffer(int buf, int64_t timestamp, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); + virtual void cancelBuffer(int buf); + + // onFrameReceivedLocked informs the buffer consumers (StageFrightRecorder) + // or listeners that a frame has been received + // The buffer is not made available for dequeueing immediately. We need to + // wait to hear from StageFrightRecorder to set the buffer FREE + // Make sure this is called when the mutex is locked + virtual status_t onFrameReceivedLocked(); + + virtual status_t setScalingMode(int mode) { } // no op for encoding + virtual int query(int what, int* value); + + // Just confirming to the ISurfaceTexture interface as of now + virtual status_t setCrop(const Rect& reg) { return OK; } + virtual status_t setTransform(uint32_t transform) {return OK;} + + // setSynchronousMode set whether dequeueBuffer is synchronous or + // asynchronous. In synchronous mode, dequeueBuffer blocks until + // a buffer is available, the currently bound buffer can be dequeued and + // queued buffers will be retired in order. + // The default mode is synchronous. + // TODO: Clarify the minute differences bet sycn /async + // modes (S.Encoder vis-a-vis SurfaceTexture) + virtual status_t setSynchronousMode(bool enabled); + + // connect attempts to connect a client API to the SurfaceMediaSource. This + // must be called before any other ISurfaceTexture methods are called except + // for getAllocator. + // + // This method will fail if the connect was previously called on the + // SurfaceMediaSource and no corresponding disconnect call was made. + virtual status_t connect(int api); + + // disconnect attempts to disconnect a client API from the SurfaceMediaSource. + // Calling this method will cause any subsequent calls to other + // ISurfaceTexture methods to fail except for getAllocator and connect. + // Successfully calling connect after this will allow the other methods to + // succeed again. + // + // This method will fail if the the SurfaceMediaSource is not currently + // connected to the specified client API. + virtual status_t disconnect(int api); + + // getqueuedCount returns the number of queued frames waiting in the + // FIFO. In asynchronous mode, this always returns 0 or 1 since + // frames are not accumulating in the FIFO. + size_t getQueuedCount() const; + + // setBufferCountServer set the buffer count. If the client has requested + // a buffer count using setBufferCount, the server-buffer count will + // take effect once the client sets the count back to zero. + status_t setBufferCountServer(int bufferCount); + + // getTimestamp retrieves the timestamp associated with the image + // set by the most recent call to updateFrameInfoLocked(). + // + // The timestamp is in nanoseconds, and is monotonically increasing. Its + // other semantics (zero point, etc) are source-dependent and should be + // documented by the source. + int64_t getTimestamp(); + + // setFrameAvailableListener sets the listener object that will be notified + // when a new frame becomes available. + void setFrameAvailableListener(const sp<FrameAvailableListener>& listener); + + // getCurrentBuffer returns the buffer associated with the current image. + sp<GraphicBuffer> getCurrentBuffer() const; + + // dump our state in a String + void dump(String8& result) const; + void dump(String8& result, const char* prefix, char* buffer, + size_t SIZE) const; + + // isMetaDataStoredInVideoBuffers tells the encoder whether we will + // pass metadata through the buffers. Currently, it is force set to true + bool isMetaDataStoredInVideoBuffers() const; + +protected: + + // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for + // all slots. + void freeAllBuffers(); + static bool isExternalFormat(uint32_t format); + +private: + + status_t setBufferCountServerLocked(int bufferCount); + + enum { INVALID_BUFFER_SLOT = -1 }; + + struct BufferSlot { + + BufferSlot() + : mBufferState(BufferSlot::FREE), + mRequestBufferCalled(false), + mTimestamp(0) { + } + + // mGraphicBuffer points to the buffer allocated for this slot or is + // NULL if no buffer has been allocated. + sp<GraphicBuffer> mGraphicBuffer; + + // BufferState represents the different states in which a buffer slot + // can be. + enum BufferState { + // FREE indicates that the buffer is not currently being used and + // will not be used in the future until it gets dequeued and + // subseqently queued by the client. + FREE = 0, + + // DEQUEUED indicates that the buffer has been dequeued by the + // client, but has not yet been queued or canceled. The buffer is + // considered 'owned' by the client, and the server should not use + // it for anything. + // + // Note that when in synchronous-mode (mSynchronousMode == true), + // the buffer that's currently attached to the texture may be + // dequeued by the client. That means that the current buffer can + // be in either the DEQUEUED or QUEUED state. In asynchronous mode, + // however, the current buffer is always in the QUEUED state. + DEQUEUED = 1, + + // QUEUED indicates that the buffer has been queued by the client, + // and has not since been made available for the client to dequeue. + // Attaching the buffer to the texture does NOT transition the + // buffer away from the QUEUED state. However, in Synchronous mode + // the current buffer may be dequeued by the client under some + // circumstances. See the note about the current buffer in the + // documentation for DEQUEUED. + QUEUED = 2, + }; + + // mBufferState is the current state of this buffer slot. + BufferState mBufferState; + + // mRequestBufferCalled is used for validating that the client did + // call requestBuffer() when told to do so. Technically this is not + // needed but useful for debugging and catching client bugs. + bool mRequestBufferCalled; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + }; + + // mSlots is the array of buffer slots that must be mirrored on the client + // side. This allows buffer ownership to be transferred between the client + // and server without sending a GraphicBuffer over binder. The entire array + // is initialized to NULL at construction time, and buffers are allocated + // for a slot when requestBuffer is called with that slot's index. + BufferSlot mSlots[NUM_BUFFER_SLOTS]; + + // mDefaultWidth holds the default width of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultWidth; + + // mDefaultHeight holds the default height of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultHeight; + + // mPixelFormat holds the pixel format of allocated buffers. It is used + // in requestBuffers() if a format of zero is specified. + uint32_t mPixelFormat; + + // mBufferCount is the number of buffer slots that the client and server + // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed + // by calling setBufferCount or setBufferCountServer + int mBufferCount; + + // mClientBufferCount is the number of buffer slots requested by the + // client. The default is zero, which means the client doesn't care how + // many buffers there are + int mClientBufferCount; + + // mServerBufferCount buffer count requested by the server-side + int mServerBufferCount; + + // mCurrentSlot is the buffer slot index of the buffer that is currently + // being used by buffer consumer + // (e.g. StageFrightRecorder in the case of SurfaceMediaSource or GLTexture + // in the case of SurfaceTexture). + // It is initialized to INVALID_BUFFER_SLOT, + // indicating that no buffer slot is currently bound to the texture. Note, + // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean + // that no buffer is bound to the texture. A call to setBufferCount will + // reset mCurrentTexture to INVALID_BUFFER_SLOT. + int mCurrentSlot; + + + // mCurrentBuf is the graphic buffer of the current slot to be used by + // buffer consumer. It's possible that this buffer is not associated + // with any buffer slot, so we must track it separately in order to + // properly use IGraphicBufferAlloc::freeAllGraphicBuffersExcept. + sp<GraphicBuffer> mCurrentBuf; + + + // mCurrentTimestamp is the timestamp for the current texture. It + // gets set to mLastQueuedTimestamp each time updateTexImage is called. + int64_t mCurrentTimestamp; + + // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to + // allocate new GraphicBuffer objects. + sp<IGraphicBufferAlloc> mGraphicBufferAlloc; + + // mFrameAvailableListener is the listener object that will be called when a + // new frame becomes available. If it is not NULL it will be called from + // queueBuffer. + sp<FrameAvailableListener> mFrameAvailableListener; + + // mSynchronousMode whether we're in synchronous mode or not + bool mSynchronousMode; + + // mConnectedApi indicates the API that is currently connected to this + // SurfaceTexture. It defaults to NO_CONNECTED_API (= 0), and gets updated + // by the connect and disconnect methods. + int mConnectedApi; + + // mDequeueCondition condition used for dequeueBuffer in synchronous mode + mutable Condition mDequeueCondition; + + + // mQueue is a FIFO of queued buffers used in synchronous mode + typedef Vector<int> Fifo; + Fifo mQueue; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of SurfaceMediaSource objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; + + ////////////////////////// For MediaSource + // Set to a default of 30 fps if not specified by the client side + int32_t mFrameRate; + + // mStarted is a flag to check if the recording has started + bool mStarted; + + // mFrameAvailableCondition condition used to indicate whether there + // is a frame available for dequeuing + Condition mFrameAvailableCondition; + Condition mFrameCompleteCondition; + + // Avoid copying and equating and default constructor + DISALLOW_IMPLICIT_CONSTRUCTORS(SurfaceMediaSource); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_SURFACEMEDIASOURCE_H diff --git a/include/media/stagefright/openmax/OMX_IVCommon.h b/include/media/stagefright/openmax/OMX_IVCommon.h index 7ed072b73f96..97170d7e7dbe 100644 --- a/include/media/stagefright/openmax/OMX_IVCommon.h +++ b/include/media/stagefright/openmax/OMX_IVCommon.h @@ -16,29 +16,29 @@ * ------------------------------------------------------------------- */ /** - * Copyright (c) 2008 The Khronos Group Inc. - * + * Copyright (c) 2008 The Khronos Group Inc. + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject - * to the following conditions: + * to the following conditions: * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * + * in all copies or substantial portions of the Software. + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ -/** +/** * @file OMX_IVCommon.h - OpenMax IL version 1.1.2 * The structures needed by Video and Image components to exchange * parameters and configuration data with the components. @@ -53,7 +53,7 @@ extern "C" { /** * Each OMX header must include all required header files to allow the header * to compile without errors. The includes below are required for this header - * file to compile successfully + * file to compile successfully */ #include <OMX_Core.h> @@ -64,8 +64,8 @@ extern "C" { */ -/** - * Enumeration defining possible uncompressed image/video formats. +/** + * Enumeration defining possible uncompressed image/video formats. * * ENUMS: * Unused : Placeholder value when format is N/A @@ -113,7 +113,7 @@ typedef enum OMX_COLOR_FORMATTYPE { OMX_COLOR_Format16bitBGR565, OMX_COLOR_Format18bitRGB666, OMX_COLOR_Format18bitARGB1665, - OMX_COLOR_Format19bitARGB1666, + OMX_COLOR_Format19bitARGB1666, OMX_COLOR_Format24bitRGB888, OMX_COLOR_Format24bitBGR888, OMX_COLOR_Format24bitARGB1887, @@ -136,55 +136,62 @@ typedef enum OMX_COLOR_FORMATTYPE { OMX_COLOR_FormatRawBayer8bit, OMX_COLOR_FormatRawBayer10bit, OMX_COLOR_FormatRawBayer8bitcompressed, - OMX_COLOR_FormatL2, - OMX_COLOR_FormatL4, - OMX_COLOR_FormatL8, - OMX_COLOR_FormatL16, - OMX_COLOR_FormatL24, + OMX_COLOR_FormatL2, + OMX_COLOR_FormatL4, + OMX_COLOR_FormatL8, + OMX_COLOR_FormatL16, + OMX_COLOR_FormatL24, OMX_COLOR_FormatL32, OMX_COLOR_FormatYUV420PackedSemiPlanar, OMX_COLOR_FormatYUV422PackedSemiPlanar, OMX_COLOR_Format18BitBGR666, OMX_COLOR_Format24BitARGB6666, OMX_COLOR_Format24BitABGR6666, - OMX_COLOR_FormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_COLOR_FormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_COLOR_FormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + /**<Reserved android opaque colorformat. Tells the encoder that + * the actual colorformat will be relayed by the + * Gralloc Buffers. + * FIXME: In the process of reserving some enum values for + * Android-specific OMX IL colorformats. Change this enum to + * an acceptable range once that is done.*/ + OMX_COLOR_FormatAndroidOpaque = 0x7F000001, OMX_TI_COLOR_FormatYUV420PackedSemiPlanar = 0x7F000100, OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00, OMX_COLOR_FormatMax = 0x7FFFFFFF } OMX_COLOR_FORMATTYPE; -/** +/** * Defines the matrix for conversion from RGB to YUV or vice versa. - * iColorMatrix should be initialized with the fixed point values + * iColorMatrix should be initialized with the fixed point values * used in converting between formats. */ typedef struct OMX_CONFIG_COLORCONVERSIONTYPE { OMX_U32 nSize; /**< Size of the structure in bytes */ - OMX_VERSIONTYPE nVersion; /**< OMX specification version info */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version info */ OMX_U32 nPortIndex; /**< Port that this struct applies to */ OMX_S32 xColorMatrix[3][3]; /**< Stored in signed Q16 format */ OMX_S32 xColorOffset[4]; /**< Stored in signed Q16 format */ }OMX_CONFIG_COLORCONVERSIONTYPE; -/** - * Structure defining percent to scale each frame dimension. For example: +/** + * Structure defining percent to scale each frame dimension. For example: * To make the width 50% larger, use fWidth = 1.5 and to make the width * 1/2 the original size, use fWidth = 0.5 */ typedef struct OMX_CONFIG_SCALEFACTORTYPE { OMX_U32 nSize; /**< Size of the structure in bytes */ - OMX_VERSIONTYPE nVersion; /**< OMX specification version info */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version info */ OMX_U32 nPortIndex; /**< Port that this struct applies to */ OMX_S32 xWidth; /**< Fixed point value stored as Q16 */ OMX_S32 xHeight; /**< Fixed point value stored as Q16 */ }OMX_CONFIG_SCALEFACTORTYPE; -/** - * Enumeration of possible image filter types +/** + * Enumeration of possible image filter types */ typedef enum OMX_IMAGEFILTERTYPE { OMX_ImageFilterNone, @@ -195,23 +202,23 @@ typedef enum OMX_IMAGEFILTERTYPE { OMX_ImageFilterOilPaint, OMX_ImageFilterHatch, OMX_ImageFilterGpen, - OMX_ImageFilterAntialias, - OMX_ImageFilterDeRing, + OMX_ImageFilterAntialias, + OMX_ImageFilterDeRing, OMX_ImageFilterSolarize, - OMX_ImageFilterKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_ImageFilterKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_ImageFilterVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_ImageFilterMax = 0x7FFFFFFF } OMX_IMAGEFILTERTYPE; -/** - * Image filter configuration +/** + * Image filter configuration * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes + * nSize : Size of the structure in bytes * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to - * eImageFilter : Image filter type enumeration + * nPortIndex : Port that this structure applies to + * eImageFilter : Image filter type enumeration */ typedef struct OMX_CONFIG_IMAGEFILTERTYPE { OMX_U32 nSize; @@ -221,22 +228,22 @@ typedef struct OMX_CONFIG_IMAGEFILTERTYPE { } OMX_CONFIG_IMAGEFILTERTYPE; -/** - * Customized U and V for color enhancement +/** + * Customized U and V for color enhancement * * STRUCT MEMBERS: * nSize : Size of the structure in bytes - * nVersion : OMX specification version information + * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * bColorEnhancement : Enable/disable color enhancement - * nCustomizedU : Practical values: 16-240, range: 0-255, value set for + * nCustomizedU : Practical values: 16-240, range: 0-255, value set for * U component - * nCustomizedV : Practical values: 16-240, range: 0-255, value set for + * nCustomizedV : Practical values: 16-240, range: 0-255, value set for * V component */ typedef struct OMX_CONFIG_COLORENHANCEMENTTYPE { OMX_U32 nSize; - OMX_VERSIONTYPE nVersion; + OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bColorEnhancement; OMX_U8 nCustomizedU; @@ -244,12 +251,12 @@ typedef struct OMX_CONFIG_COLORENHANCEMENTTYPE { } OMX_CONFIG_COLORENHANCEMENTTYPE; -/** - * Define color key and color key mask +/** + * Define color key and color key mask * * STRUCT MEMBERS: * nSize : Size of the structure in bytes - * nVersion : OMX specification version information + * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nARGBColor : 32bit Alpha, Red, Green, Blue Color * nARGBMask : 32bit Mask for Alpha, Red, Green, Blue channels @@ -263,12 +270,12 @@ typedef struct OMX_CONFIG_COLORKEYTYPE { } OMX_CONFIG_COLORKEYTYPE; -/** - * List of color blend types for pre/post processing +/** + * List of color blend types for pre/post processing * * ENUMS: * None : No color blending present - * AlphaConstant : Function is (alpha_constant * src) + + * AlphaConstant : Function is (alpha_constant * src) + * (1 - alpha_constant) * dst) * AlphaPerPixel : Function is (alpha * src) + (1 - alpha) * dst) * Alternate : Function is alternating pixels from src and dst @@ -284,21 +291,21 @@ typedef enum OMX_COLORBLENDTYPE { OMX_ColorBlendAnd, OMX_ColorBlendOr, OMX_ColorBlendInvert, - OMX_ColorBlendKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_ColorBlendKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_ColorBlendVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_ColorBlendMax = 0x7FFFFFFF } OMX_COLORBLENDTYPE; -/** - * Color blend configuration +/** + * Color blend configuration * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes - * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to * nRGBAlphaConstant : Constant global alpha values when global alpha is used - * eColorBlend : Color blend type enumeration + * eColorBlend : Color blend type enumeration */ typedef struct OMX_CONFIG_COLORBLENDTYPE { OMX_U32 nSize; @@ -309,15 +316,15 @@ typedef struct OMX_CONFIG_COLORBLENDTYPE { } OMX_CONFIG_COLORBLENDTYPE; -/** +/** * Hold frame dimension * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes + * nSize : Size of the structure in bytes * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to - * nWidth : Frame width in pixels - * nHeight : Frame height in pixels + * nPortIndex : Port that this structure applies to + * nWidth : Frame width in pixels + * nHeight : Frame height in pixels */ typedef struct OMX_FRAMESIZETYPE { OMX_U32 nSize; @@ -329,69 +336,69 @@ typedef struct OMX_FRAMESIZETYPE { /** - * Rotation configuration + * Rotation configuration * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes + * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to - * nRotation : +/- integer rotation value + * nRotation : +/- integer rotation value */ typedef struct OMX_CONFIG_ROTATIONTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; - OMX_S32 nRotation; + OMX_S32 nRotation; } OMX_CONFIG_ROTATIONTYPE; -/** - * Possible mirroring directions for pre/post processing +/** + * Possible mirroring directions for pre/post processing * * ENUMS: - * None : No mirroring - * Vertical : Vertical mirroring, flip on X axis - * Horizontal : Horizontal mirroring, flip on Y axis + * None : No mirroring + * Vertical : Vertical mirroring, flip on X axis + * Horizontal : Horizontal mirroring, flip on Y axis * Both : Both vertical and horizontal mirroring */ typedef enum OMX_MIRRORTYPE { OMX_MirrorNone = 0, OMX_MirrorVertical, OMX_MirrorHorizontal, - OMX_MirrorBoth, - OMX_MirrorKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_MirrorBoth, + OMX_MirrorKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_MirrorVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ - OMX_MirrorMax = 0x7FFFFFFF + OMX_MirrorMax = 0x7FFFFFFF } OMX_MIRRORTYPE; -/** - * Mirroring configuration +/** + * Mirroring configuration * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes + * nSize : Size of the structure in bytes * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to - * eMirror : Mirror type enumeration + * nPortIndex : Port that this structure applies to + * eMirror : Mirror type enumeration */ typedef struct OMX_CONFIG_MIRRORTYPE { OMX_U32 nSize; - OMX_VERSIONTYPE nVersion; + OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_MIRRORTYPE eMirror; } OMX_CONFIG_MIRRORTYPE; -/** - * Position information only +/** + * Position information only * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes + * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to - * nX : X coordinate for the point - * nY : Y coordinate for the point - */ + * nX : X coordinate for the point + * nY : Y coordinate for the point + */ typedef struct OMX_CONFIG_POINTTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; @@ -401,37 +408,37 @@ typedef struct OMX_CONFIG_POINTTYPE { } OMX_CONFIG_POINTTYPE; -/** - * Frame size plus position +/** + * Frame size plus position * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes - * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to * nLeft : X Coordinate of the top left corner of the rectangle * nTop : Y Coordinate of the top left corner of the rectangle - * nWidth : Width of the rectangle - * nHeight : Height of the rectangle + * nWidth : Width of the rectangle + * nHeight : Height of the rectangle */ typedef struct OMX_CONFIG_RECTTYPE { OMX_U32 nSize; - OMX_VERSIONTYPE nVersion; - OMX_U32 nPortIndex; - OMX_S32 nLeft; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_S32 nLeft; OMX_S32 nTop; OMX_U32 nWidth; OMX_U32 nHeight; } OMX_CONFIG_RECTTYPE; -/** - * Deblocking state; it is required to be set up before starting the codec +/** + * Deblocking state; it is required to be set up before starting the codec * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes - * nVersion : OMX specification version information + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to - * bDeblocking : Enable/disable deblocking mode + * bDeblocking : Enable/disable deblocking mode */ typedef struct OMX_PARAM_DEBLOCKINGTYPE { OMX_U32 nSize; @@ -441,13 +448,13 @@ typedef struct OMX_PARAM_DEBLOCKINGTYPE { } OMX_PARAM_DEBLOCKINGTYPE; -/** - * Stabilization state +/** + * Stabilization state * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes - * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to * bStab : Enable/disable frame stabilization state */ typedef struct OMX_CONFIG_FRAMESTABTYPE { @@ -458,8 +465,8 @@ typedef struct OMX_CONFIG_FRAMESTABTYPE { } OMX_CONFIG_FRAMESTABTYPE; -/** - * White Balance control type +/** + * White Balance control type * * STRUCT MEMBERS: * SunLight : Referenced in JSR-234 @@ -476,20 +483,20 @@ typedef enum OMX_WHITEBALCONTROLTYPE { OMX_WhiteBalControlIncandescent, OMX_WhiteBalControlFlash, OMX_WhiteBalControlHorizon, - OMX_WhiteBalControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_WhiteBalControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_WhiteBalControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_WhiteBalControlMax = 0x7FFFFFFF } OMX_WHITEBALCONTROLTYPE; -/** - * White Balance control configuration +/** + * White Balance control configuration * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes + * nSize : Size of the structure in bytes * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to - * eWhiteBalControl : White balance enumeration + * nPortIndex : Port that this structure applies to + * eWhiteBalControl : White balance enumeration */ typedef struct OMX_CONFIG_WHITEBALCONTROLTYPE { OMX_U32 nSize; @@ -499,8 +506,8 @@ typedef struct OMX_CONFIG_WHITEBALCONTROLTYPE { } OMX_CONFIG_WHITEBALCONTROLTYPE; -/** - * Exposure control type +/** + * Exposure control type */ typedef enum OMX_EXPOSURECONTROLTYPE { OMX_ExposureControlOff = 0, @@ -513,20 +520,20 @@ typedef enum OMX_EXPOSURECONTROLTYPE { OMX_ExposureControlBeach, OMX_ExposureControlLargeAperture, OMX_ExposureControlSmallApperture, - OMX_ExposureControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_ExposureControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_ExposureControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_ExposureControlMax = 0x7FFFFFFF } OMX_EXPOSURECONTROLTYPE; -/** - * White Balance control configuration +/** + * White Balance control configuration * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes + * nSize : Size of the structure in bytes * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to - * eExposureControl : Exposure control enumeration + * nPortIndex : Port that this structure applies to + * eExposureControl : Exposure control enumeration */ typedef struct OMX_CONFIG_EXPOSURECONTROLTYPE { OMX_U32 nSize; @@ -536,16 +543,16 @@ typedef struct OMX_CONFIG_EXPOSURECONTROLTYPE { } OMX_CONFIG_EXPOSURECONTROLTYPE; -/** - * Defines sensor supported mode. +/** + * Defines sensor supported mode. * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes + * nSize : Size of the structure in bytes * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to - * nFrameRate : Single shot mode is indicated by a 0 + * nPortIndex : Port that this structure applies to + * nFrameRate : Single shot mode is indicated by a 0 * bOneShot : Enable for single shot, disable for streaming - * sFrameSize : Framesize + * sFrameSize : Framesize */ typedef struct OMX_PARAM_SENSORMODETYPE { OMX_U32 nSize; @@ -557,13 +564,13 @@ typedef struct OMX_PARAM_SENSORMODETYPE { } OMX_PARAM_SENSORMODETYPE; -/** - * Defines contrast level +/** + * Defines contrast level * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes - * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to * nContrast : Values allowed for contrast -100 to 100, zero means no change */ typedef struct OMX_CONFIG_CONTRASTTYPE { @@ -574,14 +581,14 @@ typedef struct OMX_CONFIG_CONTRASTTYPE { } OMX_CONFIG_CONTRASTTYPE; -/** - * Defines brightness level +/** + * Defines brightness level * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes - * nVersion : OMX specification version information - * nPortIndex : Port that this structure applies to - * nBrightness : 0-100% + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nBrightness : 0-100% */ typedef struct OMX_CONFIG_BRIGHTNESSTYPE { OMX_U32 nSize; @@ -591,16 +598,16 @@ typedef struct OMX_CONFIG_BRIGHTNESSTYPE { } OMX_CONFIG_BRIGHTNESSTYPE; -/** - * Defines backlight level configuration for a video sink, e.g. LCD panel +/** + * Defines backlight level configuration for a video sink, e.g. LCD panel * * STRUCT MEMBERS: * nSize : Size of the structure in bytes - * nVersion : OMX specification version information + * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nBacklight : Values allowed for backlight 0-100% - * nTimeout : Number of milliseconds before backlight automatically turns - * off. A value of 0x0 disables backight timeout + * nTimeout : Number of milliseconds before backlight automatically turns + * off. A value of 0x0 disables backight timeout */ typedef struct OMX_CONFIG_BACKLIGHTTYPE { OMX_U32 nSize; @@ -611,12 +618,12 @@ typedef struct OMX_CONFIG_BACKLIGHTTYPE { } OMX_CONFIG_BACKLIGHTTYPE; -/** - * Defines setting for Gamma +/** + * Defines setting for Gamma * * STRUCT MEMBERS: * nSize : Size of the structure in bytes - * nVersion : OMX specification version information + * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nGamma : Values allowed for gamma -100 to 100, zero means no change */ @@ -628,14 +635,14 @@ typedef struct OMX_CONFIG_GAMMATYPE { } OMX_CONFIG_GAMMATYPE; -/** - * Define for setting saturation - * +/** + * Define for setting saturation + * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to - * nSaturation : Values allowed for saturation -100 to 100, zero means + * nSaturation : Values allowed for saturation -100 to 100, zero means * no change */ typedef struct OMX_CONFIG_SATURATIONTYPE { @@ -646,14 +653,14 @@ typedef struct OMX_CONFIG_SATURATIONTYPE { } OMX_CONFIG_SATURATIONTYPE; -/** - * Define for setting Lightness +/** + * Define for setting Lightness * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to - * nLightness : Values allowed for lightness -100 to 100, zero means no + * nLightness : Values allowed for lightness -100 to 100, zero means no * change */ typedef struct OMX_CONFIG_LIGHTNESSTYPE { @@ -664,17 +671,17 @@ typedef struct OMX_CONFIG_LIGHTNESSTYPE { } OMX_CONFIG_LIGHTNESSTYPE; -/** - * Plane blend configuration +/** + * Plane blend configuration * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes + * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Index of input port associated with the plane. - * nDepth : Depth of the plane in relation to the screen. Higher - * numbered depths are "behind" lower number depths. + * nDepth : Depth of the plane in relation to the screen. Higher + * numbered depths are "behind" lower number depths. * This number defaults to the Port Index number. - * nAlpha : Transparency blending component for the entire plane. + * nAlpha : Transparency blending component for the entire plane. * See blending modes for more detail. */ typedef struct OMX_CONFIG_PLANEBLENDTYPE { @@ -686,17 +693,17 @@ typedef struct OMX_CONFIG_PLANEBLENDTYPE { } OMX_CONFIG_PLANEBLENDTYPE; -/** +/** * Define interlace type * * STRUCT MEMBERS: - * nSize : Size of the structure in bytes - * nVersion : OMX specification version information + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to - * bEnable : Enable control variable for this functionality + * bEnable : Enable control variable for this functionality * (see below) - * nInterleavePortIndex : Index of input or output port associated with - * the interleaved plane. + * nInterleavePortIndex : Index of input or output port associated with + * the interleaved plane. * pPlanarPortIndexes[4] : Index of input or output planar ports. */ typedef struct OMX_PARAM_INTERLEAVETYPE { @@ -708,8 +715,8 @@ typedef struct OMX_PARAM_INTERLEAVETYPE { } OMX_PARAM_INTERLEAVETYPE; -/** - * Defines the picture effect used for an input picture +/** + * Defines the picture effect used for an input picture */ typedef enum OMX_TRANSITIONEFFECTTYPE { OMX_EffectNone, @@ -719,18 +726,18 @@ typedef enum OMX_TRANSITIONEFFECTTYPE { OMX_EffectDissolve, OMX_EffectWipe, OMX_EffectUnspecifiedMixOfTwoScenes, - OMX_EffectKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_EffectKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_EffectVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_EffectMax = 0x7FFFFFFF } OMX_TRANSITIONEFFECTTYPE; -/** - * Structure used to configure current transition effect +/** + * Structure used to configure current transition effect * * STRUCT MEMBERS: * nSize : Size of the structure in bytes - * nVersion : OMX specification version information + * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * eEffect : Effect to enable */ @@ -742,43 +749,43 @@ typedef struct OMX_CONFIG_TRANSITIONEFFECTTYPE { } OMX_CONFIG_TRANSITIONEFFECTTYPE; -/** - * Defines possible data unit types for encoded video data. The data unit +/** + * Defines possible data unit types for encoded video data. The data unit * types are used both for encoded video input for playback as well as - * encoded video output from recording. + * encoded video output from recording. */ typedef enum OMX_DATAUNITTYPE { OMX_DataUnitCodedPicture, OMX_DataUnitVideoSegment, OMX_DataUnitSeveralSegments, OMX_DataUnitArbitraryStreamSection, - OMX_DataUnitKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_DataUnitKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_DataUnitVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_DataUnitMax = 0x7FFFFFFF } OMX_DATAUNITTYPE; -/** - * Defines possible encapsulation types for coded video data unit. The - * encapsulation information is used both for encoded video input for - * playback as well as encoded video output from recording. +/** + * Defines possible encapsulation types for coded video data unit. The + * encapsulation information is used both for encoded video input for + * playback as well as encoded video output from recording. */ typedef enum OMX_DATAUNITENCAPSULATIONTYPE { OMX_DataEncapsulationElementaryStream, OMX_DataEncapsulationGenericPayload, OMX_DataEncapsulationRtpPayload, - OMX_DataEncapsulationKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_DataEncapsulationKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_DataEncapsulationVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_DataEncapsulationMax = 0x7FFFFFFF } OMX_DATAUNITENCAPSULATIONTYPE; -/** - * Structure used to configure the type of being decoded/encoded +/** + * Structure used to configure the type of being decoded/encoded */ typedef struct OMX_PARAM_DATAUNITTYPE { OMX_U32 nSize; /**< Size of the structure in bytes */ - OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port that this structure applies to */ OMX_DATAUNITTYPE eUnitType; OMX_DATAUNITENCAPSULATIONTYPE eEncapsulationType; @@ -786,25 +793,25 @@ typedef struct OMX_PARAM_DATAUNITTYPE { /** - * Defines dither types + * Defines dither types */ typedef enum OMX_DITHERTYPE { OMX_DitherNone, OMX_DitherOrdered, OMX_DitherErrorDiffusion, OMX_DitherOther, - OMX_DitherKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_DitherKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_DitherVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_DitherMax = 0x7FFFFFFF } OMX_DITHERTYPE; -/** - * Structure used to configure current type of dithering +/** + * Structure used to configure current type of dithering */ typedef struct OMX_CONFIG_DITHERTYPE { OMX_U32 nSize; /**< Size of the structure in bytes */ - OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port that this structure applies to */ OMX_DITHERTYPE eDither; /**< Type of dithering to use */ } OMX_CONFIG_DITHERTYPE; @@ -813,28 +820,28 @@ typedef struct OMX_CONFIG_CAPTUREMODETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; /**< Port that this structure applies to */ - OMX_BOOL bContinuous; /**< If true then ignore frame rate and emit capture + OMX_BOOL bContinuous; /**< If true then ignore frame rate and emit capture * data as fast as possible (otherwise obey port's frame rate). */ - OMX_BOOL bFrameLimited; /**< If true then terminate capture after the port emits the - * specified number of frames (otherwise the port does not - * terminate the capture until instructed to do so by the client). - * Even if set, the client may manually terminate the capture prior + OMX_BOOL bFrameLimited; /**< If true then terminate capture after the port emits the + * specified number of frames (otherwise the port does not + * terminate the capture until instructed to do so by the client). + * Even if set, the client may manually terminate the capture prior * to reaching the limit. */ OMX_U32 nFrameLimit; /**< Limit on number of frames emitted during a capture (only * valid if bFrameLimited is set). */ } OMX_CONFIG_CAPTUREMODETYPE; typedef enum OMX_METERINGTYPE { - + OMX_MeteringModeAverage, /**< Center-weighted average metering. */ OMX_MeteringModeSpot, /**< Spot (partial) metering. */ OMX_MeteringModeMatrix, /**< Matrix or evaluative metering. */ - - OMX_MeteringKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + + OMX_MeteringKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_MeteringVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_EVModeMax = 0x7fffffff } OMX_METERINGTYPE; - + typedef struct OMX_CONFIG_EXPOSUREVALUETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; @@ -843,14 +850,14 @@ typedef struct OMX_CONFIG_EXPOSUREVALUETYPE { OMX_S32 xEVCompensation; /**< Fixed point value stored as Q16 */ OMX_U32 nApertureFNumber; /**< e.g. nApertureFNumber = 2 implies "f/2" - Q16 format */ OMX_BOOL bAutoAperture; /**< Whether aperture number is defined automatically */ - OMX_U32 nShutterSpeedMsec; /**< Shutterspeed in milliseconds */ - OMX_BOOL bAutoShutterSpeed; /**< Whether shutter speed is defined automatically */ + OMX_U32 nShutterSpeedMsec; /**< Shutterspeed in milliseconds */ + OMX_BOOL bAutoShutterSpeed; /**< Whether shutter speed is defined automatically */ OMX_U32 nSensitivity; /**< e.g. nSensitivity = 100 implies "ISO 100" */ OMX_BOOL bAutoSensitivity; /**< Whether sensitivity is defined automatically */ } OMX_CONFIG_EXPOSUREVALUETYPE; -/** - * Focus region configuration +/** + * Focus region configuration * * STRUCT MEMBERS: * nSize : Size of the structure in bytes @@ -881,8 +888,8 @@ typedef struct OMX_CONFIG_FOCUSREGIONTYPE { OMX_BOOL bBottomRight; } OMX_CONFIG_FOCUSREGIONTYPE; -/** - * Focus Status type +/** + * Focus Status type */ typedef enum OMX_FOCUSSTATUSTYPE { OMX_FocusStatusOff = 0, @@ -890,13 +897,13 @@ typedef enum OMX_FOCUSSTATUSTYPE { OMX_FocusStatusReached, OMX_FocusStatusUnableToReach, OMX_FocusStatusLost, - OMX_FocusStatusKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_FocusStatusKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_FocusStatusVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_FocusStatusMax = 0x7FFFFFFF } OMX_FOCUSSTATUSTYPE; -/** - * Focus status configuration +/** + * Focus status configuration * * STRUCT MEMBERS: * nSize : Size of the structure in bytes diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h index f46f25c36d14..848c5a114907 100644 --- a/include/ui/PixelFormat.h +++ b/include/ui/PixelFormat.h @@ -55,7 +55,7 @@ enum { PIXEL_FORMAT_OPAQUE = -1, // System chooses an opaque format (no alpha bits required) - + // real pixel formats supported for rendering ----------------------------- PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA @@ -84,7 +84,7 @@ struct PixelFormatInfo INDEX_GREEN = 2, INDEX_BLUE = 3 }; - + enum { // components ALPHA = 1, RGB = 2, @@ -98,10 +98,10 @@ struct PixelFormatInfo uint8_t h; uint8_t l; }; - + inline PixelFormatInfo() : version(sizeof(PixelFormatInfo)) { } size_t getScanlineSize(unsigned int width) const; - size_t getSize(size_t ci) const { + size_t getSize(size_t ci) const { return (ci <= 3) ? (cinfo[ci].h - cinfo[ci].l) : 0; } size_t version; @@ -112,7 +112,7 @@ struct PixelFormatInfo szinfo cinfo[4]; struct { uint8_t h_alpha; - uint8_t l_alpha; + uint8_t l_alpha; uint8_t h_red; uint8_t l_red; uint8_t h_green; diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp index be90e2eec00f..55246dc902e5 100644 --- a/libs/gui/ISurfaceTexture.cpp +++ b/libs/gui/ISurfaceTexture.cpp @@ -38,7 +38,6 @@ enum { CANCEL_BUFFER, SET_CROP, SET_TRANSFORM, - GET_ALLOCATOR, QUERY, SET_SYNCHRONOUS_MODE, CONNECT, @@ -55,18 +54,18 @@ public: { } - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx) { + virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) { Parcel data, reply; data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); data.writeInt32(bufferIdx); remote()->transact(REQUEST_BUFFER, data, &reply); - sp<GraphicBuffer> buffer; bool nonNull = reply.readInt32(); if (nonNull) { - buffer = new GraphicBuffer(); - reply.read(*buffer); + *buf = new GraphicBuffer(); + reply.read(**buf); } - return buffer; + status_t result = reply.readInt32(); + return result; } virtual status_t setBufferCount(int bufferCount) @@ -144,13 +143,6 @@ public: return result; } - virtual sp<IBinder> getAllocator() { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); - remote()->transact(GET_ALLOCATOR, data, &reply); - return reply.readStrongBinder(); - } - virtual int query(int what, int* value) { Parcel data, reply; data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); @@ -200,11 +192,13 @@ status_t BnSurfaceTexture::onTransact( case REQUEST_BUFFER: { CHECK_INTERFACE(ISurfaceTexture, data, reply); int bufferIdx = data.readInt32(); - sp<GraphicBuffer> buffer(requestBuffer(bufferIdx)); + sp<GraphicBuffer> buffer; + int result = requestBuffer(bufferIdx, &buffer); reply->writeInt32(buffer != 0); if (buffer != 0) { reply->write(*buffer); } + reply->writeInt32(result); return NO_ERROR; } break; case SET_BUFFER_COUNT: { @@ -270,12 +264,6 @@ status_t BnSurfaceTexture::onTransact( reply->writeInt32(result); return NO_ERROR; } break; - case GET_ALLOCATOR: { - CHECK_INTERFACE(ISurfaceTexture, data, reply); - sp<IBinder> result = getAllocator(); - reply->writeStrongBinder(result); - return NO_ERROR; - } break; case QUERY: { CHECK_INTERFACE(ISurfaceTexture, data, reply); int value; diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 0f08570ef76f..c190195b53d0 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -94,7 +94,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode) : mTexName(tex), mSynchronousMode(false), mAllowSynchronousMode(allowSynchronousMode), - mConnectedApi(NO_CONNECTED_API) { + mConnectedApi(NO_CONNECTED_API), + mAbandoned(false) { LOGV("SurfaceTexture::SurfaceTexture"); sp<ISurfaceComposer> composer(ComposerService::getComposerService()); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); @@ -150,6 +151,11 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) { LOGV("SurfaceTexture::setBufferCount"); Mutex::Autolock lock(mMutex); + if (mAbandoned) { + LOGE("setBufferCount: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (bufferCount > NUM_BUFFER_SLOTS) { LOGE("setBufferCount: bufferCount larger than slots available"); return BAD_VALUE; @@ -199,22 +205,32 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) return OK; } -sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) { +status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) { LOGV("SurfaceTexture::requestBuffer"); Mutex::Autolock lock(mMutex); - if (buf < 0 || mBufferCount <= buf) { + if (mAbandoned) { + LOGE("requestBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (slot < 0 || mBufferCount <= slot) { LOGE("requestBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); - return 0; + mBufferCount, slot); + return BAD_VALUE; } - mSlots[buf].mRequestBufferCalled = true; - return mSlots[buf].mGraphicBuffer; + mSlots[slot].mRequestBufferCalled = true; + *buf = mSlots[slot].mGraphicBuffer; + return NO_ERROR; } status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { LOGV("SurfaceTexture::dequeueBuffer"); + if (mAbandoned) { + LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if ((w && !h) || (!w && h)) { LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); return BAD_VALUE; @@ -252,6 +268,11 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, // wait for the FIFO to drain while (!mQueue.isEmpty()) { mDequeueCondition.wait(mMutex); + if (mAbandoned) { + LOGE("dequeueBuffer: SurfaceTexture was abandoned while " + "blocked!"); + return NO_INIT; + } } minBufferCountNeeded = mSynchronousMode ? MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; @@ -380,6 +401,11 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, status_t SurfaceTexture::setSynchronousMode(bool enabled) { Mutex::Autolock lock(mMutex); + if (mAbandoned) { + LOGE("setSynchronousMode: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + status_t err = OK; if (!mAllowSynchronousMode && enabled) return err; @@ -410,6 +436,10 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp, { // scope for the lock Mutex::Autolock lock(mMutex); + if (mAbandoned) { + LOGE("queueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } if (buf < 0 || buf >= mBufferCount) { LOGE("queueBuffer: slot index out of range [0, %d]: %d", mBufferCount, buf); @@ -475,6 +505,12 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp, void SurfaceTexture::cancelBuffer(int buf) { LOGV("SurfaceTexture::cancelBuffer"); Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + LOGW("cancelBuffer: SurfaceTexture has been abandoned!"); + return; + } + if (buf < 0 || buf >= mBufferCount) { LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount, buf); @@ -491,6 +527,10 @@ void SurfaceTexture::cancelBuffer(int buf) { status_t SurfaceTexture::setCrop(const Rect& crop) { LOGV("SurfaceTexture::setCrop"); Mutex::Autolock lock(mMutex); + if (mAbandoned) { + LOGE("setCrop: SurfaceTexture has been abandoned!"); + return NO_INIT; + } mNextCrop = crop; return OK; } @@ -498,6 +538,10 @@ status_t SurfaceTexture::setCrop(const Rect& crop) { status_t SurfaceTexture::setTransform(uint32_t transform) { LOGV("SurfaceTexture::setTransform"); Mutex::Autolock lock(mMutex); + if (mAbandoned) { + LOGE("setTransform: SurfaceTexture has been abandoned!"); + return NO_INIT; + } mNextTransform = transform; return OK; } @@ -505,6 +549,12 @@ status_t SurfaceTexture::setTransform(uint32_t transform) { status_t SurfaceTexture::connect(int api) { LOGV("SurfaceTexture::connect(this=%p, %d)", this, api); Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + LOGE("connect: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + int err = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: @@ -529,6 +579,12 @@ status_t SurfaceTexture::connect(int api) { status_t SurfaceTexture::disconnect(int api) { LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api); Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + LOGE("connect: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + int err = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: @@ -786,11 +842,6 @@ void SurfaceTexture::setFrameAvailableListener( mFrameAvailableListener = listener; } -sp<IBinder> SurfaceTexture::getAllocator() { - LOGV("SurfaceTexture::getAllocator"); - return mGraphicBufferAlloc->asBinder(); -} - void SurfaceTexture::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].mGraphicBuffer = 0; @@ -842,6 +893,12 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const { int SurfaceTexture::query(int what, int* outValue) { Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + LOGE("query: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + int value; switch (what) { case NATIVE_WINDOW_WIDTH: @@ -868,6 +925,13 @@ int SurfaceTexture::query(int what, int* outValue) return NO_ERROR; } +void SurfaceTexture::abandon() { + Mutex::Autolock lock(mMutex); + freeAllBuffers(); + mAbandoned = true; + mDequeueCondition.signal(); +} + void SurfaceTexture::dump(String8& result) const { char buffer[1024]; diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index 1dc6cd2cf751..df0ad5abe3e2 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -55,6 +55,9 @@ void SurfaceTextureClient::init() { mQueryWidth = 0; mQueryHeight = 0; mQueryFormat = 0; + mDefaultWidth = 0; + mDefaultHeight = 0; + mTransformHint = 0; mConnectedToCpu = false; } @@ -62,9 +65,6 @@ void SurfaceTextureClient::setISurfaceTexture( const sp<ISurfaceTexture>& surfaceTexture) { mSurfaceTexture = surfaceTexture; - - // Get a reference to the allocator. - mAllocator = mSurfaceTexture->getAllocator(); } sp<ISurfaceTexture> SurfaceTextureClient::getISurfaceTexture() const { @@ -148,10 +148,11 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { } if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { - gbuf = mSurfaceTexture->requestBuffer(buf); - if (gbuf == 0) { - LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed"); - return NO_MEMORY; + result = mSurfaceTexture->requestBuffer(buf, &gbuf); + if (result != NO_ERROR) { + LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed: %d", + result); + return result; } mQueryWidth = gbuf->width; mQueryHeight = gbuf->height; diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 9abe89d9709c..0fac6cda05a4 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -1018,6 +1018,83 @@ TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromGLFilledRGBABufferPow2) { EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); } +TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { + class ProducerThread : public Thread { + public: + ProducerThread(const sp<ANativeWindow>& anw): + mANW(anw), + mDequeueError(NO_ERROR) { + } + + virtual ~ProducerThread() { + } + + virtual bool threadLoop() { + Mutex::Autolock lock(mMutex); + ANativeWindowBuffer* anb; + + // Frame 1 + if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb) + != NO_ERROR) { + return false; + } + + // Frame 2 + if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb) + != NO_ERROR) { + return false; + } + + // Frame 3 - error expected + mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb); + return false; + } + + status_t getDequeueError() { + Mutex::Autolock lock(mMutex); + return mDequeueError; + } + + private: + sp<ANativeWindow> mANW; + status_t mDequeueError; + Mutex mMutex; + }; + + sp<FrameWaiter> fw(new FrameWaiter); + mST->setFrameAvailableListener(fw); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, mST->setBufferCountServer(2)); + + sp<Thread> pt(new ProducerThread(mANW)); + pt->run(); + + fw->waitForFrame(); + fw->waitForFrame(); + + // Sleep for 100ms to allow the producer thread's dequeueBuffer call to + // block waiting for a buffer to become available. + usleep(100000); + + mST->abandon(); + + pt->requestExitAndWait(); + ASSERT_EQ(NO_INIT, + reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError()); +} + /* * This test is for testing GL -> GL texture streaming via SurfaceTexture. It * contains functionality to create a producer thread that will perform GL @@ -1205,7 +1282,7 @@ protected: sp<FrameCondition> mFC; }; -TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedWorks) { +TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) { class PT : public ProducerThread { virtual void render() { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); @@ -1223,7 +1300,7 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedWorks) { // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } -TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedWorks) { +TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) { class PT : public ProducerThread { virtual void render() { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); @@ -1241,7 +1318,7 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedWorks) { // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } -TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedWorks) { +TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedCompletes) { enum { NUM_ITERATIONS = 1024 }; class PT : public ProducerThread { @@ -1269,7 +1346,7 @@ TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedWorks) } } -TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedWorks) { +TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedCompletes) { enum { NUM_ITERATIONS = 1024 }; class PT : public ProducerThread { @@ -1297,4 +1374,70 @@ TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedWorks) } } +// XXX: This test is disabled because it is currently hanging on some devices. +TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) { + enum { NUM_ITERATIONS = 64 }; + + class PT : public ProducerThread { + virtual void render() { + for (int i = 0; i < NUM_ITERATIONS; i++) { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + LOGV("+swapBuffers"); + swapBuffers(); + LOGV("-swapBuffers"); + } + } + }; + + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, mST->setBufferCountServer(2)); + + runProducerThread(new PT()); + + // Allow three frames to be rendered and queued before starting the + // rendering in this thread. For the latter two frames we don't call + // updateTexImage so the next dequeue from the producer thread will block + // waiting for a frame to become available. + mFC->waitForFrame(); + mFC->finishFrame(); + + // We must call updateTexImage to consume the first frame so that the + // SurfaceTexture is able to reduce the buffer count to 2. This is because + // the GL driver may dequeue a buffer when the EGLSurface is created, and + // that happens before we call setBufferCountServer. It's possible that the + // driver does not dequeue a buffer at EGLSurface creation time, so we + // cannot rely on this to cause the second dequeueBuffer call to block. + mST->updateTexImage(); + + mFC->waitForFrame(); + mFC->finishFrame(); + mFC->waitForFrame(); + mFC->finishFrame(); + + // Sleep for 100ms to allow the producer thread's dequeueBuffer call to + // block waiting for a buffer to become available. + usleep(100000); + + // Render and present a number of images. This thread should not be blocked + // by the fact that the producer thread is blocking in dequeue. + for (int i = 0; i < NUM_ITERATIONS; i++) { + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mEglSurface); + } + + // Consume the two pending buffers to unblock the producer thread. + mST->updateTexImage(); + mST->updateTexImage(); + + // Consume the remaining buffers from the producer thread. + for (int i = 0; i < NUM_ITERATIONS-3; i++) { + mFC->waitForFrame(); + mFC->finishFrame(); + LOGV("+updateTexImage"); + mST->updateTexImage(); + LOGV("-updateTexImage"); + } +} + } // namespace android diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 8b1caeeedf59..886c05c64c07 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -883,7 +883,6 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) /////////////////////////////////////////////////////////////////////////////// DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE) { - mDisplayList = NULL; } DisplayListRenderer::~DisplayListRenderer() { @@ -923,13 +922,13 @@ void DisplayListRenderer::reset() { // Operations /////////////////////////////////////////////////////////////////////////////// -DisplayList* DisplayListRenderer::getDisplayList() { - if (mDisplayList == NULL) { - mDisplayList = new DisplayList(*this); +DisplayList* DisplayListRenderer::getDisplayList(DisplayList* displayList) { + if (!displayList) { + displayList = new DisplayList(*this); } else { - mDisplayList->initFromDisplayListRenderer(*this, true); + displayList->initFromDisplayListRenderer(*this, true); } - return mDisplayList; + return displayList; } void DisplayListRenderer::setViewport(int width, int height) { diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index b83259f82450..81576313c15f 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -217,7 +217,7 @@ public: DisplayListRenderer(); ~DisplayListRenderer(); - DisplayList* getDisplayList(); + DisplayList* getDisplayList(DisplayList* displayList); void setViewport(int width, int height); void prepareDirty(float left, float top, float right, float bottom, bool opaque); @@ -474,8 +474,6 @@ private: SkWriter32 mWriter; - DisplayList *mDisplayList; - int mRestoreSaveCount; friend class DisplayList; diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index 412552ec8eca..0e8ae619fc5c 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -310,35 +310,21 @@ int FramebufferNativeWindow::perform(ANativeWindow* window, int operation, ...) { switch (operation) { - case NATIVE_WINDOW_SET_USAGE: - // TODO: we should implement this - return NO_ERROR; case NATIVE_WINDOW_CONNECT: - // TODO: we should implement this - return NO_ERROR; case NATIVE_WINDOW_DISCONNECT: - // TODO: we should implement this + case NATIVE_WINDOW_SET_USAGE: + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + // TODO: we should implement these return NO_ERROR; + case NATIVE_WINDOW_LOCK: - return INVALID_OPERATION; case NATIVE_WINDOW_UNLOCK_AND_POST: - return INVALID_OPERATION; case NATIVE_WINDOW_SET_CROP: - return INVALID_OPERATION; case NATIVE_WINDOW_SET_BUFFER_COUNT: - // TODO: we should implement this - return INVALID_OPERATION; - case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: - return INVALID_OPERATION; - case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: - return INVALID_OPERATION; case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: - return INVALID_OPERATION; - case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: - return INVALID_OPERATION; - case NATIVE_WINDOW_SET_BUFFERS_FORMAT: - // TODO: we should implement this - return NO_ERROR; case NATIVE_WINDOW_SET_SCALING_MODE: return INVALID_OPERATION; } diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index e3cbd5783c8f..72069ac19a90 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -81,9 +81,6 @@ public class MediaRecorder private String mPath; private FileDescriptor mFd; - private boolean mPrepareAuxiliaryFile = false; - private String mPathAux; - private FileDescriptor mFdAux; private EventHandler mEventHandler; private OnErrorListener mOnErrorListener; private OnInfoListener mOnInfoListener; @@ -557,84 +554,23 @@ public class MediaRecorder } /** - * Sets the auxiliary time lapse video's resolution and bitrate. - * - * The auxiliary video's resolution and bitrate are determined by the CamcorderProfile - * quality level {@link android.media.CamcorderProfile#QUALITY_HIGH}. - */ - private void setAuxVideoParameters() { - CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); - setParameter(String.format("video-aux-param-width=%d", profile.videoFrameWidth)); - setParameter(String.format("video-aux-param-height=%d", profile.videoFrameHeight)); - setParameter(String.format("video-aux-param-encoding-bitrate=%d", profile.videoBitRate)); - } - - /** - * Pass in the file descriptor for the auxiliary time lapse video. Call this before - * prepare(). - * - * Sets file descriptor and parameters for auxiliary time lapse video. Time lapse mode - * can capture video (using the still camera) at resolutions higher than that can be - * played back on the device. This function or - * {@link #setAuxiliaryOutputFile(String)} enable capture of a smaller video in - * parallel with the main time lapse video, which can be used to play back on the - * device. The smaller video is created by downsampling the main video. This call is - * optional and does not have to be called if parallel capture of a downsampled video - * is not desired. - * - * Note that while the main video resolution and bitrate is determined from the - * CamcorderProfile in {@link #setProfile(CamcorderProfile)}, the auxiliary video's - * resolution and bitrate are determined by the CamcorderProfile quality level - * {@link android.media.CamcorderProfile#QUALITY_HIGH}. All other encoding parameters - * remain the same for the main video and the auxiliary video. - * - * E.g. if the device supports the time lapse profile quality level - * {@link android.media.CamcorderProfile#QUALITY_TIME_LAPSE_1080P} but can playback at - * most 480p, the application might want to capture an auxiliary video of resolution - * 480p using this call. - * - * @param fd an open file descriptor to be written into. + * Currently not implemented. It does nothing. + * @deprecated Time lapse mode video recording using camera still image capture + * is not desirable, and will not be supported. */ public void setAuxiliaryOutputFile(FileDescriptor fd) { - mPrepareAuxiliaryFile = true; - mPathAux = null; - mFdAux = fd; - setAuxVideoParameters(); + Log.w(TAG, "setAuxiliaryOutputFile(FileDescriptor) is no longer supported."); } /** - * Pass in the file path for the auxiliary time lapse video. Call this before - * prepare(). - * - * Sets file path and parameters for auxiliary time lapse video. Time lapse mode can - * capture video (using the still camera) at resolutions higher than that can be - * played back on the device. This function or - * {@link #setAuxiliaryOutputFile(FileDescriptor)} enable capture of a smaller - * video in parallel with the main time lapse video, which can be used to play back on - * the device. The smaller video is created by downsampling the main video. This call - * is optional and does not have to be called if parallel capture of a downsampled - * video is not desired. - * - * Note that while the main video resolution and bitrate is determined from the - * CamcorderProfile in {@link #setProfile(CamcorderProfile)}, the auxiliary video's - * resolution and bitrate are determined by the CamcorderProfile quality level - * {@link android.media.CamcorderProfile#QUALITY_HIGH}. All other encoding parameters - * remain the same for the main video and the auxiliary video. - * - * E.g. if the device supports the time lapse profile quality level - * {@link android.media.CamcorderProfile#QUALITY_TIME_LAPSE_1080P} but can playback at - * most 480p, the application might want to capture an auxiliary video of resolution - * 480p using this call. - * - * @param path The pathname to use. + * Currently not implemented. It does nothing. + * @deprecated Time lapse mode video recording using camera still image capture + * is not desirable, and will not be supported. */ public void setAuxiliaryOutputFile(String path) { - mPrepareAuxiliaryFile = true; - mFdAux = null; - mPathAux = path; - setAuxVideoParameters(); + Log.w(TAG, "setAuxiliaryOutputFile(String) is no longer supported."); } /** @@ -668,8 +604,6 @@ public class MediaRecorder // native implementation private native void _setOutputFile(FileDescriptor fd, long offset, long length) throws IllegalStateException, IOException; - private native void _setOutputFileAux(FileDescriptor fd) - throws IllegalStateException, IOException; private native void _prepare() throws IllegalStateException, IOException; /** @@ -696,21 +630,6 @@ public class MediaRecorder throw new IOException("No valid output file"); } - if (mPrepareAuxiliaryFile) { - if (mPathAux != null) { - FileOutputStream fos = new FileOutputStream(mPathAux); - try { - _setOutputFileAux(fos.getFD()); - } finally { - fos.close(); - } - } else if (mFdAux != null) { - _setOutputFileAux(mFdAux); - } else { - throw new IOException("No valid output file"); - } - } - _prepare(); } diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 12391c87171e..922f7edcb721 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -127,7 +127,7 @@ static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const ch return false; } -static sp<MediaRecorder> getMediaRecorder(JNIEnv* env, jobject thiz) +sp<MediaRecorder> getMediaRecorder(JNIEnv* env, jobject thiz) { Mutex::Autolock l(sLock); MediaRecorder* const p = (MediaRecorder*)env->GetIntField(thiz, fields.context); @@ -261,20 +261,6 @@ android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject f } static void -android_media_MediaRecorder_setOutputFileAuxFD(JNIEnv *env, jobject thiz, jobject fileDescriptor) -{ - LOGV("setOutputFile"); - if (fileDescriptor == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; - } - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - sp<MediaRecorder> mr = getMediaRecorder(env, thiz); - status_t opStatus = mr->setOutputFileAuxiliary(fd); - process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed."); -} - -static void android_media_MediaRecorder_setVideoSize(JNIEnv *env, jobject thiz, jint width, jint height) { LOGV("setVideoSize(%d, %d)", width, height); @@ -475,7 +461,6 @@ static JNINativeMethod gMethods[] = { {"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder}, {"setParameter", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameter}, {"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD}, - {"_setOutputFileAux", "(Ljava/io/FileDescriptor;)V", (void *)android_media_MediaRecorder_setOutputFileAuxFD}, {"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize}, {"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate}, {"setMaxDuration", "(I)V", (void *)android_media_MediaRecorder_setMaxDuration}, diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index a44ef5ab1b95..7e44c2991c5c 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -23,14 +23,17 @@ #include <camera/ICamera.h> #include <media/IMediaRecorderClient.h> #include <media/IMediaRecorder.h> +#include <gui/ISurfaceTexture.h> #include <unistd.h> + namespace android { enum { RELEASE = IBinder::FIRST_CALL_TRANSACTION, INIT, CLOSE, + QUERY_SURFACE_MEDIASOURCE, RESET, STOP, START, @@ -71,6 +74,19 @@ public: return reply.readInt32(); } + sp<ISurfaceTexture> querySurfaceMediaSource() + { + LOGV("Query SurfaceMediaSource"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(QUERY_SURFACE_MEDIASOURCE, data, &reply); + int returnedNull = reply.readInt32(); + if (returnedNull) { + return NULL; + } + return interface_cast<ISurfaceTexture>(reply.readStrongBinder()); + } + status_t setPreviewSurface(const sp<Surface>& surface) { LOGV("setPreviewSurface(%p)", surface.get()); @@ -440,6 +456,20 @@ status_t BnMediaRecorder::onTransact( reply->writeInt32(setCamera(camera, proxy)); return NO_ERROR; } break; + case QUERY_SURFACE_MEDIASOURCE: { + LOGV("QUERY_SURFACE_MEDIASOURCE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + // call the mediaserver side to create + // a surfacemediasource + sp<ISurfaceTexture> surfaceMediaSource = querySurfaceMediaSource(); + // The mediaserver might have failed to create a source + int returnedNull= (surfaceMediaSource == NULL) ? 1 : 0 ; + reply->writeInt32(returnedNull); + if (!returnedNull) { + reply->writeStrongBinder(surfaceMediaSource->asBinder()); + } + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index ed6e3c7aa7a5..a11fb804f134 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -228,6 +228,7 @@ status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface) NATIVE_WINDOW_API_MEDIA); if (err != OK) { + LOGE("setVideoSurface failed: %d", err); // Note that we must do the reset before disconnecting from the ANW. // Otherwise queue/dequeue calls could be made on the disconnected // ANW, which may result in errors. @@ -277,6 +278,7 @@ status_t MediaPlayer::setVideoSurfaceTexture( NATIVE_WINDOW_API_MEDIA); if (err != OK) { + LOGE("setVideoSurfaceTexture failed: %d", err); // Note that we must do the reset before disconnecting from the ANW. // Otherwise queue/dequeue calls could be made on the disconnected // ANW, which may result in errors. diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 9e4edd09c8c9..fab674ca515c 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -25,6 +25,7 @@ #include <media/IMediaPlayerService.h> #include <media/IMediaRecorder.h> #include <media/mediaplayer.h> // for MEDIA_ERROR_SERVER_DIED +#include <gui/ISurfaceTexture.h> namespace android { @@ -127,7 +128,9 @@ status_t MediaRecorder::setVideoSource(int vs) return INVALID_OPERATION; } + // following call is made over the Binder Interface status_t ret = mMediaRecorder->setVideoSource(vs); + if (OK != ret) { LOGV("setVideoSource failed: %d", ret); mCurrentState = MEDIA_RECORDER_ERROR; @@ -357,7 +360,7 @@ status_t MediaRecorder::setVideoSize(int width, int height) return INVALID_OPERATION; } if (!mIsVideoSourceSet) { - LOGE("try to set video size without setting video source first"); + LOGE("Cannot set video size without setting video source first"); return INVALID_OPERATION; } @@ -367,9 +370,27 @@ status_t MediaRecorder::setVideoSize(int width, int height) mCurrentState = MEDIA_RECORDER_ERROR; return ret; } + return ret; } +// Query a SurfaceMediaSurface through the Mediaserver, over the +// binder interface. This is used by the Filter Framework (MeidaEncoder) +// to get an <ISurfaceTexture> object to hook up to ANativeWindow. +sp<ISurfaceTexture> MediaRecorder:: + querySurfaceMediaSourceFromMediaServer() +{ + Mutex::Autolock _l(mLock); + mSurfaceMediaSource = + mMediaRecorder->querySurfaceMediaSource(); + if (mSurfaceMediaSource == NULL) { + LOGE("SurfaceMediaSource could not be initialized!"); + } + return mSurfaceMediaSource; +} + + + status_t MediaRecorder::setVideoFrameRate(int frames_per_second) { LOGV("setVideoFrameRate(%d)", frames_per_second); @@ -382,7 +403,7 @@ status_t MediaRecorder::setVideoFrameRate(int frames_per_second) return INVALID_OPERATION; } if (!mIsVideoSourceSet) { - LOGE("try to set video frame rate without setting video source first"); + LOGE("Cannot set video frame rate without setting video source first"); return INVALID_OPERATION; } @@ -621,7 +642,7 @@ status_t MediaRecorder::release() return INVALID_OPERATION; } -MediaRecorder::MediaRecorder() +MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL) { LOGV("constructor"); @@ -632,6 +653,8 @@ MediaRecorder::MediaRecorder() if (mMediaRecorder != NULL) { mCurrentState = MEDIA_RECORDER_IDLE; } + + doCleanUp(); } @@ -646,6 +669,10 @@ MediaRecorder::~MediaRecorder() if (mMediaRecorder != NULL) { mMediaRecorder.clear(); } + + if (mSurfaceMediaSource != NULL) { + mSurfaceMediaSource.clear(); + } } status_t MediaRecorder::setListener(const sp<MediaRecorderListener>& listener) diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 115db1ad2904..905b88568eb7 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -41,6 +41,7 @@ #include "MediaPlayerService.h" #include "StagefrightRecorder.h" +#include <gui/ISurfaceTexture.h> namespace android { @@ -57,6 +58,20 @@ static bool checkPermission(const char* permissionString) { return ok; } + +sp<ISurfaceTexture> MediaRecorderClient::querySurfaceMediaSource() +{ + LOGV("Query SurfaceMediaSource"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NULL; + } + return mRecorder->querySurfaceMediaSource(); +} + + + status_t MediaRecorderClient::setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy) { diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index bbca5291cd5c..c87a3c0a23e8 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -25,45 +25,51 @@ namespace android { class MediaRecorderBase; class MediaPlayerService; class ICameraRecordingProxy; +class ISurfaceTexture; class MediaRecorderClient : public BnMediaRecorder { public: - virtual status_t setCamera(const sp<ICamera>& camera, - const sp<ICameraRecordingProxy>& proxy); - virtual status_t setPreviewSurface(const sp<Surface>& surface); - virtual status_t setVideoSource(int vs); - virtual status_t setAudioSource(int as); - virtual status_t setOutputFormat(int of); - virtual status_t setVideoEncoder(int ve); - virtual status_t setAudioEncoder(int ae); - virtual status_t setOutputFile(const char* path); - virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); - virtual status_t setOutputFileAuxiliary(int fd); - virtual status_t setVideoSize(int width, int height); - virtual status_t setVideoFrameRate(int frames_per_second); - virtual status_t setParameters(const String8& params); - virtual status_t setListener(const sp<IMediaRecorderClient>& listener); - virtual status_t prepare(); - virtual status_t getMaxAmplitude(int* max); - virtual status_t start(); - virtual status_t stop(); - virtual status_t reset(); - virtual status_t init(); - virtual status_t close(); - virtual status_t release(); + virtual status_t setCamera(const sp<ICamera>& camera, + const sp<ICameraRecordingProxy>& proxy); + virtual status_t setPreviewSurface(const sp<Surface>& surface); + virtual status_t setVideoSource(int vs); + virtual status_t setAudioSource(int as); + virtual status_t setOutputFormat(int of); + virtual status_t setVideoEncoder(int ve); + virtual status_t setAudioEncoder(int ae); + virtual status_t setOutputFile(const char* path); + virtual status_t setOutputFile(int fd, int64_t offset, + int64_t length); + virtual status_t setOutputFileAuxiliary(int fd); + virtual status_t setVideoSize(int width, int height); + virtual status_t setVideoFrameRate(int frames_per_second); + virtual status_t setParameters(const String8& params); + virtual status_t setListener( + const sp<IMediaRecorderClient>& listener); + virtual status_t prepare(); + virtual status_t getMaxAmplitude(int* max); + virtual status_t start(); + virtual status_t stop(); + virtual status_t reset(); + virtual status_t init(); + virtual status_t close(); + virtual status_t release(); + virtual status_t dump(int fd, const Vector<String16>& args) const; + virtual sp<ISurfaceTexture> querySurfaceMediaSource(); - virtual status_t dump(int fd, const Vector<String16>& args) const; private: - friend class MediaPlayerService; // for accessing private constructor + friend class MediaPlayerService; // for accessing private constructor - MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid); - virtual ~MediaRecorderClient(); + MediaRecorderClient( + const sp<MediaPlayerService>& service, + pid_t pid); + virtual ~MediaRecorderClient(); - pid_t mPid; - Mutex mLock; - MediaRecorderBase *mRecorder; - sp<MediaPlayerService> mMediaPlayerService; + pid_t mPid; + Mutex mLock; + MediaRecorderBase *mRecorder; + sp<MediaPlayerService> mMediaPlayerService; }; }; // namespace android diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 223e0be21a75..6427bb701646 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -38,10 +38,12 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> +#include <media/stagefright/SurfaceMediaSource.h> #include <media/MediaProfiles.h> #include <camera/ICamera.h> #include <camera/CameraParameters.h> #include <surfaceflinger/Surface.h> + #include <utils/Errors.h> #include <sys/types.h> #include <ctype.h> @@ -69,7 +71,7 @@ StagefrightRecorder::StagefrightRecorder() mOutputFd(-1), mOutputFdAux(-1), mAudioSource(AUDIO_SOURCE_CNT), mVideoSource(VIDEO_SOURCE_LIST_END), - mStarted(false) { + mStarted(false), mSurfaceMediaSource(NULL) { LOGV("Constructor"); reset(); @@ -85,6 +87,14 @@ status_t StagefrightRecorder::init() { return OK; } +// The client side of mediaserver asks it to creat a SurfaceMediaSource +// and return a interface reference. The client side will use that +// while encoding GL Frames +sp<ISurfaceTexture> StagefrightRecorder::querySurfaceMediaSource() const { + LOGV("Get SurfaceMediaSource"); + return mSurfaceMediaSource; +} + status_t StagefrightRecorder::setAudioSource(audio_source_t as) { LOGV("setAudioSource: %d", as); if (as < AUDIO_SOURCE_DEFAULT || @@ -1006,13 +1016,13 @@ status_t StagefrightRecorder::startRTPRecording() { source = createAudioSource(); } else { - sp<CameraSource> cameraSource; - status_t err = setupCameraSource(&cameraSource); + sp<MediaSource> mediaSource; + status_t err = setupMediaSource(&mediaSource); if (err != OK) { return err; } - err = setupVideoEncoder(cameraSource, mVideoBitRate, &source); + err = setupVideoEncoder(mediaSource, mVideoBitRate, &source); if (err != OK) { return err; } @@ -1042,20 +1052,19 @@ status_t StagefrightRecorder::startMPEG2TSRecording() { } } - if (mVideoSource == VIDEO_SOURCE_DEFAULT - || mVideoSource == VIDEO_SOURCE_CAMERA) { + if (mVideoSource < VIDEO_SOURCE_LIST_END) { if (mVideoEncoder != VIDEO_ENCODER_H264) { return ERROR_UNSUPPORTED; } - sp<CameraSource> cameraSource; - status_t err = setupCameraSource(&cameraSource); + sp<MediaSource> mediaSource; + status_t err = setupMediaSource(&mediaSource); if (err != OK) { return err; } sp<MediaSource> encoder; - err = setupVideoEncoder(cameraSource, mVideoBitRate, &encoder); + err = setupVideoEncoder(mediaSource, mVideoBitRate, &encoder); if (err != OK) { return err; @@ -1289,6 +1298,60 @@ void StagefrightRecorder::clipVideoFrameHeight() { } } +// Set up the appropriate MediaSource depending on the chosen option +status_t StagefrightRecorder::setupMediaSource( + sp<MediaSource> *mediaSource) { + if (mVideoSource == VIDEO_SOURCE_DEFAULT + || mVideoSource == VIDEO_SOURCE_CAMERA) { + sp<CameraSource> cameraSource; + status_t err = setupCameraSource(&cameraSource); + if (err != OK) { + return err; + } + *mediaSource = cameraSource; + } else if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) { + // If using GRAlloc buffers, setup surfacemediasource. + // Later a handle to that will be passed + // to the client side when queried + status_t err = setupSurfaceMediaSource(); + if (err != OK) { + return err; + } + *mediaSource = mSurfaceMediaSource; + } else { + return INVALID_OPERATION; + } + return OK; +} + +// setupSurfaceMediaSource creates a source with the given +// width and height and framerate. +// TODO: This could go in a static function inside SurfaceMediaSource +// similar to that in CameraSource +status_t StagefrightRecorder::setupSurfaceMediaSource() { + status_t err = OK; + mSurfaceMediaSource = new SurfaceMediaSource(mVideoWidth, mVideoHeight); + if (mSurfaceMediaSource == NULL) { + return NO_INIT; + } + + if (mFrameRate == -1) { + int32_t frameRate = 0; + CHECK (mSurfaceMediaSource->getFormat()->findInt32( + kKeyFrameRate, &frameRate)); + LOGI("Frame rate is not explicitly set. Use the current frame " + "rate (%d fps)", frameRate); + mFrameRate = frameRate; + } else { + err = mSurfaceMediaSource->setFrameRate(mFrameRate); + } + CHECK(mFrameRate != -1); + + mIsMetaDataStoredInVideoBuffers = + mSurfaceMediaSource->isMetaDataStoredInVideoBuffers(); + return err; +} + status_t StagefrightRecorder::setupCameraSource( sp<CameraSource> *cameraSource) { status_t err = OK; @@ -1465,29 +1528,37 @@ status_t StagefrightRecorder::setupMPEG4Recording( status_t err = OK; sp<MediaWriter> writer = new MPEG4Writer(outputFd); - if (mVideoSource == VIDEO_SOURCE_DEFAULT - || mVideoSource == VIDEO_SOURCE_CAMERA) { + if (mVideoSource < VIDEO_SOURCE_LIST_END) { - sp<MediaSource> cameraMediaSource; + sp<MediaSource> mediaSource; if (useSplitCameraSource) { + // TODO: Check if there is a better way to handle this + if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) { + LOGE("Cannot use split camera when encoding frames"); + return INVALID_OPERATION; + } LOGV("Using Split camera source"); - cameraMediaSource = mCameraSourceSplitter->createClient(); + mediaSource = mCameraSourceSplitter->createClient(); } else { - sp<CameraSource> cameraSource; - err = setupCameraSource(&cameraSource); - cameraMediaSource = cameraSource; + err = setupMediaSource(&mediaSource); } + if ((videoWidth != mVideoWidth) || (videoHeight != mVideoHeight)) { + // TODO: Might be able to handle downsampling even if using GRAlloc + if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) { + LOGE("Cannot change size or Downsample when encoding frames"); + return INVALID_OPERATION; + } // Use downsampling from the original source. - cameraMediaSource = - new VideoSourceDownSampler(cameraMediaSource, videoWidth, videoHeight); + mediaSource = + new VideoSourceDownSampler(mediaSource, videoWidth, videoHeight); } if (err != OK) { return err; } sp<MediaSource> encoder; - err = setupVideoEncoder(cameraMediaSource, videoBitRate, &encoder); + err = setupVideoEncoder(mediaSource, videoBitRate, &encoder); if (err != OK) { return err; } diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 034b373337dc..1618b92a78ae 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -36,6 +36,8 @@ struct MediaWriter; class MetaData; struct AudioSource; class MediaProfiles; +class ISurfaceTexture; +class SurfaceMediaSource; struct StagefrightRecorder : public MediaRecorderBase { StagefrightRecorder(); @@ -64,6 +66,8 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t reset(); virtual status_t getMaxAmplitude(int *max); virtual status_t dump(int fd, const Vector<String16>& args) const; + // Querying a SurfaceMediaSourcer + virtual sp<ISurfaceTexture> querySurfaceMediaSource() const; private: sp<ICamera> mCamera; @@ -109,12 +113,18 @@ private: sp<MediaSourceSplitter> mCameraSourceSplitter; sp<CameraSourceTimeLapse> mCameraSourceTimeLapse; + String8 mParams; bool mIsMetaDataStoredInVideoBuffers; MediaProfiles *mEncoderProfiles; bool mStarted; + // Needed when GLFrames are encoded. + // An <ISurfaceTexture> pointer + // will be sent to the client side using which the + // frame buffers will be queued and dequeued + sp<SurfaceMediaSource> mSurfaceMediaSource; status_t setupMPEG4Recording( bool useSplitCameraSource, @@ -134,7 +144,14 @@ private: sp<MediaSource> createAudioSource(); status_t checkVideoEncoderCapabilities(); status_t checkAudioEncoderCapabilities(); + // Generic MediaSource set-up. Returns the appropriate + // source (CameraSource or SurfaceMediaSource) + // depending on the videosource type + status_t setupMediaSource(sp<MediaSource> *mediaSource); status_t setupCameraSource(sp<CameraSource> *cameraSource); + // setup the surfacemediasource for the encoder + status_t setupSurfaceMediaSource(); + status_t setupAudioEncoder(const sp<MediaWriter>& writer); status_t setupVideoEncoder( sp<MediaSource> cameraSource, @@ -176,6 +193,7 @@ private: void clipNumberOfAudioChannels(); void setDefaultProfileIfNecessary(); + StagefrightRecorder(const StagefrightRecorder &); StagefrightRecorder &operator=(const StagefrightRecorder &); }; diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index e17e1e835797..3a3c082b08de 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -42,6 +42,7 @@ LOCAL_SRC_FILES:= \ SampleTable.cpp \ StagefrightMediaScanner.cpp \ StagefrightMetadataRetriever.cpp \ + SurfaceMediaSource.cpp \ ThrottledSource.cpp \ TimeSource.cpp \ TimedEventQueue.cpp \ diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp index 77a66024a60d..4edb613c33b6 100644 --- a/media/libstagefright/NuCachedSource2.cpp +++ b/media/libstagefright/NuCachedSource2.cpp @@ -185,7 +185,8 @@ NuCachedSource2::NuCachedSource2(const sp<DataSource> &source) mFinalStatus(OK), mLastAccessPos(0), mFetching(true), - mLastFetchTimeUs(-1) { + mLastFetchTimeUs(-1), + mNumRetriesLeft(kMaxNumRetries) { mLooper->setName("NuCachedSource2"); mLooper->registerHandler(mReflector); mLooper->start(); @@ -254,7 +255,27 @@ void NuCachedSource2::onMessageReceived(const sp<AMessage> &msg) { void NuCachedSource2::fetchInternal() { LOGV("fetchInternal"); - CHECK_EQ(mFinalStatus, (status_t)OK); + { + Mutex::Autolock autoLock(mLock); + CHECK(mFinalStatus == OK || mNumRetriesLeft > 0); + + if (mFinalStatus != OK) { + --mNumRetriesLeft; + + status_t err = + mSource->reconnectAtOffset(mCacheOffset + mCache->totalSize()); + + if (err == ERROR_UNSUPPORTED) { + mNumRetriesLeft = 0; + return; + } else if (err != OK) { + LOGI("The attempt to reconnect failed, %d retries remaining", + mNumRetriesLeft); + + return; + } + } + } PageCache::Page *page = mCache->acquirePage(); @@ -264,14 +285,23 @@ void NuCachedSource2::fetchInternal() { Mutex::Autolock autoLock(mLock); if (n < 0) { - LOGE("source returned error %ld", n); + LOGE("source returned error %ld, %d retries left", n, mNumRetriesLeft); mFinalStatus = n; mCache->releasePage(page); } else if (n == 0) { LOGI("ERROR_END_OF_STREAM"); + + mNumRetriesLeft = 0; mFinalStatus = ERROR_END_OF_STREAM; + mCache->releasePage(page); } else { + if (mFinalStatus != OK) { + LOGI("retrying a previously failed read succeeded."); + } + mNumRetriesLeft = kMaxNumRetries; + mFinalStatus = OK; + page->mSize = n; mCache->appendPage(page); } @@ -280,7 +310,7 @@ void NuCachedSource2::fetchInternal() { void NuCachedSource2::onFetch() { LOGV("onFetch"); - if (mFinalStatus != OK) { + if (mFinalStatus != OK && mNumRetriesLeft == 0) { LOGV("EOS reached, done prefetching for now"); mFetching = false; } @@ -308,8 +338,19 @@ void NuCachedSource2::onFetch() { restartPrefetcherIfNecessary_l(); } - (new AMessage(kWhatFetchMore, mReflector->id()))->post( - mFetching ? 0 : 100000ll); + int64_t delayUs; + if (mFetching) { + if (mFinalStatus != OK && mNumRetriesLeft > 0) { + // We failed this time and will try again in 3 seconds. + delayUs = 3000000ll; + } else { + delayUs = 0; + } + } else { + delayUs = 100000ll; + } + + (new AMessage(kWhatFetchMore, mReflector->id()))->post(delayUs); } void NuCachedSource2::onRead(const sp<AMessage> &msg) { @@ -345,7 +386,7 @@ void NuCachedSource2::restartPrefetcherIfNecessary_l( bool ignoreLowWaterThreshold, bool force) { static const size_t kGrayArea = 1024 * 1024; - if (mFetching || mFinalStatus != OK) { + if (mFetching || (mFinalStatus != OK && mNumRetriesLeft == 0)) { return; } @@ -427,6 +468,12 @@ size_t NuCachedSource2::approxDataRemaining(status_t *finalStatus) { size_t NuCachedSource2::approxDataRemaining_l(status_t *finalStatus) { *finalStatus = mFinalStatus; + + if (mFinalStatus != OK && mNumRetriesLeft > 0) { + // Pretend that everything is fine until we're out of retries. + *finalStatus = OK; + } + off64_t lastBytePosCached = mCacheOffset + mCache->totalSize(); if (mLastAccessPos < lastBytePosCached) { return lastBytePosCached - mLastAccessPos; diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp new file mode 100644 index 000000000000..ff4b08fb6c3f --- /dev/null +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -0,0 +1,756 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define LOG_NDEBUG 0 +#define LOG_TAG "SurfaceMediaSource" + +#include <media/stagefright/SurfaceMediaSource.h> +#include <ui/GraphicBuffer.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/openmax/OMX_IVCommon.h> + +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/SurfaceComposerClient.h> +#include <surfaceflinger/IGraphicBufferAlloc.h> +#include <OMX_Component.h> + +#include <utils/Log.h> +#include <utils/String8.h> + +namespace android { + +SurfaceMediaSource::SurfaceMediaSource(uint32_t bufW, uint32_t bufH) : + mDefaultWidth(bufW), + mDefaultHeight(bufH), + mPixelFormat(0), + mBufferCount(MIN_ASYNC_BUFFER_SLOTS), + mClientBufferCount(0), + mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS), + mCurrentSlot(INVALID_BUFFER_SLOT), + mCurrentTimestamp(0), + mSynchronousMode(true), + mConnectedApi(NO_CONNECTED_API), + mFrameRate(30), + mStarted(false) { + LOGV("SurfaceMediaSource::SurfaceMediaSource"); + sp<ISurfaceComposer> composer(ComposerService::getComposerService()); + mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); +} + +SurfaceMediaSource::~SurfaceMediaSource() { + LOGV("SurfaceMediaSource::~SurfaceMediaSource"); + if (mStarted) { + stop(); + } + freeAllBuffers(); +} + +size_t SurfaceMediaSource::getQueuedCount() const { + Mutex::Autolock lock(mMutex); + return mQueue.size(); +} + +status_t SurfaceMediaSource::setBufferCountServerLocked(int bufferCount) { + if (bufferCount > NUM_BUFFER_SLOTS) + return BAD_VALUE; + + // special-case, nothing to do + if (bufferCount == mBufferCount) + return OK; + + if (!mClientBufferCount && + bufferCount >= mBufferCount) { + // easy, we just have more buffers + mBufferCount = bufferCount; + mServerBufferCount = bufferCount; + mDequeueCondition.signal(); + } else { + // we're here because we're either + // - reducing the number of available buffers + // - or there is a client-buffer-count in effect + + // less than 2 buffers is never allowed + if (bufferCount < 2) + return BAD_VALUE; + + // when there is non client-buffer-count in effect, the client is not + // allowed to dequeue more than one buffer at a time, + // so the next time they dequeue a buffer, we know that they don't + // own one. the actual resizing will happen during the next + // dequeueBuffer. + + mServerBufferCount = bufferCount; + } + return OK; +} + +// Called from the consumer side +status_t SurfaceMediaSource::setBufferCountServer(int bufferCount) { + Mutex::Autolock lock(mMutex); + return setBufferCountServerLocked(bufferCount); +} + +status_t SurfaceMediaSource::setBufferCount(int bufferCount) { + LOGV("SurfaceMediaSource::setBufferCount"); + if (bufferCount > NUM_BUFFER_SLOTS) { + LOGE("setBufferCount: bufferCount is larger than the number of buffer slots"); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + // Error out if the user has dequeued buffers + for (int i = 0 ; i < mBufferCount ; i++) { + if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { + LOGE("setBufferCount: client owns some buffers"); + return INVALID_OPERATION; + } + } + + if (bufferCount == 0) { + const int minBufferSlots = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + mClientBufferCount = 0; + bufferCount = (mServerBufferCount >= minBufferSlots) ? + mServerBufferCount : minBufferSlots; + return setBufferCountServerLocked(bufferCount); + } + + // We don't allow the client to set a buffer-count less than + // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it. + if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) { + return BAD_VALUE; + } + + // here we're guaranteed that the client doesn't have dequeued buffers + // and will release all of its buffer references. + freeAllBuffers(); + mBufferCount = bufferCount; + mClientBufferCount = bufferCount; + mCurrentSlot = INVALID_BUFFER_SLOT; + mQueue.clear(); + mDequeueCondition.signal(); + return OK; +} + +status_t SurfaceMediaSource::requestBuffer(int slot, sp<GraphicBuffer>* buf) { + LOGV("SurfaceMediaSource::requestBuffer"); + Mutex::Autolock lock(mMutex); + if (slot < 0 || mBufferCount <= slot) { + LOGE("requestBuffer: slot index out of range [0, %d]: %d", + mBufferCount, slot); + return BAD_VALUE; + } + mSlots[slot].mRequestBufferCalled = true; + *buf = mSlots[slot].mGraphicBuffer; + return NO_ERROR; +} + +status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage) { + LOGV("dequeueBuffer"); + + + // Check for the buffer size- the client should just use the + // default width and height, and not try to set those. + // This is needed since + // the getFormat() returns mDefaultWidth/ Height for the OMX. It is + // queried by OMX in the beginning and not every time a frame comes. + // Not sure if there is a way to update the + // frame size while recording. So as of now, the client side + // sets the default values via the constructor, and the encoder is + // setup to encode frames of that size + // The design might need to change in the future. + // TODO: Currently just uses mDefaultWidth/Height. In the future + // we might declare mHeight and mWidth and check against those here. + if ((w != 0) || (h != 0)) { + LOGE("dequeuebuffer: invalid buffer size! Req: %dx%d, Found: %dx%d", + mDefaultWidth, mDefaultHeight, w, h); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + + status_t returnFlags(OK); + + int found, foundSync; + int dequeuedCount = 0; + bool tryAgain = true; + while (tryAgain) { + // We need to wait for the FIFO to drain if the number of buffer + // needs to change. + // + // The condition "number of buffer needs to change" is true if + // - the client doesn't care about how many buffers there are + // - AND the actual number of buffer is different from what was + // set in the last setBufferCountServer() + // - OR - + // setBufferCountServer() was set to a value incompatible with + // the synchronization mode (for instance because the sync mode + // changed since) + // + // As long as this condition is true AND the FIFO is not empty, we + // wait on mDequeueCondition. + + int minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + + if (!mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded))) { + // wait for the FIFO to drain + while (!mQueue.isEmpty()) { + LOGV("Waiting for the FIFO to drain"); + mDequeueCondition.wait(mMutex); + } + // need to check again since the mode could have changed + // while we were waiting + minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + } + + if (!mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded))) { + // here we're guaranteed that mQueue is empty + freeAllBuffers(); + mBufferCount = mServerBufferCount; + if (mBufferCount < minBufferCountNeeded) + mBufferCount = minBufferCountNeeded; + mCurrentSlot = INVALID_BUFFER_SLOT; + returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + } + + // look for a free buffer to give to the client + found = INVALID_BUFFER_SLOT; + foundSync = INVALID_BUFFER_SLOT; + dequeuedCount = 0; + for (int i = 0; i < mBufferCount; i++) { + const int state = mSlots[i].mBufferState; + if (state == BufferSlot::DEQUEUED) { + dequeuedCount++; + continue; // won't be continuing if could + // dequeue a non 'FREE' current slot like + // that in SurfaceTexture + } + // In case of Encoding, we do not deque the mCurrentSlot buffer + // since we follow synchronous mode (unlike possibly in + // SurfaceTexture that could be using the asynch mode + // or has some mechanism in GL to be able to wait till the + // currentslot is done using the data) + // Here, we have to wait for the MPEG4Writer(or equiv) + // to tell us when it's done using the current buffer + if (state == BufferSlot::FREE) { + foundSync = i; + // Unlike that in SurfaceTexture, + // We don't need to worry if it is the + // currentslot or not as it is in state FREE + found = i; + break; + } + } + + // clients are not allowed to dequeue more than one buffer + // if they didn't set a buffer count. + if (!mClientBufferCount && dequeuedCount) { + return -EINVAL; + } + + // See whether a buffer has been queued since the last setBufferCount so + // we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below. + bool bufferHasBeenQueued = mCurrentSlot != INVALID_BUFFER_SLOT; + if (bufferHasBeenQueued) { + // make sure the client is not trying to dequeue more buffers + // than allowed. + const int avail = mBufferCount - (dequeuedCount+1); + if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { + LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)", + MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), + dequeuedCount); + return -EBUSY; + } + } + + // we're in synchronous mode and didn't find a buffer, we need to wait + // for for some buffers to be consumed + tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); + if (tryAgain) { + LOGW("Waiting..In synchronous mode and no buffer to dQ"); + mDequeueCondition.wait(mMutex); + } + } + + if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { + // foundSync guaranteed to be != INVALID_BUFFER_SLOT + found = foundSync; + } + + if (found == INVALID_BUFFER_SLOT) { + return -EBUSY; + } + + const int buf = found; + *outBuf = found; + + const bool useDefaultSize = !w && !h; + if (useDefaultSize) { + // use the default size + w = mDefaultWidth; + h = mDefaultHeight; + } + + const bool updateFormat = (format != 0); + if (!updateFormat) { + // keep the current (or default) format + format = mPixelFormat; + } + + // buffer is now in DEQUEUED (but can also be current at the same time, + // if we're in synchronous mode) + mSlots[buf].mBufferState = BufferSlot::DEQUEUED; + + const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); + if ((buffer == NULL) || + (uint32_t(buffer->width) != w) || + (uint32_t(buffer->height) != h) || + (uint32_t(buffer->format) != format) || + ((uint32_t(buffer->usage) & usage) != usage)) { + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + status_t error; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer( + w, h, format, usage, &error)); + if (graphicBuffer == 0) { + LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed"); + return error; + } + if (updateFormat) { + mPixelFormat = format; + } + mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mRequestBufferCalled = false; + returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + } + return returnFlags; +} + +status_t SurfaceMediaSource::setSynchronousMode(bool enabled) { + Mutex::Autolock lock(mMutex); + + status_t err = OK; + if (!enabled) { + // going to asynchronous mode, drain the queue + while (mSynchronousMode != enabled && !mQueue.isEmpty()) { + mDequeueCondition.wait(mMutex); + } + } + + if (mSynchronousMode != enabled) { + // - if we're going to asynchronous mode, the queue is guaranteed to be + // empty here + // - if the client set the number of buffers, we're guaranteed that + // we have at least 3 (because we don't allow less) + mSynchronousMode = enabled; + mDequeueCondition.signal(); + } + return err; +} + +status_t SurfaceMediaSource::connect(int api) { + LOGV("SurfaceMediaSource::connect"); + Mutex::Autolock lock(mMutex); + status_t err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi != NO_CONNECTED_API) { + err = -EINVAL; + } else { + mConnectedApi = api; + } + break; + default: + err = -EINVAL; + break; + } + return err; +} + +status_t SurfaceMediaSource::disconnect(int api) { + LOGV("SurfaceMediaSource::disconnect"); + Mutex::Autolock lock(mMutex); + status_t err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi == api) { + mConnectedApi = NO_CONNECTED_API; + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + break; + } + return err; +} + +status_t SurfaceMediaSource::queueBuffer(int buf, int64_t timestamp, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { + LOGV("queueBuffer"); + + Mutex::Autolock lock(mMutex); + if (buf < 0 || buf >= mBufferCount) { + LOGE("queueBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return -EINVAL; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + LOGE("queueBuffer: slot %d is not owned by the client (state=%d)", + buf, mSlots[buf].mBufferState); + return -EINVAL; + } else if (!mSlots[buf].mRequestBufferCalled) { + LOGE("queueBuffer: slot %d was enqueued without requesting a " + "buffer", buf); + return -EINVAL; + } + + if (mSynchronousMode) { + // in synchronous mode we queue all buffers in a FIFO + mQueue.push_back(buf); + LOGV("Client queued buffer on slot: %d, Q size = %d", + buf, mQueue.size()); + } else { + // in asynchronous mode we only keep the most recent buffer + if (mQueue.empty()) { + mQueue.push_back(buf); + } else { + Fifo::iterator front(mQueue.begin()); + // buffer currently queued is freed + mSlots[*front].mBufferState = BufferSlot::FREE; + // and we record the new buffer index in the queued list + *front = buf; + } + } + + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mSlots[buf].mTimestamp = timestamp; + // TODO: (Confirm) Don't want to signal dequeue here. + // May be just in asynchronous mode? + // mDequeueCondition.signal(); + + // Once the queuing is done, we need to let the listener + // and signal the buffer consumer (encoder) know that a + // buffer is available + onFrameReceivedLocked(); + + *outWidth = mDefaultWidth; + *outHeight = mDefaultHeight; + *outTransform = 0; + + return OK; +} + + +// onFrameReceivedLocked informs the buffer consumers (StageFrightRecorder) +// or listeners that a frame has been received +// It is supposed to be called only from queuebuffer. +// The buffer is NOT made available for dequeueing immediately. We need to +// wait to hear from StageFrightRecorder to set the buffer FREE +// Make sure this is called when the mutex is locked +status_t SurfaceMediaSource::onFrameReceivedLocked() { + LOGV("On Frame Received"); + // Signal the encoder that a new frame has arrived + mFrameAvailableCondition.signal(); + + // call back the listener + // TODO: The listener may not be needed in SurfaceMediaSource at all. + // This can be made a SurfaceTexture specific thing + sp<FrameAvailableListener> listener; + if (mSynchronousMode || mQueue.empty()) { + listener = mFrameAvailableListener; + } + + if (listener != 0) { + listener->onFrameAvailable(); + } + return OK; +} + + +void SurfaceMediaSource::cancelBuffer(int buf) { + LOGV("SurfaceMediaSource::cancelBuffer"); + Mutex::Autolock lock(mMutex); + if (buf < 0 || buf >= mBufferCount) { + LOGE("cancelBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", + buf, mSlots[buf].mBufferState); + return; + } + mSlots[buf].mBufferState = BufferSlot::FREE; + mDequeueCondition.signal(); +} + +nsecs_t SurfaceMediaSource::getTimestamp() { + LOGV("SurfaceMediaSource::getTimestamp"); + Mutex::Autolock lock(mMutex); + return mCurrentTimestamp; +} + + +void SurfaceMediaSource::setFrameAvailableListener( + const sp<FrameAvailableListener>& listener) { + LOGV("SurfaceMediaSource::setFrameAvailableListener"); + Mutex::Autolock lock(mMutex); + mFrameAvailableListener = listener; +} + +void SurfaceMediaSource::freeAllBuffers() { + LOGV("freeAllBuffers"); + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + mSlots[i].mGraphicBuffer = 0; + mSlots[i].mBufferState = BufferSlot::FREE; + } +} + +sp<GraphicBuffer> SurfaceMediaSource::getCurrentBuffer() const { + Mutex::Autolock lock(mMutex); + return mCurrentBuf; +} + +int SurfaceMediaSource::query(int what, int* outValue) +{ + LOGV("query"); + Mutex::Autolock lock(mMutex); + int value; + switch (what) { + case NATIVE_WINDOW_WIDTH: + value = mDefaultWidth; + if (!mDefaultWidth && !mDefaultHeight && mCurrentBuf != 0) + value = mCurrentBuf->width; + break; + case NATIVE_WINDOW_HEIGHT: + value = mDefaultHeight; + if (!mDefaultWidth && !mDefaultHeight && mCurrentBuf != 0) + value = mCurrentBuf->height; + break; + case NATIVE_WINDOW_FORMAT: + value = mPixelFormat; + break; + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + value = mSynchronousMode ? + (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS; + break; + default: + return BAD_VALUE; + } + outValue[0] = value; + return NO_ERROR; +} + +void SurfaceMediaSource::dump(String8& result) const +{ + char buffer[1024]; + dump(result, "", buffer, 1024); +} + +void SurfaceMediaSource::dump(String8& result, const char* prefix, + char* buffer, size_t SIZE) const +{ + Mutex::Autolock _l(mMutex); + snprintf(buffer, SIZE, + "%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], " + "mPixelFormat=%d, \n", + prefix, mBufferCount, mSynchronousMode, mDefaultWidth, mDefaultHeight, + mPixelFormat); + result.append(buffer); + + String8 fifo; + int fifoSize = 0; + Fifo::const_iterator i(mQueue.begin()); + while (i != mQueue.end()) { + snprintf(buffer, SIZE, "%02d ", *i++); + fifoSize++; + fifo.append(buffer); + } + + result.append(buffer); + + struct { + const char * operator()(int state) const { + switch (state) { + case BufferSlot::DEQUEUED: return "DEQUEUED"; + case BufferSlot::QUEUED: return "QUEUED"; + case BufferSlot::FREE: return "FREE"; + default: return "Unknown"; + } + } + } stateName; + + for (int i = 0; i < mBufferCount; i++) { + const BufferSlot& slot(mSlots[i]); + snprintf(buffer, SIZE, + "%s%s[%02d] state=%-8s, " + "timestamp=%lld\n", + prefix, (i==mCurrentSlot)?">":" ", i, stateName(slot.mBufferState), + slot.mTimestamp + ); + result.append(buffer); + } +} + +status_t SurfaceMediaSource::setFrameRate(int32_t fps) +{ + Mutex::Autolock lock(mMutex); + const int MAX_FRAME_RATE = 60; + if (fps < 0 || fps > MAX_FRAME_RATE) { + return BAD_VALUE; + } + mFrameRate = fps; + return OK; +} + +bool SurfaceMediaSource::isMetaDataStoredInVideoBuffers() const { + LOGV("isMetaDataStoredInVideoBuffers"); + return true; +} + +int32_t SurfaceMediaSource::getFrameRate( ) const { + Mutex::Autolock lock(mMutex); + return mFrameRate; +} + +status_t SurfaceMediaSource::start(MetaData *params) +{ + LOGV("start"); + Mutex::Autolock lock(mMutex); + CHECK(!mStarted); + mStarted = true; + return OK; +} + + +status_t SurfaceMediaSource::stop() +{ + LOGV("Stop"); + + Mutex::Autolock lock(mMutex); + // TODO: Add waiting on mFrameCompletedCondition here? + mStarted = false; + mFrameAvailableCondition.signal(); + + return OK; +} + +sp<MetaData> SurfaceMediaSource::getFormat() +{ + LOGV("getFormat"); + Mutex::Autolock autoLock(mMutex); + sp<MetaData> meta = new MetaData; + + meta->setInt32(kKeyWidth, mDefaultWidth); + meta->setInt32(kKeyHeight, mDefaultHeight); + // The encoder format is set as an opaque colorformat + // The encoder will later find out the actual colorformat + // from the GL Frames itself. + meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatAndroidOpaque); + meta->setInt32(kKeyStride, mDefaultWidth); + meta->setInt32(kKeySliceHeight, mDefaultHeight); + meta->setInt32(kKeyFrameRate, mFrameRate); + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); + return meta; +} + +status_t SurfaceMediaSource::read( MediaBuffer **buffer, + const ReadOptions *options) +{ + LOGV("Read. Size of queued buffer: %d", mQueue.size()); + *buffer = NULL; + + Mutex::Autolock autoLock(mMutex) ; + // If the recording has started and the queue is empty, then just + // wait here till the frames come in from the client side + while (mStarted && mQueue.empty()) { + LOGV("NO FRAMES! Recorder waiting for FrameAvailableCondition"); + mFrameAvailableCondition.wait(mMutex); + } + + // If the loop was exited as a result of stopping the recording, + // it is OK + if (!mStarted) { + return OK; + } + + // Update the current buffer info + // TODO: mCurrentSlot can be made a bufferstate since there + // can be more than one "current" slots. + Fifo::iterator front(mQueue.begin()); + mCurrentSlot = *front; + mCurrentBuf = mSlots[mCurrentSlot].mGraphicBuffer; + mCurrentTimestamp = mSlots[mCurrentSlot].mTimestamp; + + // Pass the data to the MediaBuffer + // TODO: Change later to pass in only the metadata + *buffer = new MediaBuffer(mCurrentBuf); + (*buffer)->setObserver(this); + (*buffer)->add_ref(); + (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp); + + return OK; +} + +void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) { + LOGV("signalBufferReturned"); + + bool foundBuffer = false; + Mutex::Autolock autoLock(mMutex); + + if (!mStarted) { + LOGV("started = false. Nothing to do"); + return; + } + + for (Fifo::iterator it = mQueue.begin(); it != mQueue.end(); ++it) { + if (mSlots[*it].mGraphicBuffer == buffer->graphicBuffer()) { + LOGV("Buffer %d returned. Setting it 'FREE'. New Queue size = %d", + *it, mQueue.size()-1); + mSlots[*it].mBufferState = BufferSlot::FREE; + mQueue.erase(it); + buffer->setObserver(0); + buffer->release(); + mDequeueCondition.signal(); + mFrameCompleteCondition.signal(); + foundBuffer = true; + break; + } + } + + if (!foundBuffer) { + CHECK_EQ(0, "signalBufferReturned: bogus buffer"); + } +} + + + +} // end of namespace android diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp index 588a74d99242..07a9eb892c5d 100644 --- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp +++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp @@ -25,6 +25,8 @@ #include "support.h" +#include <cutils/properties.h> // for property_get + namespace android { ChromiumHTTPDataSource::ChromiumHTTPDataSource(uint32_t flags) @@ -111,7 +113,7 @@ void ChromiumHTTPDataSource::onConnectionFailed(status_t err) { mState = DISCONNECTED; mCondition.broadcast(); - mURI.clear(); + // mURI.clear(); mIOResult = err; @@ -150,8 +152,18 @@ ssize_t ChromiumHTTPDataSource::readAt(off64_t offset, void *data, size_t size) Mutex::Autolock autoLock(mLock); if (mState != CONNECTED) { - return ERROR_NOT_CONNECTED; + return INVALID_OPERATION; + } + +#if 0 + char value[PROPERTY_VALUE_MAX]; + if (property_get("media.stagefright.disable-net", value, 0) + && (!strcasecmp(value, "true") || !strcmp(value, "1"))) { + LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Simulating that the network is down."); + disconnect_l(); + return ERROR_IO; } +#endif if (offset != mCurrentOffset) { AString tmp = mURI; @@ -236,7 +248,7 @@ void ChromiumHTTPDataSource::onDisconnectComplete() { CHECK_EQ((int)mState, (int)DISCONNECTING); mState = DISCONNECTED; - mURI.clear(); + // mURI.clear(); mCondition.broadcast(); @@ -299,5 +311,21 @@ void ChromiumHTTPDataSource::clearDRMState_l() { } } +status_t ChromiumHTTPDataSource::reconnectAtOffset(off64_t offset) { + Mutex::Autolock autoLock(mLock); + + if (mURI.empty()) { + return INVALID_OPERATION; + } + + LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnecting..."); + status_t err = connect_l(mURI.c_str(), &mHeaders, offset); + if (err != OK) { + LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnect failed w/ err 0x%08x", err); + } + + return err; +} + } // namespace android diff --git a/media/libstagefright/include/ChromiumHTTPDataSource.h b/media/libstagefright/include/ChromiumHTTPDataSource.h index d833e2efc1ba..18f89130c747 100644 --- a/media/libstagefright/include/ChromiumHTTPDataSource.h +++ b/media/libstagefright/include/ChromiumHTTPDataSource.h @@ -51,6 +51,8 @@ struct ChromiumHTTPDataSource : public HTTPBase { virtual String8 getMIMEType() const; + virtual status_t reconnectAtOffset(off64_t offset); + protected: virtual ~ChromiumHTTPDataSource(); diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h index 2d6cb848292a..22b2855204e9 100644 --- a/media/libstagefright/include/NuCachedSource2.h +++ b/media/libstagefright/include/NuCachedSource2.h @@ -77,6 +77,10 @@ private: kWhatRead = 'read', }; + enum { + kMaxNumRetries = 10, + }; + sp<DataSource> mSource; sp<AHandlerReflector<NuCachedSource2> > mReflector; sp<ALooper> mLooper; @@ -93,6 +97,8 @@ private: bool mFetching; int64_t mLastFetchTimeUs; + int32_t mNumRetriesLeft; + void onMessageReceived(const sp<AMessage> &msg); void onFetch(); void onRead(const sp<AMessage> &msg); diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk new file mode 100644 index 000000000000..3ea8f39ba699 --- /dev/null +++ b/media/libstagefright/tests/Android.mk @@ -0,0 +1,53 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +ifneq ($(TARGET_SIMULATOR),true) + +LOCAL_MODULE := SurfaceMediaSource_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + SurfaceMediaSource_test.cpp \ + DummyRecorder.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libEGL \ + libGLESv2 \ + libandroid \ + libbinder \ + libcutils \ + libgui \ + libstlport \ + libui \ + libutils \ + libstagefright \ + libstagefright_omx \ + libstagefright_foundation \ + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main \ + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + frameworks/base/media/libstagefright \ + frameworks/base/media/libstagefright/include \ + $(TOP)/frameworks/base/include/media/stagefright/openmax \ + +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/media/libstagefright/tests/DummyRecorder.cpp b/media/libstagefright/tests/DummyRecorder.cpp new file mode 100644 index 000000000000..8d75d6bc86ae --- /dev/null +++ b/media/libstagefright/tests/DummyRecorder.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DummyRecorder" +// #define LOG_NDEBUG 0 + +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaSource.h> +#include "DummyRecorder.h" + +#include <utils/Log.h> + +namespace android { + +// static +void *DummyRecorder::threadWrapper(void *pthis) { + LOGV("ThreadWrapper: %p", pthis); + DummyRecorder *writer = static_cast<DummyRecorder *>(pthis); + writer->readFromSource(); + return NULL; +} + + +status_t DummyRecorder::start() { + LOGV("Start"); + mStarted = true; + + mSource->start(); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + int err = pthread_create(&mThread, &attr, threadWrapper, this); + pthread_attr_destroy(&attr); + + if (err) { + LOGE("Error creating thread!"); + return -ENODEV; + } + return OK; +} + + +status_t DummyRecorder::stop() { + LOGV("Stop"); + mStarted = false; + + mSource->stop(); + void *dummy; + pthread_join(mThread, &dummy); + status_t err = (status_t) dummy; + + LOGV("Ending the reading thread"); + return err; +} + +// pretend to read the source buffers +void DummyRecorder::readFromSource() { + LOGV("ReadFromSource"); + if (!mStarted) { + return; + } + + status_t err = OK; + MediaBuffer *buffer; + LOGV("A fake writer accessing the frames"); + while (mStarted && (err = mSource->read(&buffer)) == OK){ + // if not getting a valid buffer from source, then exit + if (buffer == NULL) { + return; + } + buffer->release(); + buffer = NULL; + } +} + + +} // end of namespace android diff --git a/media/libstagefright/tests/DummyRecorder.h b/media/libstagefright/tests/DummyRecorder.h new file mode 100644 index 000000000000..1cbea1b0dea2 --- /dev/null +++ b/media/libstagefright/tests/DummyRecorder.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef DUMMY_RECORDER_H_ +#define DUMMY_RECORDER_H_ + +#include <pthread.h> +#include <utils/String8.h> +#include <media/stagefright/foundation/ABase.h> + + +namespace android { + +class MediaSource; +class MediaBuffer; + +class DummyRecorder { + public: + // The media source from which this will receive frames + sp<MediaSource> mSource; + bool mStarted; + pthread_t mThread; + + status_t start(); + status_t stop(); + + // actual entry point for the thread + void readFromSource(); + + // static function to wrap the actual thread entry point + static void *threadWrapper(void *pthis); + + DummyRecorder(const sp<MediaSource> &source) : mSource(source) + , mStarted(false) {} + ~DummyRecorder( ) {} + + private: + + DISALLOW_EVIL_CONSTRUCTORS(DummyRecorder); +}; + +} // end of namespace android +#endif + + diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp new file mode 100644 index 000000000000..ce108122b8f7 --- /dev/null +++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceMediaSource_test" +// #define LOG_NDEBUG 0 + +#include <gtest/gtest.h> +#include <utils/String8.h> +#include <utils/Errors.h> + +#include <media/stagefright/SurfaceMediaSource.h> + +#include <gui/SurfaceTextureClient.h> +#include <ui/GraphicBuffer.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <binder/ProcessState.h> +#include <ui/FramebufferNativeWindow.h> + +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MPEG4Writer.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/OMXCodec.h> +#include <OMX_Component.h> + +#include "DummyRecorder.h" + +namespace android { + + +class SurfaceMediaSourceTest : public ::testing::Test { +public: + + SurfaceMediaSourceTest( ): mYuvTexWidth(64), mYuvTexHeight(66) { } + sp<MPEG4Writer> setUpWriter(OMXClient &client ); + void oneBufferPass(int width, int height ); + static void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) ; + static void fillYV12BufferRect(uint8_t* buf, int w, int h, + int stride, const android_native_rect_t& rect) ; +protected: + + virtual void SetUp() { + mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); + mSMS->setSynchronousMode(true); + mSTC = new SurfaceTextureClient(mSMS); + mANW = mSTC; + + } + + + virtual void TearDown() { + mSMS.clear(); + mSTC.clear(); + mANW.clear(); + } + + const int mYuvTexWidth;// = 64; + const int mYuvTexHeight;// = 66; + + sp<SurfaceMediaSource> mSMS; + sp<SurfaceTextureClient> mSTC; + sp<ANativeWindow> mANW; + +}; + +void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) { + LOGV("One Buffer Pass"); + ANativeWindowBuffer* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + + // Fill the buffer with the a checkerboard pattern + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + SurfaceMediaSourceTest::fillYV12Buffer(img, width, height, buf->getStride()); + buf->unlock(); + + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); +} + +sp<MPEG4Writer> SurfaceMediaSourceTest::setUpWriter(OMXClient &client ) { + // Writing to a file + const char *fileName = "/sdcard/outputSurfEnc.mp4"; + sp<MetaData> enc_meta = new MetaData; + enc_meta->setInt32(kKeyBitRate, 300000); + enc_meta->setInt32(kKeyFrameRate, 30); + + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); + + sp<MetaData> meta = mSMS->getFormat(); + + int32_t width, height, stride, sliceHeight, colorFormat; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + CHECK(meta->findInt32(kKeyStride, &stride)); + CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); + CHECK(meta->findInt32(kKeyColorFormat, &colorFormat)); + + enc_meta->setInt32(kKeyWidth, width); + enc_meta->setInt32(kKeyHeight, height); + enc_meta->setInt32(kKeyIFramesInterval, 1); + enc_meta->setInt32(kKeyStride, stride); + enc_meta->setInt32(kKeySliceHeight, sliceHeight); + // TODO: overwriting the colorformat since the format set by GRAlloc + // could be wrong or not be read by OMX + enc_meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); + // colorFormat); + + + sp<MediaSource> encoder = + OMXCodec::Create( + client.interface(), enc_meta, true /* createEncoder */, mSMS); + + sp<MPEG4Writer> writer = new MPEG4Writer(fileName); + writer->addSource(encoder); + + return writer; +} + +// Fill a YV12 buffer with a multi-colored checkerboard pattern +void SurfaceMediaSourceTest::fillYV12Buffer(uint8_t* buf, int w, int h, int stride) { + const int blockWidth = w > 16 ? w / 16 : 1; + const int blockHeight = h > 16 ? h / 16 : 1; + const int yuvTexOffsetY = 0; + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * h; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; + int yuvTexStrideU = yuvTexStrideV; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int parityX = (x / blockWidth) & 1; + int parityY = (y / blockHeight) & 1; + unsigned char intensity = (parityX ^ parityY) ? 63 : 191; + buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; + if (x < w / 2 && y < h / 2) { + buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; + if (x * 2 < w / 2 && y * 2 < h / 2) { + buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = + buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = + buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = + buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = + intensity; + } + } + } + } +} + +// Fill a YV12 buffer with red outside a given rectangle and green inside it. +void SurfaceMediaSourceTest::fillYV12BufferRect(uint8_t* buf, int w, + int h, int stride, const android_native_rect_t& rect) { + const int yuvTexOffsetY = 0; + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * h; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; + int yuvTexStrideU = yuvTexStrideV; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + bool inside = rect.left <= x && x < rect.right && + rect.top <= y && y < rect.bottom; + buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64; + if (x < w / 2 && y < h / 2) { + bool inside = rect.left <= 2*x && 2*x < rect.right && + rect.top <= 2*y && 2*y < rect.bottom; + buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16; + buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = + inside ? 16 : 255; + } + } + } +} ///////// End of class SurfaceMediaSourceTest + +/////////////////////////////////////////////////////////////////// +// Class to imitate the recording ///////////////////////////// +// //////////////////////////////////////////////////////////////// +struct SimpleDummyRecorder { + sp<MediaSource> mSource; + + SimpleDummyRecorder + (const sp<MediaSource> &source): mSource(source) {} + + status_t start() { return mSource->start();} + status_t stop() { return mSource->stop();} + + // fakes reading from a media source + status_t readFromSource() { + MediaBuffer *buffer; + status_t err = mSource->read(&buffer); + if (err != OK) { + return err; + } + buffer->release(); + buffer = NULL; + return OK; + } +}; + +/////////////////////////////////////////////////////////////////// +// TESTS +// Just pass one buffer from the native_window to the SurfaceMediaSource +TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotOneBufferPass) { + LOGV("Testing OneBufferPass ******************************"); + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + 0, 0, HAL_PIXEL_FORMAT_YV12)); + // OMX_COLOR_FormatYUV420Planar)); // )); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + oneBufferPass(mYuvTexWidth, mYuvTexHeight); +} + +// Pass the buffer with the wrong height and weight and should not be accepted +TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) { + LOGV("Testing Wrong size BufferPass ******************************"); + + // setting the client side buffer size different than the server size + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + 10, 10, HAL_PIXEL_FORMAT_YV12)); + // OMX_COLOR_FormatYUV420Planar)); // )); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + ANativeWindowBuffer* anb; + + // make sure we get an error back when dequeuing! + ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); +} + + +// pass multiple buffers from the native_window the SurfaceMediaSource +// A dummy writer is used to simulate actual MPEG4Writer +TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { + LOGV("Testing MultiBufferPass, Dummy Recorder *********************"); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + 0, 0, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + SimpleDummyRecorder writer(mSMS); + writer.start(); + + int32_t nFramesCount = 0; + while (nFramesCount < 300) { + oneBufferPass(mYuvTexWidth, mYuvTexHeight); + + ASSERT_EQ(NO_ERROR, writer.readFromSource()); + + nFramesCount++; + } + writer.stop(); +} + +// Delayed pass of multiple buffers from the native_window the SurfaceMediaSource +// A dummy writer is used to simulate actual MPEG4Writer +TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassLag) { + LOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************"); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + 0, 0, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + SimpleDummyRecorder writer(mSMS); + writer.start(); + + int32_t nFramesCount = 1; + const int FRAMES_LAG = mSMS->getBufferCount() - 1; + while (nFramesCount <= 300) { + oneBufferPass(mYuvTexWidth, mYuvTexHeight); + // Forcing the writer to lag behind a few frames + if (nFramesCount > FRAMES_LAG) { + ASSERT_EQ(NO_ERROR, writer.readFromSource()); + } + nFramesCount++; + } + writer.stop(); +} + +// pass multiple buffers from the native_window the SurfaceMediaSource +// A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer +TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassThreaded) { + LOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********"); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + 0, 0, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + DummyRecorder writer(mSMS); + writer.start(); + + int32_t nFramesCount = 0; + while (nFramesCount <= 300) { + oneBufferPass(mYuvTexWidth, mYuvTexHeight); + + nFramesCount++; + } + writer.stop(); +} + +// Test to examine the actual encoding. Temporarily disabled till the +// colorformat and encoding from GRAlloc data is resolved +TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuFilledYV12BufferNpotWrite) { + LOGV("Testing the whole pipeline with actual Recorder"); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + 0, 0, HAL_PIXEL_FORMAT_YV12)); // OMX_COLOR_FormatYUV420Planar)); // )); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + OMXClient client; + CHECK_EQ(OK, client.connect()); + + sp<MPEG4Writer> writer = setUpWriter(client); + int64_t start = systemTime(); + CHECK_EQ(OK, writer->start()); + + int32_t nFramesCount = 0; + while (nFramesCount <= 300) { + oneBufferPass(mYuvTexWidth, mYuvTexHeight); + nFramesCount++; + } + + CHECK_EQ(OK, writer->stop()); + writer.clear(); + int64_t end = systemTime(); + client.disconnect(); +} + + +} // namespace android diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 5298f2e66927..d7d7817fc15b 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -34,7 +34,7 @@ <string name="config_systemBarComponent" translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string> <!-- Whether or not we show the number in the bar. --> - <bool name="config_statusBarShowNumber">true</bool> + <bool name="config_statusBarShowNumber">false</bool> <!-- How many icons may be shown at once in the system bar. Includes any slots that may be reused for things like IME control. --> diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index cca7947e827d..ea544456430d 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -329,6 +329,7 @@ public class RecentsPanelView extends RelativeLayout mRecentsGlowView = findViewById(R.id.recents_glow); + mRecentsScrim = (View) findViewById(R.id.recents_bg_protect); mChoreo = new Choreographer(this, mRecentsScrim, mRecentsGlowView, this); mRecentsDismissButton = findViewById(R.id.recents_dismiss_button); mRecentsDismissButton.setOnClickListener(new OnClickListener() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java index b4d2a14ecb07..d5885bb81c25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java @@ -148,6 +148,11 @@ public class NotificationRowLayout extends ViewGroup { "now sliding child %d: %s (touchY=%.1f, rowHeight=%d, count=%d)", childIdx, mSlidingChild, mInitialTouchY, mRowHeight, count)); } + + + // We need to prevent the surrounding ScrollView from intercepting us now; + // the scroll position will be locked while we swipe + requestDisallowInterceptTouchEvent(true); } } break; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index b963b131f59f..e0debf7ca994 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -388,6 +388,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { st.isHandled = false; mPreparedPanel = st; + if (st.frozenActionViewState != null) { + st.menu.restoreActionViewStates(st.frozenActionViewState); + st.frozenActionViewState = null; + } + return true; } @@ -652,7 +657,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void invalidatePanelMenu(int featureId) { PanelFeatureState st = getPanelState(featureId, true); + Bundle savedActionViewStates = null; if (st.menu != null) { + savedActionViewStates = new Bundle(); + st.menu.saveActionViewStates(savedActionViewStates); + if (savedActionViewStates.size() > 0) { + st.frozenActionViewState = savedActionViewStates; + } st.menu.clear(); } st.refreshMenuContent = true; @@ -3024,6 +3035,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { */ Bundle frozenMenuState; + /** + * Contains the state of associated action views when told to freeze. + * These are saved across invalidations. + */ + Bundle frozenActionViewState; + PanelFeatureState(int featureId) { this.featureId = featureId; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index ad6cebb60b78..ae13ab5a17a9 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -166,22 +166,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int SYSTEM_DIALOG_LAYER = 6; // toasts and the plugged-in battery thing static final int TOAST_LAYER = 7; - static final int STATUS_BAR_LAYER = 8; - static final int STATUS_BAR_PANEL_LAYER = 9; // SIM errors and unlock. Not sure if this really should be in a high layer. - static final int PRIORITY_PHONE_LAYER = 10; + static final int PRIORITY_PHONE_LAYER = 8; // like the ANR / app crashed dialogs - static final int SYSTEM_ALERT_LAYER = 11; + static final int SYSTEM_ALERT_LAYER = 9; // system-level error dialogs - static final int SYSTEM_ERROR_LAYER = 12; + static final int SYSTEM_ERROR_LAYER = 10; // on-screen keyboards and other such input method user interfaces go here. - static final int INPUT_METHOD_LAYER = 13; + static final int INPUT_METHOD_LAYER = 11; // on-screen keyboards and other such input method user interfaces go here. - static final int INPUT_METHOD_DIALOG_LAYER = 14; + static final int INPUT_METHOD_DIALOG_LAYER = 12; // the keyguard; nothing on top of these can take focus, since they are // responsible for power management when displayed. - static final int KEYGUARD_LAYER = 15; - static final int KEYGUARD_DIALOG_LAYER = 16; + static final int KEYGUARD_LAYER = 13; + static final int KEYGUARD_DIALOG_LAYER = 14; + static final int STATUS_BAR_LAYER = 15; + static final int STATUS_BAR_PANEL_LAYER = 16; // the navigation bar, if available, shows atop most things static final int NAVIGATION_BAR_LAYER = 17; // the drag layer: input for drag-and-drop is associated with this window, @@ -1703,7 +1703,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } - if (DEBUG_LAYOUT) Log.i(TAG, "mNavigationBar frame: " + navr); + if (DEBUG_LAYOUT) { + Log.i(TAG, "mNavigationBar frame: " + navr); + Log.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)", + mDockLeft, mDockTop, mDockRight, mDockBottom)); + } // apply navigation bar insets pf.left = df.left = vf.left = mDockLeft; @@ -1713,6 +1717,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { mStatusBar.computeFrameLw(pf, df, vf, vf); + // now, let's consider the navigation bar; if it exists, it must be removed from the + // available screen real estate (like an un-hideable status bar) + if (navr != null) { + if (navr.top == 0) { + // Navigation bar is vertical + if (mRestrictedScreenLeft == navr.left) { + mRestrictedScreenLeft = navr.right; + mRestrictedScreenWidth -= (navr.right - navr.left); + } else if ((mRestrictedScreenLeft+mRestrictedScreenWidth) == navr.right) { + mRestrictedScreenWidth -= (navr.right - navr.left); + } + } else { + // Navigation bar horizontal, at bottom + if ((mRestrictedScreenHeight+mRestrictedScreenTop) == navr.bottom) { + mRestrictedScreenHeight -= (navr.bottom-navr.top); + } + } + } + if (mStatusBar.isVisibleLw()) { // If the status bar is hidden, we don't want to cause // windows behind it to scroll. @@ -1745,23 +1768,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mRestrictedScreenHeight -= (r.bottom-r.top); } - if (navr != null) { - if (navr.top == 0) { - // Navigation bar is vertical - if (mRestrictedScreenLeft == navr.left) { - mRestrictedScreenLeft = navr.right; - mRestrictedScreenWidth -= (navr.right - navr.left); - } else if ((mRestrictedScreenLeft+mRestrictedScreenWidth) == navr.right) { - mRestrictedScreenWidth -= (navr.right - navr.left); - } - } else { - // Navigation bar horizontal, at bottom - if ((mRestrictedScreenHeight-mRestrictedScreenTop) == r.bottom) { - mRestrictedScreenHeight -= (navr.bottom-navr.top); - } - } - } - mContentTop = mCurTop = mDockTop = mRestrictedScreenTop; mContentBottom = mCurBottom = mDockBottom = mRestrictedScreenTop + mRestrictedScreenHeight; @@ -1873,19 +1879,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { // permission, so they have the same privileges as the status // bar itself. // - // However, they should still dodge the navigation bar if it exists. A - // straightforward way to do this is to only allow the status bar panels to - // extend to the extrema of the allowable region for the IME dock. + // However, they should still dodge the navigation bar if it exists. pf.left = df.left = hasNavBar ? mDockLeft : mUnrestrictedScreenLeft; pf.top = df.top = mUnrestrictedScreenTop; pf.right = df.right = hasNavBar - ? mDockRight + ? mRestrictedScreenLeft+mRestrictedScreenWidth : mUnrestrictedScreenLeft+mUnrestrictedScreenWidth; pf.bottom = df.bottom = hasNavBar - ? mDockBottom + ? mRestrictedScreenTop+mRestrictedScreenHeight : mUnrestrictedScreenTop+mUnrestrictedScreenHeight; + if (DEBUG_LAYOUT) { + Log.v(TAG, String.format( + "Laying out status bar window: (%d,%d - %d,%d)", + pf.left, pf.top, pf.right, pf.bottom)); + } } else { pf.left = df.left = mRestrictedScreenLeft; pf.top = df.top = mRestrictedScreenTop; @@ -1922,12 +1931,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { pf.left = df.left = cf.left = hasNavBar ? mDockLeft : mUnrestrictedScreenLeft; pf.top = df.top = cf.top = mUnrestrictedScreenTop; pf.right = df.right = cf.right = hasNavBar - ? mDockRight + ? mRestrictedScreenLeft+mRestrictedScreenWidth : mUnrestrictedScreenLeft+mUnrestrictedScreenWidth; pf.bottom = df.bottom = cf.bottom = hasNavBar - ? mDockBottom + ? mRestrictedScreenTop+mRestrictedScreenHeight : mUnrestrictedScreenTop+mUnrestrictedScreenHeight; + } else if (attrs.type == TYPE_NAVIGATION_BAR) { + // The navigation bar has Real Ultimate Power. + pf.left = df.left = mUnrestrictedScreenLeft; + pf.top = df.top = mUnrestrictedScreenTop; + pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth; + pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight; + if (DEBUG_LAYOUT) { + Log.v(TAG, String.format( + "Laying out navigation bar window: (%d,%d - %d,%d)", + pf.left, pf.top, pf.right, pf.bottom)); + } } else { pf.left = df.left = cf.left = mRestrictedScreenLeft; pf.top = df.top = cf.top = mRestrictedScreenTop; diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 482336ba9eb8..0323fe0001a3 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2954,7 +2954,7 @@ AudioFlinger::PlaybackThread::Track::Track( mStreamType = streamType; // NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack - mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * audio_bytes_per_sample(format) : sizeof(uint8_t); + mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(uint8_t); } } diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index 438883e9caf3..a679ca72daa2 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -230,8 +230,14 @@ class AppWidgetService extends IAppWidgetService.Stub pw.println(':'); pw.print(" min=("); pw.print(info.minWidth); pw.print("x"); pw.print(info.minHeight); + pw.print(") minResize=("); pw.print(info.minResizeWidth); + pw.print("x"); pw.print(info.minResizeHeight); pw.print(") updatePeriodMillis="); pw.print(info.updatePeriodMillis); + pw.print(" resizeMode="); + pw.print(info.resizeMode); + pw.print(" autoAdvanceViewId="); + pw.print(info.autoAdvanceViewId); pw.print(" initialLayout=#"); pw.print(Integer.toHexString(info.initialLayout)); pw.print(" zombie="); pw.println(p.zombie); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 5dd3a6a3a11f..79c067570982 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -1090,7 +1090,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { try { InetAddress addr = InetAddress.getByAddress(hostAddress); LinkProperties lp = tracker.getLinkProperties(); - return addRoute(lp, RouteInfo.makeHostRoute(addr)); + return addRouteToAddress(lp, addr); } catch (UnknownHostException e) {} return false; } @@ -1103,6 +1103,31 @@ public class ConnectivityService extends IConnectivityManager.Stub { return modifyRoute(p.getInterfaceName(), p, r, 0, false); } + private boolean addRouteToAddress(LinkProperties lp, InetAddress addr) { + return modifyRouteToAddress(lp, addr, true); + } + + private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr) { + return modifyRouteToAddress(lp, addr, false); + } + + private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd) { + RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), addr); + if (bestRoute == null) { + bestRoute = RouteInfo.makeHostRoute(addr); + } else { + if (bestRoute.getGateway().equals(addr)) { + // if there is no better route, add the implied hostroute for our gateway + bestRoute = RouteInfo.makeHostRoute(addr); + } else { + // if we will connect to this through another route, add a direct route + // to it's gateway + bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway()); + } + } + return modifyRoute(lp.getInterfaceName(), lp, bestRoute, 0, doAdd); + } + private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount, boolean doAdd) { if ((ifaceName == null) || (lp == null) || (r == null)) return false; @@ -1713,49 +1738,50 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private void updateRoutes(LinkProperties newLp, LinkProperties curLp, boolean isLinkDefault) { Collection<RouteInfo> routesToAdd = null; - CompareResult<InetAddress> dnsDiff = null; - + CompareResult<InetAddress> dnsDiff = new CompareResult<InetAddress>(); + CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>(); if (curLp != null) { // check for the delta between the current set and the new - CompareResult<RouteInfo> routeDiff = curLp.compareRoutes(newLp); + routeDiff = curLp.compareRoutes(newLp); dnsDiff = curLp.compareDnses(newLp); - - for (RouteInfo r : routeDiff.removed) { - if (isLinkDefault || ! r.isDefaultRoute()) { - removeRoute(curLp, r); - } - } - routesToAdd = routeDiff.added; + } else if (newLp != null) { + routeDiff.added = newLp.getRoutes(); + dnsDiff.added = newLp.getDnses(); } - if (newLp != null) { - // if we didn't get a diff from cur -> new, then just use the new - if (routesToAdd == null) { - routesToAdd = newLp.getRoutes(); + for (RouteInfo r : routeDiff.removed) { + if (isLinkDefault || ! r.isDefaultRoute()) { + removeRoute(curLp, r); } + } - for (RouteInfo r : routesToAdd) { - if (isLinkDefault || ! r.isDefaultRoute()) { - addRoute(newLp, r); - } + for (RouteInfo r : routeDiff.added) { + if (isLinkDefault || ! r.isDefaultRoute()) { + addRoute(newLp, r); } } if (!isLinkDefault) { // handle DNS routes - Collection<InetAddress> dnsToAdd = null; - if (dnsDiff != null) { - dnsToAdd = dnsDiff.added; - for (InetAddress dnsAddress : dnsDiff.removed) { - removeRoute(curLp, RouteInfo.makeHostRoute(dnsAddress)); + if (routeDiff.removed.size() == 0 && routeDiff.added.size() == 0) { + // no change in routes, check for change in dns themselves + for (InetAddress oldDns : dnsDiff.removed) { + removeRouteToAddress(curLp, oldDns); } - } - if (newLp != null) { - if (dnsToAdd == null) { - dnsToAdd = newLp.getDnses(); + for (InetAddress newDns : dnsDiff.added) { + addRouteToAddress(newLp, newDns); } - for(InetAddress dnsAddress : dnsToAdd) { - addRoute(newLp, RouteInfo.makeHostRoute(dnsAddress)); + } else { + // routes changed - remove all old dns entries and add new + if (curLp != null) { + for (InetAddress oldDns : curLp.getDnses()) { + removeRouteToAddress(curLp, oldDns); + } + } + if (newLp != null) { + for (InetAddress newDns : newLp.getDnses()) { + addRouteToAddress(newLp, newDns); + } } } } @@ -2236,6 +2262,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + public int setUsbTethering(boolean enable) { + enforceTetherAccessPermission(); + if (isTetheringSupported()) { + return mTethering.setUsbTethering(enable); + } else { + return ConnectivityManager.TETHER_ERROR_UNSUPPORTED; + } + } + // TODO - move iface listing, queries, etc to new module // javadoc from interface public String[] getTetherableIfaces() { diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 73996795205e..73d790a073c0 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -166,7 +166,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private final KeyguardManager mKeyguardManager; private final Notification mImeSwitcherNotification; private final PendingIntent mImeSwitchPendingIntent; - private final boolean mShowOngoingImeSwitcherForPhones; + private boolean mShowOngoingImeSwitcherForPhones; private boolean mNotificationShown; class SessionState { @@ -538,8 +538,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mImeSwitcherNotification.vibrate = null; Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER); mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); - mShowOngoingImeSwitcherForPhones = mRes.getBoolean( - com.android.internal.R.bool.show_ongoing_ime_switcher); + + mShowOngoingImeSwitcherForPhones = false; synchronized (mMethodMap) { mFileManager = new InputMethodFileManager(mMethodMap); @@ -612,6 +612,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub synchronized (mMethodMap) { if (!mSystemReady) { mSystemReady = true; + mShowOngoingImeSwitcherForPhones = mRes.getBoolean( + com.android.internal.R.bool.show_ongoing_ime_switcher); try { startInputInnerLocked(); } catch (RuntimeException e) { @@ -1125,13 +1127,21 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mBackDisposition = backDisposition; mStatusBar.setImeWindowStatus(token, vis, backDisposition); final boolean iconVisibility = (vis & InputMethodService.IME_ACTIVE) != 0; - if (iconVisibility && needsToShowImeSwitchOngoingNotification()) { + final InputMethodInfo imi = mMethodMap.get(mCurMethodId); + if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) { final PackageManager pm = mContext.getPackageManager(); - final CharSequence label = mMethodMap.get(mCurMethodId).loadLabel(pm); final CharSequence title = mRes.getText( com.android.internal.R.string.select_input_method); + final CharSequence imiLabel = imi.loadLabel(pm); + final CharSequence summary = mCurrentSubtype != null + ? TextUtils.concat(mCurrentSubtype.getDisplayName(mContext, + imi.getPackageName(), imi.getServiceInfo().applicationInfo), + (TextUtils.isEmpty(imiLabel) ? + "" : " (" + imiLabel + ")")) + : imiLabel; + mImeSwitcherNotification.setLatestEventInfo( - mContext, title, label, mImeSwitchPendingIntent); + mContext, title, summary, mImeSwitchPendingIntent); mNotificationManager.notify( com.android.internal.R.string.select_input_method, mImeSwitcherNotification); diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 41e8a3148223..2366fcb41c8d 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -93,6 +93,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { private static final String KEY_TX = "tx_bytes"; class NetdResponseCode { + /* Keep in sync with system/netd/ResponseCode.h */ public static final int InterfaceListResult = 110; public static final int TetherInterfaceListResult = 111; public static final int TetherDnsFwdTgtListResult = 112; @@ -108,6 +109,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { public static final int InterfaceTxThrottleResult = 219; public static final int InterfaceChange = 600; + public static final int BandwidthControl = 601; } /** @@ -265,6 +267,20 @@ class NetworkManagementService extends INetworkManagementService.Stub { } /** + * Notify our observers of a limit reached. + */ + private void notifyLimitReached(String limitName, String iface) { + for (INetworkManagementEventObserver obs : mObservers) { + try { + obs.limitReached(limitName, iface); + Slog.d(TAG, "Observer notified limit reached for " + limitName + " " + iface); + } catch (Exception ex) { + Slog.w(TAG, "Observer notifier failed", ex); + } + } + } + + /** * Let us know the daemon is connected */ protected void onConnected() { @@ -286,33 +302,52 @@ class NetworkManagementService extends INetworkManagementService.Stub { }.start(); } public boolean onEvent(int code, String raw, String[] cooked) { - if (code == NetdResponseCode.InterfaceChange) { - /* - * a network interface change occured - * Format: "NNN Iface added <name>" - * "NNN Iface removed <name>" - * "NNN Iface changed <name> <up/down>" - * "NNN Iface linkstatus <name> <up/down>" - */ - if (cooked.length < 4 || !cooked[1].equals("Iface")) { + switch (code) { + case NetdResponseCode.InterfaceChange: + /* + * a network interface change occured + * Format: "NNN Iface added <name>" + * "NNN Iface removed <name>" + * "NNN Iface changed <name> <up/down>" + * "NNN Iface linkstatus <name> <up/down>" + */ + if (cooked.length < 4 || !cooked[1].equals("Iface")) { + throw new IllegalStateException( + String.format("Invalid event from daemon (%s)", raw)); + } + if (cooked[2].equals("added")) { + notifyInterfaceAdded(cooked[3]); + return true; + } else if (cooked[2].equals("removed")) { + notifyInterfaceRemoved(cooked[3]); + return true; + } else if (cooked[2].equals("changed") && cooked.length == 5) { + notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up")); + return true; + } else if (cooked[2].equals("linkstate") && cooked.length == 5) { + notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up")); + return true; + } throw new IllegalStateException( String.format("Invalid event from daemon (%s)", raw)); - } - if (cooked[2].equals("added")) { - notifyInterfaceAdded(cooked[3]); - return true; - } else if (cooked[2].equals("removed")) { - notifyInterfaceRemoved(cooked[3]); - return true; - } else if (cooked[2].equals("changed") && cooked.length == 5) { - notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up")); - return true; - } else if (cooked[2].equals("linkstate") && cooked.length == 5) { - notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up")); - return true; - } - throw new IllegalStateException( - String.format("Invalid event from daemon (%s)", raw)); + // break; + case NetdResponseCode.BandwidthControl: + /* + * Bandwidth control needs some attention + * Format: "NNN limit alert <alertName> <ifaceName>" + */ + if (cooked.length < 5 || !cooked[1].equals("limit")) { + throw new IllegalStateException( + String.format("Invalid event from daemon (%s)", raw)); + } + if (cooked[2].equals("alert")) { + notifyLimitReached(cooked[3], cooked[4]); + return true; + } + throw new IllegalStateException( + String.format("Invalid event from daemon (%s)", raw)); + // break; + default: break; } return false; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 8c7e279b7132..766659153821 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -230,6 +230,7 @@ class ServerThread extends Thread { WallpaperManagerService wallpaper = null; LocationManagerService location = null; CountryDetectorService countryDetector = null; + TextServicesManagerService tsms = null; if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { try { @@ -273,6 +274,14 @@ class ServerThread extends Thread { } try { + Slog.i(TAG, "Text Service Manager Service"); + tsms = new TextServicesManagerService(context); + ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting Text Service Manager Service", e); + } + + try { Slog.i(TAG, "NetworkStats Service"); networkStats = new NetworkStatsService(context, networkManagement, alarm); ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats); @@ -538,6 +547,7 @@ class ServerThread extends Thread { final LocationManagerService locationF = location; final CountryDetectorService countryDetectorF = countryDetector; final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; + final TextServicesManagerService textServiceManagerServiceF = tsms; // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state @@ -571,6 +581,7 @@ class ServerThread extends Thread { if (countryDetectorF != null) countryDetectorF.systemReady(); if (throttleF != null) throttleF.systemReady(); if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady(); + if (textServiceManagerServiceF != null) textServiceManagerServiceF.systemReady(); } }); diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java new file mode 100644 index 000000000000..4a0c837df0c6 --- /dev/null +++ b/services/java/com/android/server/TextServicesManagerService.java @@ -0,0 +1,346 @@ +/* + * 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.server; + +import com.android.internal.content.PackageMonitor; +import com.android.internal.textservice.ISpellCheckerService; +import com.android.internal.textservice.ISpellCheckerSession; +import com.android.internal.textservice.ISpellCheckerSessionListener; +import com.android.internal.textservice.ITextServicesManager; +import com.android.internal.textservice.ITextServicesSessionListener; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemClock; +import android.provider.Settings; +import android.text.TextUtils; +import android.service.textservice.SpellCheckerService; +import android.util.Log; +import android.util.Slog; +import android.view.textservice.SpellCheckerInfo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +public class TextServicesManagerService extends ITextServicesManager.Stub { + private static final String TAG = TextServicesManagerService.class.getSimpleName(); + private static final boolean DBG = false; + + private final Context mContext; + private boolean mSystemReady; + private final TextServicesMonitor mMonitor; + private final HashMap<String, SpellCheckerInfo> mSpellCheckerMap = + new HashMap<String, SpellCheckerInfo>(); + private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<SpellCheckerInfo>(); + private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = + new HashMap<String, SpellCheckerBindGroup>(); + + public void systemReady() { + if (!mSystemReady) { + mSystemReady = true; + } + } + + public TextServicesManagerService(Context context) { + mSystemReady = false; + mContext = context; + mMonitor = new TextServicesMonitor(); + mMonitor.register(context, true); + synchronized (mSpellCheckerMap) { + buildSpellCheckerMapLocked(context, mSpellCheckerList, mSpellCheckerMap); + } + } + + private class TextServicesMonitor extends PackageMonitor { + @Override + public void onSomePackagesChanged() { + synchronized (mSpellCheckerMap) { + buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap); + // TODO: Update for each locale + SpellCheckerInfo sci = getCurrentSpellChecker(null); + if (sci == null) { + sci = findAvailSpellCheckerLocked(null, null); + if (sci == null) return; + // Set the current spell checker if there is one or more spell checkers + // available. In this case, "sci" is the first one in the available spell + // checkers. + setCurrentSpellChecker(sci); + } + final String packageName = sci.getPackageName(); + final int change = isPackageDisappearing(packageName); + if (change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE) { + // Package disappearing + setCurrentSpellChecker(findAvailSpellCheckerLocked(null, packageName)); + } else if (isPackageModified(packageName)) { + // Package modified + setCurrentSpellChecker(findAvailSpellCheckerLocked(null, packageName)); + } + } + } + } + + private static void buildSpellCheckerMapLocked(Context context, + ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map) { + list.clear(); + map.clear(); + final PackageManager pm = context.getPackageManager(); + List<ResolveInfo> services = pm.queryIntentServices( + new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA); + final int N = services.size(); + for (int i = 0; i < N; ++i) { + final ResolveInfo ri = services.get(i); + final ServiceInfo si = ri.serviceInfo; + final ComponentName compName = new ComponentName(si.packageName, si.name); + if (!android.Manifest.permission.BIND_TEXT_SERVICE.equals(si.permission)) { + Slog.w(TAG, "Skipping text service " + compName + + ": it does not require the permission " + + android.Manifest.permission.BIND_TEXT_SERVICE); + continue; + } + if (DBG) Slog.d(TAG, "Add: " + compName); + final SpellCheckerInfo sci = new SpellCheckerInfo(context, ri); + list.add(sci); + map.put(sci.getId(), sci); + } + } + + // TODO: find an appropriate spell checker for specified locale + private SpellCheckerInfo findAvailSpellCheckerLocked(String locale, String prefPackage) { + final int spellCheckersCount = mSpellCheckerList.size(); + if (spellCheckersCount == 0) { + Slog.w(TAG, "no available spell checker services found"); + return null; + } + if (prefPackage != null) { + for (int i = 0; i < spellCheckersCount; ++i) { + final SpellCheckerInfo sci = mSpellCheckerList.get(i); + if (prefPackage.equals(sci.getPackageName())) { + return sci; + } + } + } + if (spellCheckersCount > 1) { + Slog.w(TAG, "more than one spell checker service found, picking first"); + } + return mSpellCheckerList.get(0); + } + + // TODO: Save SpellCheckerService by supported languages. Currently only one spell + // checker is saved. + @Override + public SpellCheckerInfo getCurrentSpellChecker(String locale) { + synchronized (mSpellCheckerMap) { + final String curSpellCheckerId = + Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.SPELL_CHECKER_SERVICE); + if (TextUtils.isEmpty(curSpellCheckerId)) { + return null; + } + return mSpellCheckerMap.get(curSpellCheckerId); + } + } + + @Override + public void getSpellCheckerService(SpellCheckerInfo info, String locale, + ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener) { + if (!mSystemReady) { + return; + } + if (info == null || tsListener == null) { + Slog.e(TAG, "getSpellCheckerService: Invalid input."); + return; + } + final String sciId = info.getId(); + synchronized(mSpellCheckerMap) { + if (!mSpellCheckerMap.containsKey(sciId)) { + return; + } + if (mSpellCheckerBindGroups.containsKey(sciId)) { + mSpellCheckerBindGroups.get(sciId).addListener(tsListener, locale, scListener); + return; + } + final InternalServiceConnection connection = new InternalServiceConnection( + sciId, locale, scListener); + final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE); + serviceIntent.setComponent(info.getComponent()); + if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) { + Slog.e(TAG, "Failed to get a spell checker service."); + return; + } + final SpellCheckerBindGroup group = new SpellCheckerBindGroup( + connection, tsListener, locale, scListener); + mSpellCheckerBindGroups.put(sciId, group); + } + return; + } + + @Override + public void finishSpellCheckerService(ISpellCheckerSessionListener listener) { + synchronized(mSpellCheckerMap) { + for (SpellCheckerBindGroup group : mSpellCheckerBindGroups.values()) { + if (group == null) continue; + group.removeListener(listener); + } + } + } + + private void setCurrentSpellChecker(SpellCheckerInfo sci) { + if (sci == null || mSpellCheckerMap.containsKey(sci.getId())) return; + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.SPELL_CHECKER_SERVICE, sci == null ? "" : sci.getId()); + } + + // SpellCheckerBindGroup contains active text service session listeners. + // If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from + // mSpellCheckerBindGroups + private class SpellCheckerBindGroup { + final InternalServiceConnection mInternalConnection; + final ArrayList<InternalDeathRecipient> mListeners = + new ArrayList<InternalDeathRecipient>(); + + public SpellCheckerBindGroup(InternalServiceConnection connection, + ITextServicesSessionListener listener, String locale, + ISpellCheckerSessionListener scListener) { + mInternalConnection = connection; + addListener(listener, locale, scListener); + } + + public void onServiceConnected(ISpellCheckerService spellChecker) { + synchronized(mSpellCheckerMap) { + for (InternalDeathRecipient listener : mListeners) { + try { + final ISpellCheckerSession session = spellChecker.getISpellCheckerSession( + listener.mScLocale, listener.mScListener); + listener.mTsListener.onServiceConnected(session); + } catch (RemoteException e) { + } + } + } + } + + public void addListener(ITextServicesSessionListener tsListener, String locale, + ISpellCheckerSessionListener scListener) { + synchronized(mSpellCheckerMap) { + try { + final int size = mListeners.size(); + for (int i = 0; i < size; ++i) { + if (mListeners.get(i).hasSpellCheckerListener(scListener)) { + // do not add the lister if the group already contains this. + return; + } + } + final InternalDeathRecipient recipient = new InternalDeathRecipient( + this, tsListener, locale, scListener); + scListener.asBinder().linkToDeath(recipient, 0); + mListeners.add(new InternalDeathRecipient( + this, tsListener, locale, scListener)); + } catch(RemoteException e) { + // do nothing + } + cleanLocked(); + } + } + + public void removeListener(ISpellCheckerSessionListener listener) { + synchronized(mSpellCheckerMap) { + final int size = mListeners.size(); + final ArrayList<InternalDeathRecipient> removeList = + new ArrayList<InternalDeathRecipient>(); + for (int i = 0; i < size; ++i) { + final InternalDeathRecipient tempRecipient = mListeners.get(i); + if(tempRecipient.hasSpellCheckerListener(listener)) { + removeList.add(tempRecipient); + } + } + final int removeSize = removeList.size(); + for (int i = 0; i < removeSize; ++i) { + mListeners.remove(removeList.get(i)); + } + cleanLocked(); + } + } + + private void cleanLocked() { + if (mListeners.isEmpty()) { + mSpellCheckerBindGroups.remove(this); + // Unbind service when there is no active clients. + mContext.unbindService(mInternalConnection); + } + } + } + + private class InternalServiceConnection implements ServiceConnection { + private final ISpellCheckerSessionListener mListener; + private final String mSciId; + private final String mLocale; + public InternalServiceConnection( + String id, String locale, ISpellCheckerSessionListener listener) { + mSciId = id; + mLocale = locale; + mListener = listener; + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized(mSpellCheckerMap) { + ISpellCheckerService spellChecker = ISpellCheckerService.Stub.asInterface(service); + final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId); + if (group != null) { + group.onServiceConnected(spellChecker); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mSpellCheckerBindGroups.remove(mSciId); + } + } + + private class InternalDeathRecipient implements IBinder.DeathRecipient { + public final ITextServicesSessionListener mTsListener; + public final ISpellCheckerSessionListener mScListener; + public final String mScLocale; + private final SpellCheckerBindGroup mGroup; + public InternalDeathRecipient(SpellCheckerBindGroup group, + ITextServicesSessionListener tsListener, String scLocale, + ISpellCheckerSessionListener scListener) { + mTsListener = tsListener; + mScListener = scListener; + mScLocale = scLocale; + mGroup = group; + } + + public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) { + return mScListener.equals(listener); + } + + @Override + public void binderDied() { + mGroup.removeListener(mScListener); + } + } +} diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java index b8890aa2aa8e..cd649ce4c554 100644 --- a/services/java/com/android/server/ThrottleService.java +++ b/services/java/com/android/server/ThrottleService.java @@ -194,6 +194,7 @@ public class ThrottleService extends IThrottleManager.Stub { } public void interfaceRemoved(String iface) {} + public void limitReached(String limitName, String iface) {} } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 711255327499..f9f63b1d1bd6 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -1461,7 +1461,7 @@ public class WifiService extends IWifiManager.Stub { if (mMulticasters.size() != 0) { return; } else { - mWifiStateMachine.startPacketFiltering(); + mWifiStateMachine.startFilteringMulticastV4Packets(); } } } @@ -1472,11 +1472,11 @@ public class WifiService extends IWifiManager.Stub { synchronized (mMulticasters) { mMulticastEnabled++; mMulticasters.add(new Multicaster(tag, binder)); - // Note that we could call stopPacketFiltering only when + // Note that we could call stopFilteringMulticastV4Packets only when // our new size == 1 (first call), but this function won't // be called often and by making the stopPacket call each // time we're less fragile and self-healing. - mWifiStateMachine.stopPacketFiltering(); + mWifiStateMachine.stopFilteringMulticastV4Packets(); } int uid = Binder.getCallingUid(); @@ -1513,7 +1513,7 @@ public class WifiService extends IWifiManager.Stub { removed.unlinkDeathRecipient(); } if (mMulticasters.size() == 0) { - mWifiStateMachine.startPacketFiltering(); + mWifiStateMachine.startFilteringMulticastV4Packets(); } Long ident = Binder.clearCallingIdentity(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 0924b862c251..3389f339a7f2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -3628,6 +3628,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; app.forcingToForeground = null; app.foregroundServices = false; + app.hasShownUi = false; app.debugging = false; mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); @@ -9218,6 +9219,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.forcingToForeground = null; app.foregroundServices = false; app.foregroundActivities = false; + app.hasShownUi = false; killServicesLocked(app, true); @@ -9331,8 +9333,6 @@ public final class ActivityManagerService extends ActivityManagerNative // This app is persistent, so we need to keep its record around. // If it is not already on the pending app list, add it there // and start a new process for it. - app.forcingToForeground = null; - app.foregroundServices = false; if (mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); restart = true; @@ -12728,21 +12728,31 @@ public final class ActivityManagerService extends ActivityManagerNative while (jt.hasNext() && adj > FOREGROUND_APP_ADJ) { ServiceRecord s = jt.next(); if (s.startRequested) { - if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) { - // This service has seen some activity within - // recent memory, so we will keep its process ahead - // of the background processes. + if (app.hasShownUi) { + // If this process has shown some UI, let it immediately + // go to the LRU list because it may be pretty heavy with + // UI stuff. We'll tag it with a label just to help + // debug and understand what is going on. if (adj > SECONDARY_SERVER_ADJ) { - adj = SECONDARY_SERVER_ADJ; - app.adjType = "started-services"; - app.hidden = false; + app.adjType = "started-bg-ui-services"; + } + } else { + if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) { + // This service has seen some activity within + // recent memory, so we will keep its process ahead + // of the background processes. + if (adj > SECONDARY_SERVER_ADJ) { + adj = SECONDARY_SERVER_ADJ; + app.adjType = "started-services"; + app.hidden = false; + } + } + // If we have let the service slide into the background + // state, still have some text describing what it is doing + // even though the service no longer has an impact. + if (adj > SECONDARY_SERVER_ADJ) { + app.adjType = "started-bg-services"; } - } - // If we have let the service slide into the background - // state, still have some text describing what it is doing - // even though the service no longer has an impact. - if (adj > SECONDARY_SERVER_ADJ) { - app.adjType = "started-bg-services"; } // Don't kill this process because it is doing work; it // has said it is doing work. @@ -13351,15 +13361,15 @@ public final class ActivityManagerService extends ActivityManagerNative break; } } - } else if (app.curAdj >= PERCEPTIBLE_APP_ADJ) { - if (app.trimMemoryLevel < ComponentCallbacks.TRIM_MEMORY_INVISIBLE + } else if (app.curAdj == HEAVY_WEIGHT_APP_ADJ) { + if (app.trimMemoryLevel < ComponentCallbacks.TRIM_MEMORY_BACKGROUND && app.thread != null) { try { - app.thread.scheduleTrimMemory(ComponentCallbacks.TRIM_MEMORY_INVISIBLE); + app.thread.scheduleTrimMemory(ComponentCallbacks.TRIM_MEMORY_BACKGROUND); } catch (RemoteException e) { } } - app.trimMemoryLevel = ComponentCallbacks.TRIM_MEMORY_INVISIBLE; + app.trimMemoryLevel = ComponentCallbacks.TRIM_MEMORY_BACKGROUND; } else { app.trimMemoryLevel = 0; } @@ -13442,7 +13452,7 @@ public final class ActivityManagerService extends ActivityManagerNative } public boolean profileControl(String process, boolean start, - String path, ParcelFileDescriptor fd) throws RemoteException { + String path, ParcelFileDescriptor fd, int profileType) throws RemoteException { try { synchronized (this) { @@ -13487,7 +13497,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - proc.thread.profilerControl(start, path, fd); + proc.thread.profilerControl(start, path, fd, profileType); fd = null; return true; } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 0d89081b0813..cc58eafe8872 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -559,6 +559,7 @@ final class ActivityStack { r.forceNewConfig = false; showAskCompatModeDialogLocked(r); r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo); + app.hasShownUi = true; app.thread.scheduleLaunchActivity(new Intent(r.intent), r, System.identityHashCode(r), r.info, r.compat, r.icicle, results, newIntents, !andResume, diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 9e597aa93d18..5b593638f31a 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -66,6 +66,7 @@ class ProcessRecord { boolean setIsForeground; // Running foreground UI when last set? boolean foregroundServices; // Running any services that are foreground? boolean foregroundActivities; // Running any activities that are foreground? + boolean hasShownUi; // Has UI been shown in this process since it was started? boolean bad; // True if disabled in the bad process list boolean killedBackground; // True when proc has been killed due to too many bg String waitingToKill; // Process is waiting to be killed when in the bg; reason @@ -185,6 +186,7 @@ class ProcessRecord { pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup); pw.print(" setSchedGroup="); pw.print(setSchedGroup); pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); + pw.print(" hasShownUi="); pw.println(hasShownUi); pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground); pw.print(" foregroundServices="); pw.print(foregroundServices); pw.print(" forcingToForeground="); pw.println(forcingToForeground); diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index d7d4b0346795..7a5a641f62e8 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -36,7 +36,6 @@ import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkUtils; import android.os.Binder; -import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -76,10 +75,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private final static String TAG = "Tethering"; private final static boolean DEBUG = true; - private boolean mBooted = false; - //used to remember if we got connected before boot finished - private boolean mDeferedUsbConnection = false; - // TODO - remove both of these - should be part of interface inspection/selection stuff private String[] mTetherableUsbRegexs; private String[] mTetherableWifiRegexs; @@ -126,10 +121,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private Notification mTetheredNotification; - // whether we can tether is the && of these two - they come in as separate - // broadcasts so track them so we can decide what to do when either changes - private boolean mUsbMassStorageOff; // track the status of USB Mass Storage - private boolean mUsbConnected; // track the status of USB connection + private boolean mRndisEnabled; // track the RNDIS function enabled state + private boolean mUsbTetherRequested; // true if USB tethering should be started + // when RNDIS is enabled public Tethering(Context context, INetworkManagementService nmService, Looper looper) { mContext = context; @@ -149,7 +143,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_STATE); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - filter.addAction(Intent.ACTION_BOOT_COMPLETED); mContext.registerReceiver(mStateReceiver, filter); filter = new IntentFilter(); @@ -158,9 +151,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { filter.addDataScheme("file"); mContext.registerReceiver(mStateReceiver, filter); - mUsbMassStorageOff = !Environment.MEDIA_SHARED.equals( - Environment.getExternalStorageState()); - mDhcpRange = context.getResources().getStringArray( com.android.internal.R.array.config_tether_dhcp_range); if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) { @@ -243,6 +233,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } return false; } + public void interfaceAdded(String iface) { boolean found = false; boolean usb = false; @@ -288,6 +279,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } + public void limitReached(String limitName, String iface) {} + public int tether(String iface) { Log.d(TAG, "Tethering " + iface); TetherInterfaceSM sm = null; @@ -454,47 +447,28 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } - private void updateUsbStatus() { - boolean enable = mUsbConnected && mUsbMassStorageOff; - - if (mBooted) { - enableUsbIfaces(enable); - } - } - private class StateReceiver extends BroadcastReceiver { public void onReceive(Context content, Intent intent) { String action = intent.getAction(); if (action.equals(UsbManager.ACTION_USB_STATE)) { - mUsbConnected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED); - updateUsbStatus(); - } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) { - mUsbMassStorageOff = false; - updateUsbStatus(); - } - else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) { - mUsbMassStorageOff = true; - updateUsbStatus(); + synchronized (Tethering.this) { + boolean usbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false); + mRndisEnabled = intent.getBooleanExtra(UsbManager.USB_FUNCTION_RNDIS, false); + // start tethering if we have a request pending + if (usbConnected && mRndisEnabled && mUsbTetherRequested) { + tetherUsb(true); + } + mUsbTetherRequested = false; + } } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { if (DEBUG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION"); mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED); - } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { - mBooted = true; - updateUsbStatus(); } } } - // used on cable insert/remove - private void enableUsbIfaces(boolean enable) { - // add/remove USB interfaces when USB is connected/disconnected - for (String intf : mTetherableUsbRegexs) { - if (enable) { - interfaceAdded(intf); - } else { - interfaceRemoved(intf); - } - } + private void tetherUsb(boolean enable) { + if (DEBUG) Log.d(TAG, "tetherUsb " + enable); String[] ifaces = new String[0]; try { @@ -505,83 +479,50 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } for (String iface : ifaces) { if (isUsb(iface)) { - if (enable) { - interfaceAdded(iface); - } else { - interfaceRemoved(iface); + int result = (enable ? tether(iface) : untether(iface)); + if (result == ConnectivityManager.TETHER_ERROR_NO_ERROR) { + return; } } } - } - - // toggled when we enter/leave the fully tethered state - private boolean enableUsbRndis(boolean enabled) { - if (DEBUG) Log.d(TAG, "enableUsbRndis(" + enabled + ")"); - - UsbManager usbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE); - if (usbManager == null) { - Log.d(TAG, "could not get UsbManager"); - return false; - } - try { - if (enabled) { - usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false); - } else { - usbManager.setCurrentFunction(null, false); - } - } catch (Exception e) { - Log.e(TAG, "Error toggling usb RNDIS", e); - return false; - } - return true; + Log.e(TAG, "unable start or stop USB tethering"); } // configured when we start tethering and unconfig'd on error or conclusion private boolean configureUsbIface(boolean enabled) { if (DEBUG) Log.d(TAG, "configureUsbIface(" + enabled + ")"); - if (enabled) { - // must enable RNDIS first to create the interface - enableUsbRndis(enabled); - } - + // toggle the USB interfaces + String[] ifaces = new String[0]; try { - // bring toggle the interfaces - String[] ifaces = new String[0]; - try { - ifaces = mNMService.listInterfaces(); - } catch (Exception e) { - Log.e(TAG, "Error listing Interfaces", e); - return false; - } - for (String iface : ifaces) { - if (isUsb(iface)) { - InterfaceConfiguration ifcg = null; - try { - ifcg = mNMService.getInterfaceConfig(iface); - if (ifcg != null) { - InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR); - ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH); - if (enabled) { - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); - } else { - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down"); - } - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); - ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); - mNMService.setInterfaceConfig(iface, ifcg); + ifaces = mNMService.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces", e); + return false; + } + for (String iface : ifaces) { + if (isUsb(iface)) { + InterfaceConfiguration ifcg = null; + try { + ifcg = mNMService.getInterfaceConfig(iface); + if (ifcg != null) { + InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR); + ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH); + if (enabled) { + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); + } else { + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down"); } - } catch (Exception e) { - Log.e(TAG, "Error configuring interface " + iface, e); - return false; + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); + mNMService.setInterfaceConfig(iface, ifcg); } + } catch (Exception e) { + Log.e(TAG, "Error configuring interface " + iface, e); + return false; } - } - } finally { - if (!enabled) { - enableUsbRndis(false); } - } + } return true; } @@ -598,6 +539,28 @@ public class Tethering extends INetworkManagementEventObserver.Stub { return mTetherableBluetoothRegexs; } + public int setUsbTethering(boolean enable) { + UsbManager usbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE); + + synchronized (this) { + if (enable) { + if (mRndisEnabled) { + tetherUsb(true); + } else { + mUsbTetherRequested = true; + usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false); + } + } else { + tetherUsb(false); + if (mRndisEnabled) { + usbManager.setCurrentFunction(null, false); + } + mUsbTetherRequested = false; + } + } + return ConnectivityManager.TETHER_ERROR_NO_ERROR; + } + public int[] getUpstreamIfaceTypes() { int values[] = new int[mUpstreamIfaceTypes.size()]; Iterator<Integer> iterator = mUpstreamIfaceTypes.iterator(); diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index 05e95a7d3d89..9cb772eb315a 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -248,6 +248,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } } + public void limitReached(String limitName, String iface) {} + private void showNotification(VpnConfig config, String label, Bitmap icon) { NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); @@ -394,7 +396,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { if (mTimer == -1) { mTimer = now; Thread.sleep(1); - } else if (now - mTimer <= 30000) { + } else if (now - mTimer <= 60000) { Thread.sleep(yield ? 200 : 1); } else { mInfo.state = LegacyVpnInfo.STATE_TIMEOUT; diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index c80cd0a87115..f183f83cfba0 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -19,6 +19,7 @@ package com.android.server.usb; import android.app.PendingIntent; import android.app.Notification; import android.app.NotificationManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -80,6 +81,7 @@ public class UsbDeviceManager { private static final int MSG_ENABLE_ADB = 1; private static final int MSG_SET_CURRENT_FUNCTION = 2; private static final int MSG_SYSTEM_READY = 3; + private static final int MSG_BOOT_COMPLETED = 4; // Delay for debouncing USB disconnects. // We often get rapid connect/disconnect events when enabling USB functions, @@ -87,7 +89,7 @@ public class UsbDeviceManager { private static final int UPDATE_DELAY = 1000; private UsbHandler mHandler; - private boolean mSystemReady; + private boolean mBootCompleted; private final Context mContext; private final ContentResolver mContentResolver; @@ -141,10 +143,15 @@ public class UsbDeviceManager { Process.THREAD_PRIORITY_BACKGROUND); thread.start(); mHandler = new UsbHandler(thread.getLooper()); + + if (nativeIsStartRequested()) { + if (DEBUG) Slog.d(TAG, "accessory attached at boot"); + setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false); + } } public void systemReady() { - mSystemReady = true; + if (DEBUG) Slog.d(TAG, "systemReady"); mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); @@ -236,15 +243,22 @@ public class UsbDeviceManager { private String mCurrentFunctions; private String mDefaultFunctions; private UsbAccessory mCurrentAccessory; - private boolean mDeferAccessoryAttached; private int mUsbNotificationId; private boolean mAdbNotificationShown; + private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + if (DEBUG) Slog.d(TAG, "boot completed"); + mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); + } + }; + private static final int NOTIFICATION_NONE = 0; private static final int NOTIFICATION_MTP = 1; private static final int NOTIFICATION_PTP = 2; private static final int NOTIFICATION_INSTALLER = 3; - private static final int NOTIFICATION_ADB = 4; + private static final int NOTIFICATION_ACCESSORY = 4; + private static final int NOTIFICATION_ADB = 5; public UsbHandler(Looper looper) { super(looper); @@ -285,6 +299,9 @@ public class UsbDeviceManager { // Watch for USB configuration changes mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH); + + mContext.registerReceiver(mBootCompletedReceiver, + new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); } catch (Exception e) { Slog.e(TAG, "Error initializing UsbHandler", e); } @@ -406,11 +423,9 @@ public class UsbDeviceManager { mCurrentAccessory = new UsbAccessory(strings); Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); // defer accessoryAttached if system is not ready - if (mSystemReady) { + if (mBootCompleted) { mSettingsManager.accessoryAttached(mCurrentAccessory); - } else { - mDeferAccessoryAttached = true; - } + } // else handle in mBootCompletedReceiver } else { Slog.e(TAG, "nativeGetAccessoryStrings failed"); } @@ -421,7 +436,7 @@ public class UsbDeviceManager { setEnabledFunctions(mDefaultFunctions); if (mCurrentAccessory != null) { - if (mSystemReady) { + if (mBootCompleted) { mSettingsManager.accessoryDetached(mCurrentAccessory); } mCurrentAccessory = null; @@ -463,7 +478,7 @@ public class UsbDeviceManager { // restore defaults when USB is disconnected doSetCurrentFunctions(mDefaultFunctions); } - if (mSystemReady) { + if (mBootCompleted) { updateUsbState(); } break; @@ -497,7 +512,10 @@ public class UsbDeviceManager { updateUsbNotification(); updateAdbNotification(); updateUsbState(); - if (mCurrentAccessory != null && mDeferAccessoryAttached) { + break; + case MSG_BOOT_COMPLETED: + mBootCompleted = true; + if (mCurrentAccessory != null) { mSettingsManager.accessoryAttached(mCurrentAccessory); } break; @@ -527,6 +545,10 @@ public class UsbDeviceManager { title = r.getText( com.android.internal.R.string.usb_cd_installer_notification_title); id = NOTIFICATION_INSTALLER; + } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { + title = r.getText( + com.android.internal.R.string.usb_accessory_notification_title); + id = NOTIFICATION_ACCESSORY; } else { Slog.e(TAG, "No known USB function in updateUsbNotification"); } @@ -671,4 +693,5 @@ public class UsbDeviceManager { private native String[] nativeGetAccessoryStrings(); private native ParcelFileDescriptor nativeOpenAccessory(); + private native boolean nativeIsStartRequested(); } diff --git a/services/java/com/android/server/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java index d8fd7fe84a73..36f5dcb97056 100644 --- a/services/java/com/android/server/wm/BlackFrame.java +++ b/services/java/com/android/server/wm/BlackFrame.java @@ -32,10 +32,12 @@ public class BlackFrame { final int top; final Surface surface; - BlackSurface(SurfaceSession session, int layer, int l, int t, int w, int h) + BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b) throws Surface.OutOfResourcesException { left = l; top = t; + int w = r-l; + int h = b-t; surface = new Surface(session, 0, "BlackSurface", -1, w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); if (WindowManagerService.SHOW_TRANSACTIONS || diff --git a/services/jni/com_android_server_UsbDeviceManager.cpp b/services/jni/com_android_server_UsbDeviceManager.cpp index 69541714260e..40f0dbd81430 100644 --- a/services/jni/com_android_server_UsbDeviceManager.cpp +++ b/services/jni/com_android_server_UsbDeviceManager.cpp @@ -99,11 +99,26 @@ static jobject android_server_UsbDeviceManager_openAccessory(JNIEnv *env, jobjec gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); } +static jboolean android_server_UsbDeviceManager_isStartRequested(JNIEnv *env, jobject thiz) +{ + int fd = open(DRIVER_NAME, O_RDWR); + if (fd < 0) { + LOGE("could not open %s", DRIVER_NAME); + return false; + } + int result = ioctl(fd, ACCESSORY_IS_START_REQUESTED); + close(fd); + return (result == 1); +} + + static JNINativeMethod method_table[] = { { "nativeGetAccessoryStrings", "()[Ljava/lang/String;", (void*)android_server_UsbDeviceManager_getAccessoryStrings }, { "nativeOpenAccessory", "()Landroid/os/ParcelFileDescriptor;", (void*)android_server_UsbDeviceManager_openAccessory }, + { "nativeIsStartRequested", "()Z", + (void*)android_server_UsbDeviceManager_isStartRequested }, }; int register_android_server_UsbDeviceManager(JNIEnv *env) diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp index 11f61ccb1ada..91e010f90fe8 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.cpp +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -60,7 +60,11 @@ status_t SurfaceTextureLayer::queueBuffer(int buf, int64_t timestamp, sp<Layer> layer(mLayer.promote()); if (layer != NULL) { - *outTransform = layer->getOrientation(); + uint32_t orientation = layer->getOrientation(); + if (orientation & Transform::ROT_INVALID) { + orientation = 0; + } + *outTransform = orientation; } return res; diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index 40a70a806ce1..a0961caaa40d 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -806,10 +806,10 @@ public abstract class PhoneBase extends Handler implements Phone { mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType)); } - public void notifyDataConnection() { + public void notifyDataConnection(String reason) { String types[] = getActiveApnTypes(); for (String apnType : types) { - mNotifier.notifyDataConnection(this, null, apnType, getDataConnectionState(apnType)); + mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType)); } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java index 0d551aade502..5df2edd6a281 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java @@ -134,8 +134,6 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } } - // Not sure if this is needed in CDMALTE phone. - // mDataRoaming = regCodeIsRoaming(regState); mLteSS.setRadioTechnology(type); mLteSS.setState(regCodeToServiceState(regState)); } else { @@ -345,13 +343,14 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } if (cm.getSimState().isSIMReady()) { - // SIM is found on the device. If ERI roaming is OFF, use operator name - // from CSIM record. + // SIM is found on the device. If ERI roaming is OFF and SID/NID matches + // one configfured in SIM, use operator name from CSIM record. boolean showSpn = ((CdmaLteUiccRecords)phone.mIccRecords).getCsimSpnDisplayCondition(); int iconIndex = ss.getCdmaEriIconIndex(); - if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF)) { + if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) && + isInHomeSidNid(ss.getSystemId(), ss.getNetworkId())) { ss.setOperatorAlphaLong(phone.mIccRecords.getServiceProviderName()); } } @@ -401,7 +400,7 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } if ((hasCdmaDataConnectionChanged || hasNetworkTypeChanged)) { - phone.notifyDataConnection(); + phone.notifyDataConnection(null); } if (hasRoamingOn) { @@ -469,6 +468,34 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } /** + * Check whether the specified SID and NID pair appears in the HOME SID/NID list + * read from NV or SIM. + * + * @return true if provided sid/nid pair belongs to operator's home network. + */ + private boolean isInHomeSidNid(int sid, int nid) { + // if SID/NID is not available, assume this is home network. + if (isSidsAllZeros()) return true; + + // length of SID/NID shold be same + if (mHomeSystemId.length != mHomeNetworkId.length) return true; + + if (sid == 0) return true; + + for (int i = 0; i < mHomeSystemId.length; i++) { + // Use SID only if NID is a reserved value. + // SID 0 and NID 0 and 65535 are reserved. (C.0005 2.6.5.2) + if ((mHomeSystemId[i] == sid) && + ((mHomeNetworkId[i] == 0) || (mHomeNetworkId[i] == 65535) || + (nid == 0) || (nid == 65535) || (mHomeNetworkId[i] == nid))) { + return true; + } + } + // SID/NID are not in the list. So device is not in home network + return false; + } + + /** * Returns OTASP_NOT_NEEDED as its not needed for LTE */ @Override diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index 24a468a7d2c1..2cf4b8812a37 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -130,8 +130,8 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { protected String mCurPlmn = null; protected String mMdn; - private int mHomeSystemId[] = null; - private int mHomeNetworkId[] = null; + protected int mHomeSystemId[] = null; + protected int mHomeNetworkId[] = null; protected String mMin; protected String mPrlVersion; protected boolean mIsMinInfoReady = false; @@ -999,7 +999,7 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { } if (hasCdmaDataConnectionChanged || hasNetworkTypeChanged) { - phone.notifyDataConnection(); + phone.notifyDataConnection(null); } if (hasRoamingOn) { @@ -1481,7 +1481,7 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { } } - private boolean isSidsAllZeros() { + protected boolean isSidsAllZeros() { if (mHomeSystemId != null) { for (int i=0; i < mHomeSystemId.length; i++) { if (mHomeSystemId[i] != 0) { diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java index 93f4b4ea579a..d3645faba86f 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java @@ -915,7 +915,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } if (hasRadioTechnologyChanged) { - phone.notifyDataConnection(Phone.REASON_NW_TYPE_CHANGED, Phone.APN_TYPE_ALL); + phone.notifyDataConnection(Phone.REASON_NW_TYPE_CHANGED); } if (hasRoamingOn) { diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java index 1493ab9ffcd8..13b6129ab1ac 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java @@ -93,12 +93,9 @@ public class ListActivity extends Activity { } public void startProfiling(View v) { - ViewDebug.startLooperProfiling(new File(Environment.getExternalStorageDirectory(), - "looper.trace")); } public void stopProfiling(View v) { - ViewDebug.stopLooperProfiling(); } @Override diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java index e77646397f9e..9aa70b0e0b60 100644 --- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java +++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java @@ -87,8 +87,6 @@ public class ImageProcessingActivity extends Activity mIsProcessing = false; } - // This is a hack to work around an invalidation bug - mBitmapOut.setPixel(0, 0, 0); mOutPixelsAllocation.copyTo(mBitmapOut); mDisplayView.invalidate(); } diff --git a/tests/TileBenchmark/AndroidManifest.xml b/tests/TileBenchmark/AndroidManifest.xml index 663cc0dbea6f..ab61a9e25bb7 100644 --- a/tests/TileBenchmark/AndroidManifest.xml +++ b/tests/TileBenchmark/AndroidManifest.xml @@ -7,14 +7,16 @@ android:label="@string/app_name" android:hardwareAccelerated="true"> <activity android:name=".ProfileActivity" - android:label="@string/profile_activity"> + android:label="@string/profile_activity" + android:theme="@android:style/Theme.Holo.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".PlaybackActivity" - android:label="@string/playback_activity"> + android:label="@string/playback_activity" + android:theme="@android:style/Theme.Holo.NoActionBar"> </activity> </application> </manifest> diff --git a/tests/TileBenchmark/res/layout/main.xml b/tests/TileBenchmark/res/layout/main.xml index 4a81da65edb9..577c466e3847 100644 --- a/tests/TileBenchmark/res/layout/main.xml +++ b/tests/TileBenchmark/res/layout/main.xml @@ -23,11 +23,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" > - <Button - android:id="@+id/inspect" + <Spinner + android:id="@+id/movement" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/inspect_log" + android:prompt="@string/movement_method" /> <Spinner android:id="@+id/velocity" @@ -36,6 +36,13 @@ android:gravity="center_horizontal" android:prompt="@string/desired_scroll_velocity" /> + <ToggleButton + android:id="@+id/capture" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textOn="@string/capture_stop" + android:textOff="@string/capture_start" + /> <EditText android:id="@+id/url" android:layout_width="0dip" @@ -44,6 +51,12 @@ android:imeOptions="actionGo" android:layout_weight="1" /> + <Button + android:id="@+id/inspect" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/inspect_log" + /> </LinearLayout> <com.test.tilebenchmark.ProfiledWebView android:id="@+id/web" diff --git a/tests/TileBenchmark/res/values/colors.xml b/tests/TileBenchmark/res/values/colors.xml index 3958083febd5..dbb8e7295b8b 100644 --- a/tests/TileBenchmark/res/values/colors.xml +++ b/tests/TileBenchmark/res/values/colors.xml @@ -18,8 +18,17 @@ <color name="ready_tile">#ff4ac230</color> <!-- The color of tiles with stale / invalid textures --> <color name="unready_tile">#ff744400</color> - <!-- Background color for logged URLs --> - <color name="finished_url">#ff004000</color> - <!-- Background color for URLs with logging in progress --> - <color name="unfinished_url">#ff400000</color> + <!-- Viewport overlay in playback --> + <color name="view">#50000050</color> + <!-- Invalidated region overlay in playback - start color --> + <color name="inval_region_start">#80ff0000</color> + <!-- Invalidated region overlay in playback - stop color--> + <color name="inval_region_stop">#80ffffff</color> + + <!-- Background color for not testing --> + <color name="background_not_testing">#ff000000</color> + <!-- Background color for during testing --> + <color name="background_start_testing">#ff400000</color> + <!-- Background color for testing complete --> + <color name="background_stop_testing">#ff004000</color> </resources> diff --git a/tests/TileBenchmark/res/values/strings.xml b/tests/TileBenchmark/res/values/strings.xml index f70ee2c04a3f..66972ac26bab 100644 --- a/tests/TileBenchmark/res/values/strings.xml +++ b/tests/TileBenchmark/res/values/strings.xml @@ -28,6 +28,10 @@ <string name="loadbutton">Load</string> <!-- Button, opens the playback activity [CHAR LIMIT=20] --> <string name="inspect_log">Inspect Log</string> + <!-- ToggleButton label when pressing starts capture [CHAR LIMIT=15] --> + <string name="capture_start">Start Capture</string> + <!-- ToggleButton label when pressing stops capture [CHAR LIMIT=15] --> + <string name="capture_stop">Stop Capture</string> <!-- The speed of auto-scrolling [CHAR LIMIT=30] --> <string name="desired_scroll_velocity">Choose Scroll Velocity</string> <!-- Pixels moved per frame [CHAR LIMIT=10] --> @@ -39,6 +43,21 @@ <item>200</item> <item>400</item> </string-array> + <!-- Drop down menu for selecting scrolling vs manual navigation for + capturing [CHAR LIMIT=15] --> + <string name="movement_method">Movement Method</string> + <!-- Drop down menu entry - automatically scroll to the end of the page + with scrollBy() [CHAR LIMIT=15] --> + <string name="movement_auto_scroll">Auto-scroll</string> + <!-- Drop down menu entry - [CHAR LIMIT=15] --> + <string name="movement_auto_fling">Auto-fling</string> + <!-- Drop down menu entry - manually navigate the page(s), hit 'capture' + button [CHAR LIMIT=15] --> + <string name="movement_manual">Manual</string> + + <!-- Error popup indicating log data couldn't be loaded [CHAR LIMIT=60] --> + <string name="error_no_data">Error: log data could not be loaded.</string> + <!-- 25th percentile - 25% of frames fall below this value [CHAR LIMIT=12] --> <string name="percentile_25">25%ile</string> @@ -56,7 +75,7 @@ <string name="format_stat">%4.4f</string> <!-- Format string for displaying aggregate stats+values (nr of valid tiles, etc.) [CHAR LIMIT=20] --> - <string name="format_stat_name">%1$9s %2$3d</string> + <string name="format_stat_name">%1$-20s %2$3d</string> <!-- Text hovering over canvas, number of tiles ready [CHAR LIMIT=15] --> <string name="ready_tiles">Ready Tiles</string> <!-- Text hovering over canvas, number tiles not ready [CHAR LIMIT=15] --> @@ -64,4 +83,7 @@ <!-- Text hovering over canvas, number of tiles that haven't been allocated to a place on the page [CHAR LIMIT=15] --> <string name="unplaced_tiles">Unplaced Tiles</string> + <!-- Text hovering over canvas, number of invalidated regions this frame + [CHAR LIMIT=15] --> + <string name="number_invalidates">Invalidates</string> </resources> diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java index 5130f5d3ab3f..36694a7476f4 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackActivity.java @@ -27,6 +27,7 @@ import android.widget.Button; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; +import android.widget.Toast; import java.io.FileInputStream; import java.io.IOException; @@ -102,7 +103,10 @@ public class PlaybackActivity extends Activity { @Override protected void onPostExecute(TileData data[][]) { if (data == null) { - data = genTestPattern(); + Toast.makeText(getApplicationContext(), + getResources().getString(R.string.error_no_data), + Toast.LENGTH_LONG).show(); + return; } mPlaybackView.setData(data); @@ -166,23 +170,4 @@ public class PlaybackActivity extends Activity { new LoadFileTask().execute(ProfileActivity.TEMP_FILENAME); } - - private TileData[][] genTestPattern() { - final int XMAX = 5; - final int FRAMEMAX = 99; - - TileData example[][] = new TileData[FRAMEMAX][]; - for (int frame = 0; frame < FRAMEMAX; frame++) { - int numTiles = frame + 10; - - example[frame] = new TileData[numTiles]; - for (int t = 0; t < numTiles; t++) { - int x = t % XMAX; - int y = t / XMAX; - boolean isReady = y * 10 < frame; - example[frame][t] = new TileData(x, y, isReady, 0); - } - } - return example; - } } diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java index db4a34182bac..35b1563ed895 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java @@ -28,19 +28,17 @@ import java.util.ArrayList; import java.util.Arrays; public class PlaybackGraphs { - private static final int BAR_WIDTH = PlaybackView.TILEX * 3; + private static final int BAR_WIDTH = PlaybackView.TILE_SCALE * 3; private static final float CANVAS_SCALE = 0.2f; private static final double IDEAL_FRAMES = 60; private static final int LABELOFFSET = 100; private static Paint whiteLabels; - private static double viewportCoverage(int l, int b, int r, int t, - int tileIndexX, - int tileIndexY) { - if (tileIndexX * PlaybackView.TILEX < r - && (tileIndexX + 1) * PlaybackView.TILEX >= l - && tileIndexY * PlaybackView.TILEY < t - && (tileIndexY + 1) * PlaybackView.TILEY >= b) { + private static double viewportCoverage(TileData view, TileData tile) { + if (tile.left < view.right + && tile.right >= view.left + && tile.top < view.bottom + && tile.bottom >= view.top) { return 1.0f; } return 0.0f; @@ -76,13 +74,10 @@ public class PlaybackGraphs { // coverage graph @Override public double getValue(TileData[] frame) { - int l = frame[0].x, b = frame[0].y; - int r = frame[1].x, t = frame[1].y; double total = 0, totalCount = 0; - for (int tileID = 2; tileID < frame.length; tileID++) { + for (int tileID = 1; tileID < frame.length; tileID++) { TileData data = frame[tileID]; - double coverage = viewportCoverage(l, b, r, t, data.x, - data.y); + double coverage = viewportCoverage(frame[0], data); total += coverage * (data.isReady ? 1 : 0); totalCount += coverage; } @@ -158,7 +153,7 @@ public class PlaybackGraphs { public PlaybackGraphs() { whiteLabels = new Paint(); whiteLabels.setColor(Color.WHITE); - whiteLabels.setTextSize(PlaybackView.TILEY / 3); + whiteLabels.setTextSize(PlaybackView.TILE_SCALE / 3); } private ArrayList<ShapeDrawable> mShapes = new ArrayList<ShapeDrawable>(); @@ -177,11 +172,13 @@ public class PlaybackGraphs { int lastBar = 0; for (int frameIndex = 0; frameIndex < tileProfilingData.length; frameIndex++) { TileData frame[] = tileProfilingData[frameIndex]; - int newBar = (frame[0].y + frame[1].y) / 2; + int newBar = (frame[0].top + frame[0].bottom) / 2; MetricGen s = Metrics[metricIndex]; double absoluteValue = s.getValue(frame); double relativeValue = absoluteValue / s.getMax(); + relativeValue = Math.min(1,relativeValue); + relativeValue = Math.max(0,relativeValue); int rightPos = (int) (-BAR_WIDTH * metricIndex); int leftPos = (int) (-BAR_WIDTH * (metricIndex + relativeValue)); @@ -207,7 +204,7 @@ public class PlaybackGraphs { ArrayList<ShapeDrawable> shapes) { // Shapes drawn here are drawn relative to the viewRect Rect viewRect = shapes.get(shapes.size() - 1).getBounds(); - canvas.translate(0, 5 * PlaybackView.TILEY - viewRect.top); + canvas.translate(0, 5 * PlaybackView.TILE_SCALE - viewRect.top); for (ShapeDrawable shape : mShapes) { shape.draw(canvas); @@ -234,13 +231,15 @@ public class PlaybackGraphs { int yPos = LABELOFFSET; canvas.drawText(label, xPos, yPos, whiteLabels); for (int statIndex = 0; statIndex < Stats.length; statIndex++) { - label = resources.getString(R.string.format_stat, mStats[metricIndex][statIndex]); - yPos = LABELOFFSET + (1 + statIndex) * PlaybackView.TILEY / 2; + label = resources.getString(R.string.format_stat, + mStats[metricIndex][statIndex]); + yPos = LABELOFFSET + (1 + statIndex) * PlaybackView.TILE_SCALE + / 2; canvas.drawText(label, xPos, yPos, whiteLabels); } } for (int stringIndex = 0; stringIndex < strings.length; stringIndex++) { - int yPos = LABELOFFSET + stringIndex * PlaybackView.TILEY / 2; + int yPos = LABELOFFSET + stringIndex * PlaybackView.TILE_SCALE / 2; canvas.drawText(strings[stringIndex], 0, yPos, whiteLabels); } } diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java index f104eac21b89..edc8643e5b24 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackView.java @@ -16,6 +16,9 @@ package com.test.tilebenchmark; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; @@ -30,8 +33,9 @@ import android.view.View; import java.util.ArrayList; public class PlaybackView extends View { - public static final int TILEX = 300; - public static final int TILEY = 300; + public static final int TILE_SCALE = 300; + private static final int INVAL_FLAG = -2; + private static final int INVAL_CYCLE = 250; private Paint levelPaint = null, coordPaint = null, goldPaint = null; private PlaybackGraphs mGraphs; @@ -39,28 +43,46 @@ public class PlaybackView extends View { private ArrayList<ShapeDrawable> mTempShapes = new ArrayList<ShapeDrawable>(); private TileData mProfData[][] = null; private GestureDetector mGestureDetector = null; - private String mRenderStrings[] = new String[3]; + private String mRenderStrings[] = new String[4]; private class TileDrawable extends ShapeDrawable { TileData tile; + String label; - public TileDrawable(TileData t) { - int tileColorId = t.isReady ? R.color.ready_tile - : R.color.unready_tile; - getPaint().setColor(getResources().getColor(tileColorId)); - - setBounds(t.x * TILEX, t.y * TILEY, (t.x + 1) * TILEX, (t.y + 1) - * TILEY); + public TileDrawable(TileData t, int colorId) { this.tile = t; + getPaint().setColor(getResources().getColor(colorId)); + if (colorId == R.color.ready_tile + || colorId == R.color.unready_tile) { + + label = (int) (t.left / TILE_SCALE) + ", " + + (int) (t.top / TILE_SCALE); + // ignore scale value for tiles + setBounds(t.left, t.top, + t.right, t.bottom); + } else { + setBounds((int) (t.left * t.scale), + (int) (t.top * t.scale), + (int) (t.right * t.scale), + (int) (t.bottom * t.scale)); + } + } + + @SuppressWarnings("unused") + public void setColor(int color) { + getPaint().setColor(color); } @Override public void draw(Canvas canvas) { super.draw(canvas); - canvas.drawText(Integer.toString(tile.level), getBounds().left, - getBounds().bottom, levelPaint); - canvas.drawText(tile.x + "," + tile.y, getBounds().left, - ((getBounds().bottom + getBounds().top) / 2), coordPaint); + if (label != null) { + canvas.drawText(Integer.toString(tile.level), getBounds().left, + getBounds().bottom, levelPaint); + canvas.drawText(label, getBounds().left, + ((getBounds().bottom + getBounds().top) / 2), + coordPaint); + } } } @@ -92,10 +114,10 @@ public class PlaybackView extends View { private void init() { levelPaint = new Paint(); levelPaint.setColor(Color.WHITE); - levelPaint.setTextSize(TILEY / 2); + levelPaint.setTextSize(TILE_SCALE / 2); coordPaint = new Paint(); coordPaint.setColor(Color.BLACK); - coordPaint.setTextSize(TILEY / 3); + coordPaint.setTextSize(TILE_SCALE / 3); goldPaint = new Paint(); goldPaint.setColor(0xffa0e010); mGraphs = new PlaybackGraphs(); @@ -110,6 +132,7 @@ public class PlaybackView extends View { } mGraphs.draw(canvas, mTempShapes, mRenderStrings, getResources()); + invalidate(); // may have animations, force redraw } public int setFrame(int frame) { @@ -117,35 +140,66 @@ public class PlaybackView extends View { return 0; } - int readyTiles = 0, unreadyTiles = 0, unplacedTiles = 0; + int readyTiles = 0, unreadyTiles = 0, unplacedTiles = 0, numInvals = 0; mTempShapes.clear(); - // draw actual tiles - for (int tileID = 2; tileID < mProfData[frame].length; tileID++) { - TileData t = mProfData[frame][tileID]; - mTempShapes.add(new TileDrawable(t)); - if (t.isReady) { - readyTiles++; + // create tile shapes (as they're drawn on bottom) + for (TileData t : mProfData[frame]) { + if (t.level != INVAL_FLAG && t != mProfData[frame][0]) { + int colorId; + if (t.isReady) { + readyTiles++; + colorId = R.color.ready_tile; + } else { + unreadyTiles++; + colorId = R.color.unready_tile; + } + if (t.left < 0 || t.top < 0) { + unplacedTiles++; + } + mTempShapes.add(new TileDrawable(t, colorId)); } else { - unreadyTiles++; + numInvals++; } - if (t.x < 0 || t.y < 0) { - unplacedTiles++; + } + + // create invalidate shapes (drawn above tiles) + int invalId = 0; + for (TileData t : mProfData[frame]) { + if (t.level == INVAL_FLAG && t != mProfData[frame][0]) { + TileDrawable invalShape = new TileDrawable(t, + R.color.inval_region_start); + ValueAnimator tileAnimator = ObjectAnimator.ofInt(invalShape, + "color", + getResources().getColor(R.color.inval_region_start), + getResources().getColor(R.color.inval_region_stop)); + tileAnimator.setDuration(numInvals * INVAL_CYCLE); + tileAnimator.setEvaluator(new ArgbEvaluator()); + tileAnimator.setRepeatCount(ValueAnimator.INFINITE); + tileAnimator.setRepeatMode(ValueAnimator.RESTART); + float delay = (float) (invalId) * INVAL_CYCLE; + tileAnimator.setStartDelay((int) delay); + invalId++; + tileAnimator.start(); + + mTempShapes.add(invalShape); } } + mRenderStrings[0] = getResources().getString(R.string.format_stat_name, getResources().getString(R.string.ready_tiles), readyTiles); mRenderStrings[1] = getResources().getString(R.string.format_stat_name, getResources().getString(R.string.unready_tiles), unreadyTiles); mRenderStrings[2] = getResources().getString(R.string.format_stat_name, - getResources().getString(R.string.unplaced_tiles), unplacedTiles); - - // draw view rect (using first two TileData objects) - ShapeDrawable viewShape = new ShapeDrawable(); - viewShape.getPaint().setColor(0xff0000ff); - viewShape.setAlpha(64); - viewShape.setBounds(mProfData[frame][0].x, mProfData[frame][0].y, - mProfData[frame][1].x, mProfData[frame][1].y); + getResources().getString(R.string.unplaced_tiles), + unplacedTiles); + mRenderStrings[3] = getResources().getString(R.string.format_stat_name, + getResources().getString(R.string.number_invalidates), + numInvals); + + // draw view rect (using first TileData object, on top) + TileDrawable viewShape = new TileDrawable(mProfData[frame][0], + R.color.view); mTempShapes.add(viewShape); this.invalidate(); return frame; diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java index 23b62751b931..1521807eb1dc 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java @@ -38,6 +38,7 @@ import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; +import android.widget.ToggleButton; import java.io.FileOutputStream; import java.io.IOException; @@ -58,11 +59,24 @@ public class ProfileActivity extends Activity { // before test Button mInspectButton; + ToggleButton mCaptureButton; Spinner mVelocitySpinner; + Spinner mMovementSpinner; EditText mUrl; ProfiledWebView mWeb; ProfileCallback mCallback; + LoggingWebViewClient mLoggingWebViewClient = new LoggingWebViewClient(); + AutoLoggingWebViewClient mAutoLoggingWebViewClient = new AutoLoggingWebViewClient(); + + private enum TestingState { + NOT_TESTING, + PRE_TESTING, + START_TESTING, + STOP_TESTING, + SAVED_TESTING + }; + private class VelocitySelectedListener implements OnItemSelectedListener { @Override public void onItemSelected(AdapterView<?> parent, View view, @@ -77,6 +91,31 @@ public class ProfileActivity extends Activity { } } + private class MovementSelectedListener implements OnItemSelectedListener { + @Override + public void onItemSelected(AdapterView<?> parent, View view, + int position, long id) { + String movementStr = parent.getItemAtPosition(position).toString(); + if (movementStr == getResources().getString( + R.string.movement_auto_scroll) + || movementStr == getResources().getString( + R.string.movement_auto_fling)) { + mWeb.setWebViewClient(mAutoLoggingWebViewClient); + mCaptureButton.setEnabled(false); + mVelocitySpinner.setEnabled(true); + } else if (movementStr == getResources().getString( + R.string.movement_manual)) { + mWeb.setWebViewClient(mLoggingWebViewClient); + mCaptureButton.setEnabled(true); + mVelocitySpinner.setEnabled(false); + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + } + private class LoggingWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { @@ -88,6 +127,9 @@ public class ProfileActivity extends Activity { super.onPageStarted(view, url, favicon); mUrl.setText(url); } + } + + private class AutoLoggingWebViewClient extends LoggingWebViewClient { @Override public void onPageFinished(WebView view, String url) { @@ -100,10 +142,16 @@ public class ProfileActivity extends Activity { @Override public void onFinish() { - mWeb.startScrollTest(mCallback); + startViewProfiling(true); } }.start(); } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + setTestingState(TestingState.PRE_TESTING); + } } private class StoreFileTask extends @@ -125,24 +173,65 @@ public class ProfileActivity extends Activity { @Override protected void onPostExecute(Void v) { - mUrl.setBackgroundResource(R.color.finished_url); + setTestingState(TestingState.SAVED_TESTING); } } + public void setTestingState(TestingState state) { + switch (state) { + case NOT_TESTING: + mUrl.setBackgroundResource(R.color.background_not_testing); + mInspectButton.setEnabled(true); + mMovementSpinner.setEnabled(true); + break; + case PRE_TESTING: + mInspectButton.setEnabled(false); + mMovementSpinner.setEnabled(false); + break; + case START_TESTING: + mUrl.setBackgroundResource(R.color.background_start_testing); + mInspectButton.setEnabled(false); + mMovementSpinner.setEnabled(false); + break; + case STOP_TESTING: + mUrl.setBackgroundResource(R.color.background_stop_testing); + break; + case SAVED_TESTING: + mInspectButton.setEnabled(true); + mMovementSpinner.setEnabled(true); + break; + } + } + + /** auto - automatically scroll. */ + private void startViewProfiling(boolean auto) { + if (!auto) { + // manual, toggle capture button to indicate capture state to user + mCaptureButton.setChecked(true); + } + mWeb.startScrollTest(mCallback, auto); + setTestingState(TestingState.START_TESTING); + } + /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mInspectButton = (Button) findViewById(R.id.inspect); + mCaptureButton = (ToggleButton) findViewById(R.id.capture); mVelocitySpinner = (Spinner) findViewById(R.id.velocity); + mMovementSpinner = (Spinner) findViewById(R.id.movement); mUrl = (EditText) findViewById(R.id.url); mWeb = (ProfiledWebView) findViewById(R.id.web); mCallback = new ProfileCallback() { @SuppressWarnings("unchecked") @Override public void profileCallback(TileData[][] data) { - new StoreFileTask().execute(new Pair<String, TileData[][]>(TEMP_FILENAME, data)); + new StoreFileTask().execute(new Pair<String, TileData[][]>( + TEMP_FILENAME, data)); + mCaptureButton.setChecked(false); + setTestingState(TestingState.STOP_TESTING); } }; @@ -166,6 +255,33 @@ public class ProfileActivity extends Activity { new VelocitySelectedListener()); mVelocitySpinner.setSelection(3); + // Movement spinner + String content[] = { + getResources().getString(R.string.movement_auto_scroll), + getResources().getString(R.string.movement_auto_fling), + getResources().getString(R.string.movement_manual) + }; + adapter = new ArrayAdapter<CharSequence>(this, + android.R.layout.simple_spinner_item, content); + adapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + mMovementSpinner.setAdapter(adapter); + mMovementSpinner.setOnItemSelectedListener( + new MovementSelectedListener()); + mMovementSpinner.setSelection(0); + + // Capture toggle button + mCaptureButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mCaptureButton.isChecked()) { + startViewProfiling(false); + } else { + mWeb.stopScrollTest(); + } + } + }); + // Custom profiling WebView WebSettings settings = mWeb.getSettings(); settings.setJavaScriptEnabled(true); @@ -180,12 +296,13 @@ public class ProfileActivity extends Activity { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { String url = mUrl.getText().toString(); - mUrl.setBackgroundResource(R.color.unfinished_url); mWeb.loadUrl(url); mWeb.requestFocus(); return true; } }); + + setTestingState(TestingState.NOT_TESTING); } public void setCallback(ProfileCallback callback) { diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java index 656062421b97..d3941be41302 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java @@ -59,12 +59,13 @@ public class ProfiledWebView extends WebView { } /* - * Called once the page is loaded to start scrolling for evaluating tiles + * Called once the page is loaded to start scrolling for evaluating tiles. + * If autoScrolling isn't set, stop must be called manually. */ - public void startScrollTest(ProfileCallback callback) { - isScrolling = true; + public void startScrollTest(ProfileCallback callback, boolean autoScrolling) { + isScrolling = autoScrolling; mCallback = callback; - super.tileProfilingStart(); + tileProfilingStart(); invalidate(); } @@ -72,19 +73,31 @@ public class ProfiledWebView extends WebView { * Called once the page has stopped scrolling */ public void stopScrollTest() { - float testRatio = super.tileProfilingStop(); + super.tileProfilingStop(); + + if (mCallback == null) { + tileProfilingClear(); + return; + } TileData data[][] = new TileData[super.tileProfilingNumFrames()][]; for (int frame = 0; frame < data.length; frame++) { data[frame] = new TileData[ - super.tileProfilingNumTilesInFrame(frame)]; + tileProfilingNumTilesInFrame(frame)]; for (int tile = 0; tile < data[frame].length; tile++) { - int x = super.tileProfilingGetX(frame, tile); - int y = super.tileProfilingGetY(frame, tile); - boolean isReady = super.tileProfilingGetReady(frame, tile); - int level = super.tileProfilingGetLevel(frame, tile); + int left = tileProfilingGetInt(frame, tile, "left"); + int top = tileProfilingGetInt(frame, tile, "top"); + int right = tileProfilingGetInt(frame, tile, "right"); + int bottom = tileProfilingGetInt(frame, tile, "bottom"); + + boolean isReady = super.tileProfilingGetInt( + frame, tile, "isReady") == 1; + int level = tileProfilingGetInt(frame, tile, "level"); + + float scale = tileProfilingGetFloat(frame, tile, "scale"); - data[frame][tile] = new TileData(x, y, isReady, level); + data[frame][tile] = new TileData(left, top, right, bottom, + isReady, level, scale); } } super.tileProfilingClear(); diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java b/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java index 7d4bb9f5217f..3e729a6282be 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/TileData.java @@ -19,14 +19,24 @@ package com.test.tilebenchmark; import java.io.Serializable; public class TileData implements Serializable { - public int x, y; + int left, top, right, bottom; public boolean isReady; public int level; + public float scale; - public TileData(int x, int y, boolean isReady, int level) { - this.x = x; - this.y = y; + public TileData(int left, int top, int right, int bottom, boolean isReady, + int level, float scale) { + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; this.isReady = isReady; this.level = level; + this.scale = scale; + } + + public String toString() { + return "Tile (" + left + "," + top + ")->(" + + right + "," + bottom + ")"; } } diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 6e13d0fb349c..f1f0fcc6ac9f 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -104,18 +104,30 @@ public class WifiNative { public native static boolean stopDriverCommand(); + + /** + * Start filtering out Multicast V4 packets + * @return {@code true} if the operation succeeded, {@code false} otherwise + */ + public native static boolean startFilteringMulticastV4Packets(); + + /** + * Stop filtering out Multicast V4 packets. + * @return {@code true} if the operation succeeded, {@code false} otherwise + */ + public native static boolean stopFilteringMulticastV4Packets(); + /** - * Start filtering out multicast packets, to reduce battery consumption - * that would result from processing them, only to discard them. + * Start filtering out Multicast V6 packets * @return {@code true} if the operation succeeded, {@code false} otherwise */ - public native static boolean startPacketFiltering(); + public native static boolean startFilteringMulticastV6Packets(); /** - * Stop filtering out multicast packets. + * Stop filtering out Multicast V6 packets. * @return {@code true} if the operation succeeded, {@code false} otherwise */ - public native static boolean stopPacketFiltering(); + public native static boolean stopFilteringMulticastV6Packets(); public native static boolean setPowerModeCommand(int mode); diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 12efeb1e59f1..f08bb6a2e5ea 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -82,6 +82,7 @@ import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; /** @@ -160,6 +161,9 @@ public class WifiStateMachine extends StateMachine { /* Tracks current frequency mode */ private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO); + /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */ + private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true); + // Channel for sending replies. private AsyncChannel mReplyChannel = new AsyncChannel(); @@ -285,6 +289,11 @@ public class WifiStateMachine extends StateMachine { static final int CMD_START_PACKET_FILTERING = BASE + 84; /* Clear packet filter */ static final int CMD_STOP_PACKET_FILTERING = BASE + 85; + + /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */ + static final int MULTICAST_V6 = 1; + static final int MULTICAST_V4 = 0; + /* Connect to a specified network (network id * or WifiConfiguration) This involves increasing * the priority of the network, enabling the network @@ -868,17 +877,33 @@ public class WifiStateMachine extends StateMachine { } /** - * Start packet filtering + * Start filtering Multicast v4 packets + */ + public void startFilteringMulticastV4Packets() { + mFilteringMulticastV4Packets.set(true); + sendMessage(obtainMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0)); + } + + /** + * Stop filtering Multicast v4 packets + */ + public void stopFilteringMulticastV4Packets() { + mFilteringMulticastV4Packets.set(false); + sendMessage(obtainMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0)); + } + + /** + * Start filtering Multicast v4 packets */ - public void startPacketFiltering() { - sendMessage(CMD_START_PACKET_FILTERING); + public void startFilteringMulticastV6Packets() { + sendMessage(obtainMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0)); } /** - * Stop packet filtering + * Stop filtering Multicast v4 packets */ - public void stopPacketFiltering() { - sendMessage(CMD_STOP_PACKET_FILTERING); + public void stopFilteringMulticastV6Packets() { + sendMessage(obtainMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0)); } /** @@ -2074,9 +2099,6 @@ public class WifiStateMachine extends StateMachine { WifiConfigStore.initialize(mContext); - //TODO: initialize and fix multicast filtering - //mWM.initializeMulticastFiltering(); - sendSupplicantConnectionChangedBroadcast(true); transitionTo(mDriverStartedState); break; @@ -2359,6 +2381,16 @@ public class WifiStateMachine extends StateMachine { /* initialize network state */ setNetworkDetailedState(DetailedState.DISCONNECTED); + /* Remove any filtering on Multicast v6 at start */ + WifiNative.stopFilteringMulticastV6Packets(); + + /* Reset Multicast v4 filtering state */ + if (mFilteringMulticastV4Packets.get()) { + WifiNative.startFilteringMulticastV4Packets(); + } else { + WifiNative.stopFilteringMulticastV4Packets(); + } + if (mIsScanMode) { WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE); WifiNative.disconnectCommand(); @@ -2419,10 +2451,22 @@ public class WifiStateMachine extends StateMachine { mWakeLock.release(); break; case CMD_START_PACKET_FILTERING: - WifiNative.startPacketFiltering(); + if (message.arg1 == MULTICAST_V6) { + WifiNative.startFilteringMulticastV6Packets(); + } else if (message.arg1 == MULTICAST_V4) { + WifiNative.startFilteringMulticastV4Packets(); + } else { + Log.e(TAG, "Illegal arugments to CMD_START_PACKET_FILTERING"); + } break; case CMD_STOP_PACKET_FILTERING: - WifiNative.stopPacketFiltering(); + if (message.arg1 == MULTICAST_V6) { + WifiNative.stopFilteringMulticastV6Packets(); + } else if (message.arg1 == MULTICAST_V4) { + WifiNative.stopFilteringMulticastV4Packets(); + } else { + Log.e(TAG, "Illegal arugments to CMD_STOP_PACKET_FILTERING"); + } break; default: return NOT_HANDLED; diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java index b09e04b04c5f..fa7cf21627bf 100644 --- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java +++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java @@ -68,7 +68,7 @@ import java.util.regex.Pattern; public class WifiWatchdogStateMachine extends StateMachine { - private static final boolean VDBG = true; //TODO : Remove this before merge + private static final boolean VDBG = false; private static final boolean DBG = true; private static final String WWSM_TAG = "WifiWatchdogStateMachine"; |