diff options
256 files changed, 15214 insertions, 7803 deletions
diff --git a/api/current.txt b/api/current.txt index 36aa4a0..ffcadfc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -250,7 +250,7 @@ package android { field public static final int actionBarTabBarStyle = 16843508; // 0x10102f4 field public static final int actionBarTabStyle = 16843507; // 0x10102f3 field public static final int actionBarTabTextStyle = 16843509; // 0x10102f5 - field public static final int actionBarTheme = 16843831; // 0x1010437 + field public static final int actionBarTheme = 16843829; // 0x1010435 field public static final int actionBarWidgetTheme = 16843671; // 0x1010397 field public static final int actionButtonStyle = 16843480; // 0x10102d8 field public static final int actionDropDownStyle = 16843479; // 0x10102d7 @@ -267,7 +267,7 @@ package android { field public static final int actionModeSplitBackground = 16843677; // 0x101039d field public static final int actionModeStyle = 16843668; // 0x1010394 field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6 - field public static final int actionOverflowMenuStyle = 16843851; // 0x101044b + field public static final int actionOverflowMenuStyle = 16843849; // 0x1010449 field public static final int actionProviderClass = 16843657; // 0x1010389 field public static final int actionViewClass = 16843516; // 0x10102fc field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd @@ -313,7 +313,7 @@ package android { field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b field public static final int autoLink = 16842928; // 0x10100b0 field public static final int autoMirrored = 16843754; // 0x10103ea - field public static final int autoRemoveFromRecents = 16843853; // 0x101044d + field public static final int autoRemoveFromRecents = 16843851; // 0x101044b field public static final int autoStart = 16843445; // 0x10102b5 field public static final deprecated int autoText = 16843114; // 0x101016a field public static final int autoUrlDetect = 16843404; // 0x101028c @@ -386,14 +386,12 @@ 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 colorAccent = 16843836; // 0x101043c + field public static final int colorAccent = 16843834; // 0x101043a field public static final int colorActivatedHighlight = 16843664; // 0x1010390 field public static final int colorBackground = 16842801; // 0x1010031 field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab field public static final int colorButtonNormal = 16843823; // 0x101042f - field public static final int colorButtonNormalColored = 16843825; // 0x1010431 field public static final int colorButtonPressed = 16843824; // 0x1010430 - field public static final int colorButtonPressedColored = 16843826; // 0x1010432 field public static final int colorControlActivated = 16843822; // 0x101042e field public static final int colorControlNormal = 16843821; // 0x101042d field public static final int colorFocusedHighlight = 16843663; // 0x101038f @@ -402,9 +400,9 @@ package android { field public static final int colorLongPressedHighlight = 16843662; // 0x101038e field public static final int colorMultiSelectHighlight = 16843665; // 0x1010391 field public static final int colorPressedHighlight = 16843661; // 0x101038d - field public static final int colorPrimary = 16843834; // 0x101043a - field public static final int colorPrimaryDark = 16843835; // 0x101043b - field public static final int colorPrimaryLight = 16843833; // 0x1010439 + field public static final int colorPrimary = 16843832; // 0x1010438 + field public static final int colorPrimaryDark = 16843833; // 0x1010439 + field public static final int colorPrimaryLight = 16843831; // 0x1010437 field public static final int columnCount = 16843639; // 0x1010377 field public static final int columnDelay = 16843215; // 0x10101cf field public static final int columnOrderPreserved = 16843640; // 0x1010378 @@ -463,7 +461,7 @@ package android { field public static final int dividerHorizontal = 16843564; // 0x101032c field public static final int dividerPadding = 16843562; // 0x101032a field public static final int dividerVertical = 16843530; // 0x101030a - field public static final int documentLaunchMode = 16843852; // 0x101044c + field public static final int documentLaunchMode = 16843850; // 0x101044a field public static final int drawSelectorOnTop = 16843004; // 0x10100fc field public static final int drawable = 16843161; // 0x1010199 field public static final int drawableBottom = 16843118; // 0x101016e @@ -492,7 +490,7 @@ package android { field public static final int editTextStyle = 16842862; // 0x101006e field public static final deprecated int editable = 16843115; // 0x101016b field public static final int editorExtras = 16843300; // 0x1010224 - field public static final int elevation = 16843847; // 0x1010447 + field public static final int elevation = 16843845; // 0x1010445 field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 field public static final int enabled = 16842766; // 0x101000e @@ -502,10 +500,10 @@ package android { field public static final int entries = 16842930; // 0x10100b2 field public static final int entryValues = 16843256; // 0x10101f8 field public static final int eventsInterceptionEnabled = 16843389; // 0x101027d - field public static final int excludeClass = 16843849; // 0x1010449 + field public static final int excludeClass = 16843847; // 0x1010447 field public static final int excludeFromRecents = 16842775; // 0x1010017 - field public static final int excludeId = 16843848; // 0x1010448 - field public static final int excludeViewName = 16843860; // 0x1010454 + field public static final int excludeId = 16843846; // 0x1010446 + field public static final int excludeViewName = 16843858; // 0x1010452 field public static final int exitFadeDuration = 16843533; // 0x101030d field public static final int expandableListPreferredChildIndicatorLeft = 16842834; // 0x1010052 field public static final int expandableListPreferredChildIndicatorRight = 16842835; // 0x1010053 @@ -568,7 +566,7 @@ package android { field public static final int freezesText = 16843116; // 0x101016c field public static final int fromAlpha = 16843210; // 0x10101ca field public static final int fromDegrees = 16843187; // 0x10101b3 - field public static final int fromId = 16843856; // 0x1010450 + field public static final int fromId = 16843854; // 0x101044e field public static final int fromScene = 16843741; // 0x10103dd field public static final int fromXDelta = 16843206; // 0x10101c6 field public static final int fromXScale = 16843202; // 0x10101c2 @@ -601,7 +599,7 @@ package android { field public static final int headerBackground = 16843055; // 0x101012f field public static final int headerDividersEnabled = 16843310; // 0x101022e field public static final int height = 16843093; // 0x1010155 - field public static final int hideOnContentScroll = 16843850; // 0x101044a + field public static final int hideOnContentScroll = 16843848; // 0x1010448 field public static final int hint = 16843088; // 0x1010150 field public static final int homeAsUpIndicator = 16843531; // 0x101030b field public static final int homeLayout = 16843549; // 0x101031d @@ -832,7 +830,7 @@ package android { field public static final int name = 16842755; // 0x1010003 field public static final int navigationMode = 16843471; // 0x10102cf field public static final int negativeButtonText = 16843254; // 0x10101f6 - field public static final int nestedScrollingEnabled = 16843837; // 0x101043d + field public static final int nestedScrollingEnabled = 16843835; // 0x101043b field public static final int nextFocusDown = 16842980; // 0x10100e4 field public static final int nextFocusForward = 16843580; // 0x101033c field public static final int nextFocusLeft = 16842977; // 0x10100e1 @@ -881,7 +879,7 @@ package android { field public static final int permissionFlags = 16843719; // 0x10103c7 field public static final int permissionGroup = 16842762; // 0x101000a field public static final int permissionGroupFlags = 16843717; // 0x10103c5 - field public static final int persistable = 16843827; // 0x1010433 + field public static final int persistable = 16843825; // 0x1010431 field public static final int persistent = 16842765; // 0x101000d field public static final int persistentDrawingCache = 16842990; // 0x10100ee field public static final deprecated int phoneNumber = 16843111; // 0x1010167 @@ -958,7 +956,7 @@ package android { field public static final int restoreAnyVersion = 16843450; // 0x10102ba field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d field public static final int restrictedAccountType = 16843733; // 0x10103d5 - field public static final int reversible = 16843857; // 0x1010451 + field public static final int reversible = 16843855; // 0x101044f field public static final int right = 16843183; // 0x10101af field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093 field public static final int ringtoneType = 16843257; // 0x10101f9 @@ -1014,7 +1012,7 @@ package android { field public static final int selectableItemBackground = 16843534; // 0x101030e field public static final int selectedDateVerticalBar = 16843591; // 0x1010347 field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342 - field public static final int sessionService = 16843844; // 0x1010444 + field public static final int sessionService = 16843842; // 0x1010442 field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int shadowColor = 16843105; // 0x1010161 field public static final int shadowDx = 16843106; // 0x1010162 @@ -1035,7 +1033,7 @@ package android { field public static final int shrinkColumns = 16843082; // 0x101014a field public static final deprecated int singleLine = 16843101; // 0x101015d field public static final int singleUser = 16843711; // 0x10103bf - field public static final int slideEdge = 16843830; // 0x1010436 + field public static final int slideEdge = 16843828; // 0x1010434 field public static final int smallIcon = 16843422; // 0x101029e field public static final int smallScreens = 16843396; // 0x1010284 field public static final int smoothScrollbar = 16843313; // 0x1010231 @@ -1047,19 +1045,19 @@ package android { field public static final int spinnerStyle = 16842881; // 0x1010081 field public static final int spinnersShown = 16843595; // 0x101034b field public static final int splitMotionEvents = 16843503; // 0x10102ef - field public static final int splitTrack = 16843858; // 0x1010452 + field public static final int splitTrack = 16843856; // 0x1010450 field public static final int src = 16843033; // 0x1010119 field public static final int ssp = 16843747; // 0x10103e3 field public static final int sspPattern = 16843749; // 0x10103e5 field public static final int sspPrefix = 16843748; // 0x10103e4 field public static final int stackFromBottom = 16843005; // 0x10100fd - field public static final int stackViewStyle = 16843845; // 0x1010445 + field public static final int stackViewStyle = 16843843; // 0x1010443 field public static final int starStyle = 16842882; // 0x1010082 field public static final int startColor = 16843165; // 0x101019d field public static final int startDelay = 16843746; // 0x10103e2 field public static final int startOffset = 16843198; // 0x10101be field public static final deprecated int startYear = 16843132; // 0x101017c - field public static final int stateListAnimator = 16843854; // 0x101044e + field public static final int stateListAnimator = 16843852; // 0x101044c field public static final int stateNotNeeded = 16842774; // 0x1010016 field public static final int state_above_anchor = 16842922; // 0x10100aa field public static final int state_accelerated = 16843547; // 0x101031b @@ -1095,7 +1093,7 @@ package android { field public static final int strokeOpacity = 16843811; // 0x1010423 field public static final int strokeWidth = 16843812; // 0x1010424 field public static final int subtitle = 16843473; // 0x10102d1 - field public static final int subtitleTextAppearance = 16843829; // 0x1010435 + field public static final int subtitleTextAppearance = 16843827; // 0x1010433 field public static final int subtitleTextStyle = 16843513; // 0x10102f9 field public static final int subtypeExtraValue = 16843674; // 0x101039a field public static final int subtypeId = 16843713; // 0x10103c1 @@ -1112,7 +1110,7 @@ package android { field public static final int switchMinWidth = 16843632; // 0x1010370 field public static final int switchPadding = 16843633; // 0x1010371 field public static final int switchPreferenceStyle = 16843629; // 0x101036d - field public static final int switchStyle = 16843846; // 0x1010446 + field public static final int switchStyle = 16843844; // 0x1010444 field public static final int switchTextAppearance = 16843630; // 0x101036e field public static final int switchTextOff = 16843628; // 0x101036c field public static final int switchTextOn = 16843627; // 0x101036b @@ -1128,7 +1126,7 @@ package android { field public static final int targetId = 16843740; // 0x10103dc field public static final int targetPackage = 16842785; // 0x1010021 field public static final int targetSdkVersion = 16843376; // 0x1010270 - field public static final int targetViewName = 16843859; // 0x1010453 + field public static final int targetViewName = 16843857; // 0x1010451 field public static final int taskAffinity = 16842770; // 0x1010012 field public static final int taskCloseEnterAnimation = 16842942; // 0x10100be field public static final int taskCloseExitAnimation = 16842943; // 0x10100bf @@ -1150,7 +1148,7 @@ package android { field public static final int textAppearanceLargeInverse = 16842819; // 0x1010043 field public static final int textAppearanceLargePopupMenu = 16843521; // 0x1010301 field public static final int textAppearanceListItem = 16843678; // 0x101039e - field public static final int textAppearanceListItemSecondary = 16843832; // 0x1010438 + field public static final int textAppearanceListItemSecondary = 16843830; // 0x1010436 field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f field public static final int textAppearanceMedium = 16842817; // 0x1010041 field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044 @@ -1214,11 +1212,11 @@ package android { field public static final int tintMode = 16843798; // 0x1010416 field public static final int title = 16843233; // 0x10101e1 field public static final int titleCondensed = 16843234; // 0x10101e2 - field public static final int titleTextAppearance = 16843828; // 0x1010434 + field public static final int titleTextAppearance = 16843826; // 0x1010432 field public static final int titleTextStyle = 16843512; // 0x10102f8 field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 - field public static final int toId = 16843855; // 0x101044f + field public static final int toId = 16843853; // 0x101044d field public static final int toScene = 16843742; // 0x10103de field public static final int toXDelta = 16843207; // 0x10101c7 field public static final int toXScale = 16843203; // 0x10101c3 @@ -1298,8 +1296,8 @@ package android { field public static final int windowActionBar = 16843469; // 0x10102cd field public static final int windowActionBarOverlay = 16843492; // 0x10102e4 field public static final int windowActionModeOverlay = 16843485; // 0x10102dd - field public static final int windowAllowEnterTransitionOverlap = 16843843; // 0x1010443 - field public static final int windowAllowExitTransitionOverlap = 16843842; // 0x1010442 + field public static final int windowAllowEnterTransitionOverlap = 16843841; // 0x1010441 + field public static final int windowAllowExitTransitionOverlap = 16843840; // 0x1010440 field public static final int windowAnimationStyle = 16842926; // 0x10100ae field public static final int windowBackground = 16842836; // 0x1010054 field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b @@ -1309,9 +1307,9 @@ package android { field public static final int windowDisablePreview = 16843298; // 0x1010222 field public static final int windowEnableSplitTouch = 16843543; // 0x1010317 field public static final int windowEnterAnimation = 16842932; // 0x10100b4 - field public static final int windowEnterTransition = 16843838; // 0x101043e + field public static final int windowEnterTransition = 16843836; // 0x101043c field public static final int windowExitAnimation = 16842933; // 0x10100b5 - field public static final int windowExitTransition = 16843839; // 0x101043f + field public static final int windowExitTransition = 16843837; // 0x101043d field public static final int windowFrame = 16842837; // 0x1010055 field public static final int windowFullscreen = 16843277; // 0x101020d field public static final int windowHideAnimation = 16842935; // 0x10100b7 @@ -1322,8 +1320,8 @@ package android { field public static final int windowNoDisplay = 16843294; // 0x101021e field public static final int windowNoTitle = 16842838; // 0x1010056 field public static final int windowOverscan = 16843727; // 0x10103cf - field public static final int windowSharedElementEnterTransition = 16843840; // 0x1010440 - field public static final int windowSharedElementExitTransition = 16843841; // 0x1010441 + field public static final int windowSharedElementEnterTransition = 16843838; // 0x101043e + field public static final int windowSharedElementExitTransition = 16843839; // 0x101043f field public static final int windowShowAnimation = 16842934; // 0x10100b6 field public static final int windowShowWallpaper = 16843410; // 0x1010292 field public static final int windowSoftInputMode = 16843307; // 0x101022b @@ -19644,7 +19642,7 @@ package android.os { field public static final int JELLY_BEAN_MR1 = 17; // 0x11 field public static final int JELLY_BEAN_MR2 = 18; // 0x12 field public static final int KITKAT = 19; // 0x13 - field public static final int KITKAT_WATCH = 10000; // 0x2710 + field public static final int KITKAT_WATCH = 20; // 0x14 field public static final int L = 10000; // 0x2710 } @@ -24008,25 +24006,32 @@ package android.provider { } public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns { - field public static final java.lang.String PACKAGE_NAME = "package_name"; + field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name"; } public static final class TvContract.Channels implements android.provider.TvContract.BaseTvColumns { - field public static final java.lang.String BROWSABLE = "browsable"; + field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; + field public static final java.lang.String COLUMN_DATA = "data"; + field public static final java.lang.String COLUMN_DESCRIPTION = "description"; + field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name"; + field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number"; + field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id"; + field public static final java.lang.String COLUMN_SERVICE_ID = "service_id"; + field public static final java.lang.String COLUMN_SERVICE_NAME = "service_name"; + field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type"; + field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id"; + field public static final java.lang.String COLUMN_TYPE = "type"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.android.tv.channels"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.android.tv.channels"; field public static final android.net.Uri CONTENT_URI; - field public static final java.lang.String DATA = "data"; - field public static final java.lang.String DESCRIPTION = "description"; - field public static final java.lang.String DISPLAY_NAME = "display_name"; - field public static final java.lang.String DISPLAY_NUMBER = "display_number"; - field public static final java.lang.String SERVICE_NAME = "service_name"; - field public static final java.lang.String TRANSPORT_STREAM_ID = "transport_stream_id"; - field public static final java.lang.String TYPE = "type"; + field public static final int SERVICE_TYPE_OTHER = 0; // 0x0 + field public static final int SERVICE_TYPE_RADIO = 2; // 0x2 + field public static final int SERVICE_TYPE_TV = 1; // 0x1 field public static final int TYPE_1SEG = 263168; // 0x40400 - field public static final int TYPE_ATSC = 196608; // 0x30000 - field public static final int TYPE_ATSC_2_0 = 196609; // 0x30001 - field public static final int TYPE_ATSC_M_H = 196864; // 0x30100 + field public static final int TYPE_ATSC_C = 197120; // 0x30200 + field public static final int TYPE_ATSC_M_H = 197120; // 0x30200 + field public static final int TYPE_ATSC_T = 196608; // 0x30000 field public static final int TYPE_CMMB = 327936; // 0x50100 field public static final int TYPE_DTMB = 327680; // 0x50000 field public static final int TYPE_DVB_C = 131584; // 0x20200 @@ -24045,21 +24050,20 @@ package android.provider { field public static final int TYPE_PASSTHROUGH = 65536; // 0x10000 field public static final int TYPE_S_DMB = 393472; // 0x60100 field public static final int TYPE_T_DMB = 393216; // 0x60000 - field public static final java.lang.String VERSION_NUMBER = "version_number"; } public static final class TvContract.Programs implements android.provider.TvContract.BaseTvColumns { - field public static final java.lang.String CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id"; + field public static final java.lang.String COLUMN_DATA = "data"; + field public static final java.lang.String COLUMN_DESCRIPTION = "description"; + field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; + field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description"; + field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis"; + field public static final java.lang.String COLUMN_TITLE = "title"; + field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.android.tv.programs"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.android.tv.programs"; field public static final android.net.Uri CONTENT_URI; - field public static final java.lang.String DATA = "data"; - field public static final java.lang.String DESCRIPTION = "description"; - field public static final java.lang.String END_TIME_UTC_MILLIS = "end_time_utc_millis"; - field public static final java.lang.String LONG_DESCRIPTION = "long_description"; - field public static final java.lang.String START_TIME_UTC_MILLIS = "start_time_utc_millis"; - field public static final java.lang.String TITLE = "title"; - field public static final java.lang.String VERSION_NUMBER = "version_number"; } public class UserDictionary { diff --git a/cmds/svc/src/com/android/commands/svc/DataCommand.java b/cmds/svc/src/com/android/commands/svc/DataCommand.java index 72cb86d..406e33b 100644 --- a/cmds/svc/src/com/android/commands/svc/DataCommand.java +++ b/cmds/svc/src/com/android/commands/svc/DataCommand.java @@ -36,9 +36,7 @@ public class DataCommand extends Svc.Command { return shortHelp() + "\n" + "\n" + "usage: svc data [enable|disable]\n" - + " Turn mobile data on or off.\n\n" - + " svc data prefer\n" - + " Set mobile as the preferred data network\n"; + + " Turn mobile data on or off.\n\n"; } public void run(String[] args) { @@ -51,15 +49,6 @@ public class DataCommand extends Svc.Command { } else if ("disable".equals(args[1])) { flag = false; validCommand = true; - } else if ("prefer".equals(args[1])) { - IConnectivityManager connMgr = - IConnectivityManager.Stub.asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - try { - connMgr.setNetworkPreference(ConnectivityManager.TYPE_MOBILE); - } catch (RemoteException e) { - System.err.println("Failed to set preferred network: " + e); - } - return; } if (validCommand) { ITelephony phoneMgr @@ -78,4 +67,4 @@ public class DataCommand extends Svc.Command { } System.err.println(longHelp()); } -}
\ No newline at end of file +} diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java index d29e8b2..39f0e35 100644 --- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java +++ b/cmds/svc/src/com/android/commands/svc/WifiCommand.java @@ -36,9 +36,7 @@ public class WifiCommand extends Svc.Command { return shortHelp() + "\n" + "\n" + "usage: svc wifi [enable|disable]\n" - + " Turn Wi-Fi on or off.\n\n" - + " svc wifi prefer\n" - + " Set Wi-Fi as the preferred data network\n"; + + " Turn Wi-Fi on or off.\n\n"; } public void run(String[] args) { @@ -51,15 +49,6 @@ public class WifiCommand extends Svc.Command { } else if ("disable".equals(args[1])) { flag = false; validCommand = true; - } else if ("prefer".equals(args[1])) { - IConnectivityManager connMgr = - IConnectivityManager.Stub.asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - try { - connMgr.setNetworkPreference(ConnectivityManager.TYPE_WIFI); - } catch (RemoteException e) { - System.err.println("Failed to set preferred network: " + e); - } - return; } if (validCommand) { IWifiManager wifiMgr @@ -75,4 +64,4 @@ public class WifiCommand extends Svc.Command { } System.err.println(longHelp()); } -}
\ No newline at end of file +} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index b4b3c99..4a30b05 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -30,7 +30,6 @@ import com.android.internal.policy.PolicyManager; import android.annotation.IntDef; import android.annotation.Nullable; -import android.app.admin.DevicePolicyManager; import android.content.ComponentCallbacks2; import android.content.ComponentName; import android.content.ContentResolver; @@ -1150,6 +1149,12 @@ public class Activity extends ContextThemeWrapper } getApplication().dispatchActivityStarted(this); + + final ActivityOptions activityOptions = getActivityOptions(); + if (activityOptions != null && + activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { + mEnterTransitionCoordinator = activityOptions.createEnterActivityTransition(this); + } } /** @@ -5272,19 +5277,29 @@ public class Activity extends ContextThemeWrapper * * @param callback the method to call when all visible Activities behind this one have been * drawn and it is safe to make this Activity translucent again. + * @param options activity options delivered to the activity below this one. The options + * are retrieved using {@link #getActivityOptions}. * * @see #convertFromTranslucent() * @see TranslucentConversionListener * * @hide */ - public void convertToTranslucent(TranslucentConversionListener callback) { + void convertToTranslucent(TranslucentConversionListener callback, ActivityOptions options) { + boolean drawComplete; try { mTranslucentCallback = callback; mChangeCanvasToTranslucent = - ActivityManagerNative.getDefault().convertToTranslucent(mToken); + ActivityManagerNative.getDefault().convertToTranslucent(mToken, options); + drawComplete = true; } catch (RemoteException e) { - // pass + // Make callback return as though it timed out. + mChangeCanvasToTranslucent = false; + drawComplete = false; + } + if (!mChangeCanvasToTranslucent && mTranslucentCallback != null) { + // Window is already translucent. + mTranslucentCallback.onTranslucentConversionComplete(drawComplete); } } @@ -5300,6 +5315,22 @@ public class Activity extends ContextThemeWrapper } /** + * Retrieve the ActivityOptions passed in from the launching activity or passed back + * from an activity launched by this activity in its call to {@link + * #convertToTranslucent(TranslucentConversionListener, ActivityOptions)} + * + * @return The ActivityOptions passed to {@link #convertToTranslucent}. + * @hide + */ + ActivityOptions getActivityOptions() { + try { + return ActivityManagerNative.getDefault().getActivityOptions(mToken); + } catch (RemoteException e) { + } + return null; + } + + /** * Adjust the current immersive mode setting. * * Note that changing this value will have no effect on the activity's @@ -5533,30 +5564,12 @@ public class Activity extends ContextThemeWrapper mParent = parent; } - final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, - Application application, Intent intent, ActivityInfo info, CharSequence title, - Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, - Configuration config) { - attach(context, aThread, instr, token, 0, application, intent, info, title, parent, id, - lastNonConfigurationInstances, config); - } - - final void attach(Context context, ActivityThread aThread, - Instrumentation instr, IBinder token, int ident, - Application application, Intent intent, ActivityInfo info, - CharSequence title, Activity parent, String id, - NonConfigurationInstances lastNonConfigurationInstances, - Configuration config) { - attach(context, aThread, instr, token, ident, application, intent, info, title, parent, id, - lastNonConfigurationInstances, config, null, null); - } - final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, - Configuration config, Bundle options, IVoiceInteractor voiceInteractor) { + Configuration config, IVoiceInteractor voiceInteractor) { attachBaseContext(context); mFragments.attachActivity(this, mContainer, null); @@ -5597,12 +5610,6 @@ public class Activity extends ContextThemeWrapper } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config; - if (options != null) { - ActivityOptions activityOptions = new ActivityOptions(options); - if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { - mEnterTransitionCoordinator = activityOptions.createEnterActivityTransition(this); - } - } } /** @hide */ @@ -5873,7 +5880,7 @@ public class Activity extends ContextThemeWrapper * occurred waiting for the Activity to complete drawing. * * @see Activity#convertFromTranslucent() - * @see Activity#convertToTranslucent(TranslucentConversionListener) + * @see Activity#convertToTranslucent(TranslucentConversionListener, ActivityOptions) */ public void onTranslucentConversionComplete(boolean drawComplete); } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 296e9d3..2f924d3 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1547,12 +1547,28 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case CONVERT_TO_TRANSLUCENT_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); - boolean converted = convertToTranslucent(token); + final Bundle bundle; + if (data.readInt() == 0) { + bundle = null; + } else { + bundle = data.readBundle(); + } + final ActivityOptions options = bundle == null ? null : new ActivityOptions(bundle); + boolean converted = convertToTranslucent(token, options); reply.writeNoException(); reply.writeInt(converted ? 1 : 0); return true; } + case GET_ACTIVITY_OPTIONS_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + final ActivityOptions options = getActivityOptions(token); + reply.writeNoException(); + reply.writeBundle(options == null ? null : options.toBundle()); + return true; + } + case SET_IMMERSIVE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -4074,12 +4090,18 @@ class ActivityManagerProxy implements IActivityManager return res; } - public boolean convertToTranslucent(IBinder token) + public boolean convertToTranslucent(IBinder token, ActivityOptions options) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); + if (options == null) { + data.writeInt(0); + } else { + data.writeInt(1); + data.writeBundle(options.toBundle()); + } mRemote.transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, reply, 0); reply.readException(); boolean res = reply.readInt() != 0; @@ -4088,6 +4110,20 @@ class ActivityManagerProxy implements IActivityManager return res; } + public ActivityOptions getActivityOptions(IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(GET_ACTIVITY_OPTIONS_TRANSACTION, data, reply, 0); + reply.readException(); + Bundle bundle = reply.readBundle(); + ActivityOptions options = bundle == null ? null : new ActivityOptions(bundle); + data.recycle(); + reply.recycle(); + return options; + } + public void setImmersive(IBinder token, boolean immersive) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 4cf30ae..71e4e82 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -77,7 +77,6 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.LogPrinter; -import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SuperNotCalledException; @@ -295,7 +294,6 @@ public final class ActivityThread { boolean isForward; int pendingConfigChanges; boolean onlyLocalRequest; - Bundle activityOptions; View mPendingRemoveWindow; WindowManager mPendingRemoveWindowManager; @@ -594,8 +592,7 @@ public final class ActivityThread { public final void scheduleResumeActivity(IBinder token, int processState, boolean isForward, Bundle resumeArgs) { updateProcessState(processState, false); - sendMessage(H.RESUME_ACTIVITY, new Pair<IBinder, Bundle>(token, resumeArgs), - isForward ? 1 : 0); + sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0); } public final void scheduleSendResult(IBinder token, List<ResultInfo> results) { @@ -612,8 +609,7 @@ public final class ActivityThread { IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, - String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, - Bundle resumeArgs) { + String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) { updateProcessState(procState, false); @@ -637,7 +633,6 @@ public final class ActivityThread { r.profileFile = profileName; r.profileFd = profileFd; r.autoStopProfiler = autoStopProfiler; - r.activityOptions = resumeArgs; updatePendingConfiguration(curConfig); @@ -1302,9 +1297,7 @@ public final class ActivityThread { break; case RESUME_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume"); - final Pair<IBinder, Bundle> resumeArgs = (Pair<IBinder, Bundle>) msg.obj; - handleResumeActivity(resumeArgs.first, resumeArgs.second, true, - msg.arg1 != 0, true); + handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case SEND_RESULT: @@ -2084,7 +2077,7 @@ public final class ActivityThread { + ", comp=" + name + ", token=" + token); } - return performLaunchActivity(r, null, null); + return performLaunchActivity(r, null); } public final Activity getActivity(IBinder token) { @@ -2137,8 +2130,7 @@ public final class ActivityThread { sendMessage(H.CLEAN_UP_CONTEXT, cci); } - private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent, - Bundle options) { + private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); ActivityInfo aInfo = r.activityInfo; @@ -2196,7 +2188,7 @@ public final class ActivityThread { + r.activityInfo.name + " with config " + config); activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, - r.embeddedID, r.lastNonConfigurationInstances, config, options, + r.embeddedID, r.lastNonConfigurationInstances, config, r.voiceInteractor); if (customIntent != null) { @@ -2322,12 +2314,12 @@ public final class ActivityThread { if (localLOGV) Slog.v( TAG, "Handling launch of " + r); - Activity a = performLaunchActivity(r, customIntent, r.activityOptions); + Activity a = performLaunchActivity(r, customIntent); if (a != null) { r.createdConfig = new Configuration(mConfiguration); Bundle oldState = r.state; - handleResumeActivity(r.token, r.activityOptions, false, r.isForward, + handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed); if (!r.activity.mFinished && r.startsNotResumed) { @@ -2887,7 +2879,7 @@ public final class ActivityThread { r.mPendingRemoveWindowManager = null; } - final void handleResumeActivity(IBinder token, Bundle resumeArgs, + final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. @@ -3810,7 +3802,6 @@ public final class ActivityThread { } } r.startsNotResumed = tmp.startsNotResumed; - r.activityOptions = null; handleLaunchActivity(r, currentIntent); } diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java index ab148a9..4ce7835 100644 --- a/core/java/android/app/AlertDialog.java +++ b/core/java/android/app/AlertDialog.java @@ -92,6 +92,18 @@ public class AlertDialog extends Dialog implements DialogInterface { * the device's default alert theme with a light background. */ public static final int THEME_DEVICE_DEFAULT_LIGHT = 5; + + /** + * No layout hint. + * @hide + */ + public static final int LAYOUT_HINT_NONE = 0; + + /** + * Hint layout to the side. + * @hide + */ + public static final int LAYOUT_HINT_SIDE = 1; protected AlertDialog(Context context) { this(context, resolveDialogTheme(context, 0), true); @@ -208,6 +220,14 @@ public class AlertDialog extends Dialog implements DialogInterface { } /** + * Internal api to allow hinting for the best button panel layout. + * @hide + */ + void setButtonPanelLayoutHint(int layoutHint) { + mAlert.setButtonPanelLayoutHint(layoutHint); + } + + /** * Set a message to be sent when a button is pressed. * * @param whichButton Which button to set the message for, can be one of diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 0029efa..ef4099f 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -152,11 +152,10 @@ public abstract class ApplicationThreadNative extends Binder ParcelFileDescriptor profileFd = data.readInt() != 0 ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; boolean autoStopProfiler = data.readInt() != 0; - Bundle resumeArgs = data.readBundle(); scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, voiceInteractor, procState, state, persistentState, ri, pi, notResumed, isForward, profileName, profileFd, - autoStopProfiler, resumeArgs); + autoStopProfiler); return true; } @@ -737,8 +736,7 @@ class ApplicationThreadProxy implements IApplicationThread { IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, - String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, - Bundle resumeArgs) + String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -764,7 +762,6 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeInt(0); } data.writeInt(autoStopProfiler ? 1 : 0); - data.writeBundle(resumeArgs); mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java index d168800..26c2c30 100644 --- a/core/java/android/app/DatePickerDialog.java +++ b/core/java/android/app/DatePickerDialog.java @@ -107,6 +107,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, (LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.date_picker_dialog, null); setView(view); + setButtonPanelLayoutHint(LAYOUT_HINT_SIDE); mDatePicker = (DatePicker) view.findViewById(R.id.datePicker); mDatePicker.init(year, monthOfYear, dayOfMonth, this); updateTitle(year, monthOfYear, dayOfMonth); diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index cbb8359..d2d8ed1 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -121,7 +121,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator mActivity.convertFromTranslucent(); } } - }); + }, null); Drawable background = getDecor().getBackground(); if (background != null) { window.setBackgroundDrawable(null); @@ -230,7 +230,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator public void onTranslucentConversionComplete(boolean drawComplete) { fadeOutBackground(); } - }); + }, null); } else { fadeOutBackground(); } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index a30a64a..d259b30 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -311,8 +311,9 @@ public interface IActivityManager extends IInterface { public void finishHeavyWeightApp() throws RemoteException; public boolean convertFromTranslucent(IBinder token) throws RemoteException; - public boolean convertToTranslucent(IBinder token) throws RemoteException; + public boolean convertToTranslucent(IBinder token, ActivityOptions options) throws RemoteException; public void notifyActivityDrawn(IBinder token) throws RemoteException; + public ActivityOptions getActivityOptions(IBinder token) throws RemoteException; public void setImmersive(IBinder token, boolean immersive) throws RemoteException; public boolean isImmersive(IBinder token) throws RemoteException; @@ -739,4 +740,5 @@ public interface IActivityManager extends IInterface { int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216; int SET_RECENTS_ACTIVITY_VALUES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217; int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218; + int GET_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+219; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index d5fbd0b..d0df7c3 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -62,8 +62,7 @@ public interface IApplicationThread extends IInterface { IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, - String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, - Bundle resumeArgs) + String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) throws RemoteException; void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, int configChanges, diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index bb3e002..b78b9c9 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1036,10 +1036,10 @@ public class Instrumentation { IllegalAccessException { Activity activity = (Activity)clazz.newInstance(); ActivityThread aThread = null; - activity.attach(context, aThread, this, token, application, intent, + activity.attach(context, aThread, this, token, 0, application, intent, info, title, parent, id, (Activity.NonConfigurationInstances)lastNonConfigurationInstance, - new Configuration()); + new Configuration(), null); return activity; } diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 96c7246..9ec7f41 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -340,9 +340,11 @@ public abstract class LauncherActivity extends ListActivity { super.onCreate(icicle); mPackageManager = getPackageManager(); - - requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - setProgressBarIndeterminateVisibility(true); + + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setProgressBarIndeterminateVisibility(true); + } onSetContentView(); mIconResizer = new IconResizer(); @@ -357,7 +359,9 @@ public abstract class LauncherActivity extends ListActivity { updateAlertTitle(); updateButtonText(); - setProgressBarIndeterminateVisibility(false); + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + setProgressBarIndeterminateVisibility(false); + } } private void updateAlertTitle() { diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java index a85c61f..8cf8c25 100644 --- a/core/java/android/app/TimePickerDialog.java +++ b/core/java/android/app/TimePickerDialog.java @@ -110,6 +110,7 @@ public class TimePickerDialog extends AlertDialog (LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.time_picker_dialog, null); setView(view); + setButtonPanelLayoutHint(LAYOUT_HINT_SIDE); mTimePicker = (TimePicker) view.findViewById(R.id.timePicker); // Initialize state diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 6e38a22..6659278 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -17,6 +17,7 @@ package android.hardware.camera2; import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.utils.TypeReference; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -126,11 +127,13 @@ public abstract class CameraMetadata { return keyList; } + // TODO: make final or abstract public static class Key<T> { private boolean mHasTag; private int mTag; private final Class<T> mType; + private final TypeReference<T> mTypeReference; private final String mName; /** @@ -144,6 +147,22 @@ public abstract class CameraMetadata { } mName = name; mType = type; + mTypeReference = TypeReference.createSpecializedTypeReference(type); + } + + /** + * @hide + */ + @SuppressWarnings("unchecked") + public Key(String name, TypeReference<T> typeReference) { + if (name == null) { + throw new NullPointerException("Key needs a valid name"); + } else if (typeReference == null) { + throw new NullPointerException("TypeReference needs to be non-null"); + } + mName = name; + mType = (Class<T>)typeReference.getRawType(); + mTypeReference = typeReference; } public final String getName() { @@ -152,11 +171,10 @@ public abstract class CameraMetadata { @Override public final int hashCode() { - return mName.hashCode(); + return mName.hashCode() ^ mTypeReference.hashCode(); } @Override - @SuppressWarnings("unchecked") public final boolean equals(Object o) { if (this == o) { return true; @@ -166,9 +184,8 @@ public abstract class CameraMetadata { return false; } - Key lhs = (Key) o; - - return mName.equals(lhs.mName) && mType.equals(lhs.mType); + Key<?> lhs = (Key<?>)o; + return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference); } /** @@ -192,11 +209,29 @@ public abstract class CameraMetadata { } /** + * Get the raw class backing the type {@code T} for this key. + * + * <p>The distinction is only important if {@code T} is a generic, e.g. + * {@code Range<Integer>} since the nested type will be erased.</p> + * * @hide */ public final Class<T> getType() { + // TODO: remove this; other places should use #getTypeReference() instead return mType; } + + /** + * Get the type reference backing the type {@code T} for this key. + * + * <p>The distinction is only important if {@code T} is a generic, e.g. + * {@code Range<Integer>} since the nested type will be retained.</p> + * + * @hide + */ + public final TypeReference<T> getTypeReference() { + return mTypeReference; + } } /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ diff --git a/core/java/android/hardware/camera2/ColorSpaceTransform.java b/core/java/android/hardware/camera2/ColorSpaceTransform.java index 9912e4b..5e4c0a2 100644 --- a/core/java/android/hardware/camera2/ColorSpaceTransform.java +++ b/core/java/android/hardware/camera2/ColorSpaceTransform.java @@ -17,7 +17,8 @@ package android.hardware.camera2; import static com.android.internal.util.Preconditions.*; -import android.hardware.camera2.impl.HashCodeHelpers; + +import android.hardware.camera2.utils.HashCodeHelpers; import java.util.Arrays; diff --git a/core/java/android/hardware/camera2/LensShadingMap.java b/core/java/android/hardware/camera2/LensShadingMap.java index 2c224f6..2b0108c 100644 --- a/core/java/android/hardware/camera2/LensShadingMap.java +++ b/core/java/android/hardware/camera2/LensShadingMap.java @@ -19,7 +19,7 @@ package android.hardware.camera2; import static com.android.internal.util.Preconditions.*; import static android.hardware.camera2.RggbChannelVector.*; -import android.hardware.camera2.impl.HashCodeHelpers; +import android.hardware.camera2.utils.HashCodeHelpers; import java.util.Arrays; diff --git a/core/java/android/hardware/camera2/MeteringRectangle.java b/core/java/android/hardware/camera2/MeteringRectangle.java index ff7a745..bb8e5b1 100644 --- a/core/java/android/hardware/camera2/MeteringRectangle.java +++ b/core/java/android/hardware/camera2/MeteringRectangle.java @@ -20,7 +20,7 @@ import static com.android.internal.util.Preconditions.*; import android.graphics.Point; import android.graphics.Rect; -import android.hardware.camera2.impl.HashCodeHelpers; +import android.hardware.camera2.utils.HashCodeHelpers; /** * An immutable class to represent a rectangle {@code (x,y, width, height)} with an @@ -186,10 +186,7 @@ public final class MeteringRectangle { */ @Override public boolean equals(final Object other) { - if (other instanceof MeteringRectangle) { - return equals(other); - } - return false; + return other instanceof MeteringRectangle && equals((MeteringRectangle)other); } /** diff --git a/core/java/android/hardware/camera2/Rational.java b/core/java/android/hardware/camera2/Rational.java index 77b8c26..693ee2b 100644 --- a/core/java/android/hardware/camera2/Rational.java +++ b/core/java/android/hardware/camera2/Rational.java @@ -91,14 +91,14 @@ public final class Rational { * <p>A reduced form of a Rational is calculated by dividing both the numerator and the * denominator by their greatest common divisor.</p> * - * <pre> + * <pre>{@code * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction * (new Rational(0, 0)).equals(new Rational(0, 0)) == true // NaN.equals(NaN) * (new Rational(1, 0)).equals(new Rational(5, 0)) == true // both are +infinity * (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity - * </pre> + * }</pre> * * @param obj a reference to another object * @@ -159,16 +159,15 @@ public final class Rational { return (float) mNumerator / (float) mDenominator; } + /** + * {@inheritDoc} + */ @Override public int hashCode() { - final long INT_MASK = 0xffffffffL; - - long asLong = INT_MASK & mNumerator; - asLong <<= 32; - - asLong |= (INT_MASK & mDenominator); + // Bias the hash code for the first (2^16) values for both numerator and denominator + int numeratorFlipped = mNumerator << 16 | mNumerator >>> 16; - return ((Long)asLong).hashCode(); + return mDenominator ^ numeratorFlipped; } /** diff --git a/core/java/android/hardware/camera2/ReprocessFormatsMap.java b/core/java/android/hardware/camera2/ReprocessFormatsMap.java index c6c59d4..894a499 100644 --- a/core/java/android/hardware/camera2/ReprocessFormatsMap.java +++ b/core/java/android/hardware/camera2/ReprocessFormatsMap.java @@ -18,7 +18,7 @@ package android.hardware.camera2; import static com.android.internal.util.Preconditions.*; -import android.hardware.camera2.impl.HashCodeHelpers; +import android.hardware.camera2.utils.HashCodeHelpers; import java.util.Arrays; diff --git a/core/java/android/hardware/camera2/StreamConfiguration.java b/core/java/android/hardware/camera2/StreamConfiguration.java index c53dd7c..a514034 100644 --- a/core/java/android/hardware/camera2/StreamConfiguration.java +++ b/core/java/android/hardware/camera2/StreamConfiguration.java @@ -20,7 +20,7 @@ import static com.android.internal.util.Preconditions.*; import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal; import android.graphics.ImageFormat; -import android.hardware.camera2.impl.HashCodeHelpers; +import android.hardware.camera2.utils.HashCodeHelpers; import android.util.Size; /** @@ -57,7 +57,7 @@ public final class StreamConfiguration { final int format, final int width, final int height, final boolean input) { mFormat = checkArgumentFormatInternal(format); mWidth = checkArgumentPositive(width, "width must be positive"); - mHeight = checkArgumentPositive(width, "height must be positive"); + mHeight = checkArgumentPositive(height, "height must be positive"); mInput = input; } diff --git a/core/java/android/hardware/camera2/StreamConfigurationDuration.java b/core/java/android/hardware/camera2/StreamConfigurationDuration.java index 189ae62..6a31156 100644 --- a/core/java/android/hardware/camera2/StreamConfigurationDuration.java +++ b/core/java/android/hardware/camera2/StreamConfigurationDuration.java @@ -20,7 +20,7 @@ import static com.android.internal.util.Preconditions.*; import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal; import android.graphics.ImageFormat; -import android.hardware.camera2.impl.HashCodeHelpers; +import android.hardware.camera2.utils.HashCodeHelpers; import android.util.Size; /** @@ -54,7 +54,7 @@ public final class StreamConfigurationDuration { final int format, final int width, final int height, final long durationNs) { mFormat = checkArgumentFormatInternal(format); mWidth = checkArgumentPositive(width, "width must be positive"); - mHeight = checkArgumentPositive(width, "height must be positive"); + mHeight = checkArgumentPositive(height, "height must be positive"); mDurationNs = checkArgumentNonnegative(durationNs, "durationNs must be non-negative"); } diff --git a/core/java/android/hardware/camera2/StreamConfigurationMap.java b/core/java/android/hardware/camera2/StreamConfigurationMap.java index e24fd1b..5ddd7d6 100644 --- a/core/java/android/hardware/camera2/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/StreamConfigurationMap.java @@ -18,7 +18,7 @@ package android.hardware.camera2; import android.graphics.ImageFormat; import android.graphics.PixelFormat; -import android.hardware.camera2.impl.HashCodeHelpers; +import android.hardware.camera2.utils.HashCodeHelpers; import android.view.Surface; import android.util.Size; diff --git a/core/java/android/hardware/camera2/TonemapCurve.java b/core/java/android/hardware/camera2/TonemapCurve.java index ee20d68..2958ebf 100644 --- a/core/java/android/hardware/camera2/TonemapCurve.java +++ b/core/java/android/hardware/camera2/TonemapCurve.java @@ -19,7 +19,7 @@ package android.hardware.camera2; import static com.android.internal.util.Preconditions.*; import android.graphics.PointF; -import android.hardware.camera2.impl.HashCodeHelpers; +import android.hardware.camera2.utils.HashCodeHelpers; import java.util.Arrays; diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index c5e5753..9a06e97 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -23,16 +23,33 @@ import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.Face; -import android.hardware.camera2.Rational; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.marshal.MarshalRegistry; +import android.hardware.camera2.marshal.impl.MarshalQueryableArray; +import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean; +import android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform; +import android.hardware.camera2.marshal.impl.MarshalQueryableEnum; +import android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle; +import android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger; +import android.hardware.camera2.marshal.impl.MarshalQueryableParcelable; +import android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive; +import android.hardware.camera2.marshal.impl.MarshalQueryableRange; +import android.hardware.camera2.marshal.impl.MarshalQueryableRect; +import android.hardware.camera2.marshal.impl.MarshalQueryableReprocessFormatsMap; +import android.hardware.camera2.marshal.impl.MarshalQueryableRggbChannelVector; +import android.hardware.camera2.marshal.impl.MarshalQueryableSize; +import android.hardware.camera2.marshal.impl.MarshalQueryableSizeF; +import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration; +import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration; +import android.hardware.camera2.marshal.impl.MarshalQueryableString; import android.os.Parcelable; import android.os.Parcel; import android.util.Log; -import java.lang.reflect.Array; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; -import java.util.HashMap; /** * Implementation of camera metadata marshal/unmarshal across Binder to @@ -89,7 +106,6 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { nativeWriteToParcel(dest); } - @SuppressWarnings("unchecked") @Override public <T> T get(Key<T> key) { T value = getOverride(key); @@ -169,275 +185,6 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final } - private static int getTypeSize(int nativeType) { - switch(nativeType) { - case TYPE_BYTE: - return 1; - case TYPE_INT32: - case TYPE_FLOAT: - return 4; - case TYPE_INT64: - case TYPE_DOUBLE: - case TYPE_RATIONAL: - return 8; - } - - throw new UnsupportedOperationException("Unknown type, can't get size " - + nativeType); - } - - private static Class<?> getExpectedType(int nativeType) { - switch(nativeType) { - case TYPE_BYTE: - return Byte.TYPE; - case TYPE_INT32: - return Integer.TYPE; - case TYPE_FLOAT: - return Float.TYPE; - case TYPE_INT64: - return Long.TYPE; - case TYPE_DOUBLE: - return Double.TYPE; - case TYPE_RATIONAL: - return Rational.class; - } - - throw new UnsupportedOperationException("Unknown type, can't map to Java type " - + nativeType); - } - - @SuppressWarnings("unchecked") - private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type, - int nativeType, boolean sizeOnly) { - - if (!sizeOnly) { - /** - * Rewrite types when the native type doesn't match the managed type - * - Boolean -> Byte - * - Integer -> Byte - */ - - if (nativeType == TYPE_BYTE && type == Boolean.TYPE) { - // Since a boolean can't be cast to byte, and we don't want to use putBoolean - boolean asBool = (Boolean) value; - byte asByte = (byte) (asBool ? 1 : 0); - value = (T) (Byte) asByte; - } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) { - int asInt = (Integer) value; - byte asByte = (byte) asInt; - value = (T) (Byte) asByte; - } else if (type != getExpectedType(nativeType)) { - throw new UnsupportedOperationException("Tried to pack a type of " + type + - " but we expected the type to be " + getExpectedType(nativeType)); - } - - if (nativeType == TYPE_BYTE) { - buffer.put((Byte) value); - } else if (nativeType == TYPE_INT32) { - buffer.putInt((Integer) value); - } else if (nativeType == TYPE_FLOAT) { - buffer.putFloat((Float) value); - } else if (nativeType == TYPE_INT64) { - buffer.putLong((Long) value); - } else if (nativeType == TYPE_DOUBLE) { - buffer.putDouble((Double) value); - } else if (nativeType == TYPE_RATIONAL) { - Rational r = (Rational) value; - buffer.putInt(r.getNumerator()); - buffer.putInt(r.getDenominator()); - } - - } - - return getTypeSize(nativeType); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType, - boolean sizeOnly) { - - int size = 0; - - if (type.isPrimitive() || type == Rational.class) { - size = packSingleNative(value, buffer, type, nativeType, sizeOnly); - } else if (type.isEnum()) { - size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly); - } else if (type.isArray()) { - size = packArray(value, buffer, type, nativeType, sizeOnly); - } else { - size = packClass(value, buffer, type, nativeType, sizeOnly); - } - - return size; - } - - private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type, - int nativeType, boolean sizeOnly) { - - // TODO: add support for enums with their own values. - return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly); - } - - @SuppressWarnings("unchecked") - private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType, - boolean sizeOnly) { - - MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType); - if (marshaler == null) { - throw new IllegalArgumentException(String.format("Unknown Key type: %s", type)); - } - - return marshaler.marshal(value, buffer, nativeType, sizeOnly); - } - - private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType, - boolean sizeOnly) { - - int size = 0; - int arrayLength = Array.getLength(value); - - @SuppressWarnings("unchecked") - Class<Object> componentType = (Class<Object>)type.getComponentType(); - - for (int i = 0; i < arrayLength; ++i) { - size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly); - } - - return size; - } - - @SuppressWarnings("unchecked") - private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) { - - T val; - - if (nativeType == TYPE_BYTE) { - val = (T) (Byte) buffer.get(); - } else if (nativeType == TYPE_INT32) { - val = (T) (Integer) buffer.getInt(); - } else if (nativeType == TYPE_FLOAT) { - val = (T) (Float) buffer.getFloat(); - } else if (nativeType == TYPE_INT64) { - val = (T) (Long) buffer.getLong(); - } else if (nativeType == TYPE_DOUBLE) { - val = (T) (Double) buffer.getDouble(); - } else if (nativeType == TYPE_RATIONAL) { - val = (T) new Rational(buffer.getInt(), buffer.getInt()); - } else { - throw new UnsupportedOperationException("Unknown type, can't unpack a native type " - + nativeType); - } - - /** - * Rewrite types when the native type doesn't match the managed type - * - Byte -> Boolean - * - Byte -> Integer - */ - - if (nativeType == TYPE_BYTE && type == Boolean.TYPE) { - // Since a boolean can't be cast to byte, and we don't want to use getBoolean - byte asByte = (Byte) val; - boolean asBool = asByte != 0; - val = (T) (Boolean) asBool; - } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) { - byte asByte = (Byte) val; - int asInt = asByte; - val = (T) (Integer) asInt; - } else if (type != getExpectedType(nativeType)) { - throw new UnsupportedOperationException("Tried to unpack a type of " + type + - " but we expected the type to be " + getExpectedType(nativeType)); - } - - return val; - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) { - - if (type.isPrimitive() || type == Rational.class) { - return unpackSingleNative(buffer, type, nativeType); - } - - if (type.isEnum()) { - return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType); - } - - if (type.isArray()) { - return unpackArray(buffer, type, nativeType); - } - - T instance = unpackClass(buffer, type, nativeType); - - return instance; - } - - private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type, - int nativeType) { - int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType); - return getEnumFromValue(type, ordinal); - } - - private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) { - - MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType); - if (marshaler == null) { - throw new IllegalArgumentException("Unknown class type: " + type); - } - - return marshaler.unmarshal(buffer, nativeType); - } - - @SuppressWarnings("unchecked") - private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) { - - Class<?> componentType = type.getComponentType(); - Object array; - - int elementSize = getTypeSize(nativeType); - - MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType); - if (marshaler != null) { - elementSize = marshaler.getNativeSize(nativeType); - } - - if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) { - int remaining = buffer.remaining(); - int arraySize = remaining / elementSize; - - if (VERBOSE) { - Log.v(TAG, - String.format( - "Attempting to unpack array (count = %d, element size = %d, bytes " + - "remaining = %d) for type %s", - arraySize, elementSize, remaining, type)); - } - - array = Array.newInstance(componentType, arraySize); - for (int i = 0; i < arraySize; ++i) { - Object elem = unpackSingle(buffer, componentType, nativeType); - Array.set(array, i, elem); - } - } else { - // Dynamic size, use an array list. - ArrayList<Object> arrayList = new ArrayList<Object>(); - - int primitiveSize = getTypeSize(nativeType); - while (buffer.remaining() >= primitiveSize) { - Object elem = unpackSingle(buffer, componentType, nativeType); - arrayList.add(elem); - } - - array = arrayList.toArray((T[]) Array.newInstance(componentType, 0)); - } - - if (buffer.remaining() != 0) { - Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking " - + type); - } - - return (T) array; - } - private <T> T getBase(Key<T> key) { int tag = key.getTag(); byte[] values = readValues(tag); @@ -445,10 +192,9 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return null; } - int nativeType = getNativeType(tag); - + Marshaler<T> marshaler = getMarshalerForKey(key); ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - return unpackSingle(buffer, key.getType(), nativeType); + return marshaler.unmarshal(buffer); } // Need overwrite some metadata that has different definitions between native @@ -632,19 +378,19 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { int tag = key.getTag(); if (value == null) { - writeValues(tag, null); + // Erase the entry + writeValues(tag, /*src*/null); return; - } - - int nativeType = getNativeType(tag); + } // else update the entry to a new value - int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true); + Marshaler<T> marshaler = getMarshalerForKey(key); + int size = marshaler.calculateMarshalSize(value); // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. byte[] values = new byte[size]; ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false); + marshaler.marshal(value, buffer); writeValues(tag, values); } @@ -870,125 +616,64 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { } } - private static final HashMap<Class<? extends Enum>, int[]> sEnumValues = - new HashMap<Class<? extends Enum>, int[]>(); /** - * Register a non-sequential set of values to be used with the pack/unpack functions. - * This enables get/set to correctly marshal the enum into a value that is C-compatible. + * Get the marshaler compatible with the {@code key} and type {@code T}. * - * @param enumType The class for an enum - * @param values A list of values mapping to the ordinals of the enum - * - * @hide + * @throws UnsupportedOperationException + * if the native/managed type combination for {@code key} is not supported */ - public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) { - if (enumType.getEnumConstants().length != values.length) { - throw new IllegalArgumentException( - "Expected values array to be the same size as the enumTypes values " - + values.length + " for type " + enumType); - } - if (VERBOSE) { - Log.v(TAG, "Registered enum values for type " + enumType + " values"); - } - - sEnumValues.put(enumType, values); - } - - /** - * Get the numeric value from an enum. This is usually the same as the ordinal value for - * enums that have fully sequential values, although for C-style enums the range of values - * may not map 1:1. - * - * @param enumValue Enum instance - * @return Int guaranteed to be ABI-compatible with the C enum equivalent - */ - private static <T extends Enum<T>> int getEnumValue(T enumValue) { - int[] values; - values = sEnumValues.get(enumValue.getClass()); - - int ordinal = enumValue.ordinal(); - if (values != null) { - return values[ordinal]; - } - - return ordinal; + private static <T> Marshaler<T> getMarshalerForKey(Key<T> key) { + return MarshalRegistry.getMarshaler(key.getTypeReference(), + getNativeType(key.getTag())); } - /** - * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method. - * - * @param enumType Class of the enum we want to find - * @param value The numeric value of the enum - * @return An instance of the enum - */ - private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) { - int ordinal; - - int[] registeredValues = sEnumValues.get(enumType); - if (registeredValues != null) { - ordinal = -1; - - for (int i = 0; i < registeredValues.length; ++i) { - if (registeredValues[i] == value) { - ordinal = i; - break; - } - } - } else { - ordinal = value; + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static void registerAllMarshalers() { + if (VERBOSE) { + Log.v(TAG, "Shall register metadata marshalers"); } - T[] values = enumType.getEnumConstants(); - - if (ordinal < 0 || ordinal >= values.length) { - throw new IllegalArgumentException( - String.format( - "Argument 'value' (%d) was not a valid enum value for type %s " - + "(registered? %b)", - value, - enumType, (registeredValues != null))); + MarshalQueryable[] queryList = new MarshalQueryable[] { + // marshalers for standard types + new MarshalQueryablePrimitive(), + new MarshalQueryableEnum(), + new MarshalQueryableArray(), + + // pseudo standard types, that expand/narrow the native type into a managed type + new MarshalQueryableBoolean(), + new MarshalQueryableNativeByteToInteger(), + + // marshalers for custom types + new MarshalQueryableRect(), + new MarshalQueryableSize(), + new MarshalQueryableSizeF(), + new MarshalQueryableString(), + new MarshalQueryableReprocessFormatsMap(), + new MarshalQueryableRange(), + new MarshalQueryableMeteringRectangle(), + new MarshalQueryableColorSpaceTransform(), + new MarshalQueryableStreamConfiguration(), + new MarshalQueryableStreamConfigurationDuration(), + new MarshalQueryableRggbChannelVector(), + + // generic parcelable marshaler (MUST BE LAST since it has lowest priority) + new MarshalQueryableParcelable(), + }; + + for (MarshalQueryable query : queryList) { + MarshalRegistry.registerMarshalQueryable(query); } - - return values[ordinal]; - } - - static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new - HashMap<Class<?>, MetadataMarshalClass<?>>(); - - private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) { - sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler); - } - - @SuppressWarnings("unchecked") - private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) { - MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type); - - if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) { - throw new UnsupportedOperationException("Unsupported type " + nativeType + - " to be marshalled to/from a " + type); + if (VERBOSE) { + Log.v(TAG, "Registered metadata marshalers"); } - - return marshaler; } - /** - * We use a class initializer to allow the native code to cache some field offsets - */ static { + /* + * We use a class initializer to allow the native code to cache some field offsets + */ nativeClassInit(); - - if (VERBOSE) { - Log.v(TAG, "Shall register metadata marshalers"); - } - - // load built-in marshallers - registerMarshaler(new MetadataMarshalRect()); - registerMarshaler(new MetadataMarshalSize()); - registerMarshaler(new MetadataMarshalString()); - - if (VERBOSE) { - Log.v(TAG, "Registered metadata marshalers"); - } + registerAllMarshalers(); } } diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java b/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java deleted file mode 100644 index 6d224ef..0000000 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2013 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.hardware.camera2.impl; - -import java.nio.ByteBuffer; - -public interface MetadataMarshalClass<T> { - - /** - * Marshal the specified object instance (value) into a byte buffer. - * - * @param value the value of type T that we wish to write into the byte buffer - * @param buffer the byte buffer into which the marshalled object will be written - * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}. - * Guaranteed to be one for which isNativeTypeSupported returns true. - * @param sizeOnly if this is true, don't write to the byte buffer. calculate the size only. - * @return the size that needs to be written to the byte buffer - */ - int marshal(T value, ByteBuffer buffer, int nativeType, boolean sizeOnly); - - /** - * Unmarshal a new object instance from the byte buffer. - * @param buffer the byte buffer, from which we will read the object - * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}. - * Guaranteed to be one for which isNativeTypeSupported returns true. - * @return a new instance of type T read from the byte buffer - */ - T unmarshal(ByteBuffer buffer, int nativeType); - - Class<T> getMarshalingClass(); - - /** - * Determines whether or not this marshaller supports this native type. Most marshallers - * will are likely to only support one type. - * - * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE} - * @return true if it supports, false otherwise - */ - boolean isNativeTypeSupported(int nativeType); - - public static int NATIVE_SIZE_DYNAMIC = -1; - - /** - * How many bytes T will take up if marshalled to/from nativeType - * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE} - * @return a size in bytes, or NATIVE_SIZE_DYNAMIC if the size is dynamic - */ - int getNativeSize(int nativeType); -} diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java b/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java deleted file mode 100644 index ab72c4f..0000000 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2013 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.hardware.camera2.impl; - -import android.graphics.Rect; - -import java.nio.ByteBuffer; - -public class MetadataMarshalRect implements MetadataMarshalClass<Rect> { - private static final int SIZE = 16; - - @Override - public int marshal(Rect value, ByteBuffer buffer, int nativeType, boolean sizeOnly) { - if (sizeOnly) { - return SIZE; - } - - buffer.putInt(value.left); - buffer.putInt(value.top); - buffer.putInt(value.width()); - buffer.putInt(value.height()); - - return SIZE; - } - - @Override - public Rect unmarshal(ByteBuffer buffer, int nativeType) { - - int left = buffer.getInt(); - int top = buffer.getInt(); - int width = buffer.getInt(); - int height = buffer.getInt(); - - int right = left + width; - int bottom = top + height; - - return new Rect(left, top, right, bottom); - } - - @Override - public Class<Rect> getMarshalingClass() { - return Rect.class; - } - - @Override - public boolean isNativeTypeSupported(int nativeType) { - return nativeType == CameraMetadataNative.TYPE_INT32; - } - - @Override - public int getNativeSize(int nativeType) { - return SIZE; - } -} diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java b/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java deleted file mode 100644 index e8143e0..0000000 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2013 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.hardware.camera2.impl; - -import android.hardware.camera2.Size; - -import java.nio.ByteBuffer; - -public class MetadataMarshalSize implements MetadataMarshalClass<Size> { - - private static final int SIZE = 8; - - @Override - public int marshal(Size value, ByteBuffer buffer, int nativeType, boolean sizeOnly) { - if (sizeOnly) { - return SIZE; - } - - buffer.putInt(value.getWidth()); - buffer.putInt(value.getHeight()); - - return SIZE; - } - - @Override - public Size unmarshal(ByteBuffer buffer, int nativeType) { - int width = buffer.getInt(); - int height = buffer.getInt(); - - return new Size(width, height); - } - - @Override - public Class<Size> getMarshalingClass() { - return Size.class; - } - - @Override - public boolean isNativeTypeSupported(int nativeType) { - return nativeType == CameraMetadataNative.TYPE_INT32; - } - - @Override - public int getNativeSize(int nativeType) { - return SIZE; - } -} diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java b/core/java/android/hardware/camera2/impl/MetadataMarshalString.java deleted file mode 100644 index b61b8d3..0000000 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2013 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.hardware.camera2.impl; - -import java.nio.ByteBuffer; -import java.nio.charset.Charset; - -public class MetadataMarshalString implements MetadataMarshalClass<String> { - - private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - - @Override - public int marshal(String value, ByteBuffer buffer, int nativeType, boolean sizeOnly) { - byte[] arr = value.getBytes(UTF8_CHARSET); - - if (!sizeOnly) { - buffer.put(arr); - buffer.put((byte)0); // metadata strings are NULL-terminated - } - - return arr.length + 1; - } - - @Override - public String unmarshal(ByteBuffer buffer, int nativeType) { - - buffer.mark(); // save the current position - - boolean foundNull = false; - int stringLength = 0; - while (buffer.hasRemaining()) { - if (buffer.get() == (byte)0) { - foundNull = true; - break; - } - - stringLength++; - } - if (!foundNull) { - throw new IllegalArgumentException("Strings must be null-terminated"); - } - - buffer.reset(); // go back to the previously marked position - - byte[] strBytes = new byte[stringLength + 1]; - buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character - - // not including null character - return new String(strBytes, /*offset*/0, stringLength, UTF8_CHARSET); - } - - @Override - public Class<String> getMarshalingClass() { - return String.class; - } - - @Override - public boolean isNativeTypeSupported(int nativeType) { - return nativeType == CameraMetadataNative.TYPE_BYTE; - } - - @Override - public int getNativeSize(int nativeType) { - return NATIVE_SIZE_DYNAMIC; - } -} diff --git a/core/java/android/hardware/camera2/marshal/MarshalHelpers.java b/core/java/android/hardware/camera2/marshal/MarshalHelpers.java new file mode 100644 index 0000000..fd72ee2 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/MarshalHelpers.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static com.android.internal.util.Preconditions.*; + +import android.hardware.camera2.Rational; +import android.hardware.camera2.impl.CameraMetadataNative; + +/** + * Static functions in order to help implementing various marshaler functionality. + * + * <p>The intention is to statically import everything from this file into another file when + * implementing a new marshaler (or marshal queryable).</p> + * + * <p>The helpers are centered around providing primitive knowledge of the native types, + * such as the native size, the managed class wrappers, and various precondition checks.</p> + */ +public final class MarshalHelpers { + + public static final int SIZEOF_BYTE = 1; + public static final int SIZEOF_INT32 = Integer.SIZE / Byte.SIZE; + public static final int SIZEOF_INT64 = Long.SIZE / Byte.SIZE; + public static final int SIZEOF_FLOAT = Float.SIZE / Byte.SIZE; + public static final int SIZEOF_DOUBLE = Double.SIZE / Byte.SIZE; + public static final int SIZEOF_RATIONAL = SIZEOF_INT32 * 2; + + /** + * Get the size in bytes for the native camera metadata type. + * + * <p>This used to determine how many bytes it would take to encode/decode a single value + * of that {@link nativeType}.</p> + * + * @param nativeType the native type, e.g. + * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}. + * @return size in bytes >= 1 + * + * @throws UnsupportedOperationException if nativeType was not one of the built-in types + */ + public static int getPrimitiveTypeSize(int nativeType) { + switch (nativeType) { + case TYPE_BYTE: + return SIZEOF_BYTE; + case TYPE_INT32: + return SIZEOF_INT32; + case TYPE_FLOAT: + return SIZEOF_FLOAT; + case TYPE_INT64: + return SIZEOF_INT64; + case TYPE_DOUBLE: + return SIZEOF_DOUBLE; + case TYPE_RATIONAL: + return SIZEOF_RATIONAL; + } + + throw new UnsupportedOperationException("Unknown type, can't get size for " + + nativeType); + } + + + /** + * Ensure that the {@code klass} is one of the metadata-primitive classes. + * + * @param klass a non-{@code null} reference + * @return {@code klass} instance + * + * @throws UnsupportedOperationException if klass was not one of the built-in classes + * @throws NullPointerException if klass was null + * + * @see #isPrimitiveClass + */ + public static <T> Class<T> checkPrimitiveClass(Class<T> klass) { + checkNotNull(klass, "klass must not be null"); + + if (isPrimitiveClass(klass)) { + return klass; + } + + throw new UnsupportedOperationException("Unsupported class '" + klass + + "'; expected a metadata primitive class"); + } + + /** + * Checks whether or not {@code klass} is one of the metadata-primitive classes. + * + * <p>The following types (whether boxed or unboxed) are considered primitive: + * <ul> + * <li>byte + * <li>int + * <li>float + * <li>double + * <li>Rational + * </ul> + * </p> + * + * <p>This doesn't strictly follow the java understanding of primitive since + * boxed objects are included, Rational is included, and other types such as char and + * short are not included.</p> + * + * @param klass a {@link Class} instance; using {@code null} will return {@code false} + * @return {@code true} if primitive, {@code false} otherwise + */ + public static <T> boolean isPrimitiveClass(Class<T> klass) { + if (klass == null) { + return false; + } + + if (klass == byte.class || klass == Byte.class) { + return true; + } else if (klass == int.class || klass == Integer.class) { + return true; + } else if (klass == float.class || klass == Float.class) { + return true; + } else if (klass == long.class || klass == Long.class) { + return true; + } else if (klass == double.class || klass == Double.class) { + return true; + } else if (klass == Rational.class) { + return true; + } + + return false; + } + + /** + * Wrap {@code klass} with its wrapper variant if it was a {@code Class} corresponding + * to a Java primitive. + * + * <p>Non-primitive classes are passed through as-is.</p> + * + * <p>For example, for a primitive {@code int.class => Integer.class}, + * but for a non-primitive {@code Rational.class => Rational.class}.</p> + * + * @param klass a {@code Class} reference + * + * @return wrapped class object, or same class object if non-primitive + */ + @SuppressWarnings("unchecked") + public static <T> Class<T> wrapClassIfPrimitive(Class<T> klass) { + if (klass == byte.class) { + return (Class<T>)Byte.class; + } else if (klass == int.class) { + return (Class<T>)Integer.class; + } else if (klass == float.class) { + return (Class<T>)Float.class; + } else if (klass == long.class) { + return (Class<T>)Long.class; + } else if (klass == double.class) { + return (Class<T>)Double.class; + } + + return klass; + } + + /** + * Return a human-readable representation of the {@code nativeType}, e.g. "TYPE_INT32" + * + * <p>Out-of-range values return a string with "UNKNOWN" as the prefix.</p> + * + * @param nativeType the native type + * + * @return human readable type name + */ + public static String toStringNativeType(int nativeType) { + switch (nativeType) { + case TYPE_BYTE: + return "TYPE_BYTE"; + case TYPE_INT32: + return "TYPE_INT32"; + case TYPE_FLOAT: + return "TYPE_FLOAT"; + case TYPE_INT64: + return "TYPE_INT64"; + case TYPE_DOUBLE: + return "TYPE_DOUBLE"; + case TYPE_RATIONAL: + return "TYPE_RATIONAL"; + } + + return "UNKNOWN(" + nativeType + ")"; + } + + /** + * Ensure that the {@code nativeType} is one of the native types supported + * by {@link CameraMetadataNative}. + * + * @param nativeType the native type + * + * @return the native type + * + * @throws UnsupportedOperationException if the native type was invalid + */ + public static int checkNativeType(int nativeType) { + switch (nativeType) { + case TYPE_BYTE: + case TYPE_INT32: + case TYPE_FLOAT: + case TYPE_INT64: + case TYPE_DOUBLE: + case TYPE_RATIONAL: + return nativeType; + } + + throw new UnsupportedOperationException("Unknown nativeType " + nativeType); + } + + /** + * Ensure that the expected and actual native types are equal. + * + * @param expectedNativeType the expected native type + * @param actualNativeType the actual native type + * @return the actual native type + * + * @throws UnsupportedOperationException if the types are not equal + */ + public static int checkNativeTypeEquals(int expectedNativeType, int actualNativeType) { + if (expectedNativeType != actualNativeType) { + throw new UnsupportedOperationException( + String.format("Expected native type %d, but got %d", + expectedNativeType, actualNativeType)); + } + + return actualNativeType; + } + + private MarshalHelpers() { + throw new AssertionError(); + } +} diff --git a/core/java/android/hardware/camera2/marshal/MarshalQueryable.java b/core/java/android/hardware/camera2/marshal/MarshalQueryable.java new file mode 100644 index 0000000..35fed1f --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/MarshalQueryable.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal; + +import android.hardware.camera2.utils.TypeReference; + +/** + * Query if a marshaler can marshal to/from a particular native and managed type; if it supports + * the combination, allow creating a marshaler instance to do the serialization. + * + * <p>Not all queryable instances will support exactly one combination. Some, such as the + * primitive queryable will support all primitive to/from managed mappings (as long as they are + * 1:1). Others, such as the rectangle queryable will only support integer to rectangle mappings. + * </p> + * + * <p>Yet some others are codependent on other queryables; e.g. array queryables might only support + * a type map for {@code T[]} if another queryable exists with support for the component type + * {@code T}.</p> + */ +public interface MarshalQueryable<T> { + /** + * Create a marshaler between the selected managed and native type. + * + * <p>This marshaler instance is only good for that specific type mapping; and will refuse + * to map other managed types, other native types, or an other combination that isn't + * this exact one.</p> + * + * @param managedType a managed type reference + * @param nativeType the native type, e.g. + * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE} + * @return + * + * @throws UnsupportedOperationException + * if {@link #isTypeMappingSupported} returns {@code false} + */ + public Marshaler<T> createMarshaler( + TypeReference<T> managedType, int nativeType); + + /** + * Determine whether or not this query marshal is able to create a marshaler that will + * support the managed type and native type mapping. + * + * <p>If this returns {@code true}, then a marshaler can be instantiated by + * {@link #createMarshaler} that will marshal data to/from the native type + * from/to the managed type.</p> + * + * <p>Most marshalers are likely to only support one type map.</p> + */ + public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType); +} diff --git a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java new file mode 100644 index 0000000..92d9057 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal; + +import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.utils.TypeReference; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Registry of supported marshalers; add new query-able marshalers or lookup existing ones.</p> + */ +public class MarshalRegistry { + + /** + * Register a marshal queryable for the managed type {@code T}. + * + * <p>Multiple marshal queryables for the same managed type {@code T} may be registered; + * this is desirable if they support different native types (e.g. marshaler 1 supports + * {@code Integer <-> TYPE_INT32}, marshaler 2 supports {@code Integer <-> TYPE_BYTE}.</p> + * + * @param queryable a non-{@code null} marshal queryable that supports marshaling {@code T} + */ + public static <T> void registerMarshalQueryable(MarshalQueryable<T> queryable) { + sRegisteredMarshalQueryables.add(queryable); + } + + /** + * Lookup a marshaler between {@code T} and {@code nativeType}. + * + * <p>Marshalers are looked up in the order they were registered; earlier registered + * marshal queriers get priority.</p> + * + * @param typeToken The compile-time type reference for {@code T} + * @param nativeType The native type, e.g. {@link CameraMetadataNative#TYPE_BYTE TYPE_BYTE} + * @return marshaler a non-{@code null} marshaler that supports marshaling the type combo + * + * @throws UnsupportedOperationException If no marshaler matching the args could be found + */ + @SuppressWarnings("unchecked") + public static <T> Marshaler<T> getMarshaler(TypeReference<T> typeToken, int nativeType) { + // TODO: can avoid making a new token each time by code-genning + // the list of type tokens and native types from the keys (at the call sites) + MarshalToken<T> marshalToken = new MarshalToken<T>(typeToken, nativeType); + + /* + * Marshalers are instantiated lazily once they are looked up; successive lookups + * will not instantiate new marshalers. + */ + Marshaler<T> marshaler = + (Marshaler<T>) sMarshalerMap.get(marshalToken); + + if (sRegisteredMarshalQueryables.size() == 0) { + throw new AssertionError("No available query marshalers registered"); + } + + if (marshaler == null) { + // Query each marshaler to see if they support the native/managed type combination + for (MarshalQueryable<?> potentialMarshaler : sRegisteredMarshalQueryables) { + + MarshalQueryable<T> castedPotential = + (MarshalQueryable<T>)potentialMarshaler; + + if (castedPotential.isTypeMappingSupported(typeToken, nativeType)) { + marshaler = castedPotential.createMarshaler(typeToken, nativeType); + break; + } + } + } + + if (marshaler == null) { + throw new UnsupportedOperationException( + "Could not find marshaler that matches the requested " + + "combination of type reference " + + typeToken + " and native type " + + MarshalHelpers.toStringNativeType(nativeType)); + } + + sMarshalerMap.put(marshalToken, marshaler); + + return marshaler; + } + + private static class MarshalToken<T> { + public MarshalToken(TypeReference<T> typeReference, int nativeType) { + this.typeReference = typeReference; + this.nativeType = nativeType; + } + + final TypeReference<T> typeReference; + final int nativeType; + + @Override + public boolean equals(Object other) { + if (other instanceof MarshalToken<?>) { + MarshalToken<?> otherToken = (MarshalToken<?>)other; + return typeReference.equals(otherToken.typeReference) && + nativeType == otherToken.nativeType; + } + + return false; + } + + @Override + public int hashCode() { + return typeReference.hashCode() ^ nativeType; + } + } + + private static List<MarshalQueryable<?>> sRegisteredMarshalQueryables = + new ArrayList<MarshalQueryable<?>>(); + private static HashMap<MarshalToken<?>, Marshaler<?>> sMarshalerMap = + new HashMap<MarshalToken<?>, Marshaler<?>>(); + + private MarshalRegistry() { + throw new AssertionError(); + } +} diff --git a/core/java/android/hardware/camera2/marshal/Marshaler.java b/core/java/android/hardware/camera2/marshal/Marshaler.java new file mode 100644 index 0000000..eb0ad15 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/Marshaler.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal; + +import android.hardware.camera2.utils.TypeReference; + +import java.nio.ByteBuffer; + +import static android.hardware.camera2.marshal.MarshalHelpers.*; +import static com.android.internal.util.Preconditions.*; + +/** + * Base class to marshal data to/from managed/native metadata byte buffers. + * + * <p>This class should not be created directly; an instance of it can be obtained + * using {@link MarshalQueryable#createMarshaler} for the same type {@code T} if the native type + * mapping for {@code T} {@link MarshalQueryable#isTypeMappingSupported supported}.</p> + * + * @param <T> the compile-time managed type + */ +public abstract class Marshaler<T> { + + protected final TypeReference<T> mTypeReference; + protected final int mNativeType; + + /** + * Instantiate a marshaler between a single managed/native type combination. + * + * <p>This particular managed/native type combination must be supported by + * {@link #isTypeMappingSupported}.</p> + * + * @param query an instance of {@link MarshalQueryable} + * @param typeReference the managed type reference + * Must be one for which {@link #isTypeMappingSupported} returns {@code true} + * @param nativeType the native type, e.g. + * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}. + * Must be one for which {@link #isTypeMappingSupported} returns {@code true}. + * + * @throws NullPointerException if any args were {@code null} + * @throws UnsupportedOperationException if the type mapping was not supported + */ + protected Marshaler( + MarshalQueryable<T> query, TypeReference<T> typeReference, int nativeType) { + mTypeReference = checkNotNull(typeReference, "typeReference must not be null"); + mNativeType = checkNativeType(nativeType); + + if (!query.isTypeMappingSupported(typeReference, nativeType)) { + throw new UnsupportedOperationException( + "Unsupported type marshaling for managed type " + + typeReference + " and native type " + + MarshalHelpers.toStringNativeType(nativeType)); + } + } + + /** + * Marshal the specified object instance (value) into a byte buffer. + * + * <p>Upon completion, the {@link ByteBuffer#position()} will have advanced by + * the {@link #calculateMarshalSize marshal size} of {@code value}.</p> + * + * @param value the value of type T that we wish to write into the byte buffer + * @param buffer the byte buffer into which the marshaled object will be written + */ + public abstract void marshal(T value, ByteBuffer buffer); + + /** + * Get the size in bytes for how much space would be required to write this {@code value} + * into a byte buffer using the given {@code nativeType}. + * + * <p>If the size of this {@code T} instance when serialized into a buffer is always constant, + * then this method will always return the same value (and particularly, it will return + * an equivalent value to {@link #getNativeSize()}.</p> + * + * <p>Overriding this method is a must when the size is {@link NATIVE_SIZE_DYNAMIC dynamic}.</p> + * + * @param value the value of type T that we wish to write into the byte buffer + * @return the size that would need to be written to the byte buffer + */ + public int calculateMarshalSize(T value) { + int nativeSize = getNativeSize(); + + if (nativeSize == NATIVE_SIZE_DYNAMIC) { + throw new AssertionError("Override this function for dynamically-sized objects"); + } + + return nativeSize; + } + + /** + * Unmarshal a new object instance from the byte buffer into its managed type. + * + * <p>Upon completion, the {@link ByteBuffer#position()} will have advanced by + * the {@link #calculateMarshalSize marshal size} of the returned {@code T} instance.</p> + * + * @param buffer the byte buffer, from which we will read the object + * @return a new instance of type T read from the byte buffer + */ + public abstract T unmarshal(ByteBuffer buffer); + + /** + * Used to denote variable-length data structures. + * + * <p>If the size is dynamic then we can't know ahead of time how big of a data structure + * to preallocate for e.g. arrays, so one object must be unmarshaled at a time.</p> + */ + public static int NATIVE_SIZE_DYNAMIC = -1; + + /** + * How many bytes a single instance of {@code T} will take up if marshalled to/from + * {@code nativeType}. + * + * <p>When unmarshaling data from native to managed, the instance {@code T} is not yet + * available. If the native size is always a fixed mapping regardless of the instance of + * {@code T} (e.g. if the type is not a container of some sort), it can be used to preallocate + * containers for {@code T} to avoid resizing them.</p> + * + * <p>In particular, the array marshaler takes advantage of this (when size is not dynamic) + * to preallocate arrays of the right length when unmarshaling an array {@code T[]}.</p> + * + * @return a size in bytes, or {@link #NATIVE_SIZE_DYNAMIC} if the size is dynamic + */ + public abstract int getNativeSize(); + + /** + * The type reference for {@code T} for the managed type side of this marshaler. + */ + public TypeReference<T> getTypeReference() { + return mTypeReference; + } + + /** The native type corresponding to this marshaler for the native side of this marshaler.*/ + public int getNativeType() { + return mNativeType; + } +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java new file mode 100644 index 0000000..22b87ef --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.marshal.MarshalRegistry; +import android.hardware.camera2.utils.TypeReference; +import android.util.Log; + +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.util.ArrayList; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +/** + * Marshal any array {@code T}. + * + * <p>To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type + * also has to exist.</p> + * + * <p>{@code T} can be either a T2[] where T2 is an object type, or a P[] where P is a + * built-in primitive (e.g. int[], float[], etc).</p> + + * @param <T> the type of the array (e.g. T = int[], or T = Rational[]) + */ +public class MarshalQueryableArray<T> implements MarshalQueryable<T> { + + private static final String TAG = MarshalQueryableArray.class.getSimpleName(); + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + private class MarshalerArray extends Marshaler<T> { + private final Class<T> mClass; + private final Marshaler<?> mComponentMarshaler; + private final Class<?> mComponentClass; + + @SuppressWarnings("unchecked") + protected MarshalerArray(TypeReference<T> typeReference, int nativeType) { + super(MarshalQueryableArray.this, typeReference, nativeType); + + mClass = (Class<T>)typeReference.getRawType(); + + TypeReference<?> componentToken = typeReference.getComponentType(); + mComponentMarshaler = MarshalRegistry.getMarshaler(componentToken, mNativeType); + mComponentClass = componentToken.getRawType(); + } + + @Override + public void marshal(T value, ByteBuffer buffer) { + int length = Array.getLength(value); + for (int i = 0; i < length; ++i) { + marshalArrayElement(mComponentMarshaler, buffer, value, i); + } + } + + @Override + public T unmarshal(ByteBuffer buffer) { + Object array; + + int elementSize = mComponentMarshaler.getNativeSize(); + + if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) { + int remaining = buffer.remaining(); + int arraySize = remaining / elementSize; + + if (remaining % elementSize != 0) { + throw new UnsupportedOperationException("Arrays for " + mTypeReference + + " must be packed tighly into a multiple of " + elementSize + + "; but there are " + (remaining % elementSize) + " left over bytes"); + } + + if (VERBOSE) { + Log.v(TAG, String.format( + "Attempting to unpack array (count = %d, element size = %d, bytes " + + "remaining = %d) for type %s", + arraySize, elementSize, remaining, mClass)); + } + + array = Array.newInstance(mComponentClass, arraySize); + for (int i = 0; i < arraySize; ++i) { + Object elem = mComponentMarshaler.unmarshal(buffer); + Array.set(array, i, elem); + } + } else { + // Dynamic size, use an array list. + ArrayList<Object> arrayList = new ArrayList<Object>(); + + // Assumes array is packed tightly; no unused bytes allowed + while (buffer.hasRemaining()) { + Object elem = mComponentMarshaler.unmarshal(buffer); + arrayList.add(elem); + } + + int arraySize = arrayList.size(); + array = copyListToArray(arrayList, Array.newInstance(mComponentClass, arraySize)); + } + + if (buffer.remaining() != 0) { + Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking " + + mClass); + } + + return mClass.cast(array); + } + + @Override + public int getNativeSize() { + return NATIVE_SIZE_DYNAMIC; + } + + @Override + public int calculateMarshalSize(T value) { + int elementSize = mComponentMarshaler.getNativeSize(); + int arrayLength = Array.getLength(value); + + if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) { + // The fast way. Every element size is uniform. + return elementSize * arrayLength; + } else { + // The slow way. Accumulate size for each element. + int size = 0; + for (int i = 0; i < arrayLength; ++i) { + size += calculateElementMarshalSize(mComponentMarshaler, value, i); + } + + return size; + } + } + + /* + * Helpers to avoid compiler errors regarding types with wildcards (?) + */ + + @SuppressWarnings("unchecked") + private <TElem> void marshalArrayElement(Marshaler<TElem> marshaler, + ByteBuffer buffer, Object array, int index) { + marshaler.marshal((TElem)Array.get(array, index), buffer); + } + + @SuppressWarnings("unchecked") + private Object copyListToArray(ArrayList<?> arrayList, Object arrayDest) { + return arrayList.toArray((T[]) arrayDest); + } + + @SuppressWarnings("unchecked") + private <TElem> int calculateElementMarshalSize(Marshaler<TElem> marshaler, + Object array, int index) { + Object elem = Array.get(array, index); + + return marshaler.calculateMarshalSize((TElem) elem); + } + } + + @Override + public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) { + return new MarshalerArray(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) { + // support both ConcreteType[] and GenericType<ConcreteType>[] + return managedType.getRawType().isArray(); + + // TODO: Should this recurse deeper and check that there is + // a valid marshaler for the ConcreteType as well? + } +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java new file mode 100644 index 0000000..4aa4b4a --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import java.nio.ByteBuffer; + +/** + * Marshal booleans: TYPE_BYTE <-> boolean/Boolean + */ +public class MarshalQueryableBoolean implements MarshalQueryable<Boolean> { + + private class MarshalerBoolean extends Marshaler<Boolean> { + protected MarshalerBoolean(TypeReference<Boolean> typeReference, int nativeType) { + super(MarshalQueryableBoolean.this, typeReference, nativeType); + } + + @Override + public void marshal(Boolean value, ByteBuffer buffer) { + boolean unboxValue = value; + buffer.put((byte)(unboxValue ? 1 : 0)); + } + + @Override + public Boolean unmarshal(ByteBuffer buffer) { + return buffer.get() != 0; + } + + @Override + public int getNativeSize() { + return SIZEOF_BYTE; + } + } + + @Override + public Marshaler<Boolean> createMarshaler(TypeReference<Boolean> managedType, + int nativeType) { + return new MarshalerBoolean(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Boolean> managedType, int nativeType) { + return (Boolean.class.equals(managedType.getType()) + || boolean.class.equals(managedType.getType())) && nativeType == TYPE_BYTE; + } + + +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java new file mode 100644 index 0000000..d3796db --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.ColorSpaceTransform; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import java.nio.ByteBuffer; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +/** + * Marshal {@link ColorSpaceTransform} to/from {@link #TYPE_RATIONAL} + */ +public class MarshalQueryableColorSpaceTransform implements + MarshalQueryable<ColorSpaceTransform> { + + private static final int ELEMENTS_INT32 = 3 * 3 * (SIZEOF_RATIONAL / SIZEOF_INT32); + private static final int SIZE = SIZEOF_INT32 * ELEMENTS_INT32; + + /** rational x 3 x 3 */ + private class MarshalerColorSpaceTransform extends Marshaler<ColorSpaceTransform> { + protected MarshalerColorSpaceTransform(TypeReference<ColorSpaceTransform> typeReference, + int nativeType) { + super(MarshalQueryableColorSpaceTransform.this, typeReference, nativeType); + } + + @Override + public void marshal(ColorSpaceTransform value, ByteBuffer buffer) { + int[] transformAsArray = new int[ELEMENTS_INT32]; + value.copyElements(transformAsArray, /*offset*/0); + + for (int i = 0; i < ELEMENTS_INT32; ++i) { + buffer.putInt(transformAsArray[i]); + } + } + + @Override + public ColorSpaceTransform unmarshal(ByteBuffer buffer) { + int[] transformAsArray = new int[ELEMENTS_INT32]; + + for (int i = 0; i < ELEMENTS_INT32; ++i) { + transformAsArray[i] = buffer.getInt(); + } + + return new ColorSpaceTransform(transformAsArray); + } + + @Override + public int getNativeSize() { + return SIZE; + } + } + + @Override + public Marshaler<ColorSpaceTransform> createMarshaler( + TypeReference<ColorSpaceTransform> managedType, int nativeType) { + return new MarshalerColorSpaceTransform(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported( + TypeReference<ColorSpaceTransform> managedType, int nativeType) { + return nativeType == TYPE_RATIONAL && + ColorSpaceTransform.class.equals(managedType.getType()); + } + +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java new file mode 100644 index 0000000..fa53db2 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.util.HashMap; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +/** + * Marshal any simple enum (0-arg constructors only) into/from either + * {@code TYPE_BYTE} or {@code TYPE_INT32}. + * + * <p>Default values of the enum are mapped to its ordinal; this can be overridden + * by providing a manual value with {@link #registerEnumValues}.</p> + + * @param <T> the type of {@code Enum} + */ +public class MarshalQueryableEnum<T extends Enum<T>> implements MarshalQueryable<T> { + + private static final String TAG = MarshalQueryableEnum.class.getSimpleName(); + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + private static final int UINT8_MIN = 0x0; + private static final int UINT8_MAX = (1 << Byte.SIZE) - 1; + private static final int UINT8_MASK = UINT8_MAX; + + private class MarshalerEnum extends Marshaler<T> { + + private final Class<T> mClass; + + @SuppressWarnings("unchecked") + protected MarshalerEnum(TypeReference<T> typeReference, int nativeType) { + super(MarshalQueryableEnum.this, typeReference, nativeType); + + mClass = (Class<T>)typeReference.getRawType(); + } + + @Override + public void marshal(T value, ByteBuffer buffer) { + int enumValue = getEnumValue(value); + + if (mNativeType == TYPE_INT32) { + buffer.putInt(enumValue); + } else if (mNativeType == TYPE_BYTE) { + if (enumValue < UINT8_MIN || enumValue > UINT8_MAX) { + throw new UnsupportedOperationException(String.format( + "Enum value %x too large to fit into unsigned byte", enumValue)); + } + buffer.put((byte)enumValue); + } else { + throw new AssertionError(); + } + } + + @Override + public T unmarshal(ByteBuffer buffer) { + int enumValue; + + switch (mNativeType) { + case TYPE_INT32: + enumValue = buffer.getInt(); + break; + case TYPE_BYTE: + // get the unsigned byte value; avoid sign extension + enumValue = buffer.get() & UINT8_MASK; + break; + default: + throw new AssertionError( + "Unexpected native type; impossible since its not supported"); + } + + return getEnumFromValue(mClass, enumValue); + } + + @Override + public int getNativeSize() { + return getPrimitiveTypeSize(mNativeType); + } + } + + @Override + public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) { + return new MarshalerEnum(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) { + if (nativeType == TYPE_INT32 || nativeType == TYPE_BYTE) { + if (managedType.getType() instanceof Class<?>) { + Class<?> typeClass = (Class<?>)managedType.getType(); + + if (typeClass.isEnum()) { + if (VERBOSE) { + Log.v(TAG, "possible enum detected for " + typeClass); + } + + // The enum must not take extra arguments + try { + // match a class like: "public enum Fruits { Apple, Orange; }" + typeClass.getDeclaredConstructor(String.class, int.class); + return true; + } catch (NoSuchMethodException e) { + // Skip: custom enum with a special constructor e.g. Foo(T), but need Foo() + Log.e(TAG, "Can't marshal class " + typeClass + "; no default constructor"); + } catch (SecurityException e) { + // Skip: wouldn't be able to touch the enum anyway + Log.e(TAG, "Can't marshal class " + typeClass + "; not accessible"); + } + } + } + } + + return false; + } + + @SuppressWarnings("rawtypes") + private static final HashMap<Class<? extends Enum>, int[]> sEnumValues = + new HashMap<Class<? extends Enum>, int[]>(); + + /** + * Register a non-sequential set of values to be used with the marshal/unmarshal functions. + * + * <p>This enables get/set to correctly marshal the enum into a value that is C-compatible.</p> + * + * @param enumType The class for an enum + * @param values A list of values mapping to the ordinals of the enum + */ + public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) { + if (enumType.getEnumConstants().length != values.length) { + throw new IllegalArgumentException( + "Expected values array to be the same size as the enumTypes values " + + values.length + " for type " + enumType); + } + if (VERBOSE) { + Log.v(TAG, "Registered enum values for type " + enumType + " values"); + } + + sEnumValues.put(enumType, values); + } + + /** + * Get the numeric value from an enum. + * + * <p>This is usually the same as the ordinal value for + * enums that have fully sequential values, although for C-style enums the range of values + * may not map 1:1.</p> + * + * @param enumValue Enum instance + * @return Int guaranteed to be ABI-compatible with the C enum equivalent + */ + private static <T extends Enum<T>> int getEnumValue(T enumValue) { + int[] values; + values = sEnumValues.get(enumValue.getClass()); + + int ordinal = enumValue.ordinal(); + if (values != null) { + return values[ordinal]; + } + + return ordinal; + } + + /** + * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method. + * + * @param enumType Class of the enum we want to find + * @param value The numeric value of the enum + * @return An instance of the enum + */ + private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) { + int ordinal; + + int[] registeredValues = sEnumValues.get(enumType); + if (registeredValues != null) { + ordinal = -1; + + for (int i = 0; i < registeredValues.length; ++i) { + if (registeredValues[i] == value) { + ordinal = i; + break; + } + } + } else { + ordinal = value; + } + + T[] values = enumType.getEnumConstants(); + + if (ordinal < 0 || ordinal >= values.length) { + throw new IllegalArgumentException( + String.format( + "Argument 'value' (%d) was not a valid enum value for type %s " + + "(registered? %b)", + value, + enumType, (registeredValues != null))); + } + + return values[ordinal]; + } +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java new file mode 100644 index 0000000..c8b9bd8 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.MeteringRectangle; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import java.nio.ByteBuffer; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +/** + * Marshal {@link MeteringRectangle} to/from {@link #TYPE_INT32} + */ +public class MarshalQueryableMeteringRectangle implements MarshalQueryable<MeteringRectangle> { + private static final int SIZE = SIZEOF_INT32 * 5; + + /** (xmin, ymin, xmax, ymax, weight) */ + private class MarshalerMeteringRectangle extends Marshaler<MeteringRectangle> { + protected MarshalerMeteringRectangle(TypeReference<MeteringRectangle> typeReference, + int nativeType) { + super(MarshalQueryableMeteringRectangle.this, typeReference, nativeType); + } + + @Override + public void marshal(MeteringRectangle value, ByteBuffer buffer) { + int xMin = value.getX(); + int yMin = value.getY(); + int xMax = xMin + value.getWidth(); + int yMax = yMin + value.getHeight(); + int weight = value.getMeteringWeight(); + + buffer.putInt(xMin); + buffer.putInt(yMin); + buffer.putInt(xMax); + buffer.putInt(yMax); + buffer.putInt(weight); + } + + @Override + public MeteringRectangle unmarshal(ByteBuffer buffer) { + int xMin = buffer.getInt(); + int yMin = buffer.getInt(); + int xMax = buffer.getInt(); + int yMax = buffer.getInt(); + int weight = buffer.getInt(); + + int width = xMax - xMin; + int height = yMax - yMin; + + return new MeteringRectangle(xMin, yMin, width, height, weight); + } + + @Override + public int getNativeSize() { + return SIZE; + } + } + + @Override + public Marshaler<MeteringRectangle> createMarshaler( + TypeReference<MeteringRectangle> managedType, int nativeType) { + return new MarshalerMeteringRectangle(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported( + TypeReference<MeteringRectangle> managedType, int nativeType) { + return nativeType == TYPE_INT32 && MeteringRectangle.class.equals(managedType.getType()); + } + +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java new file mode 100644 index 0000000..3b89c82 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import java.nio.ByteBuffer; + +/** + * Marshal fake native enums (ints): TYPE_BYTE <-> int/Integer + */ +public class MarshalQueryableNativeByteToInteger implements MarshalQueryable<Integer> { + + private static final int UINT8_MASK = (1 << Byte.SIZE) - 1; + + private class MarshalerNativeByteToInteger extends Marshaler<Integer> { + protected MarshalerNativeByteToInteger(TypeReference<Integer> typeReference, + int nativeType) { + super(MarshalQueryableNativeByteToInteger.this, typeReference, nativeType); + } + + @Override + public void marshal(Integer value, ByteBuffer buffer) { + buffer.put((byte)(int)value); // truncate down to byte + } + + @Override + public Integer unmarshal(ByteBuffer buffer) { + // expand unsigned byte to int; avoid sign extension + return buffer.get() & UINT8_MASK; + } + + @Override + public int getNativeSize() { + return SIZEOF_BYTE; + } + } + + @Override + public Marshaler<Integer> createMarshaler(TypeReference<Integer> managedType, + int nativeType) { + return new MarshalerNativeByteToInteger(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Integer> managedType, int nativeType) { + return (Integer.class.equals(managedType.getType()) + || int.class.equals(managedType.getType())) && nativeType == TYPE_BYTE; + } + + +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java new file mode 100644 index 0000000..1fd6a1d --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.lang.reflect.Field; +import java.nio.ByteBuffer; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +/** + * Marshal any {@code T extends Parcelable} to/from any native type + * + * <p>Use with extreme caution! File descriptors and binders will not be marshaled across.</p> + */ +public class MarshalQueryableParcelable<T extends Parcelable> + implements MarshalQueryable<T> { + + private static final String TAG = "MarshalParcelable"; + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + private static final String FIELD_CREATOR = "CREATOR"; + + private class MarshalerParcelable extends Marshaler<T> { + + private final Class<T> mClass; + private final Parcelable.Creator<T> mCreator; + + @SuppressWarnings("unchecked") + protected MarshalerParcelable(TypeReference<T> typeReference, + int nativeType) { + super(MarshalQueryableParcelable.this, typeReference, nativeType); + + mClass = (Class<T>)typeReference.getRawType(); + Field creatorField; + try { + creatorField = mClass.getDeclaredField(FIELD_CREATOR); + } catch (NoSuchFieldException e) { + // Impossible. All Parcelable implementations must have a 'CREATOR' static field + throw new AssertionError(e); + } + + try { + mCreator = (Parcelable.Creator<T>)creatorField.get(null); + } catch (IllegalAccessException e) { + // Impossible: All 'CREATOR' static fields must be public + throw new AssertionError(e); + } catch (IllegalArgumentException e) { + // Impossible: This is a static field, so null must be ok + throw new AssertionError(e); + } + } + + @Override + public void marshal(T value, ByteBuffer buffer) { + if (VERBOSE) { + Log.v(TAG, "marshal " + value); + } + + Parcel parcel = Parcel.obtain(); + byte[] parcelContents; + + try { + value.writeToParcel(parcel, /*flags*/0); + + if (parcel.hasFileDescriptors()) { + throw new UnsupportedOperationException( + "Parcelable " + value + " must not have file descriptors"); + } + + parcelContents = parcel.marshall(); + } + finally { + parcel.recycle(); + } + + if (parcelContents.length == 0) { + throw new AssertionError("No data marshaled for " + value); + } + + buffer.put(parcelContents); + } + + @Override + public T unmarshal(ByteBuffer buffer) { + if (VERBOSE) { + Log.v(TAG, "unmarshal, buffer remaining " + buffer.remaining()); + } + + /* + * Quadratically slow when marshaling an array of parcelables. + * + * Read out the entire byte buffer as an array, then copy it into the parcel. + * + * Once we unparcel the entire object, advance the byte buffer by only how many + * bytes the parcel actually used up. + * + * Future: If we ever do need to use parcelable arrays, we can do this a little smarter + * by reading out a chunk like 4,8,16,24 each time, but not sure how to detect + * parcels being too short in this case. + * + * Future: Alternatively use Parcel#obtain(long) directly into the native + * pointer of a ByteBuffer, which would not copy if the ByteBuffer was direct. + */ + buffer.mark(); + + Parcel parcel = Parcel.obtain(); + try { + int maxLength = buffer.remaining(); + + byte[] remaining = new byte[maxLength]; + buffer.get(remaining); + + parcel.unmarshall(remaining, /*offset*/0, maxLength); + parcel.setDataPosition(/*pos*/0); + + T value = mCreator.createFromParcel(parcel); + int actualLength = parcel.dataPosition(); + + if (actualLength == 0) { + throw new AssertionError("No data marshaled for " + value); + } + + // set the position past the bytes the parcelable actually used + buffer.reset(); + buffer.position(buffer.position() + actualLength); + + if (VERBOSE) { + Log.v(TAG, "unmarshal, parcel length was " + actualLength); + Log.v(TAG, "unmarshal, value is " + value); + } + + return mClass.cast(value); + } finally { + parcel.recycle(); + } + } + + @Override + public int getNativeSize() { + return NATIVE_SIZE_DYNAMIC; + } + + @Override + public int calculateMarshalSize(T value) { + Parcel parcel = Parcel.obtain(); + try { + value.writeToParcel(parcel, /*flags*/0); + int length = parcel.marshall().length; + + if (VERBOSE) { + Log.v(TAG, "calculateMarshalSize, length when parceling " + + value + " is " + length); + } + + return length; + } finally { + parcel.recycle(); + } + } + } + + @Override + public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) { + return new MarshalerParcelable(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) { + return Parcelable.class.isAssignableFrom(managedType.getRawType()); + } + +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java new file mode 100644 index 0000000..708da70 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.Rational; +import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; +import static com.android.internal.util.Preconditions.*; + +import java.nio.ByteBuffer; + +/** + * Marshal/unmarshal built-in primitive types to and from a {@link ByteBuffer}. + * + * <p>The following list of type marshaling is supported: + * <ul> + * <li>byte <-> TYPE_BYTE + * <li>int <-> TYPE_INT32 + * <li>long <-> TYPE_INT64 + * <li>float <-> TYPE_FLOAT + * <li>double <-> TYPE_DOUBLE + * <li>Rational <-> TYPE_RATIONAL + * </ul> + * </p> + * + * <p>Due to the nature of generics, values are always boxed; this also means that both + * the boxed and unboxed types are supported (i.e. both {@code int} and {@code Integer}).</p> + * + * <p>Each managed type <!--(other than boolean)--> must correspond 1:1 to the native type + * (e.g. a byte will not map to a {@link CameraMetadataNative#TYPE_INT32 TYPE_INT32} or vice versa) + * for marshaling.</p> + */ +public final class MarshalQueryablePrimitive<T> implements MarshalQueryable<T> { + + private class MarshalerPrimitive extends Marshaler<T> { + /** Always the wrapped class variant of the primitive class for {@code T} */ + private final Class<T> mClass; + + @SuppressWarnings("unchecked") + protected MarshalerPrimitive(TypeReference<T> typeReference, int nativeType) { + super(MarshalQueryablePrimitive.this, typeReference, nativeType); + + // Turn primitives into wrappers, otherwise int.class.cast(Integer) will fail + mClass = wrapClassIfPrimitive((Class<T>)typeReference.getRawType()); + } + + @Override + public T unmarshal(ByteBuffer buffer) { + return mClass.cast(unmarshalObject(buffer)); + } + + @Override + public int calculateMarshalSize(T value) { + return getPrimitiveTypeSize(mNativeType); + } + + @Override + public void marshal(T value, ByteBuffer buffer) { + if (value instanceof Integer) { + checkNativeTypeEquals(TYPE_INT32, mNativeType); + final int val = (Integer) value; + marshalPrimitive(val, buffer); + } else if (value instanceof Float) { + checkNativeTypeEquals(TYPE_FLOAT, mNativeType); + final float val = (Float) value; + marshalPrimitive(val, buffer); + } else if (value instanceof Long) { + checkNativeTypeEquals(TYPE_INT64, mNativeType); + final long val = (Long) value; + marshalPrimitive(val, buffer); + } else if (value instanceof Rational) { + checkNativeTypeEquals(TYPE_RATIONAL, mNativeType); + marshalPrimitive((Rational) value, buffer); + } else if (value instanceof Double) { + checkNativeTypeEquals(TYPE_DOUBLE, mNativeType); + final double val = (Double) value; + marshalPrimitive(val, buffer); + } else if (value instanceof Byte) { + checkNativeTypeEquals(TYPE_BYTE, mNativeType); + final byte val = (Byte) value; + marshalPrimitive(val, buffer); + } else { + throw new UnsupportedOperationException( + "Can't marshal managed type " + mTypeReference); + } + } + + private void marshalPrimitive(int value, ByteBuffer buffer) { + buffer.putInt(value); + } + + private void marshalPrimitive(float value, ByteBuffer buffer) { + buffer.putFloat(value); + } + + private void marshalPrimitive(double value, ByteBuffer buffer) { + buffer.putDouble(value); + } + + private void marshalPrimitive(long value, ByteBuffer buffer) { + buffer.putLong(value); + } + + private void marshalPrimitive(Rational value, ByteBuffer buffer) { + buffer.putInt(value.getNumerator()); + buffer.putInt(value.getDenominator()); + } + + private void marshalPrimitive(byte value, ByteBuffer buffer) { + buffer.put(value); + } + + private Object unmarshalObject(ByteBuffer buffer) { + switch (mNativeType) { + case TYPE_INT32: + return buffer.getInt(); + case TYPE_FLOAT: + return buffer.getFloat(); + case TYPE_INT64: + return buffer.getLong(); + case TYPE_RATIONAL: + int numerator = buffer.getInt(); + int denominator = buffer.getInt(); + return new Rational(numerator, denominator); + case TYPE_DOUBLE: + return buffer.getDouble(); + case TYPE_BYTE: + return buffer.get(); // getByte + default: + throw new UnsupportedOperationException( + "Can't unmarshal native type " + mNativeType); + } + } + + @Override + public int getNativeSize() { + return getPrimitiveTypeSize(mNativeType); + } + } + + @Override + public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) { + return new MarshalerPrimitive(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) { + if (managedType.getType() instanceof Class<?>) { + Class<?> klass = (Class<?>)managedType.getType(); + + if (klass == byte.class || klass == Byte.class) { + return nativeType == TYPE_BYTE; + } else if (klass == int.class || klass == Integer.class) { + return nativeType == TYPE_INT32; + } else if (klass == float.class || klass == Float.class) { + return nativeType == TYPE_FLOAT; + } else if (klass == long.class || klass == Long.class) { + return nativeType == TYPE_INT64; + } else if (klass == double.class || klass == Double.class) { + return nativeType == TYPE_DOUBLE; + } else if (klass == Rational.class) { + return nativeType == TYPE_RATIONAL; + } + } + return false; + } +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java new file mode 100644 index 0000000..8512804 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.marshal.MarshalRegistry; +import android.hardware.camera2.utils.TypeReference; +import android.util.Range; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.ByteBuffer; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +/** + * Marshal {@link Range} to/from any native type + */ +public class MarshalQueryableRange<T extends Comparable<? super T>> + implements MarshalQueryable<Range<T>> { + private static final int RANGE_COUNT = 2; + + private class MarshalerRange extends Marshaler<Range<T>> { + private final Class<? super Range<T>> mClass; + private final Constructor<Range<T>> mConstructor; + /** Marshal the {@code T} inside of {@code Range<T>} */ + private final Marshaler<T> mNestedTypeMarshaler; + + @SuppressWarnings("unchecked") + protected MarshalerRange(TypeReference<Range<T>> typeReference, + int nativeType) { + super(MarshalQueryableRange.this, typeReference, nativeType); + + mClass = typeReference.getRawType(); + + /* + * Lookup the actual type argument, e.g. Range<Integer> --> Integer + * and then get the marshaler for that managed type. + */ + ParameterizedType paramType; + try { + paramType = (ParameterizedType) typeReference.getType(); + } catch (ClassCastException e) { + throw new AssertionError("Raw use of Range is not supported", e); + } + Type actualTypeArgument = paramType.getActualTypeArguments()[0]; + + TypeReference<?> actualTypeArgToken = + TypeReference.createSpecializedTypeReference(actualTypeArgument); + + mNestedTypeMarshaler = (Marshaler<T>)MarshalRegistry.getMarshaler( + actualTypeArgToken, mNativeType); + try { + mConstructor = (Constructor<Range<T>>)mClass.getConstructor( + Comparable.class, Comparable.class); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + @Override + public void marshal(Range<T> value, ByteBuffer buffer) { + mNestedTypeMarshaler.marshal(value.getLower(), buffer); + mNestedTypeMarshaler.marshal(value.getUpper(), buffer); + } + + @Override + public Range<T> unmarshal(ByteBuffer buffer) { + T lower = mNestedTypeMarshaler.unmarshal(buffer); + T upper = mNestedTypeMarshaler.unmarshal(buffer); + + try { + return mConstructor.newInstance(lower, upper); + } catch (InstantiationException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } catch (IllegalArgumentException e) { + throw new AssertionError(e); + } catch (InvocationTargetException e) { + throw new AssertionError(e); + } + } + + @Override + public int getNativeSize() { + int nestedSize = mNestedTypeMarshaler.getNativeSize(); + + if (nestedSize != NATIVE_SIZE_DYNAMIC) { + return nestedSize * RANGE_COUNT; + } else { + return NATIVE_SIZE_DYNAMIC; + } + } + + @Override + public int calculateMarshalSize(Range<T> value) { + int nativeSize = getNativeSize(); + + if (nativeSize != NATIVE_SIZE_DYNAMIC) { + return nativeSize; + } else { + int lowerSize = mNestedTypeMarshaler.calculateMarshalSize(value.getLower()); + int upperSize = mNestedTypeMarshaler.calculateMarshalSize(value.getUpper()); + + return lowerSize + upperSize; + } + } + } + + @Override + public Marshaler<Range<T>> createMarshaler(TypeReference<Range<T>> managedType, + int nativeType) { + return new MarshalerRange(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Range<T>> managedType, int nativeType) { + return (Range.class.equals(managedType.getRawType())); + } + +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java new file mode 100644 index 0000000..de20a1f --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.graphics.Rect; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import java.nio.ByteBuffer; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +/** + * Marshal {@link Rect} to/from {@link #TYPE_INT32} + */ +public class MarshalQueryableRect implements MarshalQueryable<Rect> { + private static final int SIZE = SIZEOF_INT32 * 4; + + private class MarshalerRect extends Marshaler<Rect> { + protected MarshalerRect(TypeReference<Rect> typeReference, + int nativeType) { + super(MarshalQueryableRect.this, typeReference, nativeType); + } + + @Override + public void marshal(Rect value, ByteBuffer buffer) { + buffer.putInt(value.left); + buffer.putInt(value.top); + buffer.putInt(value.width()); + buffer.putInt(value.height()); + } + + @Override + public Rect unmarshal(ByteBuffer buffer) { + int left = buffer.getInt(); + int top = buffer.getInt(); + int width = buffer.getInt(); + int height = buffer.getInt(); + + int right = left + width; + int bottom = top + height; + + return new Rect(left, top, right, bottom); + } + + @Override + public int getNativeSize() { + return SIZE; + } + } + + @Override + public Marshaler<Rect> createMarshaler(TypeReference<Rect> managedType, int nativeType) { + return new MarshalerRect(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Rect> managedType, int nativeType) { + return nativeType == TYPE_INT32 && (Rect.class.equals(managedType.getType())); + } + +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java new file mode 100644 index 0000000..3025cb4 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.ReprocessFormatsMap; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +/** + * Marshaler for {@code android.scaler.availableInputOutputFormatsMap} custom class + * {@link ReprocessFormatsMap} + */ +public class MarshalQueryableReprocessFormatsMap + implements MarshalQueryable<ReprocessFormatsMap> { + + private class MarshalerReprocessFormatsMap extends Marshaler<ReprocessFormatsMap> { + protected MarshalerReprocessFormatsMap( + TypeReference<ReprocessFormatsMap> typeReference, int nativeType) { + super(MarshalQueryableReprocessFormatsMap.this, typeReference, nativeType); + } + + @Override + public void marshal(ReprocessFormatsMap value, ByteBuffer buffer) { + /* + * // writing (static example, DNG+ZSL) + * int32_t[] contents = { + * RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB, + * RAW16, 2, YUV_420_888, BLOB, + * ..., + * INPUT_FORMAT, OUTPUT_FORMAT_COUNT, [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1] + * }; + */ + int[] inputs = value.getInputs(); + for (int input : inputs) { + // INPUT_FORMAT + buffer.putInt(input); + + int[] outputs = value.getOutputs(input); + // OUTPUT_FORMAT_COUNT + buffer.putInt(outputs.length); + + // [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1] + for (int output : outputs) { + buffer.putInt(output); + } + } + } + + @Override + public ReprocessFormatsMap unmarshal(ByteBuffer buffer) { + int len = buffer.remaining() / SIZEOF_INT32; + if (buffer.remaining() % SIZEOF_INT32 != 0) { + throw new AssertionError("ReprocessFormatsMap was not TYPE_INT32"); + } + + int[] entries = new int[len]; + + IntBuffer intBuffer = buffer.asIntBuffer(); + intBuffer.get(entries); + + // TODO: consider moving rest of parsing code from ReprocessFormatsMap to here + + return new ReprocessFormatsMap(entries); + } + + @Override + public int getNativeSize() { + return NATIVE_SIZE_DYNAMIC; + } + + @Override + public int calculateMarshalSize(ReprocessFormatsMap value) { + /* + * // writing (static example, DNG+ZSL) + * int32_t[] contents = { + * RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB, + * RAW16, 2, YUV_420_888, BLOB, + * ..., + * INPUT_FORMAT, OUTPUT_FORMAT_COUNT, [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1] + * }; + */ + int length = 0; + + int[] inputs = value.getInputs(); + for (int input : inputs) { + + length += 1; // INPUT_FORMAT + length += 1; // OUTPUT_FORMAT_COUNT + + int[] outputs = value.getOutputs(input); + length += outputs.length; // [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1] + } + + return length * SIZEOF_INT32; + } + } + + @Override + public Marshaler<ReprocessFormatsMap> createMarshaler( + TypeReference<ReprocessFormatsMap> managedType, int nativeType) { + return new MarshalerReprocessFormatsMap(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<ReprocessFormatsMap> managedType, + int nativeType) { + return nativeType == TYPE_INT32 && managedType.getType().equals(ReprocessFormatsMap.class); + } +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java new file mode 100644 index 0000000..93c0e92 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.RggbChannelVector; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import java.nio.ByteBuffer; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +/** + * Marshal {@link RggbChannelVector} to/from {@link #TYPE_FLOAT} {@code x 4} + */ +public class MarshalQueryableRggbChannelVector implements MarshalQueryable<RggbChannelVector> { + private static final int SIZE = SIZEOF_FLOAT * RggbChannelVector.COUNT; + + private class MarshalerRggbChannelVector extends Marshaler<RggbChannelVector> { + protected MarshalerRggbChannelVector(TypeReference<RggbChannelVector> typeReference, + int nativeType) { + super(MarshalQueryableRggbChannelVector.this, typeReference, nativeType); + } + + @Override + public void marshal(RggbChannelVector value, ByteBuffer buffer) { + for (int i = 0; i < RggbChannelVector.COUNT; ++i) { + buffer.putFloat(value.getComponent(i)); + } + } + + @Override + public RggbChannelVector unmarshal(ByteBuffer buffer) { + float red = buffer.getFloat(); + float gEven = buffer.getFloat(); + float gOdd = buffer.getFloat(); + float blue = buffer.getFloat(); + + return new RggbChannelVector(red, gEven, gOdd, blue); + } + + @Override + public int getNativeSize() { + return SIZE; + } + } + + @Override + public Marshaler<RggbChannelVector> createMarshaler( + TypeReference<RggbChannelVector> managedType, int nativeType) { + return new MarshalerRggbChannelVector(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported( + TypeReference<RggbChannelVector> managedType, int nativeType) { + return nativeType == TYPE_FLOAT && (RggbChannelVector.class.equals(managedType.getType())); + } + +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java new file mode 100644 index 0000000..6a73bee --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.Size; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +import java.nio.ByteBuffer; + +/** + * Marshal {@link Size} to/from {@code TYPE_INT32} + */ +public class MarshalQueryableSize implements MarshalQueryable<Size> { + private static final int SIZE = SIZEOF_INT32 * 2; + + private class MarshalerSize extends Marshaler<Size> { + protected MarshalerSize(TypeReference<Size> typeReference, int nativeType) { + super(MarshalQueryableSize.this, typeReference, nativeType); + } + + @Override + public void marshal(Size value, ByteBuffer buffer) { + buffer.putInt(value.getWidth()); + buffer.putInt(value.getHeight()); + } + + @Override + public Size unmarshal(ByteBuffer buffer) { + int width = buffer.getInt(); + int height = buffer.getInt(); + + return new Size(width, height); + } + + @Override + public int getNativeSize() { + return SIZE; + } + } + + @Override + public Marshaler<Size> createMarshaler(TypeReference<Size> managedType, int nativeType) { + return new MarshalerSize(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Size> managedType, int nativeType) { + return nativeType == TYPE_INT32 && (Size.class.equals(managedType.getType())); + } +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java new file mode 100644 index 0000000..b60a46d --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; +import android.util.SizeF; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +import java.nio.ByteBuffer; + +/** + * Marshal {@link SizeF} to/from {@code TYPE_FLOAT} + */ +public class MarshalQueryableSizeF implements MarshalQueryable<SizeF> { + + private static final int SIZE = SIZEOF_FLOAT * 2; + + private class MarshalerSizeF extends Marshaler<SizeF> { + + protected MarshalerSizeF(TypeReference<SizeF> typeReference, int nativeType) { + super(MarshalQueryableSizeF.this, typeReference, nativeType); + } + + @Override + public void marshal(SizeF value, ByteBuffer buffer) { + buffer.putFloat(value.getWidth()); + buffer.putFloat(value.getHeight()); + } + + @Override + public SizeF unmarshal(ByteBuffer buffer) { + float width = buffer.getFloat(); + float height = buffer.getFloat(); + + return new SizeF(width, height); + } + + @Override + public int getNativeSize() { + return SIZE; + } + } + + @Override + public Marshaler<SizeF> createMarshaler( + TypeReference<SizeF> managedType, int nativeType) { + return new MarshalerSizeF(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<SizeF> managedType, int nativeType) { + return nativeType == TYPE_FLOAT && (SizeF.class.equals(managedType.getType())); + } +} + diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java new file mode 100644 index 0000000..6a4e821 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.StreamConfiguration; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +import java.nio.ByteBuffer; + +/** + * Marshaler for {@code android.scaler.availableStreamConfigurations} custom class + * {@link StreamConfiguration} + * + * <p>Data is stored as {@code (format, width, height, input?)} tuples (int32).</p> + */ +public class MarshalQueryableStreamConfiguration + implements MarshalQueryable<StreamConfiguration> { + private static final int SIZE = SIZEOF_INT32 * 4; + + private class MarshalerStreamConfiguration extends Marshaler<StreamConfiguration> { + protected MarshalerStreamConfiguration(TypeReference<StreamConfiguration> typeReference, + int nativeType) { + super(MarshalQueryableStreamConfiguration.this, typeReference, nativeType); + } + + @Override + public void marshal(StreamConfiguration value, ByteBuffer buffer) { + buffer.putInt(value.getFormat()); + buffer.putInt(value.getWidth()); + buffer.putInt(value.getHeight()); + buffer.putInt(value.isInput() ? 1 : 0); + } + + @Override + public StreamConfiguration unmarshal(ByteBuffer buffer) { + int format = buffer.getInt(); + int width = buffer.getInt(); + int height = buffer.getInt(); + boolean input = buffer.getInt() != 0; + + return new StreamConfiguration(format, width, height, input); + } + + @Override + public int getNativeSize() { + return SIZE; + } + + } + + @Override + public Marshaler<StreamConfiguration> createMarshaler( + TypeReference<StreamConfiguration> managedType, int nativeType) { + return new MarshalerStreamConfiguration(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<StreamConfiguration> managedType, + int nativeType) { + return nativeType == TYPE_INT32 && managedType.getType().equals(StreamConfiguration.class); + } +} diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java new file mode 100644 index 0000000..c3d564e --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.StreamConfigurationDuration; +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static android.hardware.camera2.marshal.MarshalHelpers.*; + +import java.nio.ByteBuffer; + +/** + * Marshaler for custom class {@link StreamConfigurationDuration} for min-frame and stall durations. + * + * <p> + * Data is stored as {@code (format, width, height, durationNs)} tuples (int64). + * </p> + */ +public class MarshalQueryableStreamConfigurationDuration + implements MarshalQueryable<StreamConfigurationDuration> { + + private static final int SIZE = SIZEOF_INT64 * 4; + /** + * Values and-ed with this will do an unsigned int to signed long conversion; + * in other words the sign bit from the int will not be extended. + * */ + private static final long MASK_UNSIGNED_INT = 0x00000000ffffffffL; + + private class MarshalerStreamConfigurationDuration + extends Marshaler<StreamConfigurationDuration> { + + protected MarshalerStreamConfigurationDuration( + TypeReference<StreamConfigurationDuration> typeReference, int nativeType) { + super(MarshalQueryableStreamConfigurationDuration.this, typeReference, nativeType); + } + + @Override + public void marshal(StreamConfigurationDuration value, ByteBuffer buffer) { + buffer.putLong(value.getFormat() & MASK_UNSIGNED_INT); // unsigned int -> long + buffer.putLong(value.getWidth()); + buffer.putLong(value.getHeight()); + buffer.putLong(value.getDuration()); + } + + @Override + public StreamConfigurationDuration unmarshal(ByteBuffer buffer) { + int format = (int)buffer.getLong(); + int width = (int)buffer.getLong(); + int height = (int)buffer.getLong(); + long durationNs = buffer.getLong(); + + return new StreamConfigurationDuration(format, width, height, durationNs); + } + + @Override + public int getNativeSize() { + return SIZE; + } + } + + @Override + public Marshaler<StreamConfigurationDuration> createMarshaler( + TypeReference<StreamConfigurationDuration> managedType, int nativeType) { + return new MarshalerStreamConfigurationDuration(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<StreamConfigurationDuration> managedType, + int nativeType) { + return nativeType == TYPE_INT64 && + (StreamConfigurationDuration.class.equals(managedType.getType())); + } + +}
\ No newline at end of file diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java new file mode 100644 index 0000000..bf518bb --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.marshal.impl; + +import android.hardware.camera2.marshal.Marshaler; +import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.utils.TypeReference; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import static android.hardware.camera2.impl.CameraMetadataNative.*; + +/** + * Marshal {@link String} to/from {@link #TYPE_BYTE}. + */ +public class MarshalQueryableString implements MarshalQueryable<String> { + + private static final String TAG = MarshalQueryableString.class.getSimpleName(); + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + private static final byte NUL = (byte)'\0'; // used as string terminator + + private class MarshalerString extends Marshaler<String> { + + protected MarshalerString(TypeReference<String> typeReference, int nativeType) { + super(MarshalQueryableString.this, typeReference, nativeType); + } + + @Override + public void marshal(String value, ByteBuffer buffer) { + byte[] arr = value.getBytes(UTF8_CHARSET); + + buffer.put(arr); + buffer.put(NUL); // metadata strings are NUL-terminated + } + + @Override + public int calculateMarshalSize(String value) { + byte[] arr = value.getBytes(UTF8_CHARSET); + + return arr.length + 1; // metadata strings are NUL-terminated + } + + @Override + public String unmarshal(ByteBuffer buffer) { + buffer.mark(); // save the current position + + boolean foundNull = false; + int stringLength = 0; + while (buffer.hasRemaining()) { + if (buffer.get() == NUL) { + foundNull = true; + break; + } + + stringLength++; + } + + if (VERBOSE) { + Log.v(TAG, + "unmarshal - scanned " + stringLength + " characters; found null? " + + foundNull); + } + + if (!foundNull) { + throw new UnsupportedOperationException("Strings must be null-terminated"); + } + + buffer.reset(); // go back to the previously marked position + + byte[] strBytes = new byte[stringLength + 1]; + buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character + + // not including null character + return new String(strBytes, /*offset*/0, stringLength, UTF8_CHARSET); + } + + @Override + public int getNativeSize() { + return NATIVE_SIZE_DYNAMIC; + } + } + + @Override + public Marshaler<String> createMarshaler( + TypeReference<String> managedType, int nativeType) { + return new MarshalerString(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<String> managedType, int nativeType) { + return nativeType == TYPE_BYTE && String.class.equals(managedType.getType()); + } +} diff --git a/core/java/android/hardware/camera2/marshal/impl/package.html b/core/java/android/hardware/camera2/marshal/impl/package.html new file mode 100644 index 0000000..783d0a1 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/package.html @@ -0,0 +1,3 @@ +<body> +{@hide} +</body> diff --git a/core/java/android/hardware/camera2/marshal/package.html b/core/java/android/hardware/camera2/marshal/package.html new file mode 100644 index 0000000..783d0a1 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/package.html @@ -0,0 +1,3 @@ +<body> +{@hide} +</body> diff --git a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java index 2d63827..b980549 100644 --- a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java +++ b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.hardware.camera2.impl; +package android.hardware.camera2.utils; /** * Provide hashing functions using the Modified Bernstein hash diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java new file mode 100644 index 0000000..d0c919c --- /dev/null +++ b/core/java/android/hardware/camera2/utils/TypeReference.java @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.utils; + +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; + +import static com.android.internal.util.Preconditions.*; + +/** + * Super type token; allows capturing generic types at runtime by forcing them to be reified. + * + * <p>Usage example: <pre>{@code + * // using anonymous classes (preferred) + * TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }}; + * + * // using named classes + * class IntTypeReference extends TypeReference<Integer> {...} + * TypeReference<Integer> intToken = new IntTypeReference(); + * }</p></pre> + * + * <p>Unlike the reference implementation, this bans nested TypeVariables; that is all + * dynamic types must equal to the static types.</p> + * + * <p>See <a href="http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html"> + * http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html</a> + * for more details.</p> + */ +public abstract class TypeReference<T> { + private final Type mType; + + /** + * Create a new type reference for {@code T}. + * + * @throws IllegalArgumentException if {@code T}'s actual type contains a type variable + * + * @see TypeReference + */ + protected TypeReference() { + ParameterizedType thisType = (ParameterizedType)getClass().getGenericSuperclass(); + + // extract the "T" from TypeReference<T> + mType = thisType.getActualTypeArguments()[0]; + + /* + * Prohibit type references with type variables such as + * + * class GenericListToken<T> extends TypeReference<List<T>> + * + * Since the "T" there is not known without an instance of T, type equality would + * consider *all* Lists equal regardless of T. Allowing this would defeat + * some of the type safety of a type reference. + */ + if (containsTypeVariable(mType)) { + throw new IllegalArgumentException( + "Including a type variable in a type reference is not allowed"); + } + } + + /** + * Return the dynamic {@link Type} corresponding to the captured type {@code T}. + */ + public Type getType() { + return mType; + } + + private TypeReference(Type type) { + mType = type; + + if (containsTypeVariable(mType)) { + throw new IllegalArgumentException( + "Including a type variable in a type reference is not allowed"); + } + } + + private static class SpecializedTypeReference<T> extends TypeReference<T> { + public SpecializedTypeReference(Class<T> klass) { + super(klass); + } + } + + @SuppressWarnings("rawtypes") + private static class SpecializedBaseTypeReference extends TypeReference { + public SpecializedBaseTypeReference(Type type) { + super(type); + } + } + + /** + * Create a specialized type reference from a dynamic class instance, + * bypassing the standard compile-time checks. + * + * <p>As with a regular type reference, the {@code klass} must not contain + * any type variables.</p> + * + * @param klass a non-{@code null} {@link Class} instance + * + * @return a type reference which captures {@code T} at runtime + * + * @throws IllegalArgumentException if {@code T} had any type variables + */ + public static <T> TypeReference<T> createSpecializedTypeReference(Class<T> klass) { + return new SpecializedTypeReference<T>(klass); + } + + /** + * Create a specialized type reference from a dynamic {@link Type} instance, + * bypassing the standard compile-time checks. + * + * <p>As with a regular type reference, the {@code type} must not contain + * any type variables.</p> + * + * @param type a non-{@code null} {@link Type} instance + * + * @return a type reference which captures {@code T} at runtime + * + * @throws IllegalArgumentException if {@code type} had any type variables + */ + public static TypeReference<?> createSpecializedTypeReference(Type type) { + return new SpecializedBaseTypeReference(type); + } + + /** + * Returns the raw type of T. + * + * <p><ul> + * <li>If T is a Class itself, T itself is returned. + * <li>If T is a ParameterizedType, the raw type of the parameterized type is returned. + * <li>If T is a GenericArrayType, the returned type is the corresponding array class. + * For example: {@code List<Integer>[]} => {@code List[]}. + * <li>If T is a type variable or a wildcard type, the raw type of the first upper bound is + * returned. For example: {@code <X extends Foo>} => {@code Foo}. + * </ul> + * + * @return the raw type of {@code T} + */ + @SuppressWarnings("unchecked") + public final Class<? super T> getRawType() { + return (Class<? super T>)getRawType(mType); + } + + private static final Class<?> getRawType(Type type) { + if (type == null) { + throw new NullPointerException("type must not be null"); + } + + if (type instanceof Class<?>) { + return (Class<?>)type; + } else if (type instanceof ParameterizedType) { + return (Class<?>)(((ParameterizedType)type).getRawType()); + } else if (type instanceof GenericArrayType) { + return getArrayClass(getRawType(((GenericArrayType)type).getGenericComponentType())); + } else if (type instanceof WildcardType) { + // Should be at most 1 upper bound, but treat it like an array for simplicity + return getRawType(((WildcardType) type).getUpperBounds()); + } else if (type instanceof TypeVariable) { + throw new AssertionError("Type variables are not allowed in type references"); + } else { + // Impossible + throw new AssertionError("Unhandled branch to get raw type for type " + type); + } + } + + private static final Class<?> getRawType(Type[] types) { + if (types == null) { + return null; + } + + for (Type type : types) { + Class<?> klass = getRawType(type); + if (klass != null) { + return klass; + } + } + + return null; + } + + private static final Class<?> getArrayClass(Class<?> componentType) { + return Array.newInstance(componentType, 0).getClass(); + } + + /** + * Get the component type, e.g. {@code T} from {@code T[]}. + * + * @return component type, or {@code null} if {@code T} is not an array + */ + public TypeReference<?> getComponentType() { + Type componentType = getComponentType(mType); + + return (componentType != null) ? + createSpecializedTypeReference(componentType) : + null; + } + + private static Type getComponentType(Type type) { + checkNotNull(type, "type must not be null"); + + if (type instanceof Class<?>) { + return ((Class<?>) type).getComponentType(); + } else if (type instanceof ParameterizedType) { + return null; + } else if (type instanceof GenericArrayType) { + return ((GenericArrayType)type).getGenericComponentType(); + } else if (type instanceof WildcardType) { + // Should be at most 1 upper bound, but treat it like an array for simplicity + throw new UnsupportedOperationException("TODO: support wild card components"); + } else if (type instanceof TypeVariable) { + throw new AssertionError("Type variables are not allowed in type references"); + } else { + // Impossible + throw new AssertionError("Unhandled branch to get component type for type " + type); + } + } + + /** + * Compare two objects for equality. + * + * <p>A TypeReference is only equal to another TypeReference if their captured type {@code T} + * is also equal.</p> + */ + @Override + public boolean equals(Object o) { + // Note that this comparison could inaccurately return true when comparing types + // with nested type variables; therefore we ban type variables in the constructor. + return o instanceof TypeReference<?> && mType.equals(((TypeReference<?>)o).mType); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return mType.hashCode(); + } + + /** + * Check if the {@code type} contains a {@link TypeVariable} recursively. + * + * <p>Intuitively, a type variable is a type in a type expression that refers to a generic + * type which is not known at the definition of the expression (commonly seen when + * type parameters are used, e.g. {@code class Foo<T>}).</p> + * + * <p>See <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4"> + * http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4</a> + * for a more formal definition of a type variable</p>. + * + * @param type a type object ({@code null} is allowed) + * @return {@code true} if there were nested type variables; {@code false} otherwise + */ + public static boolean containsTypeVariable(Type type) { + if (type == null) { + // Trivially false + return false; + } else if (type instanceof TypeVariable<?>) { + /* + * T -> trivially true + */ + return true; + } else if (type instanceof Class<?>) { + /* + * class Foo -> no type variable + * class Foo<T> - has a type variable + * + * This also covers the case of class Foo<T> extends ... / implements ... + * since everything on the right hand side would either include a type variable T + * or have no type variables. + */ + Class<?> klass = (Class<?>)type; + + // Empty array => class is not generic + if (klass.getTypeParameters().length != 0) { + return true; + } else { + // Does the outer class(es) contain any type variables? + + /* + * class Outer<T> { + * class Inner { + * T field; + * } + * } + * + * In this case 'Inner' has no type parameters itself, but it still has a type + * variable as part of the type definition. + */ + return containsTypeVariable(klass.getDeclaringClass()); + } + } else if (type instanceof ParameterizedType) { + /* + * This is the "Foo<T1, T2, T3, ... Tn>" in the scope of a + * + * // no type variables here, T1-Tn are known at this definition + * class X extends Foo<T1, T2, T3, ... Tn> + * + * // T1 is a type variable, T2-Tn are known at this definition + * class X<T1> extends Foo<T1, T2, T3, ... Tn> + */ + ParameterizedType p = (ParameterizedType) type; + + // This needs to be recursively checked + for (Type arg : p.getActualTypeArguments()) { + if (containsTypeVariable(arg)) { + return true; + } + } + + return false; + } else if (type instanceof WildcardType) { + WildcardType wild = (WildcardType) type; + + /* + * This is is the "?" inside of a + * + * Foo<?> --> unbounded; trivially no type variables + * Foo<? super T> --> lower bound; does T have a type variable? + * Foo<? extends T> --> upper bound; does T have a type variable? + */ + + /* + * According to JLS 4.5.1 + * (http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.5.1): + * + * - More than 1 lower/upper bound is illegal + * - Both a lower and upper bound is illegal + * + * However, we use this 'array OR array' approach for readability + */ + return containsTypeVariable(wild.getLowerBounds()) || + containsTypeVariable(wild.getUpperBounds()); + } + + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("TypeReference<"); + toString(getType(), builder); + builder.append(">"); + + return builder.toString(); + } + + private static void toString(Type type, StringBuilder out) { + if (type == null) { + return; + } else if (type instanceof TypeVariable<?>) { + // T + out.append(((TypeVariable<?>)type).getName()); + } else if (type instanceof Class<?>) { + Class<?> klass = (Class<?>)type; + + out.append(klass.getName()); + toString(klass.getTypeParameters(), out); + } else if (type instanceof ParameterizedType) { + // "Foo<T1, T2, T3, ... Tn>" + ParameterizedType p = (ParameterizedType) type; + + out.append(((Class<?>)p.getRawType()).getName()); + toString(p.getActualTypeArguments(), out); + } else if (type instanceof GenericArrayType) { + GenericArrayType gat = (GenericArrayType)type; + + toString(gat.getGenericComponentType(), out); + out.append("[]"); + } else { // WildcardType, BoundedType + // TODO: + out.append(type.toString()); + } + } + + private static void toString(Type[] types, StringBuilder out) { + if (types == null) { + return; + } else if (types.length == 0) { + return; + } + + out.append("<"); + + for (int i = 0; i < types.length; ++i) { + toString(types[i], out); + if (i != types.length - 1) { + out.append(", "); + } + } + + out.append(">"); + } + + /** + * Check if any of the elements in this array contained a type variable. + * + * <p>Empty and null arrays trivially have no type variables.</p> + * + * @param typeArray an array ({@code null} is ok) of types + * @return true if any elements contained a type variable; false otherwise + */ + private static boolean containsTypeVariable(Type[] typeArray) { + if (typeArray == null) { + return false; + } + + for (Type type : typeArray) { + if (containsTypeVariable(type)) { + return true; + } + } + + return false; + } +} diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 3e00250..a414421 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -13,26 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package android.net; import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.PendingIntent; import android.content.Context; import android.os.Binder; import android.os.Build.VERSION_CODES; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.INetworkActivityListener; import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.util.ArrayMap; +import android.util.Log; import java.net.InetAddress; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.HashMap; + +import com.android.internal.util.Protocol; /** * Class that answers queries about the state of network connectivity. It also @@ -534,26 +543,21 @@ public class ConnectivityManager { /** * Specifies the preferred network type. When the device has more * than one type available the preferred network type will be used. - * Note that this made sense when we only had 2 network types, - * but with more and more default networks we need an array to list - * their ordering. This will be deprecated soon. * * @param preference the network type to prefer over all others. It is * unspecified what happens to the old preferred network in the * overall ordering. */ public void setNetworkPreference(int preference) { - try { - mService.setNetworkPreference(preference); - } catch (RemoteException e) { - } + // TODO - deprecate with: + // @deprecated Functionality has been removed as it no longer makes sense, + // with many more than two networks - we'd need an array to express + // preference. Instead we use dynamic network properties of + // the networks to describe their precedence. } /** * Retrieves the current preferred network type. - * Note that this made sense when we only had 2 network types, - * but with more and more default networks we need an array to list - * their ordering. This will be deprecated soon. * * @return an integer representing the preferred network type * @@ -561,11 +565,12 @@ public class ConnectivityManager { * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. */ public int getNetworkPreference() { - try { - return mService.getNetworkPreference(); - } catch (RemoteException e) { - return -1; - } + // TODO - deprecate with: + // @deprecated Functionality has been removed as it no longer makes sense, + // with many more than two networks - we'd need an array to express + // preference. Instead we use dynamic network properties of + // the networks to describe their precedence. + return -1; } /** @@ -705,7 +710,25 @@ public class ConnectivityManager { */ public LinkProperties getLinkProperties(int networkType) { try { - return mService.getLinkProperties(networkType); + return mService.getLinkPropertiesForType(networkType); + } catch (RemoteException e) { + return null; + } + } + + /** {@hide} */ + public LinkProperties getLinkProperties(Network network) { + try { + return mService.getLinkProperties(network); + } catch (RemoteException e) { + return null; + } + } + + /** {@hide} */ + public NetworkCapabilities getNetworkCapabilities(Network network) { + try { + return mService.getNetworkCapabilities(network); } catch (RemoteException e) { return null; } @@ -723,13 +746,14 @@ public class ConnectivityManager { * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. * {@hide} */ - public boolean setRadios(boolean turnOn) { - try { - return mService.setRadios(turnOn); - } catch (RemoteException e) { - return false; - } - } +// TODO - check for any callers and remove +// public boolean setRadios(boolean turnOn) { +// try { +// return mService.setRadios(turnOn); +// } catch (RemoteException e) { +// return false; +// } +// } /** * Tells a given networkType to set its radio power state as directed. @@ -743,13 +767,14 @@ public class ConnectivityManager { * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. * {@hide} */ - public boolean setRadio(int networkType, boolean turnOn) { - try { - return mService.setRadio(networkType, turnOn); - } catch (RemoteException e) { - return false; - } - } +// TODO - check for any callers and remove +// public boolean setRadio(int networkType, boolean turnOn) { +// try { +// return mService.setRadio(networkType, turnOn); +// } catch (RemoteException e) { +// return false; +// } +// } /** * Tells the underlying networking system that the caller wants to @@ -1307,6 +1332,22 @@ public class ConnectivityManager { } /** + * Report a problem network to the framework. This will cause the framework + * to evaluate the situation and try to fix any problems. Note that false + * may be subsequently ignored. + * + * @param network The Network the application was attempting to use or null + * to indicate the current default network. + * {@hide} + */ + public void reportBadNetwork(Network network) { + try { + mService.reportBadNetwork(network); + } catch (RemoteException e) { + } + } + + /** * Set a network-independent global http proxy. This is not normally what you want * for typical HTTP proxies - they are general network dependent. However if you're * doing something unusual like general internal filtering this may be useful. On @@ -1587,4 +1628,440 @@ public class ConnectivityManager { } catch (RemoteException e) { } } + + /** {@hide} */ + public void registerNetworkFactory(Messenger messenger) { + try { + mService.registerNetworkFactory(messenger); + } catch (RemoteException e) { } + } + + /** {@hide} */ + public void registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp, + NetworkCapabilities nc, int score) { + try { + mService.registerNetworkAgent(messenger, ni, lp, nc, score); + } catch (RemoteException e) { } + } + + /** + * Interface for NetworkRequest callbacks. Used for notifications about network + * changes. + * @hide + */ + public static class NetworkCallbacks { + /** @hide */ + public static final int PRECHECK = 1; + /** @hide */ + public static final int AVAILABLE = 2; + /** @hide */ + public static final int LOSING = 3; + /** @hide */ + public static final int LOST = 4; + /** @hide */ + public static final int UNAVAIL = 5; + /** @hide */ + public static final int CAP_CHANGED = 6; + /** @hide */ + public static final int PROP_CHANGED = 7; + /** @hide */ + public static final int CANCELED = 8; + + /** + * @hide + * Called whenever the framework connects to a network that it may use to + * satisfy this request + */ + public void onPreCheck(NetworkRequest networkRequest, Network network) {} + + /** + * Called when the framework connects and has validated the new network. + */ + public void onAvailable(NetworkRequest networkRequest, Network network) {} + + /** + * Called when the framework is losing the network. Often paired with an + * onAvailable call with the new replacement network for graceful handover. + * This may not be called if we have a hard loss (loss without warning). + * This may be followed by either an onLost call or an onAvailable call for this + * network depending on if we lose or regain it. + */ + public void onLosing(NetworkRequest networkRequest, Network network, int maxSecToLive) {} + + /** + * Called when the framework has a hard loss of the network or when the + * graceful failure ends. Note applications should only request this callback + * if the application is willing to track the Available and Lost callbacks + * together, else the application may think it has no network when it + * really does (A Avail, B Avail, A Lost.. still have B). + */ + public void onLost(NetworkRequest networkRequest, Network network) {} + + /** + * Called if no network is found in the given timeout time. If no timeout is given, + * this will not be called. + */ + public void onUnavailable(NetworkRequest networkRequest) {} + + /** + * Called when the network the framework connected to for this request + * changes capabilities but still satisfies the stated need. + */ + public void onNetworkCapabilitiesChanged(NetworkRequest networkRequest, Network network, + NetworkCapabilities networkCapabilities) {} + + /** + * Called when the network the framework connected to for this request + * changes LinkProperties. + */ + public void onLinkPropertiesChanged(NetworkRequest networkRequest, Network network, + LinkProperties linkProperties) {} + + /** + * Called when a releaseNetworkRequest call concludes and the registered callbacks will + * no longer be used. + */ + public void onReleased(NetworkRequest networkRequest) {} + } + + private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER; + /** @hide obj = pair(NetworkRequest, Network) */ + public static final int CALLBACK_PRECHECK = BASE + 1; + /** @hide obj = pair(NetworkRequest, Network) */ + public static final int CALLBACK_AVAILABLE = BASE + 2; + /** @hide obj = pair(NetworkRequest, Network), arg1 = ttl */ + public static final int CALLBACK_LOSING = BASE + 3; + /** @hide obj = pair(NetworkRequest, Network) */ + public static final int CALLBACK_LOST = BASE + 4; + /** @hide obj = NetworkRequest */ + public static final int CALLBACK_UNAVAIL = BASE + 5; + /** @hide obj = pair(NetworkRequest, Network) */ + public static final int CALLBACK_CAP_CHANGED = BASE + 6; + /** @hide obj = pair(NetworkRequest, Network) */ + public static final int CALLBACK_IP_CHANGED = BASE + 7; + /** @hide obj = NetworkRequest */ + public static final int CALLBACK_RELEASED = BASE + 8; + /** @hide */ + public static final int CALLBACK_EXIT = BASE + 9; + + private static class CallbackHandler extends Handler { + private final HashMap<NetworkRequest, NetworkCallbacks>mCallbackMap; + private final AtomicInteger mRefCount; + private static final String TAG = "ConnectivityManager.CallbackHandler"; + private final ConnectivityManager mCm; + + CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallbacks>callbackMap, + AtomicInteger refCount, ConnectivityManager cm) { + super(looper); + mCallbackMap = callbackMap; + mRefCount = refCount; + mCm = cm; + } + + @Override + public void handleMessage(Message message) { + Log.d(TAG, "CM callback handler got msg " + message.what); + switch (message.what) { + case CALLBACK_PRECHECK: { + NetworkRequest request = getNetworkRequest(message); + NetworkCallbacks callbacks = getCallbacks(request); + if (callbacks != null) { + callbacks.onPreCheck(request, getNetwork(message)); + } else { + Log.e(TAG, "callback not found for PRECHECK message"); + } + break; + } + case CALLBACK_AVAILABLE: { + NetworkRequest request = getNetworkRequest(message); + NetworkCallbacks callbacks = getCallbacks(request); + if (callbacks != null) { + callbacks.onAvailable(request, getNetwork(message)); + } else { + Log.e(TAG, "callback not found for AVAILABLE message"); + } + break; + } + case CALLBACK_LOSING: { + NetworkRequest request = getNetworkRequest(message); + NetworkCallbacks callbacks = getCallbacks(request); + if (callbacks != null) { + callbacks.onLosing(request, getNetwork(message), message.arg1); + } else { + Log.e(TAG, "callback not found for LOSING message"); + } + break; + } + case CALLBACK_LOST: { + NetworkRequest request = getNetworkRequest(message); + NetworkCallbacks callbacks = getCallbacks(request); + if (callbacks != null) { + callbacks.onLost(request, getNetwork(message)); + } else { + Log.e(TAG, "callback not found for LOST message"); + } + break; + } + case CALLBACK_UNAVAIL: { + NetworkRequest req = (NetworkRequest)message.obj; + NetworkCallbacks callbacks = null; + synchronized(mCallbackMap) { + callbacks = mCallbackMap.get(req); + } + if (callbacks != null) { + callbacks.onUnavailable(req); + } else { + Log.e(TAG, "callback not found for UNAVAIL message"); + } + break; + } + case CALLBACK_CAP_CHANGED: { + NetworkRequest request = getNetworkRequest(message); + NetworkCallbacks callbacks = getCallbacks(request); + if (callbacks != null) { + Network network = getNetwork(message); + NetworkCapabilities cap = mCm.getNetworkCapabilities(network); + + callbacks.onNetworkCapabilitiesChanged(request, network, cap); + } else { + Log.e(TAG, "callback not found for CHANGED message"); + } + break; + } + case CALLBACK_IP_CHANGED: { + NetworkRequest request = getNetworkRequest(message); + NetworkCallbacks callbacks = getCallbacks(request); + if (callbacks != null) { + Network network = getNetwork(message); + LinkProperties lp = mCm.getLinkProperties(network); + + callbacks.onLinkPropertiesChanged(request, network, lp); + } else { + Log.e(TAG, "callback not found for CHANGED message"); + } + break; + } + case CALLBACK_RELEASED: { + NetworkRequest req = (NetworkRequest)message.obj; + NetworkCallbacks callbacks = null; + synchronized(mCallbackMap) { + callbacks = mCallbackMap.remove(req); + } + if (callbacks != null) { + callbacks.onReleased(req); + } else { + Log.e(TAG, "callback not found for CANCELED message"); + } + synchronized(mRefCount) { + if (mRefCount.decrementAndGet() == 0) { + getLooper().quit(); + } + } + break; + } + case CALLBACK_EXIT: { + Log.d(TAG, "Listener quiting"); + getLooper().quit(); + break; + } + } + } + + private NetworkRequest getNetworkRequest(Message msg) { + return (NetworkRequest)(msg.obj); + } + private NetworkCallbacks getCallbacks(NetworkRequest req) { + synchronized(mCallbackMap) { + return mCallbackMap.get(req); + } + } + private Network getNetwork(Message msg) { + return new Network(msg.arg2); + } + private NetworkCallbacks removeCallbacks(Message msg) { + NetworkRequest req = (NetworkRequest)msg.obj; + synchronized(mCallbackMap) { + return mCallbackMap.remove(req); + } + } + } + + private void addCallbackListener() { + synchronized(sCallbackRefCount) { + if (sCallbackRefCount.incrementAndGet() == 1) { + // TODO - switch this over to a ManagerThread or expire it when done + HandlerThread callbackThread = new HandlerThread("ConnectivityManager"); + callbackThread.start(); + sCallbackHandler = new CallbackHandler(callbackThread.getLooper(), + sNetworkCallbacks, sCallbackRefCount, this); + } + } + } + + private void removeCallbackListener() { + synchronized(sCallbackRefCount) { + if (sCallbackRefCount.decrementAndGet() == 0) { + sCallbackHandler.obtainMessage(CALLBACK_EXIT).sendToTarget(); + sCallbackHandler = null; + } + } + } + + static final HashMap<NetworkRequest, NetworkCallbacks> sNetworkCallbacks = + new HashMap<NetworkRequest, NetworkCallbacks>(); + static final AtomicInteger sCallbackRefCount = new AtomicInteger(0); + static CallbackHandler sCallbackHandler = null; + + private final static int LISTEN = 1; + private final static int REQUEST = 2; + + private NetworkRequest somethingForNetwork(NetworkCapabilities need, + NetworkCallbacks networkCallbacks, int timeoutSec, int action) { + NetworkRequest networkRequest = null; + if (networkCallbacks == null) throw new IllegalArgumentException("null NetworkCallbacks"); + if (need == null) throw new IllegalArgumentException("null NetworkCapabilities"); + try { + addCallbackListener(); + if (action == LISTEN) { + networkRequest = mService.listenForNetwork(need, new Messenger(sCallbackHandler), + new Binder()); + } else { + networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler), + timeoutSec, new Binder()); + } + if (networkRequest != null) { + synchronized(sNetworkCallbacks) { + sNetworkCallbacks.put(networkRequest, networkCallbacks); + } + } + } catch (RemoteException e) {} + if (networkRequest == null) removeCallbackListener(); + return networkRequest; + } + + /** + * Request a network to satisfy a set of {@link NetworkCapabilities}. + * + * This {@link NetworkRequest} will live until released via + * {@link releaseNetworkRequest} or the calling application exits. + * Status of the request can be follwed by listening to the various + * callbacks described in {@link NetworkCallbacks}. The {@link Network} + * can be used by using the {@link bindSocketToNetwork}, + * {@link bindApplicationToNetwork} and {@link getAddrInfoOnNetwork} functions. + * + * @param need {@link NetworkCapabilities} required by this request. + * @param networkCallbacks The callbacks to be utilized for this request. Note + * the callbacks can be shared by multiple requests and + * the NetworkRequest token utilized to determine to which + * request the callback relates. + * @return A {@link NetworkRequest} object identifying the request. + * @hide + */ + public NetworkRequest requestNetwork(NetworkCapabilities need, + NetworkCallbacks networkCallbacks) { + return somethingForNetwork(need, networkCallbacks, 0, REQUEST); + } + + /** + * Request a network to satisfy a set of {@link NetworkCapabilities}, limited + * by a timeout. + * + * This function behaves identically, but if a suitable network is not found + * within the given time (in Seconds) the {@link NetworkCallbacks#unavailable} + * callback is called. The request must still be released normally by + * calling {@link releaseNetworkRequest}. + * @param need {@link NetworkCapabilities} required by this request. + * @param networkCallbacks The callbacks to be utilized for this request. Note + * the callbacks can be shared by multiple requests and + * the NetworkRequest token utilized to determine to which + * request the callback relates. + * @param timeoutSec The time in seconds to attempt looking for a suitable network + * before {@link NetworkCallbacks#unavailable} is called. + * @return A {@link NetworkRequest} object identifying the request. + * @hide + */ + public NetworkRequest requestNetwork(NetworkCapabilities need, + NetworkCallbacks networkCallbacks, int timeoutSec) { + return somethingForNetwork(need, networkCallbacks, timeoutSec, REQUEST); + } + + /** + * The maximum number of seconds the framework will look for a suitable network + * during a timeout-equiped call to {@link requestNetwork}. + * {@hide} + */ + public final static int MAX_NETWORK_REQUEST_TIMEOUT_SEC = 100 * 60; + + /** + * Request a network to satisfy a set of {@link NetworkCapabilities}. + * + * This function behavies identically, but instead of {@link NetworkCallbacks} + * a {@link PendingIntent} is used. This means the request may outlive the + * calling application and get called back when a suitable network is found. + * <p> + * The operation is an Intent broadcast that goes to a broadcast receiver that + * you registered with {@link Context#registerReceiver} or through the + * <receiver> tag in an AndroidManifest.xml file + * <p> + * The operation Intent is delivered with two extras, a {@link Network} typed + * extra called {@link EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkCapabilities} + * typed extra called {@link EXTRA_NETWORK_REQUEST_NETWORK_CAPABILTIES} containing + * the original requests parameters. It is important to create a new, + * {@link NetworkCallbacks} based request before completing the processing of the + * Intent to reserve the network or it will be released shortly after the Intent + * is processed. + * <p> + * If there is already an request for this Intent registered (with the equality of + * two Intents defined by {@link Intent#filterEquals}), then it will be removed and + * replace by this one, effectively releasing the previous {@link NetworkRequest}. + * <p> + * The request may be released normally by calling {@link releaseNetworkRequest}. + * + * @param need {@link NetworkCapabilties} required by this request. + * @param operation Action to perform when the network is available (corresponds + * to the {@link NetworkCallbacks#onAvailable} call. Typically + * comes from {@link PendingIntent#getBroadcast}. + * @return A {@link NetworkRequest} object identifying the request. + * @hide + */ + public NetworkRequest requestNetwork(NetworkCapabilities need, PendingIntent operation) { + try { + return mService.pendingRequestForNetwork(need, operation); + } catch (RemoteException e) {} + return null; + } + + /** + * Registers to receive notifications about all networks which satisfy the given + * {@link NetworkCapabilities}. The callbacks will continue to be called until + * either the application exits or the request is released using + * {@link releaseNetworkRequest}. + * + * @param need {@link NetworkCapabilities} required by this request. + * @param networkCallbacks The {@link NetworkCallbacks} to be called as suitable + * networks change state. + * @return A {@link NetworkRequest} object identifying the request. + * @hide + */ + public NetworkRequest listenForNetwork(NetworkCapabilities need, + NetworkCallbacks networkCallbacks) { + return somethingForNetwork(need, networkCallbacks, 0, LISTEN); + } + + /** + * Releases a {NetworkRequest} generated either through a {@link requestNetwork} + * or a {@link listenForNetwork} call. The {@link NetworkCallbacks} given in the + * earlier call may continue receiving calls until the {@link NetworkCallbacks#onReleased} + * function is called, signifiying the end of the request. + * + * @param networkRequest The {@link NetworkRequest} generated by an earlier call to + * {@link requestNetwork} or {@link listenForNetwork}. + * @hide + */ + public void releaseNetworkRequest(NetworkRequest networkRequest) { + if (networkRequest == null) throw new IllegalArgumentException("null NetworkRequest"); + try { + mService.releaseNetworkRequest(networkRequest); + } catch (RemoteException e) {} + } } diff --git a/core/java/android/net/ConnectivityServiceProtocol.java b/core/java/android/net/ConnectivityServiceProtocol.java new file mode 100644 index 0000000..74096b4 --- /dev/null +++ b/core/java/android/net/ConnectivityServiceProtocol.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 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 static com.android.internal.util.Protocol.BASE_CONNECTIVITY_SERVICE; + +/** + * Describes the Internal protocols used to communicate with ConnectivityService. + * @hide + */ +public class ConnectivityServiceProtocol { + + private static final int BASE = BASE_CONNECTIVITY_SERVICE; + + private ConnectivityServiceProtocol() {} + + /** + * This is a contract between ConnectivityService and various bearers. + * A NetworkFactory is an abstract entity that creates NetworkAgent objects. + * The bearers register with ConnectivityService using + * ConnectivityManager.registerNetworkFactory, where they pass in a Messenger + * to be used to deliver the following Messages. + */ + public static class NetworkFactoryProtocol { + private NetworkFactoryProtocol() {} + /** + * Pass a network request to the bearer. If the bearer believes it can + * satisfy the request it should connect to the network and create a + * NetworkAgent. Once the NetworkAgent is fully functional it will + * register itself with ConnectivityService using registerNetworkAgent. + * If the bearer cannot immediately satisfy the request (no network, + * user disabled the radio, lower-scored network) it should remember + * any NetworkRequests it may be able to satisfy in the future. It may + * disregard any that it will never be able to service, for example + * those requiring a different bearer. + * msg.obj = NetworkRequest + * msg.arg1 = score - the score of the any network currently satisfying this + * request. If this bearer knows in advance it cannot + * exceed this score it should not try to connect, holding the request + * for the future. + * Note that subsequent events may give a different (lower + * or higher) score for this request, transmitted to each + * NetworkFactory through additional CMD_REQUEST_NETWORK msgs + * with the same NetworkRequest but an updated score. + * Also, network conditions may change for this bearer + * allowing for a better score in the future. + */ + public static final int CMD_REQUEST_NETWORK = BASE; + + /** + * Cancel a network request + * msg.obj = NetworkRequest + */ + public static final int CMD_CANCEL_REQUEST = BASE + 1; + } +} diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d53a856..885b8b6 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -16,10 +16,14 @@ package android.net; +import android.app.PendingIntent; import android.net.LinkQualityInfo; import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkQuotaInfo; +import android.net.NetworkRequest; import android.net.NetworkState; import android.net.ProxyInfo; import android.os.IBinder; @@ -41,10 +45,6 @@ interface IConnectivityManager // Keep this in sync with framework/native/services/connectivitymanager/ConnectivityManager.h void markSocketAsUser(in ParcelFileDescriptor socket, int uid); - void setNetworkPreference(int pref); - - int getNetworkPreference(); - NetworkInfo getActiveNetworkInfo(); NetworkInfo getActiveNetworkInfoForUid(int uid); NetworkInfo getNetworkInfo(int networkType); @@ -55,17 +55,16 @@ interface IConnectivityManager boolean isNetworkSupported(int networkType); LinkProperties getActiveLinkProperties(); - LinkProperties getLinkProperties(int networkType); + LinkProperties getLinkPropertiesForType(int networkType); + LinkProperties getLinkProperties(in Network network); + + NetworkCapabilities getNetworkCapabilities(in Network network); NetworkState[] getAllNetworkState(); NetworkQuotaInfo getActiveNetworkQuotaInfo(); boolean isActiveNetworkMetered(); - boolean setRadios(boolean onOff); - - boolean setRadio(int networkType, boolean turnOn); - int startUsingNetworkFeature(int networkType, in String feature, in IBinder binder); @@ -107,6 +106,8 @@ interface IConnectivityManager void reportInetCondition(int networkType, int percentage); + void reportBadNetwork(in Network network); + ProxyInfo getGlobalProxy(); void setGlobalProxy(in ProxyInfo p); @@ -147,7 +148,27 @@ interface IConnectivityManager LinkQualityInfo[] getAllLinkQualityInfo(); - void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo, in String url); + void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo, + in String url); void setAirplaneMode(boolean enable); + + void registerNetworkFactory(in Messenger messenger); + + void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp, + in NetworkCapabilities nc, int score); + + NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, + in Messenger messenger, int timeoutSec, in IBinder binder); + + NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities, + in PendingIntent operation); + + NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities, + in Messenger messenger, in IBinder binder); + + void pendingListenForNetwork(in NetworkCapabilities networkCapabilities, + in PendingIntent operation); + + void releaseNetworkRequest(in NetworkRequest networkRequest); } diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 2dcc544..0a09fcb 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -642,6 +642,35 @@ public class LinkProperties implements Parcelable { return result; } + /** + * Compares all interface names in this LinkProperties with another + * LinkProperties, examining both the the base link and all stacked links. + * + * @param target a LinkProperties with the new list of interface names + * @return the differences between the interface names. + * @hide + */ + public CompareResult<String> compareAllInterfaceNames(LinkProperties target) { + /* + * Duplicate the interface names into removed, we will be removing + * interface names which are common between this and target + * leaving the interface names that are different. And interface names which + * are in target but not in this are placed in added. + */ + CompareResult<String> result = new CompareResult<String>(); + + result.removed = getAllInterfaceNames(); + result.added.clear(); + if (target != null) { + for (String r : target.getAllInterfaceNames()) { + if (! result.removed.remove(r)) { + result.added.add(r); + } + } + } + return result; + } + @Override /** diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index f82bc22..ac1289b 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -19,6 +19,8 @@ package android.net; import android.os.Parcelable; import android.os.Parcel; +import java.net.InetAddress; +import java.net.UnknownHostException; /** * Identifies the Network. @@ -36,6 +38,32 @@ public class Network implements Parcelable { this.netId = that.netId; } + /** + * Operates the same as {@code InetAddress.getAllByName} except that host + * resolution is done on this network. + * + * @param host the hostname or literal IP string to be resolved. + * @return the array of addresses associated with the specified host. + * @throws UnknownHostException if the address lookup fails. + */ + public InetAddress[] getAllByName(String host) throws UnknownHostException { + return InetAddress.getAllByNameOnNet(host, netId); + } + + /** + * Operates the same as {@code InetAddress.getByName} except that host + * resolution is done on this network. + * + * @param host + * the hostName to be resolved to an address or {@code null}. + * @return the {@code InetAddress} instance representing the host. + * @throws UnknownHostException + * if the address lookup fails. + */ + public InetAddress getByName(String host) throws UnknownHostException { + return InetAddress.getByNameOnNet(host, netId); + } + // implement the Parcelable interface public int describeContents() { return 0; diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java new file mode 100644 index 0000000..4b85398 --- /dev/null +++ b/core/java/android/net/NetworkAgent.java @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2014 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.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.util.AsyncChannel; +import com.android.internal.util.Protocol; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * A Utility class for handling NetworkRequests. + * + * Created by bearer-specific code to handle tracking requests, scores, + * network data and handle communicating with ConnectivityService. Two + * abstract methods: connect and disconnect are used to act on the + * underlying bearer code. Connect is called when we have a NetworkRequest + * and our score is better than the current handling network's score, while + * disconnect is used when ConnectivityService requests a disconnect. + * + * A bearer may have more than one NetworkAgent if it can simultaneously + * support separate networks (IMS / Internet / MMS Apns on cellular, or + * perhaps connections with different SSID or P2P for Wi-Fi). The bearer + * code should pass its NetworkAgents the NetworkRequests each NetworkAgent + * can handle, demultiplexing for different network types. The bearer code + * can also filter out requests it can never handle. + * + * Each NetworkAgent needs to be given a score and NetworkCapabilities for + * their potential network. While disconnected, the NetworkAgent will check + * each time its score changes or a NetworkRequest changes to see if + * the NetworkAgent can provide a higher scored network for a NetworkRequest + * that the NetworkAgent's NetworkCapabilties can satisfy. This condition will + * trigger a connect request via connect(). After connection, connection data + * should be given to the NetworkAgent by the bearer, including LinkProperties + * NetworkCapabilties and NetworkInfo. After that the NetworkAgent will register + * with ConnectivityService and forward the data on. + * @hide + */ +public abstract class NetworkAgent extends Handler { + private final SparseArray<NetworkRequestAndScore> mNetworkRequests = new SparseArray<>(); + private boolean mConnectionRequested = false; + + private AsyncChannel mAsyncChannel; + private final String LOG_TAG; + private static final boolean DBG = true; + // TODO - this class shouldn't cache data or it runs the risk of getting out of sync + // Make the API require each of these when any is updated so we have the data we need, + // without caching. + private LinkProperties mLinkProperties; + private NetworkInfo mNetworkInfo; + private NetworkCapabilities mNetworkCapabilities; + private int mNetworkScore; + private boolean mRegistered = false; + private final Context mContext; + private AtomicBoolean mHasRequests = new AtomicBoolean(false); + + // TODO - add a name member for logging purposes. + + protected final Object mLockObj = new Object(); + + + private static final int BASE = Protocol.BASE_NETWORK_AGENT; + + /** + * Sent by self to queue up a new/modified request. + * obj = NetworkRequestAndScore + */ + private static final int CMD_ADD_REQUEST = BASE + 1; + + /** + * Sent by self to queue up the removal of a request. + * obj = NetworkRequest + */ + private static final int CMD_REMOVE_REQUEST = BASE + 2; + + /** + * Sent by ConnectivityService to the NetworkAgent to inform it of + * suspected connectivity problems on its network. The NetworkAgent + * should take steps to verify and correct connectivity. + */ + public static final int CMD_SUSPECT_BAD = BASE + 3; + + /** + * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to + * ConnectivityService to pass the current NetworkInfo (connection state). + * Sent when the NetworkInfo changes, mainly due to change of state. + * obj = NetworkInfo + */ + public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 4; + + /** + * Sent by the NetworkAgent to ConnectivityService to pass the current + * NetworkCapabilties. + * obj = NetworkCapabilities + */ + public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 5; + + /** + * Sent by the NetworkAgent to ConnectivityService to pass the current + * NetworkProperties. + * obj = NetworkProperties + */ + public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 6; + + /** + * Sent by the NetworkAgent to ConnectivityService to pass the current + * network score. + * arg1 = network score int + */ + public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 7; + + public NetworkAgent(Looper looper, Context context, String logTag) { + super(looper); + LOG_TAG = logTag; + mContext = context; + } + + /** + * When conditions are right, register with ConnectivityService. + * Connditions include having a well defined network and a request + * that justifies it. The NetworkAgent will remain registered until + * disconnected. + * TODO - this should have all data passed in rather than caching + */ + private void registerSelf() { + synchronized(mLockObj) { + if (!mRegistered && mConnectionRequested && + mNetworkInfo != null && mNetworkInfo.isConnected() && + mNetworkCapabilities != null && + mLinkProperties != null && + mNetworkScore != 0) { + if (DBG) log("Registering NetworkAgent"); + mRegistered = true; + ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(mNetworkInfo), + new LinkProperties(mLinkProperties), + new NetworkCapabilities(mNetworkCapabilities), mNetworkScore); + } else if (DBG && !mRegistered) { + String err = "Not registering due to "; + if (mConnectionRequested == false) err += "no Connect requested "; + if (mNetworkInfo == null) err += "null NetworkInfo "; + if (mNetworkInfo != null && mNetworkInfo.isConnected() == false) { + err += "NetworkInfo disconnected "; + } + if (mLinkProperties == null) err += "null LinkProperties "; + if (mNetworkCapabilities == null) err += "null NetworkCapabilities "; + if (mNetworkScore == 0) err += "null NetworkScore"; + log(err); + } + } + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { + synchronized (mLockObj) { + if (mAsyncChannel != null) { + log("Received new connection while already connected!"); + } else { + if (DBG) log("NetworkAgent fully connected"); + mAsyncChannel = new AsyncChannel(); + mAsyncChannel.connected(null, this, msg.replyTo); + mAsyncChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, + AsyncChannel.STATUS_SUCCESSFUL); + } + } + break; + } + case AsyncChannel.CMD_CHANNEL_DISCONNECT: { + if (DBG) log("CMD_CHANNEL_DISCONNECT"); + if (mAsyncChannel != null) mAsyncChannel.disconnect(); + break; + } + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { + if (DBG) log("NetworkAgent channel lost"); + disconnect(); + clear(); + break; + } + case CMD_SUSPECT_BAD: { + log("Unhandled Message " + msg); + break; + } + case CMD_ADD_REQUEST: { + handleAddRequest(msg); + break; + } + case CMD_REMOVE_REQUEST: { + handleRemoveRequest(msg); + break; + } + } + } + + private void clear() { + synchronized(mLockObj) { + mNetworkRequests.clear(); + mHasRequests.set(false); + mConnectionRequested = false; + mAsyncChannel = null; + mRegistered = false; + } + } + + private static class NetworkRequestAndScore { + NetworkRequest req; + int score; + + NetworkRequestAndScore(NetworkRequest networkRequest, int score) { + req = networkRequest; + this.score = score; + } + } + + private void handleAddRequest(Message msg) { + NetworkRequestAndScore n = (NetworkRequestAndScore)msg.obj; + // replaces old request, updating score + mNetworkRequests.put(n.req.requestId, n); + mHasRequests.set(true); + evalScores(); + } + + private void handleRemoveRequest(Message msg) { + NetworkRequest networkRequest = (NetworkRequest)msg.obj; + + if (mNetworkRequests.get(networkRequest.requestId) != null) { + mNetworkRequests.remove(networkRequest.requestId); + if (mNetworkRequests.size() == 0) mHasRequests.set(false); + evalScores(); + } + } + + /** + * called to go through our list of requests and see if we're + * good enough to try connecting. + * + * Only does connects - we disconnect when requested via + * CMD_CHANNEL_DISCONNECTED, generated by either a loss of connection + * between modules (bearer or ConnectivityService dies) or more commonly + * when the NetworkInfo reports to ConnectivityService it is disconnected. + */ + private void evalScores() { + if (mConnectionRequested) { + // already trying + return; + } + for (int i=0; i < mNetworkRequests.size(); i++) { + int score = mNetworkRequests.valueAt(i).score; + if (score < mNetworkScore) { + // have a request that has a lower scored network servicing it + // (or no network) than we could provide, so lets connect! + mConnectionRequested = true; + connect(); + return; + } + } + } + + public void addNetworkRequest(NetworkRequest networkRequest, int score) { + if (DBG) log("adding NetworkRequest " + networkRequest + " with score " + score); + sendMessage(obtainMessage(CMD_ADD_REQUEST, + new NetworkRequestAndScore(networkRequest, score))); + } + + public void removeNetworkRequest(NetworkRequest networkRequest) { + if (DBG) log("removing NetworkRequest " + networkRequest); + sendMessage(obtainMessage(CMD_REMOVE_REQUEST, networkRequest)); + } + + /** + * Called by the bearer code when it has new LinkProperties data. + * If we're a registered NetworkAgent, this new data will get forwarded on, + * otherwise we store a copy in anticipation of registering. This call + * may also prompt registration if it causes the NetworkAgent to meet + * the conditions (fully configured, connected, satisfys a request and + * has sufficient score). + */ + public void sendLinkProperties(LinkProperties linkProperties) { + linkProperties = new LinkProperties(linkProperties); + synchronized(mLockObj) { + mLinkProperties = linkProperties; + if (mAsyncChannel != null) { + mAsyncChannel.sendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, linkProperties); + } else { + registerSelf(); + } + } + } + + /** + * Called by the bearer code when it has new NetworkInfo data. + * If we're a registered NetworkAgent, this new data will get forwarded on, + * otherwise we store a copy in anticipation of registering. This call + * may also prompt registration if it causes the NetworkAgent to meet + * the conditions (fully configured, connected, satisfys a request and + * has sufficient score). + */ + public void sendNetworkInfo(NetworkInfo networkInfo) { + networkInfo = new NetworkInfo(networkInfo); + synchronized(mLockObj) { + mNetworkInfo = networkInfo; + if (mAsyncChannel != null) { + mAsyncChannel.sendMessage(EVENT_NETWORK_INFO_CHANGED, networkInfo); + } else { + registerSelf(); + } + } + } + + /** + * Called by the bearer code when it has new NetworkCapabilities data. + * If we're a registered NetworkAgent, this new data will get forwarded on, + * otherwise we store a copy in anticipation of registering. This call + * may also prompt registration if it causes the NetworkAgent to meet + * the conditions (fully configured, connected, satisfys a request and + * has sufficient score). + * Note that if these capabilities make the network non-useful, + * ConnectivityServce will tear this network down. + */ + public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) { + networkCapabilities = new NetworkCapabilities(networkCapabilities); + synchronized(mLockObj) { + mNetworkCapabilities = networkCapabilities; + if (mAsyncChannel != null) { + mAsyncChannel.sendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, networkCapabilities); + } else { + registerSelf(); + } + } + } + + public NetworkCapabilities getNetworkCapabilities() { + synchronized(mLockObj) { + return new NetworkCapabilities(mNetworkCapabilities); + } + } + + /** + * Called by the bearer code when it has a new score for this network. + * If we're a registered NetworkAgent, this new data will get forwarded on, + * otherwise we store a copy. + */ + public synchronized void sendNetworkScore(int score) { + synchronized(mLockObj) { + mNetworkScore = score; + evalScores(); + if (mAsyncChannel != null) { + mAsyncChannel.sendMessage(EVENT_NETWORK_SCORE_CHANGED, mNetworkScore); + } else { + registerSelf(); + } + } + } + + public boolean hasRequests() { + return mHasRequests.get(); + } + + public boolean isConnectionRequested() { + synchronized(mLockObj) { + return mConnectionRequested; + } + } + + + abstract protected void connect(); + abstract protected void disconnect(); + + protected void log(String s) { + Log.d(LOG_TAG, "NetworkAgent: " + s); + } +} diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index b783046..8005e5c 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -323,6 +323,6 @@ public final class NetworkCapabilities implements Parcelable { String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" + mLinkDownBandwidthKbps + "Kbps" : ""); - return "NetworkCapabilities: [" + transports + capabilities + upBand + dnBand + "]"; + return "[" + transports + capabilities + upBand + dnBand + "]"; } } diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 53b1308..9e656ee 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -420,7 +420,7 @@ public class NetworkInfo implements Parcelable { @Override public String toString() { synchronized (this) { - StringBuilder builder = new StringBuilder("NetworkInfo: "); + StringBuilder builder = new StringBuilder("["); builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()). append("], state: ").append(mState).append("/").append(mDetailedState). append(", reason: ").append(mReason == null ? "(unspecified)" : mReason). @@ -429,7 +429,8 @@ public class NetworkInfo implements Parcelable { append(", failover: ").append(mIsFailover). append(", isAvailable: ").append(mIsAvailable). append(", isConnectedToProvisioningNetwork: "). - append(mIsConnectedToProvisioningNetwork); + append(mIsConnectedToProvisioningNetwork). + append("]"); return builder.toString(); } } diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 7e3a06d..b3ae3f5 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -25,23 +25,55 @@ import java.util.concurrent.atomic.AtomicInteger; * @hide */ public class NetworkRequest implements Parcelable { + /** + * The NetworkCapabilities that define this request + */ public final NetworkCapabilities networkCapabilities; + + /** + * Identifies the request. NetworkRequests should only be constructed by + * the Framework and given out to applications as tokens to be used to identify + * the request. + * TODO - make sure this input is checked whenever a NR is passed in a public API + */ public final int requestId; - public final boolean legacy; - private static final AtomicInteger sRequestId = new AtomicInteger(); + /** + * Set for legacy requests and the default. + * Causes CONNECTIVITY_ACTION broadcasts to be sent. + * @hide + */ + public final boolean needsBroadcasts; + + private static final AtomicInteger sNextRequestId = new AtomicInteger(1); + + /** + * @hide + */ public NetworkRequest(NetworkCapabilities nc) { - this(nc, false, sRequestId.incrementAndGet()); + this(nc, false, sNextRequestId.getAndIncrement()); } - public NetworkRequest(NetworkCapabilities nc, boolean legacy) { - this(nc, legacy, sRequestId.incrementAndGet()); + /** + * @hide + */ + public NetworkRequest(NetworkCapabilities nc, boolean needsBroadcasts) { + this(nc, needsBroadcasts, sNextRequestId.getAndIncrement()); } - private NetworkRequest(NetworkCapabilities nc, boolean legacy, int rId) { + /** + * @hide + */ + private NetworkRequest(NetworkCapabilities nc, boolean needsBroadcasts, int rId) { requestId = rId; networkCapabilities = nc; - this.legacy = legacy; + this.needsBroadcasts = needsBroadcasts; + } + + public NetworkRequest(NetworkRequest that) { + networkCapabilities = new NetworkCapabilities(that.networkCapabilities); + requestId = that.requestId; + needsBroadcasts = that.needsBroadcasts; } // implement the Parcelable interface @@ -50,16 +82,17 @@ public class NetworkRequest implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(networkCapabilities, flags); - dest.writeInt(legacy ? 1 : 0); + dest.writeInt(needsBroadcasts ? 1 : 0); dest.writeInt(requestId); } public static final Creator<NetworkRequest> CREATOR = new Creator<NetworkRequest>() { public NetworkRequest createFromParcel(Parcel in) { NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null); - boolean legacy = (in.readInt() == 1); + boolean needsBroadcasts = (in.readInt() == 1); int requestId = in.readInt(); - return new NetworkRequest(nc, legacy, requestId); + NetworkRequest result = new NetworkRequest(nc, needsBroadcasts, requestId); + return result; } public NetworkRequest[] newArray(int size) { return new NetworkRequest[size]; @@ -67,14 +100,14 @@ public class NetworkRequest implements Parcelable { }; public String toString() { - return "NetworkRequest [ id=" + requestId + ", legacy=" + legacy + ", " + - networkCapabilities.toString() + " ]"; + return "NetworkRequest [ id=" + requestId + ", needsBroadcasts=" + needsBroadcasts + + ", " + networkCapabilities.toString() + " ]"; } public boolean equals(Object obj) { if (obj instanceof NetworkRequest == false) return false; NetworkRequest that = (NetworkRequest)obj; - return (that.legacy == this.legacy && + return (that.needsBroadcasts == this.needsBroadcasts && that.requestId == this.requestId && ((that.networkCapabilities == null && this.networkCapabilities == null) || (that.networkCapabilities != null && @@ -82,6 +115,7 @@ public class NetworkRequest implements Parcelable { } public int hashCode() { - return requestId + (legacy ? 1013 : 2026) + (networkCapabilities.hashCode() * 1051); + return requestId + (needsBroadcasts ? 1013 : 2026) + + (networkCapabilities.hashCode() * 1051); } } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 1ca6b90..a7485b4 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -88,7 +88,8 @@ public class Build { * * @hide */ - public static final String[] SUPPORTED_ABIS = getString("ro.product.cpu.abilist").split(","); + public static final String[] SUPPORTED_ABIS = SystemProperties.get("ro.product.cpu.abilist") + .split(","); /** * An ordered list of <b>32 bit</b> ABIs supported by this device. The most preferred ABI @@ -98,8 +99,8 @@ public class Build { * * @hide */ - public static final String[] SUPPORTED_32_BIT_ABIS = getString("ro.product.cpu.abilist32") - .split(","); + public static final String[] SUPPORTED_32_BIT_ABIS = + SystemProperties.get("ro.product.cpu.abilist32").split(","); /** * An ordered list of <b>64 bit</b> ABIs supported by this device. The most preferred ABI @@ -109,8 +110,8 @@ public class Build { * * @hide */ - public static final String[] SUPPORTED_64_BIT_ABIS = getString("ro.product.cpu.abilist64") - .split(","); + public static final String[] SUPPORTED_64_BIT_ABIS = + SystemProperties.get("ro.product.cpu.abilist64").split(","); /** Various version strings. */ @@ -515,9 +516,16 @@ public class Build { public static final int KITKAT = 19; /** - * Android 4.5: KitKat for watches, snacks on the run. + * Android 4.4W: KitKat for watches, snacks on the run. + * + * <p>Applications targeting this or a later release will get these + * new changes in behavior:</p> + * <ul> + * <li>{@link android.app.AlertDialog} might not have a default background if the theme does + * not specify one.</li> + * </ul> */ - public static final int KITKAT_WATCH = CUR_DEVELOPMENT; // STOPSHIP: update API level + public static final int KITKAT_WATCH = 20; /** * L! diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 9e03f95..eb9ba13 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -99,24 +99,12 @@ interface INetworkManagementService /** * Add the specified route to the interface. */ - void addRoute(String iface, in RouteInfo route); + void addRoute(int netId, in RouteInfo route); /** * Remove the specified route from the interface. */ - void removeRoute(String iface, in RouteInfo route); - - /** - * Add the specified route to a secondary interface - * This will go into a special route table to be accessed - * via ip rules - */ - void addSecondaryRoute(String iface, in RouteInfo route); - - /** - * Remove the specified secondary route. - */ - void removeSecondaryRoute(String iface, in RouteInfo route); + void removeRoute(int netId, in RouteInfo route); /** * Set the specified MTU size @@ -320,24 +308,14 @@ interface INetworkManagementService void removeIdleTimer(String iface); /** - * Sets the name of the default interface in the DNS resolver. - */ - void setDefaultInterfaceForDns(String iface); - - /** - * Bind name servers to an interface in the DNS resolver. + * Bind name servers to a network in the DNS resolver. */ - void setDnsServersForInterface(String iface, in String[] servers, String domains); + void setDnsServersForNetwork(int netId, in String[] servers, String domains); /** - * Flush the DNS cache associated with the default interface. + * Flush the DNS cache associated with the specified network. */ - void flushDefaultDnsCache(); - - /** - * Flush the DNS cache associated with the specified interface. - */ - void flushInterfaceDnsCache(String iface); + void flushNetworkDnsCache(int netId); void setFirewallEnabled(boolean enabled); boolean isFirewallEnabled(); @@ -350,7 +328,7 @@ interface INetworkManagementService * Set all packets from users [uid_start,uid_end] to go through interface iface * iface must already be set for marked forwarding by {@link setMarkedForwarding} */ - void setUidRangeRoute(String iface, int uid_start, int uid_end); + void setUidRangeRoute(String iface, int uid_start, int uid_end, boolean forward_dns); /** * Clears the special routing rules for users [uid_start,uid_end] @@ -402,31 +380,6 @@ interface INetworkManagementService void clearHostExemption(in LinkAddress host); /** - * Set a process (pid) to use the name servers associated with the specified interface. - */ - void setDnsInterfaceForPid(String iface, int pid); - - /** - * Clear a process (pid) from being associated with an interface. - */ - void clearDnsInterfaceForPid(int pid); - - /** - * Set a range of user ids to use the name servers associated with the specified interface. - */ - void setDnsInterfaceForUidRange(String iface, int uid_start, int uid_end); - - /** - * Clear a user range from being associated with an interface. - */ - void clearDnsInterfaceForUidRange(String iface, int uid_start, int uid_end); - - /** - * Clear the mappings from pid to Dns interface and from uid range to Dns interface. - */ - void clearDnsInterfaceMaps(); - - /** * Start the clatd (464xlat) service */ void startClatd(String interfaceName); @@ -457,12 +410,31 @@ interface INetworkManagementService boolean isNetworkActive(); /** - * setup a new network + * Setup a new network. */ - void createNetwork(int netId, String iface); + void createNetwork(int netId); /** - * remove a network + * Remove a network. */ void removeNetwork(int netId); + + /** + * Add an interface to a network. + */ + void addInterfaceToNetwork(String iface, int netId); + + /** + * Remove an Interface from a network. + */ + void removeInterfaceFromNetwork(String iface, int netId); + + void addLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid); + void removeLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid); + + void setDefaultNetId(int netId); + void clearDefaultNetId(); + + void setPermission(boolean internal, boolean changeNetState, in int[] uids); + void clearPermission(in int[] uids); } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index f8d7c3e..5b2c8db 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -366,15 +366,6 @@ public final class PowerManager { } /** - * Returns true if the screen auto-brightness adjustment setting should - * be available in the UI. This setting is experimental and disabled by default. - * @hide - */ - public static boolean useScreenAutoBrightnessAdjustmentFeature() { - return SystemProperties.getBoolean("persist.power.useautobrightadj", false); - } - - /** * Returns true if the twilight service should be used to adjust screen brightness * policy. This setting is experimental and disabled by default. * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e1fd46e..3cf51b4 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6147,6 +6147,13 @@ public final class Settings { /** @hide */ public static final int HEADS_UP_ON = 1; /** + * The name of the device + * + * @hide + */ + public static final String DEVICE_NAME = "device_name"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * diff --git a/core/java/android/provider/TvContract.java b/core/java/android/provider/TvContract.java index 62252be..5ffffb5 100644 --- a/core/java/android/provider/TvContract.java +++ b/core/java/android/provider/TvContract.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentUris; import android.net.Uri; +import android.tv.TvInputService; import java.util.List; @@ -139,9 +140,9 @@ public final class TvContract { * * @param channelUri The URI of the channel to return programs for. * @param startTime The start time used to filter programs. The returned programs should have - * {@link Programs#END_TIME_UTC_MILLIS} that is greater than this time. + * {@link Programs#COLUMN_END_TIME_UTC_MILLIS} that is greater than this time. * @param endTime The end time used to filter programs. The returned programs should have - * {@link Programs#START_TIME_UTC_MILLIS} that is less than this time. + * {@link Programs#COLUMN_START_TIME_UTC_MILLIS} that is less than this time. */ public static final Uri buildProgramsUriForChannel(Uri channelUri, long startTime, long endTime) { @@ -161,7 +162,7 @@ public final class TvContract { } /** - * Extracts the {@link Channels#PACKAGE_NAME} from a given URI. + * Extracts the {@link Channels#COLUMN_PACKAGE_NAME} from a given URI. * * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or * {@link #buildChannelsUriForInput(ComponentName, boolean)}. @@ -179,7 +180,7 @@ public final class TvContract { } /** - * Extracts the {@link Channels#SERVICE_NAME} from a given URI. + * Extracts the {@link Channels#COLUMN_SERVICE_NAME} from a given URI. * * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or * {@link #buildChannelsUriForInput(ComponentName, boolean)}. @@ -231,7 +232,7 @@ public final class TvContract { * Type: TEXT * </p> */ - public static final String PACKAGE_NAME = "package_name"; + public static final String COLUMN_PACKAGE_NAME = "package_name"; } /** Column definitions for the TV channels table. */ @@ -279,14 +280,14 @@ public final class TvContract { /** The channel type for DVB-SH (satellite). */ public static final int TYPE_DVB_SH = 0x00020400; - /** The channel type for ATSC (terrestrial/cable). */ - public static final int TYPE_ATSC = 0x00030000; + /** The channel type for ATSC (terrestrial). */ + public static final int TYPE_ATSC_T = 0x00030000; - /** The channel type for ATSC 2.0. */ - public static final int TYPE_ATSC_2_0 = 0x00030001; + /** The channel type for ATSC (cable). */ + public static final int TYPE_ATSC_C = 0x00030200; /** The channel type for ATSC-M/H (mobile/handheld). */ - public static final int TYPE_ATSC_M_H = 0x00030100; + public static final int TYPE_ATSC_M_H = 0x00030200; /** The channel type for ISDB-T (terrestrial). */ public static final int TYPE_ISDB_T = 0x00040000; @@ -315,49 +316,124 @@ public final class TvContract { /** The channel type for S-DMB (satellite). */ public static final int TYPE_S_DMB = 0x00060100; + /** A generic service type. */ + public static final int SERVICE_TYPE_OTHER = 0x0; + + /** The service type for regular TV channels. */ + public static final int SERVICE_TYPE_TV = 0x1; + + /** The service type for radio channels. */ + public static final int SERVICE_TYPE_RADIO = 0x2; + /** - * The name of the TV input service that provides this TV channel. + * The name of the {@link TvInputService} subclass that provides this TV channel. This + * should be a fully qualified class name (such as, "com.example.project.TvInputService"). * <p> * This is a required field. * </p><p> * Type: TEXT * </p> */ - public static final String SERVICE_NAME = "service_name"; + public static final String COLUMN_SERVICE_NAME = "service_name"; /** * The predefined type of this TV channel. * <p> - * This is used to indicate which broadcast standard (e.g. ATSC, DVB or ISDB) the current - * channel conforms to. + * This is primarily used to indicate which broadcast standard (e.g. ATSC, DVB or ISDB) the + * current channel conforms to, with an exception being {@link #TYPE_PASSTHROUGH}, which is + * a special channel type used only by pass-through inputs such as HDMI. The value should + * match to one of the followings: {@link #TYPE_OTHER}, {@link #TYPE_PASSTHROUGH}, + * {@link #TYPE_DVB_T}, {@link #TYPE_DVB_T2}, {@link #TYPE_DVB_S}, {@link #TYPE_DVB_S2}, + * {@link #TYPE_DVB_C}, {@link #TYPE_DVB_C2}, {@link #TYPE_DVB_H}, {@link #TYPE_DVB_SH}, + * {@link #TYPE_ATSC_T}, {@link #TYPE_ATSC_C}, {@link #TYPE_ATSC_M_H}, {@link #TYPE_ISDB_T}, + * {@link #TYPE_ISDB_TB}, {@link #TYPE_ISDB_S}, {@link #TYPE_ISDB_C} {@link #TYPE_1SEG}, + * {@link #TYPE_DTMB}, {@link #TYPE_CMMB}, {@link #TYPE_T_DMB}, {@link #TYPE_S_DMB} + * </p><p> + * This is a required field. + * </p><p> + * Type: INTEGER + * </p> + */ + public static final String COLUMN_TYPE = "type"; + + /** + * The predefined service type of this TV channel. + * <p> + * This is primarily used to indicate whether the current channel is a regular TV channel or + * a radio-like channel. Use the same coding for {@code service_type} in the underlying + * broadcast standard if it is defined there (e.g. ATSC A/53, ETSI EN 300 468 and ARIB + * STD-B10). Otherwise use one of the followings: {@link #SERVICE_TYPE_OTHER}, + * {@link #SERVICE_TYPE_TV}, {@link #SERVICE_TYPE_RADIO} * </p><p> * This is a required field. * </p><p> * Type: INTEGER * </p> */ - public static final String TYPE = "type"; + public static final String COLUMN_SERVICE_TYPE = "service_type"; /** - * The transport stream ID as appeared in various broadcast standards. + * The original network ID of this TV channel. * <p> - * This is not a required field but if provided, can significantly increase the accuracy of - * channel identification. + * This is used to identify the originating delivery system, if applicable. Use the same + * coding for {@code origianal_network_id} in the underlying broadcast standard if it is + * defined there (e.g. ETSI EN 300 468/TR 101 211 and ARIB STD-B10). If channels cannot be + * globally identified by 2-tuple {{@link #COLUMN_TRANSPORT_STREAM_ID}, + * {@link #COLUMN_SERVICE_ID}}, one must carefully assign a value to this field to form a + * unique 3-tuple identification {{@link #COLUMN_ORIGINAL_NETWORK_ID}, + * {@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}} for its channels. + * </p><p> + * This is a required field if the channel cannot be uniquely identified by a 2-tuple + * {{@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}}. * </p><p> * Type: INTEGER * </p> */ - public static final String TRANSPORT_STREAM_ID = "transport_stream_id"; + public static final String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id"; + + /** + * The transport stream ID of this channel. + * <p> + * This is used to identify the Transport Stream that contains the current channel from any + * other multiplex within a network, if applicable. Use the same coding for + * {@code transport_stream_id} defined in ISO/IEC 13818-1 if the channel is transmitted via + * the MPEG Transport Stream as is the case for many digital broadcast standards. + * </p><p> + * This is a required field if the current channel is transmitted via the MPEG Transport + * Stream. + * </p><p> + * Type: INTEGER + * </p> + */ + public static final String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id"; + + /** + * The service ID of this channel. + * <p> + * This is used to identify the current service (roughly equivalent to channel) from any + * other service within the Transport Stream, if applicable. Use the same coding for + * {@code service_id} in the underlying broadcast standard if it is defined there (e.g. ETSI + * EN 300 468 and ARIB STD-B10) or {@code program_number} (which usually has the same value + * as {@code service_id}) in ISO/IEC 13818-1 if the channel is transmitted via the MPEG + * Transport Stream. + * </p><p> + * This is a required field if the current channel is transmitted via the MPEG Transport + * Stream. + * </p><p> + * Type: INTEGER + * </p> + */ + public static final String COLUMN_SERVICE_ID = "service_id"; /** * The channel number that is displayed to the user. * <p> * The format can vary depending on broadcast standard and product specification. * </p><p> - * Type: INTEGER + * Type: TEXT * </p> */ - public static final String DISPLAY_NUMBER = "display_number"; + public static final String COLUMN_DISPLAY_NUMBER = "display_number"; /** * The channel name that is displayed to the user. @@ -369,7 +445,7 @@ public final class TvContract { * Type: TEXT * </p> */ - public static final String DISPLAY_NAME = "display_name"; + public static final String COLUMN_DISPLAY_NAME = "display_name"; /** * The description of this TV channel. @@ -379,7 +455,7 @@ public final class TvContract { * Type: TEXT * </p> */ - public static final String DESCRIPTION = "description"; + public static final String COLUMN_DESCRIPTION = "description"; /** * The flag indicating whether this TV channel is browsable or not. @@ -391,7 +467,7 @@ public final class TvContract { * Type: INTEGER (boolean) * </p> */ - public static final String BROWSABLE = "browsable"; + public static final String COLUMN_BROWSABLE = "browsable"; /** * Generic data used by individual TV input services. @@ -399,7 +475,7 @@ public final class TvContract { * Type: BLOB * </p> */ - public static final String DATA = "data"; + public static final String COLUMN_DATA = "data"; /** @@ -413,7 +489,7 @@ public final class TvContract { * Type: INTEGER * </p> */ - public static final String VERSION_NUMBER = "version_number"; + public static final String COLUMN_VERSION_NUMBER = "version_number"; private Channels() {} } @@ -441,7 +517,7 @@ public final class TvContract { * Type: INTEGER (long) * </p> */ - public static final String CHANNEL_ID = "channel_id"; + public static final String COLUMN_CHANNEL_ID = "channel_id"; /** * The title of this TV program. @@ -449,7 +525,7 @@ public final class TvContract { * Type: TEXT * </p> **/ - public static final String TITLE = "title"; + public static final String COLUMN_TITLE = "title"; /** * The start time of this TV program, in milliseconds since the epoch. @@ -457,7 +533,7 @@ public final class TvContract { * Type: INTEGER (long) * </p> */ - public static final String START_TIME_UTC_MILLIS = "start_time_utc_millis"; + public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis"; /** * The end time of this TV program, in milliseconds since the epoch. @@ -465,7 +541,7 @@ public final class TvContract { * Type: INTEGER (long) * </p> */ - public static final String END_TIME_UTC_MILLIS = "end_time_utc_millis"; + public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; /** * The description of this TV program that is displayed to the user by default. @@ -475,19 +551,19 @@ public final class TvContract { * Type: TEXT * </p> */ - public static final String DESCRIPTION = "description"; + public static final String COLUMN_DESCRIPTION = "description"; /** * The detailed, lengthy description of this TV program that is displayed only when the user * wants to see more information. * <p> * TV input services should leave this field empty if they have no additional - * details beyond {@link #DESCRIPTION}. + * details beyond {@link #COLUMN_DESCRIPTION}. * </p><p> * Type: TEXT * </p> */ - public static final String LONG_DESCRIPTION = "long_description"; + public static final String COLUMN_LONG_DESCRIPTION = "long_description"; /** * Generic data used by TV input services. @@ -495,7 +571,7 @@ public final class TvContract { * Type: BLOB * </p> */ - public static final String DATA = "data"; + public static final String COLUMN_DATA = "data"; /** * The version number of this row entry used by TV input services. @@ -508,7 +584,7 @@ public final class TvContract { * Type: INTEGER * </p> */ - public static final String VERSION_NUMBER = "version_number"; + public static final String COLUMN_VERSION_NUMBER = "version_number"; private Programs() {} } @@ -540,7 +616,8 @@ public final class TvContract { * Type: INTEGER (long) * </p> */ - public static final String WATCH_START_TIME_UTC_MILLIS = "watch_start_time_utc_millis"; + public static final String COLUMN_WATCH_START_TIME_UTC_MILLIS = + "watch_start_time_utc_millis"; /** * The UTC time that the user stopped watching this TV program, in milliseconds since the @@ -549,7 +626,7 @@ public final class TvContract { * Type: INTEGER (long) * </p> */ - public static final String WATCH_END_TIME_UTC_MILLIS = "watch_end_time_utc_millis"; + public static final String COLUMN_WATCH_END_TIME_UTC_MILLIS = "watch_end_time_utc_millis"; /** * The channel ID that contains this TV program. @@ -557,7 +634,7 @@ public final class TvContract { * Type: INTEGER (long) * </p> */ - public static final String CHANNEL_ID = "channel_id"; + public static final String COLUMN_CHANNEL_ID = "channel_id"; /** * The title of this TV program. @@ -565,7 +642,7 @@ public final class TvContract { * Type: TEXT * </p> */ - public static final String TITLE = "title"; + public static final String COLUMN_TITLE = "title"; /** * The start time of this TV program, in milliseconds since the epoch. @@ -573,7 +650,7 @@ public final class TvContract { * Type: INTEGER (long) * </p> */ - public static final String START_TIME_UTC_MILLIS = "start_time_utc_millis"; + public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis"; /** * The end time of this TV program, in milliseconds since the epoch. @@ -581,7 +658,7 @@ public final class TvContract { * Type: INTEGER (long) * </p> */ - public static final String END_TIME_UTC_MILLIS = "end_time_utc_millis"; + public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis"; /** * The description of this TV program. @@ -589,7 +666,7 @@ public final class TvContract { * Type: TEXT * </p> */ - public static final String DESCRIPTION = "description"; + public static final String COLUMN_DESCRIPTION = "description"; private WatchedPrograms() {} } diff --git a/core/java/android/transition/MoveImage.java b/core/java/android/transition/MoveImage.java index 183cdd2..6f1b6f7 100644 --- a/core/java/android/transition/MoveImage.java +++ b/core/java/android/transition/MoveImage.java @@ -64,7 +64,13 @@ public class MoveImage extends Transition { if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) { return; } + ImageView imageView = (ImageView) view; + Drawable drawable = imageView.getDrawable(); + if (drawable == null) { + return; + } Map<String, Object> values = transitionValues.values; + values.put(PROPNAME_DRAWABLE, drawable); ViewGroup parent = (ViewGroup) view.getParent(); parent.getLocationInWindow(mTempLoc); @@ -79,11 +85,9 @@ public class MoveImage extends Transition { Rect bounds = new Rect(left, top, right, bottom); values.put(PROPNAME_BOUNDS, bounds); - ImageView imageView = (ImageView) view; Matrix matrix = getMatrix(imageView); values.put(PROPNAME_MATRIX, matrix); values.put(PROPNAME_CLIP, findClip(imageView)); - values.put(PROPNAME_DRAWABLE, imageView.getDrawable()); } @Override diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java index 1d6298d..eeb738d 100644 --- a/core/java/android/tv/TvInputService.java +++ b/core/java/android/tv/TvInputService.java @@ -489,7 +489,7 @@ public abstract class TvInputService extends Service { } } } - if (mOverlayView == null) { + if (mOverlayView == null || !mOverlayView.isAttachedToWindow()) { return Session.DISPATCH_NOT_HANDLED; } if (!mOverlayView.hasWindowFocus()) { diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java index 80501e8..7721575 100644 --- a/core/java/android/tv/TvView.java +++ b/core/java/android/tv/TvView.java @@ -31,6 +31,7 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.ViewRootImpl; /** * View playing TV @@ -89,7 +90,10 @@ public class TvView extends SurfaceView { if (dispatchUnhandledInputEvent(event)) { return; } - getViewRootImpl().dispatchUnhandledInputEvent(event); + ViewRootImpl viewRootImpl = getViewRootImpl(); + if (viewRootImpl != null) { + viewRootImpl.dispatchUnhandledInputEvent(event); + } } }; @@ -347,7 +351,9 @@ public class TvView extends SurfaceView { public void onSessionCreated(Session session) { if (this != mSessionCallback) { // This callback is obsolete. - session.release(); + if (session != null) { + session.release(); + } return; } mSession = session; diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java index 9a4bd4b..d7e8cf0 100644 --- a/core/java/android/util/Range.java +++ b/core/java/android/util/Range.java @@ -18,7 +18,7 @@ package android.util; import static com.android.internal.util.Preconditions.*; -import android.hardware.camera2.impl.HashCodeHelpers; +import android.hardware.camera2.utils.HashCodeHelpers; /** * Immutable class for describing the range of two numeric values. diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 2d1016a..852fce5 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -2698,10 +2698,10 @@ public class KeyEvent extends InputEvent implements Parcelable { public static int keyCodeFromString(String symbolicName) { if (symbolicName.startsWith(LABEL_PREFIX)) { symbolicName = symbolicName.substring(LABEL_PREFIX.length()); - } - int keyCode = nativeKeyCodeFromString(symbolicName); - if (keyCode > 0) { - return keyCode; + int keyCode = nativeKeyCodeFromString(symbolicName); + if (keyCode > 0) { + return keyCode; + } } try { return Integer.parseInt(symbolicName, 10); diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 0626ab9..7f2defd 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -3070,10 +3070,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static int axisFromString(String symbolicName) { if (symbolicName.startsWith(LABEL_PREFIX)) { symbolicName = symbolicName.substring(LABEL_PREFIX.length()); - } - int axis = nativeAxisFromString(symbolicName); - if (axis >= 0) { - return axis; + int axis = nativeAxisFromString(symbolicName); + if (axis >= 0) { + return axis; + } } try { return Integer.parseInt(symbolicName, 10); diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 5653066..17035b1 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -19,7 +19,11 @@ package android.view; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.Trace; +import android.util.Log; import android.util.TimeUtils; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; @@ -65,6 +69,8 @@ public class ThreadedRenderer extends HardwareRenderer { private Choreographer mChoreographer; ThreadedRenderer(boolean translucent) { + AtlasInitializer.sInstance.init(); + long rootNodePtr = nCreateRootRenderNode(); mRootNode = RenderNode.adopt(rootNodePtr); mRootNode.setClipToBounds(false); @@ -292,8 +298,43 @@ public class ThreadedRenderer extends HardwareRenderer { } } - /** @hide */ - public static native void postToRenderThread(Runnable runnable); + private static class AtlasInitializer { + static AtlasInitializer sInstance = new AtlasInitializer(); + + private boolean mInitialized = false; + + private AtlasInitializer() {} + + synchronized void init() { + if (mInitialized) return; + IBinder binder = ServiceManager.getService("assetatlas"); + if (binder == null) return; + + IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder); + try { + if (atlas.isCompatible(android.os.Process.myPpid())) { + GraphicBuffer buffer = atlas.getBuffer(); + if (buffer != null) { + long[] map = atlas.getMap(); + if (map != null) { + nSetAtlas(buffer, map); + mInitialized = true; + } + // If IAssetAtlas is not the same class as the IBinder + // we are using a remote service and we can safely + // destroy the graphic buffer + if (atlas.getClass() != binder.getClass()) { + buffer.destroy(); + } + } + } + } catch (RemoteException e) { + Log.w(LOG_TAG, "Could not acquire atlas", e); + } + } + } + + private static native void nSetAtlas(GraphicBuffer buffer, long[] map); private static native long nCreateRootRenderNode(); private static native long nCreateProxy(boolean translucent, long rootRenderNode); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 3fee0ac..f874eb7 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -24,8 +24,6 @@ import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; -import libcore.util.Objects; - import android.content.Context; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; @@ -58,6 +56,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -1550,7 +1549,7 @@ public final class InputMethodManager { } if (DEBUG) Log.d(TAG, "updateCursor"); mTmpCursorRect.set(left, top, right, bottom); - if (!Objects.equal(mCursorRect, mTmpCursorRect)) { + if (!Objects.equals(mCursorRect, mTmpCursorRect)) { try { if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod); mCursorRect.set(mTmpCursorRect); @@ -1581,7 +1580,7 @@ public final class InputMethodManager { || mCurrentTextBoxAttribute == null || mCurMethod == null) { return; } - if (Objects.equal(mCursorAnchorInfo, cursorAnchorInfo)) { + if (Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) { Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info=" + cursorAnchorInfo); return; } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index d9a4f57..f91ef1a 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3404,7 +3404,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_OVERSCROLL; } if (incrementalDeltaY > 0) { - mEdgeGlowTop.onPull((float) overscroll / getHeight(), + mEdgeGlowTop.onPull((float) -overscroll / getHeight(), (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index 83fbe8f..c4a40b4 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -22,14 +22,10 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.Xfermode; -import android.util.Log; -import com.android.internal.R; import android.content.Context; -import android.content.res.Resources; import android.graphics.Canvas; -import android.graphics.drawable.Drawable; +import android.util.FloatMath; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -62,12 +58,9 @@ public class EdgeEffect { // Time it will take before a pulled glow begins receding in ms private static final int PULL_TIME = 167; - // Time it will take in ms for a pulled glow to decay to partial strength before release - private static final int PULL_DECAY_TIME = 1000; - private static final float MAX_ALPHA = 1.f; - private static final float MAX_GLOW_HEIGHT = 1.5f; + private static final float MAX_GLOW_SCALE = 2.f; private static final float PULL_GLOW_BEGIN = 0.f; @@ -78,7 +71,9 @@ public class EdgeEffect { private static final float EPSILON = 0.001f; - private static final float SIN_45 = (float) Math.sin(Math.PI / 4); + private static final double ANGLE = Math.PI / 6; + private static final float SIN = (float) Math.sin(ANGLE); + private static final float COS = (float) Math.cos(ANGLE); private float mGlowAlpha; private float mGlowScaleY; @@ -114,6 +109,7 @@ public class EdgeEffect { private final RectF mArcRect = new RectF(); private final Paint mPaint = new Paint(); private float mRadius; + private float mBaseGlowHeight; private float mDisplacement = 0.5f; private float mTargetDisplacement = 0.5f; @@ -128,7 +124,7 @@ public class EdgeEffect { final int themeColor = a.getColor( com.android.internal.R.styleable.EdgeEffect_colorPrimaryLight, 0xff666666); a.recycle(); - mPaint.setColor((themeColor & 0xffffff) | 0x66000000); + mPaint.setColor((themeColor & 0xffffff) | 0x33000000); mPaint.setStyle(Paint.Style.FILL); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); mInterpolator = new DecelerateInterpolator(); @@ -141,10 +137,11 @@ public class EdgeEffect { * @param height Effect height in pixels */ public void setSize(int width, int height) { - final float r = width * 0.5f / SIN_45; - final float y = SIN_45 * r; + final float r = width * 0.75f / SIN; + final float y = COS * r; final float h = r - y; mRadius = r; + mBaseGlowHeight = h; mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h)); } @@ -214,21 +211,18 @@ public class EdgeEffect { mPullDistance += deltaDistance; + final float absdd = Math.abs(deltaDistance); mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA, - mGlowAlpha + - (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR)); + mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR)); - float glowChange = Math.abs(deltaDistance); - if (deltaDistance > 0 && mPullDistance < 0) { - glowChange = -glowChange; - } if (mPullDistance == 0) { - mGlowScaleY = 0; - } + mGlowScaleY = mGlowScaleYStart = 0; + } else { + final float scale = Math.max(0, 1 - 1 / + FloatMath.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3f) / 0.7f; - // Do not allow glow to get larger than MAX_GLOW_HEIGHT. - mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max( - 0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR)); + mGlowScaleY = mGlowScaleYStart = scale; + } mGlowAlphaFinish = mGlowAlpha; mGlowScaleYFinish = mGlowScaleY; @@ -311,19 +305,17 @@ public class EdgeEffect { final float y = mBounds.height(); final float centerY = y - mRadius; final float centerX = mBounds.centerX(); + mArcRect.set(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius); canvas.scale(1.f, Math.min(mGlowScaleY, 1.f), centerX, 0); final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f; - float translateX = mBounds.width() * displacement; - float translateY = 0; - if (mGlowScaleY > 1.f) { - translateY = (mGlowScaleY - 1.f) * mBounds.height(); - } + float translateX = mBounds.width() * displacement / 2; + canvas.clipRect(Float.MIN_VALUE, mBounds.top, Float.MAX_VALUE, Float.MAX_VALUE); - canvas.translate(translateX, translateY); - canvas.drawArc(mArcRect, 0, 180, true, mPaint); + canvas.translate(translateX, 0); + canvas.drawArc(mArcRect, 45, 90, true, mPaint); canvas.restoreToCount(count); boolean oneLastFrame = false; @@ -341,7 +333,7 @@ public class EdgeEffect { * @return The maximum height of the edge effect */ public int getMaxHeight() { - return (int) (mBounds.height() * MAX_GLOW_HEIGHT + 0.5f); + return (int) (mBounds.height() * MAX_GLOW_SCALE + 0.5f); } private void update() { @@ -369,16 +361,7 @@ public class EdgeEffect { mGlowScaleYFinish = 0.f; break; case STATE_PULL: - mState = STATE_PULL_DECAY; - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mDuration = PULL_DECAY_TIME; - - mGlowAlphaStart = mGlowAlpha; - mGlowScaleYStart = mGlowScaleY; - - // After pull, the glow should fade to nothing. - mGlowAlphaFinish = 0.f; - mGlowScaleYFinish = 0.f; + // Hold in this state until explicitly released. break; case STATE_PULL_DECAY: mState = STATE_RECEDE; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index b0a4e24..cbe7511 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -39,6 +39,7 @@ import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; @@ -94,6 +95,8 @@ import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.CursorAnchorInfo; +import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -215,6 +218,8 @@ public class Editor { private TextView mTextView; + final CursorAnchorInfoNotifier mCursorAnchorInfoNotifier = new CursorAnchorInfoNotifier(); + Editor(TextView textView) { mTextView = textView; } @@ -249,9 +254,13 @@ public class Editor { // We had an active selection from before, start the selection mode. startSelectionActionMode(); } + + getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true); } void onDetachedFromWindow() { + getPositionListener().removeSubscriber(mCursorAnchorInfoNotifier); + if (mError != null) { hideError(); } @@ -780,7 +789,7 @@ public class Editor { boolean parentPositionChanged, boolean parentScrolled); } - private boolean isPositionVisible(int positionX, int positionY) { + private boolean isPositionVisible(final float positionX, final float positionY) { synchronized (TEMP_POSITION) { final float[] position = TEMP_POSITION; position[0] = positionX; @@ -2134,7 +2143,8 @@ public class Editor { private class PositionListener implements ViewTreeObserver.OnPreDrawListener { // 3 handles // 3 ActionPopup [replace, suggestion, easyedit] (suggestionsPopup first hides the others) - private final int MAXIMUM_NUMBER_OF_LISTENERS = 6; + // 1 CursorAnchorInfoNotifier + private final int MAXIMUM_NUMBER_OF_LISTENERS = 7; private TextViewPositionListener[] mPositionListeners = new TextViewPositionListener[MAXIMUM_NUMBER_OF_LISTENERS]; private boolean mCanMove[] = new boolean[MAXIMUM_NUMBER_OF_LISTENERS]; @@ -2997,6 +3007,116 @@ public class Editor { } } + /** + * A listener to call {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)} + * while the input method is requesting the cursor/anchor position. Does nothing as long as + * {@link InputMethodManager#isWatchingCursor(View)} returns false. + */ + private final class CursorAnchorInfoNotifier implements TextViewPositionListener { + final CursorAnchorInfoBuilder mSelectionInfoBuilder = new CursorAnchorInfoBuilder(); + final int[] mTmpIntOffset = new int[2]; + final Matrix mViewToScreenMatrix = new Matrix(); + + @Override + public void updatePosition(int parentPositionX, int parentPositionY, + boolean parentPositionChanged, boolean parentScrolled) { + final InputMethodState ims = mInputMethodState; + if (ims == null || ims.mBatchEditNesting > 0) { + return; + } + final InputMethodManager imm = InputMethodManager.peekInstance(); + if (null == imm) { + return; + } + // Skip if the IME has not requested the cursor/anchor position. + if (!imm.isWatchingCursor(mTextView)) { + return; + } + Layout layout = mTextView.getLayout(); + if (layout == null) { + return; + } + + final CursorAnchorInfoBuilder builder = mSelectionInfoBuilder; + builder.reset(); + + final int selectionStart = mTextView.getSelectionStart(); + final int selectionEnd = mTextView.getSelectionEnd(); + builder.setSelectionRange(mTextView.getSelectionStart(), mTextView.getSelectionEnd()); + + // Construct transformation matrix from view local coordinates to screen coordinates. + mViewToScreenMatrix.set(mTextView.getMatrix()); + mTextView.getLocationOnScreen(mTmpIntOffset); + mViewToScreenMatrix.postTranslate(mTmpIntOffset[0], mTmpIntOffset[1]); + builder.setMatrix(mViewToScreenMatrix); + + final float viewportToContentHorizontalOffset = + mTextView.viewportToContentHorizontalOffset(); + final float viewportToContentVerticalOffset = + mTextView.viewportToContentVerticalOffset(); + + if (mTextView.getText() instanceof Spannable) { + final Spannable sp = (Spannable) mTextView.getText(); + int compositionStart = EditableInputConnection.getComposingSpanStart(sp); + int compositionEnd = EditableInputConnection.getComposingSpanEnd(sp); + if (compositionEnd < compositionStart) { + final int temp = compositionEnd; + compositionEnd = compositionStart; + compositionStart = temp; + } + builder.setCandidateRange(compositionStart, compositionEnd); + for (int offset = compositionStart; offset < compositionEnd; offset++) { + if (offset < 0) { + continue; + } + final int line = layout.getLineForOffset(offset); + final float left = layout.getPrimaryHorizontal(offset) + + viewportToContentHorizontalOffset; + final float top = layout.getLineTop(line) + viewportToContentVerticalOffset; + // Here we are tentatively passing offset + 1 to calculate the other side of + // the primary horizontal to preserve as many positions as possible so that + // the IME can reconstruct the layout entirely. However, we should revisit this + // to have a clear specification about the relationship between the index of + // the character and its bounding box. See also the TODO comment below. + final float right = layout.getPrimaryHorizontal(offset + 1) + + viewportToContentHorizontalOffset; + final float bottom = layout.getLineBottom(line) + + viewportToContentVerticalOffset; + // Take TextView's padding and scroll into account. + if (isPositionVisible(left, top) && isPositionVisible(right, bottom)) { + // Here offset is the index in Java chars. + // TODO: We must have a well-defined specification. For example, how + // RTL, surrogate pairs, and composition letters are handled must be + // documented. + builder.addCharacterRect(offset, left, top, right, bottom); + } + } + } + + // Treat selectionStart as the insertion point. + if (0 <= selectionStart) { + final int offset = selectionStart; + final int line = layout.getLineForOffset(offset); + final float insertionMarkerX = layout.getPrimaryHorizontal(offset) + + viewportToContentHorizontalOffset; + final float insertionMarkerTop = layout.getLineTop(line) + + viewportToContentVerticalOffset; + final float insertionMarkerBaseline = layout.getLineBaseline(line) + + viewportToContentVerticalOffset; + final float insertionMarkerBottom = layout.getLineBottom(line) + + viewportToContentVerticalOffset; + // Take TextView's padding and scroll into account. + if (isPositionVisible(insertionMarkerX, insertionMarkerTop) && + isPositionVisible(insertionMarkerX, insertionMarkerBottom)) { + builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop, + insertionMarkerBaseline, insertionMarkerBottom); + } + } + + imm.updateCursorAnchorInfo(mTextView, builder.build()); + } + } + private abstract class HandleView extends View implements TextViewPositionListener { protected Drawable mDrawable; protected Drawable mDrawableLtr; diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index b568121..664f9db 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -123,11 +123,14 @@ public class AlertController { private int mCheckedItem = -1; private int mAlertDialogLayout; + private int mButtonPanelSideLayout; private int mListLayout; private int mMultiChoiceItemLayout; private int mSingleChoiceItemLayout; private int mListItemLayout; + private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE; + private Handler mHandler; private final View.OnClickListener mButtonHandler = new View.OnClickListener() { @@ -199,6 +202,9 @@ public class AlertController { mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout, com.android.internal.R.layout.alert_dialog); + mButtonPanelSideLayout = a.getResourceId( + com.android.internal.R.styleable.AlertDialog_buttonPanelSideLayout, 0); + mListLayout = a.getResourceId( com.android.internal.R.styleable.AlertDialog_listLayout, com.android.internal.R.layout.select_dialog); @@ -240,10 +246,22 @@ public class AlertController { public void installContent() { /* We use a custom title so never request a window title */ mWindow.requestFeature(Window.FEATURE_NO_TITLE); - mWindow.setContentView(mAlertDialogLayout); + int contentView = selectContentView(); + mWindow.setContentView(contentView); setupView(); setupDecor(); } + + private int selectContentView() { + if (mButtonPanelSideLayout == 0) { + return mAlertDialogLayout; + } + if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) { + return mButtonPanelSideLayout; + } + // TODO: use layout hint side for long messages/lists + return mAlertDialogLayout; + } public void setTitle(CharSequence title) { mTitle = title; @@ -299,6 +317,13 @@ public class AlertController { } /** + * Sets a hint for the best button panel layout. + */ + public void setButtonPanelLayoutHint(int layoutHint) { + mButtonPanelLayoutHint = layoutHint; + } + + /** * Sets a click listener or a message to be sent when the button is clicked. * You only need to pass one of {@code listener} or {@code msg}. * diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index bc92c4a..4b84941 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -55,5 +55,9 @@ public class Protocol { public static final int BASE_DNS_PINGER = 0x00050000; public static final int BASE_NSD_MANAGER = 0x00060000; public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000; + public static final int BASE_CONNECTIVITY_SERVICE = 0x00080000; + public static final int BASE_NETWORK_AGENT = 0x00081000; + public static final int BASE_NETWORK_MONITOR = 0x00082000; + public static final int BASE_CONNECTIVITY_MANAGER = 0x00083000; //TODO: define all used protocols } diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp index 05a99a3..fa2cfe3 100644 --- a/core/jni/android_hardware_camera2_CameraMetadata.cpp +++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp @@ -489,8 +489,13 @@ static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyN sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor(); - SortedVector<String8> vendorSections = vTags->getAllSectionNames(); - size_t vendorSectionCount = vendorSections.size(); + SortedVector<String8> vendorSections; + size_t vendorSectionCount = 0; + + if (vTags != 0) { + vendorSections = vTags->getAllSectionNames(); + vendorSectionCount = vendorSections.size(); + } // First, find the section by the longest string match const char *section = NULL; @@ -561,7 +566,7 @@ static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyN "Could not find tag name for key '%s')", key); return 0; } - } else { + } else if (vTags != 0) { // Match vendor tags (typically com.*) const String8 sectionName(section); const String8 tagName(keyTagName); diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index cdd036e..d130a6d 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -26,8 +26,11 @@ #include <android_runtime/android_view_Surface.h> #include <system/window.h> +#include "android_view_GraphicBuffer.h" + #include <Animator.h> #include <RenderNode.h> +#include <renderthread/CanvasContext.h> #include <renderthread/RenderProxy.h> #include <renderthread/RenderTask.h> #include <renderthread/RenderThread.h> @@ -67,6 +70,26 @@ private: jobject mRunnable; }; +class SetAtlasTask : public RenderTask { +public: + SetAtlasTask(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) + : mBuffer(buffer) + , mMap(map) + , mMapSize(size) { + } + + virtual void run() { + CanvasContext::setTextureAtlas(mBuffer, mMap, mMapSize); + mMap = 0; + delete this; + } + +private: + sp<GraphicBuffer> mBuffer; + int64_t* mMap; + size_t mMapSize; +}; + class OnFinishedEvent { public: OnFinishedEvent(BaseAnimator* animator, AnimationListener* listener) @@ -127,9 +150,18 @@ private: std::vector<OnFinishedEvent> mOnFinishedEvents; }; -static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz, - jobject jrunnable) { - RenderTask* task = new JavaTask(env, jrunnable); +static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz, + jobject graphicBuffer, jlongArray atlasMapArray) { + sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer); + jsize len = env->GetArrayLength(atlasMapArray); + if (len <= 0) { + ALOGW("Failed to initialize atlas, invalid map length: %d", len); + return; + } + int64_t* map = new int64_t[len]; + env->GetLongArrayRegion(atlasMapArray, 0, len, map); + + SetAtlasTask* task = new SetAtlasTask(buffer, map, len); RenderThread::getInstance().queue(task); } @@ -275,7 +307,7 @@ const char* const kClassPathName = "android/view/ThreadedRenderer"; static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER - { "postToRenderThread", "(Ljava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_postToRenderThread }, + { "nSetAtlas", "(Landroid/view/GraphicBuffer;[J)V", (void*) android_view_ThreadedRenderer_setAtlas }, { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode }, { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy }, { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy }, diff --git a/core/res/res/layout/alert_dialog_leanback.xml b/core/res/res/layout/alert_dialog_leanback.xml new file mode 100644 index 0000000..8655aea --- /dev/null +++ b/core/res/res/layout/alert_dialog_leanback.xml @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/parentPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/leanback_alert_dialog_horizontal_margin" + android:layout_marginRight="@dimen/leanback_alert_dialog_horizontal_margin" + android:layout_marginTop="@dimen/leanback_alert_dialog_vertical_margin" + android:layout_marginBottom="@dimen/leanback_alert_dialog_vertical_margin" + android:orientation="vertical"> + + <LinearLayout android:id="@+id/topPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + <LinearLayout android:id="@+id/title_template" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical|start" + android:minHeight="@dimen/alert_dialog_title_height" + android:layout_marginStart="16dip" + android:layout_marginEnd="16dip"> + <ImageView android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingEnd="8dip" + android:src="@null" /> + <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle" + style="?android:attr/windowTitleStyle" + android:singleLine="true" + android:ellipsize="end" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="viewStart" /> + </LinearLayout> + <!-- If the client uses a customTitle, it will be added here. --> + </LinearLayout> + + <LinearLayout android:id="@+id/contentPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical" + android:minHeight="64dp"> + <ScrollView android:id="@+id/scrollView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipToPadding="false"> + <TextView android:id="@+id/message" + style="?android:attr/textAppearanceMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="16dip" + android:paddingEnd="16dip" + android:paddingTop="8dip" + android:paddingBottom="8dip"/> + </ScrollView> + </LinearLayout> + + <FrameLayout android:id="@+id/customPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:minHeight="64dp"> + <FrameLayout android:id="@+android:id/custom" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </FrameLayout> + + <LinearLayout android:id="@+id/buttonPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/alert_dialog_button_bar_height" + android:orientation="vertical"> + <LinearLayout + style="?android:attr/buttonBarStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layoutDirection="locale" + android:measureWithLargestChild="true"> + <Button android:id="@+id/button2" + android:layout_width="wrap_content" + android:layout_gravity="start" + android:layout_weight="1" + android:maxLines="2" + style="?android:attr/buttonBarButtonStyle" + android:textSize="14sp" + android:minHeight="@dimen/alert_dialog_button_bar_height" + android:layout_height="wrap_content" /> + <Button android:id="@+id/button3" + android:layout_width="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_weight="1" + android:maxLines="2" + style="?android:attr/buttonBarButtonStyle" + android:textSize="14sp" + android:minHeight="@dimen/alert_dialog_button_bar_height" + android:layout_height="wrap_content" /> + <Button android:id="@+id/button1" + android:layout_width="wrap_content" + android:layout_gravity="end" + android:layout_weight="1" + android:maxLines="2" + android:minHeight="@dimen/alert_dialog_button_bar_height" + style="?android:attr/buttonBarButtonStyle" + android:textSize="14sp" + android:layout_height="wrap_content" /> + </LinearLayout> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/layout/alert_dialog_leanback_button_panel_right.xml b/core/res/res/layout/alert_dialog_leanback_button_panel_right.xml new file mode 100644 index 0000000..096b015 --- /dev/null +++ b/core/res/res/layout/alert_dialog_leanback_button_panel_right.xml @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/parentPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/leanback_alert_dialog_horizontal_margin" + android:layout_marginRight="@dimen/leanback_alert_dialog_horizontal_margin" + android:layout_marginTop="@dimen/leanback_alert_dialog_vertical_margin" + android:layout_marginBottom="@dimen/leanback_alert_dialog_vertical_margin" + android:orientation="horizontal"> + + <LinearLayout + android:id="@+id/leftPanel" + android:layout_width="0dp" + android:layout_weight="0.66" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout android:id="@+id/topPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + <LinearLayout android:id="@+id/title_template" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical|start" + android:minHeight="@dimen/alert_dialog_title_height" + android:layout_marginStart="16dip" + android:layout_marginEnd="16dip"> + <ImageView android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingEnd="8dip" + android:src="@null" /> + <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle" + style="?android:attr/windowTitleStyle" + android:singleLine="true" + android:ellipsize="end" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="viewStart" /> + </LinearLayout> + <!-- If the client uses a customTitle, it will be added here. --> + </LinearLayout> + + <LinearLayout android:id="@+id/contentPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:minHeight="64dp"> + <ScrollView android:id="@+id/scrollView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipToPadding="false"> + <TextView android:id="@+id/message" + style="?android:attr/textAppearanceMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="16dip" + android:paddingEnd="16dip" + android:paddingTop="8dip" + android:paddingBottom="8dip"/> + </ScrollView> + </LinearLayout> + + <FrameLayout android:id="@+id/customPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:minHeight="64dp"> + <FrameLayout android:id="@+android:id/custom" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </FrameLayout> + </LinearLayout> + + <LinearLayout android:id="@+id/buttonPanel" + android:layout_width="0dp" + android:layout_weight="0.33" + android:layout_height="match_parent" + android:minHeight="@dimen/alert_dialog_button_bar_height" + android:paddingLeft="32dp" + android:paddingRight="32dp" + android:orientation="horizontal"> + <LinearLayout + style="?android:attr/buttonBarStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:orientation="vertical" + android:layoutDirection="locale" + android:measureWithLargestChild="true"> + <Button android:id="@+id/button1" + android:layout_width="match_parent" + android:gravity="center_vertical" + android:layout_weight="1" + android:maxLines="2" + style="?android:attr/buttonBarButtonStyle" + android:textSize="14sp" + android:minHeight="@dimen/alert_dialog_button_bar_height" + android:layout_height="wrap_content" /> + <Button android:id="@+id/button3" + android:layout_width="match_parent" + android:gravity="center_vertical" + android:layout_weight="1" + android:maxLines="2" + style="?android:attr/buttonBarButtonStyle" + android:textSize="14sp" + android:minHeight="@dimen/alert_dialog_button_bar_height" + android:layout_height="wrap_content" /> + <Button android:id="@+id/button2" + android:layout_width="match_parent" + android:gravity="center_vertical" + android:layout_weight="1" + android:maxLines="2" + android:minHeight="@dimen/alert_dialog_button_bar_height" + style="?android:attr/buttonBarButtonStyle" + android:textSize="14sp" + android:layout_height="wrap_content" /> + </LinearLayout> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/layout/progress_dialog_leanback.xml b/core/res/res/layout/progress_dialog_leanback.xml new file mode 100644 index 0000000..6bcad7a --- /dev/null +++ b/core/res/res/layout/progress_dialog_leanback.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2014, 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="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <View + android:layout_width="match_parent" + android:layout_height="2dip" + android:background="@android:color/leanback_dark_gray" /> + <LinearLayout android:id="@+id/body" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:baselineAligned="false" + android:padding="16dip"> + + <ProgressBar android:id="@android:id/progress" + style="?android:attr/progressBarStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:max="10000" + android:layout_marginEnd="16dip" /> + + <TextView android:id="@+id/message" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" /> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml new file mode 100644 index 0000000..6e17cdd --- /dev/null +++ b/core/res/res/values-television/themes.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2014 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. +--> +<resources> + <style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> + <style name="Theme.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" /> + <style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" /> + <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> + <style name="Theme.Quantum.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" /> + <style name="Theme.Quantum.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> +</resources> diff --git a/packages/SystemUI/res/layout/quick_settings_tile.xml b/core/res/res/values-television/themes_device_defaults.xml index 911f6a2..e01caa3 100644 --- a/packages/SystemUI/res/layout/quick_settings_tile.xml +++ b/core/res/res/values-television/themes_device_defaults.xml @@ -1,5 +1,5 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,8 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.systemui.statusbar.phone.QuickSettingsTileView - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="@dimen/quick_settings_cell_height" - android:background="@drawable/qs_tile_background" />
\ No newline at end of file +<resources> + <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" /> + <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> +</resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 03435c5..327f6cf 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -955,18 +955,33 @@ <!-- ============= --> <!-- Color palette --> <!-- ============= --> - <attr name="colorPrimaryDark" format="color" /> + <eat-comment /> + + <!-- The primary branding color for the app. By default, this is the color applied to the + action bar background and framework controls (via colorControlActivated). --> <attr name="colorPrimary" format="color" /> + + <!-- Dark variant of the primary branding color. By default, this is the color applied to + the status bar (via statusBarColor) and navigation bar (via navigationBarColor). --> + <attr name="colorPrimaryDark" format="color" /> + + <!-- Light variant of the primary branding color. TODO: Not used? --> <attr name="colorPrimaryLight" format="color" /> + + <!-- Bright complement to the primary branding color. TODO: Not used? --> <attr name="colorAccent" format="color" /> + <!-- The color applied to framework controls in their normal state. --> <attr name="colorControlNormal" format="color" /> + + <!-- The color applied to framework controls in their activated (ex. checked) state. --> <attr name="colorControlActivated" format="color" /> + <!-- The color applied to framework buttons in their normal state. --> <attr name="colorButtonNormal" format="color" /> + + <!-- The color applied to framework buttons in their pressed state. --> <attr name="colorButtonPressed" format="color" /> - <attr name="colorButtonNormalColored" format="color" /> - <attr name="colorButtonPressedColored" format="color" /> </declare-styleable> <!-- **************************************************************** --> @@ -1775,6 +1790,7 @@ <attr name="bottomMedium" format="reference|color" /> <attr name="centerMedium" format="reference|color" /> <attr name="layout" /> + <attr name="buttonPanelSideLayout" format="reference" /> <attr name="listLayout" format="reference" /> <attr name="multiChoiceItemLayout" format="reference" /> <attr name="singleChoiceItemLayout" format="reference" /> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 7d3fb44..ad29505 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -120,6 +120,9 @@ <color name="micro_text_light">#434343</color> + <color name="leanback_dark_gray">#ff333333</color> + <color name="leanback_light_gray">#ff888888</color> + <drawable name="notification_template_icon_bg">#3333B5E5</drawable> <drawable name="notification_template_icon_low_bg">#0cffffff</drawable> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 83cbb74..f5117b2 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1394,9 +1394,10 @@ </string-array> <!-- The list of classes that should be added to the notification ranking pipline. - See {@link com.android.server.notification.NotificationSignalExtractortor} --> + See {@link com.android.server.notification.NotificationSignalExtractor} --> <string-array name="config_notificationSignalExtractors"> <item>com.android.server.notification.ValidateNotificationPeople</item> + <item>com.android.server.notification.NotificationIntrusivenessExtractor</item> </string-array> <!-- Flag indicating that this device does not rotate and will always remain in its default diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index bf92f9b..69b11cd 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -149,6 +149,10 @@ <dimen name="alert_dialog_title_height">64dip</dimen> <!-- Dialog button bar height --> <dimen name="alert_dialog_button_bar_height">48dip</dimen> + <!-- Leanback dialog vertical margin --> + <dimen name="leanback_alert_dialog_vertical_margin">27dip</dimen> + <!-- Leanback dialog horizontal margin --> + <dimen name="leanback_alert_dialog_horizontal_margin">54dip</dimen> <!-- Default height of an action bar. --> <dimen name="action_bar_default_height">48dip</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 74b2f7b..3ef4ab6 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2136,8 +2136,6 @@ <public type="attr" name="colorControlActivated" /> <public type="attr" name="colorButtonNormal" /> <public type="attr" name="colorButtonPressed" /> - <public type="attr" name="colorButtonNormalColored" /> - <public type="attr" name="colorButtonPressedColored" /> <public type="attr" name="persistable" /> <public type="attr" name="titleTextAppearance" /> <public type="attr" name="subtitleTextAppearance" /> diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml new file mode 100644 index 0000000..a37da4e --- /dev/null +++ b/core/res/res/values/styles_leanback.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2014 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. +--> +<resources> + <style name="DialogWindowTitle.Leanback" parent="DialogWindowTitle.Holo"> + <item name="android:textAppearance">@style/TextAppearance.Leanback.DialogWindowTitle</item> + </style> + + <style name="TextAppearance.Leanback.DialogWindowTitle" parent="TextAppearance.Holo.DialogWindowTitle"> + <item name="android:fontFamily">sans-serif-condensed</item> + <item name="android:textColor">?attr/textColorPrimary</item> + </style> + + <style name="Leanback.ButtonBar" parent="Holo.ButtonBar"> + <item name="showDividers">none</item> + </style> + + <style name="AlertDialog.Leanback" parent="AlertDialog.Holo"> + <item name="layout">@android:layout/alert_dialog_leanback</item> + <item name="buttonPanelSideLayout">@android:layout/alert_dialog_leanback_button_panel_right</item> + <item name="progressLayout">@android:layout/progress_dialog_leanback</item> + <item name="fullDark">@android:color/background_dark</item> + <item name="topDark">@android:color/background_dark</item> + <item name="centerDark">@android:color/background_dark</item> + <item name="bottomDark">@android:color/background_dark</item> + <item name="fullBright">@android:color/background_dark</item> + <item name="topBright">@android:color/background_dark</item> + <item name="centerBright">@android:color/background_dark</item> + <item name="bottomBright">@android:color/background_dark</item> + <item name="bottomMedium">@android:color/background_dark</item> + <item name="centerMedium">@android:color/background_dark</item> + </style> + + <style name="AlertDialog.Leanback.Light"> + <item name="fullDark">@android:color/leanback_light_gray</item> + <item name="topDark">@android:color/leanback_light_gray</item> + <item name="centerDark">@android:color/leanback_light_gray</item> + <item name="bottomDark">@android:color/leanback_light_gray</item> + <item name="fullBright">@android:color/leanback_light_gray</item> + <item name="topBright">@android:color/leanback_light_gray</item> + <item name="centerBright">@android:color/leanback_light_gray</item> + <item name="bottomBright">@android:color/leanback_light_gray</item> + <item name="bottomMedium">@android:color/leanback_light_gray</item> + <item name="centerMedium">@android:color/leanback_light_gray</item> + </style> + +</resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1057cc2..a14f241 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1630,6 +1630,7 @@ <java-symbol type="string" name="wifi_display_notification_disconnect" /> <java-symbol type="style" name="Theme.Dialog.AppError" /> <java-symbol type="style" name="Theme.Micro.Dialog.Alert" /> + <java-symbol type="style" name="Theme.Leanback.Dialog.Alert" /> <java-symbol type="style" name="Theme.Toast" /> <java-symbol type="xml" name="storage_list" /> <java-symbol type="bool" name="config_dreamsSupported" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 34ef508..1d9bbae 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -1811,12 +1811,7 @@ please see themes_device_defaults.xml. <item name="android:windowCloseOnTouchOutside">false</item> </style> - <!-- Holo theme for alert dialog windows, which is used by the - {@link android.app.AlertDialog} class. This is basically a dialog - but sets the background to empty so it can do two-tone backgrounds. - For applications targeting Honeycomb or newer, this is the default - AlertDialog theme. --> - <style name="Theme.Holo.Dialog.Alert"> + <style name="Theme.Holo.Dialog.BaseAlert"> <item name="windowBackground">@android:color/transparent</item> <item name="windowTitleStyle">@android:style/DialogWindowTitle.Holo</item> <item name="windowContentOverlay">@null</item> @@ -1824,6 +1819,13 @@ please see themes_device_defaults.xml. <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item> </style> + <!-- Holo theme for alert dialog windows, which is used by the + {@link android.app.AlertDialog} class. This is basically a dialog + but sets the background to empty so it can do two-tone backgrounds. + For applications targeting Honeycomb or newer, this is the default + AlertDialog theme. --> + <style name="Theme.Holo.Dialog.Alert" parent="Theme.Holo.Dialog.BaseAlert"/> + <!-- Holo theme for the TimePicker dialog windows, which is used by the {@link android.app.TimePickerDialog} class. --> <style name="Theme.Holo.Dialog.TimePicker"> @@ -1934,12 +1936,7 @@ please see themes_device_defaults.xml. parent="@android:style/Theme.Holo.Light.NoActionBar"> </style> - <!-- Holo light theme for alert dialog windows, which is used by the - {@link android.app.AlertDialog} class. This is basically a dialog - but sets the background to empty so it can do two-tone backgrounds. - For applications targeting Honeycomb or newer, this is the default - AlertDialog theme. --> - <style name="Theme.Holo.Light.Dialog.Alert"> + <style name="Theme.Holo.Light.Dialog.BaseAlert"> <item name="windowBackground">@android:color/transparent</item> <item name="windowTitleStyle">@android:style/DialogWindowTitle.Holo.Light</item> <item name="windowContentOverlay">@null</item> @@ -1947,6 +1944,13 @@ please see themes_device_defaults.xml. <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item> </style> + <!-- Holo light theme for alert dialog windows, which is used by the + {@link android.app.AlertDialog} class. This is basically a dialog + but sets the background to empty so it can do two-tone backgrounds. + For applications targeting Honeycomb or newer, this is the default + AlertDialog theme. --> + <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Holo.Light.Dialog.BaseAlert"/> + <!-- Holo Light theme for the TimePicker dialog windows, which is used by the {@link android.app.TimePickerDialog} class. --> <style name="Theme.Holo.Light.Dialog.TimePicker"> diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml new file mode 100644 index 0000000..eba8dec --- /dev/null +++ b/core/res/res/values/themes_leanback.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2014 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. +--> +<resources> + <style name="Theme.Leanback.Dialog.Alert" parent="Theme.Holo.Dialog.BaseAlert"> + <item name="windowTitleStyle">@style/DialogWindowTitle.Leanback</item> + <item name="alertDialogStyle">@style/AlertDialog.Leanback</item> + <item name="buttonBarStyle">@style/Leanback.ButtonBar</item> + <item name="listDividerAlertDialog">@null</item> + </style> + + <style name="Theme.Leanback.Light.Dialog.Alert" parent="Theme.Holo.Light.Dialog.BaseAlert"> + <item name="windowTitleStyle">@style/DialogWindowTitle.Leanback</item> + <item name="alertDialogStyle">@style/AlertDialog.Leanback.Light</item> + <item name="buttonBarStyle">@style/Leanback.ButtonBar</item> + <item name="listDividerAlertDialog">@null</item> + </style> + + <style name="Theme.Leanback.Light.Dialog" parent="Theme.Holo.Light.Dialog"> + <item name="windowTitleStyle">@style/DialogWindowTitle.Leanback</item> + </style> + + <style name="Theme.Leanback.Dialog" parent="Theme.Holo.Dialog"> + <item name="windowTitleStyle">@style/DialogWindowTitle.Leanback</item> + </style> + + <style name="Theme.Leanback.Dialog.AppError" parent="Theme.Leanback.Dialog"> + <item name="windowFrame">@null</item> + <item name="windowBackground">@color/transparent</item> + <item name="windowIsFloating">true</item> + <item name="windowContentOverlay">@null</item> + <item name="windowCloseOnTouchOutside">false</item> + <item name="alertDialogStyle">@style/AlertDialog.Leanback</item> + </style> +</resources> diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml index 18d6f80..6f93c829 100644 --- a/core/res/res/values/themes_quantum.xml +++ b/core/res/res/values/themes_quantum.xml @@ -377,9 +377,6 @@ please see themes_device_defaults.xml. <item name="colorControlActivated">?attr/colorPrimary</item> <item name="colorButtonNormal">@color/quantum_grey_700</item> <item name="colorButtonPressed">@color/quantum_grey_500</item> - <!-- TODO: Remove these attrs and move into button style. --> - <item name="colorButtonNormalColored">?attr/colorPrimary</item> - <item name="colorButtonPressedColored">?attr/colorPrimaryLight</item> </style> <!-- Quantum Paper theme (light version). --> diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 6a3003e..a033f86 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -199,13 +199,18 @@ float CanvasPropertyPaintAnimator::getValue() const { return -1; } +static uint8_t to_uint8(float value) { + int c = (int) (value + .5f); + return static_cast<uint8_t>( c < 0 ? 0 : c > 255 ? 255 : c ); +} + void CanvasPropertyPaintAnimator::setValue(float value) { switch (mField) { case STROKE_WIDTH: mProperty->value.setStrokeWidth(value); return; case ALPHA: - mProperty->value.setAlpha(value); + mProperty->value.setAlpha(to_uint8(value)); return; } LOG_ALWAYS_FATAL("Unknown field %d", (int) mField); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5a23158..5754536 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -98,6 +98,8 @@ public: bool enableDirtyRegions(EGLSurface surface); + void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); + private: GlobalContext(); // GlobalContext is never destroyed, method is purposely not implemented @@ -118,6 +120,10 @@ private: bool mCanSetDirtyRegions; EGLSurface mCurrentSurface; + + sp<GraphicBuffer> mAtlasBuffer; + int64_t* mAtlasMap; + size_t mAtlasMapSize; }; GlobalContext* GlobalContext::sContext = 0; @@ -135,7 +141,9 @@ GlobalContext::GlobalContext() , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) , mRequestDirtyRegions(load_dirty_regions_property()) - , mCurrentSurface(EGL_NO_SURFACE) { + , mCurrentSurface(EGL_NO_SURFACE) + , mAtlasMap(NULL) + , mAtlasMapSize(0) { mCanSetDirtyRegions = mRequestDirtyRegions; ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); } @@ -201,9 +209,28 @@ void GlobalContext::createContext() { "Failed to create context, error = %s", egl_error_str()); } +void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, + int64_t* map, size_t mapSize) { + + // Already initialized + if (mAtlasBuffer.get()) { + ALOGW("Multiple calls to setTextureAtlas!"); + delete map; + return; + } + + mAtlasBuffer = buffer; + mAtlasMap = map; + mAtlasMapSize = mapSize; + + if (hasContext()) { + usePBufferSurface(); + initAtlas(); + } +} + void GlobalContext::initAtlas() { - // TODO implement - // For now just run without an atlas + Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); } void GlobalContext::usePBufferSurface() { @@ -533,6 +560,11 @@ void CanvasContext::requireGlContext() { } } +void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, + int64_t* map, size_t mapSize) { + GlobalContext::get()->setTextureAtlas(buffer, map, mapSize); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index dcb9858..a793d42 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -70,6 +70,9 @@ public: Layer* createRenderLayer(int width, int height); Layer* createTextureLayer(); + ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer, + int64_t* map, size_t mapSize); + private: void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info); void prepareTree(TreeInfo& info); diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk index 1e6b2e7..42da48d 100644 --- a/media/tests/MediaFrameworkTest/Android.mk +++ b/media/tests/MediaFrameworkTest/Android.mk @@ -7,7 +7,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := easymocklib mockito-target +LOCAL_STATIC_JAVA_LIBRARIES := easymocklib mockito-target core-tests android-support-test LOCAL_PACKAGE_NAME := mediaframeworktest diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml index 91ee2c6..c62199f 100644 --- a/media/tests/MediaFrameworkTest/AndroidManifest.xml +++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml @@ -76,4 +76,8 @@ android:label="MediaFramework integration tests InstrumentationRunner"> </instrumentation> + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.mediaframeworktest" + android:label="media framework tests"/> + </manifest> diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java index 64b12b7..11d9070 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java @@ -63,6 +63,7 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner { suite.addTestSuite(CameraUtilsRuntimeExceptionTest.class); suite.addTestSuite(CameraUtilsUncheckedThrowTest.class); suite.addTestSuite(CameraUtilsBinderDecoratorTest.class); + suite.addTestSuite(CameraUtilsTypeReferenceTest.class); suite.addTestSuite(CameraMetadataTest.class); } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java new file mode 100644 index 0000000..6f31480 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2014 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.mediaframeworktest.unit; + +import android.util.Log; + +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; + +public class ByteArrayHelpers { + + private static final String TAG = "ByteArrayHelpers"; + private static boolean VERBOSE = false; + + /** + * Convert an array of byte primitives to a {@code byte[]} using native endian order. + * + * <p>This function is a pass-through; it's here only to provide overloads for + * every single type of primitive arrays. + * + * @param array array of primitives + * @return array + */ + public static byte[] toByteArray(byte[] array) { + return array; + } + + /** + * Convert an array of shorts to a {@code byte[]} using native endian order. + * + * @param array array of shorts + * @return array converted into byte array using native endian order + */ + public static byte[] toByteArray(short[] array) { + return toByteArray(array, Short.SIZE); + } + + /** + * Convert an array of chars to a {@code byte[]} using native endian order. + * + * @param array array of chars + * @return array converted into byte array using native endian order + */ + public static byte[] toByteArray(char[] array) { + return toByteArray(array, Character.SIZE); + } + /** + * Convert an array of ints to a {@code byte[]} using native endian order. + * + * @param array array of ints + * @return array converted into byte array using native endian order + */ + public static byte[] toByteArray(int[] array) { + return toByteArray(array, Integer.SIZE); + } + /** + * Convert an array of longs to a {@code byte[]} using native endian order. + * + * @param array array of longs + * @return array converted into byte array using native endian order + */ + public static byte[] toByteArray(long[] array) { + return toByteArray(array, Long.SIZE); + } + /** + * Convert an array of floats to a {@code byte[]} using native endian order. + * + * @param array array of floats + * @return array converted into byte array using native endian order + */ + public static byte[] toByteArray(float[] array) { + return toByteArray(array, Float.SIZE); + } + /** + * Convert an array of doubles to a {@code byte[]} using native endian order. + * + * @param array array of doubles + * @return array converted into byte array using native endian order + */ + public static byte[] toByteArray(double[] array) { + return toByteArray(array, Double.SIZE); + } + + /** + * Convert an array of primitives to a {@code byte[]} using native endian order. + * + * <p>Arguments other than arrays are not supported. The array component must be primitive, + * the wrapper class is not allowed (e.g. {@code int[]} is ok, but {@code Integer[]} is not.</p> + * + * @param array array of primitives + * @return array converted into byte array using native endian order + * + * @throws IllegalArgumentException if {@code array} was not an array of primitives + */ + public static <T> byte[] toByteArray(T array) { + @SuppressWarnings("unchecked") + Class<T> klass = (Class<T>) array.getClass(); + + if (!klass.isArray()) { + throw new IllegalArgumentException("array class must be an array"); + } + + Class<?> componentClass = klass.getComponentType(); + + if (!componentClass.isPrimitive()) { + throw new IllegalArgumentException("array's component must be a primitive"); + } + + int sizeInBits; + if (klass == int.class) { + sizeInBits = Integer.SIZE; + } else if (klass == float.class) { + sizeInBits = Float.SIZE; + } else if (klass == double.class) { + sizeInBits = Double.SIZE; + } else if (klass == short.class) { + sizeInBits = Short.SIZE; + } else if (klass == char.class) { + sizeInBits = Character.SIZE; + } else if (klass == long.class) { + sizeInBits = Long.SIZE; + } else if (klass == byte.class) { + sizeInBits = Byte.SIZE; + } else { + throw new AssertionError(); + } + + return toByteArray(array, sizeInBits); + } + + /** + * Convert a variadic list of {@code Number}s into a byte array using native endian order. + * + * <p>Each {@link Number} must be an instance of a primitive wrapper class + * (e.g. {@link Integer} is OK, since it wraps {@code int}, but {@code BigInteger} is not.</p> + * + * @param numbers variadic list of numeric values + * @return array converted into byte array using native endian order + * + * @throws IllegalArgumentException + * if {@code numbers} contained a class that wasn't a primitive wrapper + */ + public static byte[] toByteArray(Number... numbers) { + if (numbers.length == 0) { + throw new IllegalArgumentException("too few numbers"); + } + + if (VERBOSE) Log.v(TAG, "toByteArray - input: " + Arrays.toString(numbers)); + + // Have a large enough capacity to fit in every number as a double + ByteBuffer byteBuffer = ByteBuffer.allocate(numbers.length * (Double.SIZE / Byte.SIZE)) + .order(ByteOrder.nativeOrder()); + + for (int i = 0; i < numbers.length; ++i) { + Number value = numbers[i]; + Class<? extends Number> klass = value.getClass(); + + if (VERBOSE) Log.v(TAG, "toByteArray - number " + i + ", class " + klass); + + if (klass == Integer.class) { + byteBuffer.putInt((Integer)value); + } else if (klass == Float.class) { + byteBuffer.putFloat((Float)value); + } else if (klass == Double.class) { + byteBuffer.putDouble((Double)value); + } else if (klass == Short.class) { + byteBuffer.putShort((Short)value); + } else if (klass == Long.class) { + byteBuffer.putLong((Long)value); + } else if (klass == Byte.class) { + byteBuffer.put((Byte)value); + } else { + throw new IllegalArgumentException( + "number class invalid; must be wrapper around primitive class"); + } + } + + if (VERBOSE) Log.v(TAG, "toByteArray - end of loop"); + + // Each number written is at least 1 byte, so the position should be at least length + if (numbers.length != 0 && byteBuffer.position() < numbers.length) { + throw new AssertionError(String.format( + "Had %d numbers, but byte buffer position was only %d", + numbers.length, byteBuffer.position())); + } + + byteBuffer.flip(); + + byte[] bytes = new byte[byteBuffer.limit()]; + byteBuffer.get(bytes); + + if (VERBOSE) Log.v(TAG, "toByteArray - output: " + Arrays.toString(bytes)); + + return bytes; + } + + private static <T> byte[] toByteArray(T array, int sizeOfTBits) { + @SuppressWarnings("unchecked") + Class<T> klass = (Class<T>) array.getClass(); + + if (!klass.isArray()) { + throw new IllegalArgumentException("array class must be an array"); + } + + int sizeOfT = sizeOfTBits / Byte.SIZE; + int byteLength = Array.getLength(array) * sizeOfT; + + if (klass == byte[].class) { + // Always return a copy + return Arrays.copyOf((byte[])array, byteLength); + } + + ByteBuffer byteBuffer = ByteBuffer.allocate(byteLength).order(ByteOrder.nativeOrder()); + + if (klass == int[].class) { + byteBuffer.asIntBuffer().put((int[])array); + } else if (klass == float[].class) { + byteBuffer.asFloatBuffer().put((float[])array); + } else if (klass == double[].class) { + byteBuffer.asDoubleBuffer().put((double[])array); + } else if (klass == short[].class) { + byteBuffer.asShortBuffer().put((short[])array); + } else if (klass == char[].class) { + byteBuffer.asCharBuffer().put((char[])array); + } else if (klass == long[].class) { + byteBuffer.asLongBuffer().put((long[])array); + } else { + throw new IllegalArgumentException("array class invalid; must be a primitive array"); + } + + return byteBuffer.array(); + } + + private ByteArrayHelpers() { + throw new AssertionError(); + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java index 45df065..b28733a 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java @@ -16,18 +16,30 @@ package com.android.mediaframeworktest.unit; -import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; +import android.util.Range; +import android.util.SizeF; +import android.graphics.ImageFormat; import android.graphics.Point; +import android.graphics.PointF; import android.graphics.Rect; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.ColorSpaceTransform; import android.hardware.camera2.Face; +import android.hardware.camera2.MeteringRectangle; import android.hardware.camera2.Rational; +import android.hardware.camera2.ReprocessFormatsMap; +import android.hardware.camera2.RggbChannelVector; import android.hardware.camera2.Size; +import android.hardware.camera2.StreamConfiguration; +import android.hardware.camera2.StreamConfigurationDuration; import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.marshal.impl.MarshalQueryableEnum; +import android.hardware.camera2.utils.TypeReference; import static android.hardware.camera2.impl.CameraMetadataNative.*; +import static com.android.mediaframeworktest.unit.ByteArrayHelpers.*; import java.lang.reflect.Array; import java.nio.ByteBuffer; @@ -43,7 +55,6 @@ import java.nio.ByteOrder; public class CameraMetadataTest extends junit.framework.TestCase { CameraMetadataNative mMetadata; - Parcel mParcel; // Sections static final int ANDROID_COLOR_CORRECTION = 0; @@ -64,15 +75,11 @@ public class CameraMetadataTest extends junit.framework.TestCase { @Override public void setUp() { mMetadata = new CameraMetadataNative(); - mParcel = Parcel.obtain(); } @Override public void tearDown() throws Exception { mMetadata = null; - - mParcel.recycle(); - mParcel = null; } @SmallTest @@ -130,10 +137,14 @@ public class CameraMetadataTest extends junit.framework.TestCase { @SmallTest public void testGetTypeFromTag() { - assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE)); - assertEquals(TYPE_RATIONAL, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM)); - assertEquals(TYPE_FLOAT, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS)); - assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE)); + assertEquals(TYPE_BYTE, + CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE)); + assertEquals(TYPE_RATIONAL, + CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM)); + assertEquals(TYPE_FLOAT, + CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS)); + assertEquals(TYPE_BYTE, + CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE)); assertEquals(TYPE_INT32, CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION)); @@ -215,35 +226,163 @@ public class CameraMetadataTest extends junit.framework.TestCase { assertEquals(1, mMetadata.getEntryCount()); } + /** + * Format an array into a string with the {@code badIndex} highlighted with {@code **}. + * + * <p>Numbers are printed as hexadecimal values.</p> + * + * <p>Example: {@code "[hello, **world**]"} for a {@code string[]}, + * or a {@code "[**0xFF**, 0xFF]"} for a {@code int[]}.</p> + */ + private static <T> String formatArray(T array, int badIndex) { + StringBuilder builder = new StringBuilder(); + + builder.append("["); + + int len = Array.getLength(array); + for (int i = 0; i < len; ++i) { + + Object elem = Array.get(array, i); + + if (i == badIndex) { + builder.append("**"); + } + + if (elem instanceof Number) { + builder.append(String.format("%x", elem)); + } else { + builder.append(elem); + } + + if (i == badIndex) { + builder.append("**"); + } + + if (i != len - 1) { + builder.append(", "); + } + } + + builder.append("]"); + + return builder.toString(); + } + private static <T> void assertArrayEquals(T expected, T actual) { - assertEquals(Array.getLength(expected), Array.getLength(actual)); + if (!expected.getClass().isArray() || !actual.getClass().isArray()) { + throw new IllegalArgumentException("expected, actual must both be arrays"); + } + + assertEquals("Array lengths must be equal", + Array.getLength(expected), Array.getLength(actual)); int len = Array.getLength(expected); for (int i = 0; i < len; ++i) { - assertEquals(Array.get(expected, i), Array.get(actual, i)); + + Object expectedElement = Array.get(expected, i); + Object actualElement = Array.get(actual, i); + + if (!expectedElement.equals(actualElement)) { + fail(String.format( + "element %d in array was not equal (expected %s, actual %s). " + + "Arrays were: (expected %s, actual %s).", + i, expectedElement, actualElement, + formatArray(expected, i), + formatArray(actual, i))); + } } } - private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T value) { - assertFalse("Use checkKeyGetAndSetArray to compare array Keys", type.isArray()); - - Key<T> key = new Key<T>(keyStr, type); + private <T> void checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected, + boolean reuse) { + Key<T> key = new Key<T>(keyStr, typeToken); assertNull(mMetadata.get(key)); mMetadata.set(key, null); assertNull(mMetadata.get(key)); - mMetadata.set(key, value); + mMetadata.set(key, expected); T actual = mMetadata.get(key); - assertEquals(value, actual); + + if (typeToken.getRawType().isArray()) { + assertArrayEquals(expected, actual); + } else { + assertEquals(expected, actual); + } + + if (reuse) { + // reset the key incase we want to use it again + mMetadata.set(key, null); + } + } + + private <T> void checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected) { + checkKeyGetAndSet(keyStr, typeToken, expected, /*reuse*/false); } - private <T> void checkKeyGetAndSetArray(String keyStr, Class<T> type, T value) { - assertTrue(type.isArray()); + private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T expected) { + checkKeyGetAndSet(keyStr, TypeReference.createSpecializedTypeReference(type), expected); + } + + /** + * Ensure that the data survives a marshal/unmarshal round-trip; + * it must also be equal to the {@code expectedNative} byte array. + * + * <p>As a side-effect, the metadata value corresponding to the key is now set to + * {@code expected}.</p> + * + * @return key created with {@code keyName} and {@code T} + */ + private <T> Key<T> checkKeyMarshal(String keyName, TypeReference<T> typeReference, + T expected, byte[] expectedNative) { + Key<T> key = new Key<T>(keyName, typeReference); - Key<T> key = new Key<T>(keyStr, type); + mMetadata.set(key, null); assertNull(mMetadata.get(key)); - mMetadata.set(key, value); - assertArrayEquals(value, mMetadata.get(key)); + + // Write managed value -> make sure native bytes are what we expect + mMetadata.set(key, expected); + + byte[] actualValues = mMetadata.readValues(key.getTag()); + assertArrayEquals(expectedNative, actualValues); + + // Write managed value -> make sure read-out managed value is what we expect + T actual = mMetadata.get(key); + + if (typeReference.getRawType().isArray()) { + assertArrayEquals(expected, actual); + } else { + assertEquals(expected, actual); + } + + // Write native bytes -> make sure read-out managed value is what we expect + mMetadata.writeValues(key.getTag(), expectedNative); + actual = mMetadata.get(key); + + if (typeReference.getRawType().isArray()) { + assertArrayEquals(expected, actual); + } else { + assertEquals(expected, actual); + } + + return key; + } + + /** + * Ensure that the data survives a marshal/unmarshal round-trip; + * it must also be equal to the {@code expectedNative} byte array. + * + * <p>As a side-effect, + * the metadata value corresponding to the key is now set to {@code expected}.</p> + * + * @return key created with {@code keyName} and {@code T} + */ + private <T> Key<T> checkKeyMarshal(String keyName, T expected, byte[] expectedNative) { + @SuppressWarnings("unchecked") + Class<T> expectedClass = (Class<T>) expected.getClass(); + return checkKeyMarshal(keyName, + TypeReference.createSpecializedTypeReference(expectedClass), + expected, + expectedNative); } @SmallTest @@ -280,36 +419,36 @@ public class CameraMetadataTest extends junit.framework.TestCase { @SmallTest public void testReadWritePrimitiveArray() { // int32 (n) - checkKeyGetAndSetArray("android.sensor.info.sensitivityRange", int[].class, + checkKeyGetAndSet("android.sensor.info.sensitivityRange", int[].class, new int[] { 0xC0FFEE, 0xDEADF00D }); // byte (n) - checkKeyGetAndSetArray("android.statistics.faceScores", byte[].class, new byte[] { + checkKeyGetAndSet("android.statistics.faceScores", byte[].class, new byte[] { 1, 2, 3, 4 }); // int64 (n) - checkKeyGetAndSetArray("android.scaler.availableProcessedMinDurations", long[].class, + checkKeyGetAndSet("android.scaler.availableProcessedMinDurations", long[].class, new long[] { 0xABCD12345678FFFFL, 0x1234ABCD5678FFFFL, 0xFFFF12345678ABCDL }); // float (n) - checkKeyGetAndSetArray("android.lens.info.availableApertures", float[].class, + checkKeyGetAndSet("android.lens.info.availableApertures", float[].class, new float[] { Float.MAX_VALUE, Float.MIN_NORMAL, Float.MIN_VALUE }); // double (n) -- in particular double x 3 - checkKeyGetAndSetArray("android.jpeg.gpsCoordinates", double[].class, + checkKeyGetAndSet("android.jpeg.gpsCoordinates", double[].class, new double[] { Double.MAX_VALUE, Double.MIN_NORMAL, Double.MIN_VALUE }); // rational (n) -- in particular rational x 9 - checkKeyGetAndSetArray("android.sensor.calibrationTransform1", Rational[].class, + checkKeyGetAndSet("android.sensor.calibrationTransform1", Rational[].class, new Rational[] { new Rational(1, 2), new Rational(3, 4), new Rational(5, 6), new Rational(7, 8), new Rational(9, 10), new Rational(10, 11), @@ -321,13 +460,12 @@ public class CameraMetadataTest extends junit.framework.TestCase { */ // bool (n) -- with TYPE_BYTE - checkKeyGetAndSetArray("android.control.aeLock", boolean[].class, new boolean[] { + checkKeyGetAndSet("android.control.aeLock", boolean[].class, new boolean[] { true, false, true }); - // integer (n) -- with TYPE_BYTE - checkKeyGetAndSetArray("android.control.aeAvailableModes", int[].class, new int[] { + checkKeyGetAndSet("android.control.aeAvailableModes", int[].class, new int[] { 1, 2, 3, 4 }); } @@ -345,7 +483,6 @@ public class CameraMetadataTest extends junit.framework.TestCase { AUTO } - // TODO: special values for the enum. private enum AvailableFormat { RAW_SENSOR, YV12, @@ -366,7 +503,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { AeAntibandingMode.AUTO); // byte (n) - checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes", + checkKeyGetAndSet("android.control.aeAvailableAntibandingModes", AeAntibandingMode[].class, new AeAntibandingMode[] { AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ, AeAntibandingMode.AUTO @@ -376,7 +513,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { * Stranger cases that don't use byte enums */ // int (n) - checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class, + checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class, new AvailableFormat[] { AvailableFormat.RAW_SENSOR, AvailableFormat.YV12, @@ -389,7 +526,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { @SmallTest public void testReadWriteEnumWithCustomValues() { - CameraMetadataNative.registerEnumValues(AeAntibandingMode.class, new int[] { + MarshalQueryableEnum.registerEnumValues(AeAntibandingMode.class, new int[] { 0, 10, 20, @@ -401,15 +538,12 @@ public class CameraMetadataTest extends junit.framework.TestCase { AeAntibandingMode.AUTO); // byte (n) - checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes", + checkKeyGetAndSet("android.control.aeAvailableAntibandingModes", AeAntibandingMode[].class, new AeAntibandingMode[] { AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ, AeAntibandingMode.AUTO }); - Key<AeAntibandingMode[]> aeAntibandingModeKey = - new Key<AeAntibandingMode[]>("android.control.aeAvailableAntibandingModes", - AeAntibandingMode[].class); byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadataNative .getTag("android.control.aeAvailableAntibandingModes")); byte[] expectedValues = new byte[] { 0, 10, 20, 30 }; @@ -420,7 +554,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { * Stranger cases that don't use byte enums */ // int (n) - CameraMetadataNative.registerEnumValues(AvailableFormat.class, new int[] { + MarshalQueryableEnum.registerEnumValues(AvailableFormat.class, new int[] { 0x20, 0x32315659, 0x11, @@ -429,7 +563,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { 0x21, }); - checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class, + checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class, new AvailableFormat[] { AvailableFormat.RAW_SENSOR, AvailableFormat.YV12, @@ -466,7 +600,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { checkKeyGetAndSet("android.jpeg.thumbnailSize", Size.class, new Size(123, 456)); // int32 x 2 x n - checkKeyGetAndSetArray("android.scaler.availableJpegSizes", Size[].class, new Size[] { + checkKeyGetAndSet("android.scaler.availableJpegSizes", Size[].class, new Size[] { new Size(123, 456), new Size(0xDEAD, 0xF00D), new Size(0xF00, 0xB00) @@ -474,16 +608,217 @@ public class CameraMetadataTest extends junit.framework.TestCase { } @SmallTest - public void testReadWriteRectangle() { + public void testReadWriteRggbChannelVector() { // int32 x n - checkKeyGetAndSet("android.scaler.cropRegion", Rect.class, new Rect(10, 11, 1280, 1024)); + checkKeyMarshal("android.colorCorrection.gains", + new RggbChannelVector(1.0f, 2.1f, 3.2f, 4.5f), + toByteArray(1.0f, 2.1f, 3.2f, 4.5f)); + + // int32 x 2 x n [pretend; actual is not array] + checkKeyMarshal("android.colorCorrection.gains", + new RggbChannelVector[] { + new RggbChannelVector(1.0f, 2.0f, 3.0f, 4.0f), + new RggbChannelVector(9.0f, 8.0f, 7.0f, 6.0f), + new RggbChannelVector(1.3f, 5.5f, 2.4f, 6.7f), + }, toByteArray( + 1.0f, 2.0f, 3.0f, 4.0f, + 9.0f, 8.0f, 7.0f, 6.0f, + 1.3f, 5.5f, 2.4f, 6.7f + )); + } + + @SmallTest + public void testReadWriteSizeF() { + // int32 x n + checkKeyMarshal("android.sensor.info.physicalSize", + new SizeF(123f, 456f), + toByteArray(123f, 456f)); // int32 x 2 x n - checkKeyGetAndSetArray("android.statistics.faceRectangles", Rect[].class, new Rect[] { + checkKeyMarshal("android.sensor.info.physicalSize", + new SizeF[] { + new SizeF(123f, 456f), + new SizeF(1.234f, 4.567f), + new SizeF(999.0f, 555.0f) + }, + toByteArray( + 123f, 456f, + 1.234f, 4.567f, + 999.0f, 555.0f) + ); + } + + @SmallTest + public void testReadWriteRectangle() { + // int32 x n + checkKeyMarshal("android.scaler.cropRegion", + // x1, y1, x2, y2 + new Rect(10, 11, 1280, 1024), + // x, y, width, height + toByteArray(10, 11, 1280 - 10, 1024 - 11)); + + // int32 x 2 x n [actually not array, but we pretend it is] + checkKeyMarshal("android.scaler.cropRegion", new Rect[] { new Rect(110, 111, 11280, 11024), new Rect(210, 111, 21280, 21024), new Rect(310, 111, 31280, 31024) - }); + }, toByteArray( + 110, 111, 11280 - 110, 11024 - 111, + 210, 111, 21280 - 210, 21024 - 111, + 310, 111, 31280 - 310, 31024 - 111 + )); + } + + @SmallTest + public void testReadWriteMeteringRectangle() { + // int32 x 5 x area_count [but we pretend it's a single element] + checkKeyMarshal("android.control.aeRegions", + new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5), + /* xmin, ymin, xmax, ymax, weight */ + toByteArray(1, 2, 1 + 100, 2 + 200, 5)); + + // int32 x 5 x area_count + checkKeyMarshal("android.control.afRegions", + new MeteringRectangle[] { + new MeteringRectangle(/*x*/5, /*y*/6, /*width*/123, /*height*/456, /*weight*/7), + new MeteringRectangle(/*x*/7, /*y*/8, /*width*/456, /*height*/999, /*weight*/6), + new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5) + }, + toByteArray( + 5, 6, 5 + 123, 6 + 456, 7, + 7, 8, 7 + 456, 8 + 999, 6, + 1, 2, 1 + 100, 2 + 200, 5 + )); + } + + @SmallTest + public void testReadWriteColorSpaceTransform() { + // rational x 3 x 3 + checkKeyMarshal("android.colorCorrection.transform", + new ColorSpaceTransform(new Rational[] { + new Rational(1, 2), new Rational(3, 4), new Rational(5, 6), + new Rational(7, 8), new Rational(8, 9), new Rational(10, 11), + new Rational(1, 5), new Rational(2, 8), new Rational(3, 9), + }), + toByteArray( + 1, 2, 3, 4, 5, 6, + 7, 8, 8, 9, 10, 11, + 1, 5, 2, 8, 3, 9)); + } + + @SmallTest + public void testReadWritePoint() { + // int32 x 2 [actually 'x n' but pretend it's a single value for now] + checkKeyMarshal("android.statistics.hotPixelMap", + new Point(1, 2), + toByteArray(1, 2)); + + // int32 x 2 x samples + checkKeyMarshal("android.statistics.hotPixelMap", + new Point[] { + new Point(1, 2), + new Point(3, 4), + new Point(5, 6), + new Point(7, 8), + }, + toByteArray( + 1, 2, + 3, 4, + 5, 6, + 7, 8) + ); + } + + @SmallTest + public void testReadWritePointF() { + // float x 2 [actually 'x samples' but pretend it's a single value for now] + checkKeyMarshal( + "android.sensor.profileToneCurve", + new PointF(1.0f, 2.0f), + toByteArray(1.0f, 2.0f)); + + // float x 2 x samples + checkKeyMarshal("android.sensor.profileToneCurve", + new PointF[] { + new PointF(1.0f, 2.0f), + new PointF(3.0f, 4.0f), + new PointF(5.0f, 6.0f), + new PointF(7.0f, 8.0f), + }, + toByteArray( + 1.0f, 2.0f, + 3.0f, 4.0f, + 5.0f, 6.0f, + 7.0f, 8.0f)); + } + + @SmallTest + public void testReadWriteRange() { + // int32 x 2 + checkKeyMarshal("android.control.aeTargetFpsRange", + new TypeReference<Range<Integer>>() {{ }}, + Range.create(123, 456), + toByteArray(123, 456)); + + // int64 x 2 + checkKeyMarshal("android.sensor.info.exposureTimeRange", + new TypeReference<Range<Long>>() {{ }}, + Range.create(123L, 456L), + toByteArray(123L, 456L)); + } + + @SmallTest + public void testReadWriteStreamConfiguration() { + // int32 x 4 x n + checkKeyMarshal("android.scaler.availableStreamConfigurations", + new StreamConfiguration[] { + new StreamConfiguration(ImageFormat.YUV_420_888, 640, 480, /*input*/false), + new StreamConfiguration(ImageFormat.RGB_565, 320, 240, /*input*/true), + }, + toByteArray( + ImageFormat.YUV_420_888, 640, 480, /*input*/0, + ImageFormat.RGB_565, 320, 240, /*input*/1) + ); + } + + @SmallTest + public void testReadWriteStreamConfigurationDuration() { + // Avoid sign extending ints when converting to a long + final long MASK_UNSIGNED_INT = 0x00000000ffffffffL; + + // int64 x 4 x n + checkKeyMarshal("android.scaler.availableMinFrameDurations", + new StreamConfigurationDuration[] { + new StreamConfigurationDuration( + ImageFormat.YUV_420_888, 640, 480, /*duration*/123L), + new StreamConfigurationDuration( + ImageFormat.RGB_565, 320, 240, /*duration*/345L), + }, + toByteArray( + ImageFormat.YUV_420_888 & MASK_UNSIGNED_INT, 640L, 480L, /*duration*/123L, + ImageFormat.RGB_565 & MASK_UNSIGNED_INT, 320L, 240L, /*duration*/345L) + ); + } + + + @SmallTest + public void testReadWriteReprocessFormatsMap() { + + final int RAW_OPAQUE = 0x24; + final int RAW16 = ImageFormat.RAW_SENSOR; + final int YUV_420_888 = ImageFormat.YUV_420_888; + final int BLOB = 0x21; + + int[] contents = new int[] { + RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB, + RAW16, 2, YUV_420_888, BLOB, + }; + + // int32 x n + checkKeyMarshal("android.scaler.availableInputOutputFormatsMap", + new ReprocessFormatsMap(contents), + toByteArray(contents) + ); } @SmallTest @@ -525,10 +860,6 @@ public class CameraMetadataTest extends junit.framework.TestCase { assertArrayEquals(gpsBytes, actualBytes2); } - <T> void compareGeneric(T expected, T actual) { - assertEquals(expected, actual); - } - @SmallTest public void testReadWriteOverride() { // @@ -691,7 +1022,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { */ private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T writeValues, T readValues, int tag) { - Class<T> type = key.getType(); + Class<?> type = writeValues.getClass(); if (!type.isArray()) { throw new IllegalArgumentException("This function expects an key with array type"); } else if (type != int[].class && type != long[].class) { @@ -737,4 +1068,20 @@ public class CameraMetadataTest extends junit.framework.TestCase { assertNotNull(key.getName() + " result shouldn't be null", result); assertArrayEquals(writeValues, result); } + + // TODO: move somewhere else + @SmallTest + public void testToByteArray() { + assertArrayEquals(new byte[] { 5, 0, 0, 0, 6, 0, 0, 0 }, + toByteArray(5, 6)); + assertArrayEquals(new byte[] { 5, 0, 6, 0, }, + toByteArray((short)5, (short)6)); + assertArrayEquals(new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, + (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,}, + toByteArray(~0, ~0)); + + assertArrayEquals(new byte[] { (byte)0xAB, (byte)0xFF, 0, 0, + 0x0D, (byte)0xF0, (byte)0xAD, (byte)0xDE }, + toByteArray(0xFFAB, 0xDEADF00D)); + } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java new file mode 100644 index 0000000..1b765ea --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2014 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.mediaframeworktest.unit; + +import static android.hardware.camera2.utils.TypeReference.*; + +import android.hardware.camera2.utils.TypeReference; + +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import java.lang.reflect.Type; +import java.util.List; + +public class CameraUtilsTypeReferenceTest extends junit.framework.TestCase { + private static final String TAG = CameraUtilsTypeReferenceTest.class.getSimpleName(); + private static final boolean VERBOSE = false; + + private class RegularClass {} + private class SubClass extends RegularClass {} + + private class GenericClass<T> {} + private class SubGenericClass<T> extends GenericClass<T> {} + + private class SpecificClass extends GenericClass<Integer> {} + + private interface RegularInterface {} + private interface GenericInterface<T> {} + private interface GenericInterface2<T> {} + + private class ImplementsRegularInterface implements RegularInterface {} + private class ImplementsGenericInterface<T> implements GenericInterface<T> {} + private class Implements2GenericInterface<T> + implements GenericInterface<Integer>, GenericInterface2<T> {} + + private class GenericOuterClass<T> { + class GenericInnerClass { + @SuppressWarnings("unused") + T field; + } + } + + private static void assertContainsTypeVariable(Type type) { + assertTrue(type.toString() + " was expected to have a type variable, but it didn't", + containsTypeVariable(type)); + } + + private static void assertLacksTypeVariable(Type type) { + assertFalse(type.toString() + " was expected to *not* have a type variable, but it did", + containsTypeVariable(type)); + } + + /* + * Only test classes and interfaces. Other types are not tested (e.g. fields, methods, etc). + */ + + @SmallTest + public void testLacksTypeVariables() { + assertLacksTypeVariable(RegularClass.class); + assertLacksTypeVariable(SubClass.class); + assertLacksTypeVariable(SpecificClass.class); + + assertLacksTypeVariable(RegularInterface.class); + assertLacksTypeVariable(ImplementsRegularInterface.class); + } + + @SmallTest + public void testContainsTypeVariables() { + assertContainsTypeVariable(GenericClass.class); + assertContainsTypeVariable(SubGenericClass.class); + + assertContainsTypeVariable(GenericInterface.class); + assertContainsTypeVariable(ImplementsGenericInterface.class); + assertContainsTypeVariable(Implements2GenericInterface.class); + + assertContainsTypeVariable(GenericOuterClass.class); + assertContainsTypeVariable(GenericOuterClass.GenericInnerClass.class); + } + + /** + * This should always throw an IllegalArgumentException since the + * type reference to {@code T} will contain a type variable (also {@code T}). + * + * @throws IllegalArgumentException unconditionally + */ + private static <T> TypeReference<T> createTypeRefWithTypeVar() { + return new TypeReference<T>() {{ }}; + } + + @SmallTest + public void testTypeReferences() { + TypeReference<Integer> typeRefInt = new TypeReference<Integer>() {{ }}; + TypeReference<Integer> typeRefInt2 = new TypeReference<Integer>() {{ }}; + + assertEquals(typeRefInt, typeRefInt2); + assertEquals("The type ref's captured type should be the Integer class", + Integer.class, typeRefInt.getType()); + + TypeReference<Float> typeRefFloat = new TypeReference<Float>() {{ }}; + assertFalse("Integer/Float type references must not be equal", + typeRefInt.equals(typeRefFloat)); + assertEquals("The type ref's captured type should be the Float class", + Float.class, typeRefFloat.getType()); + + try { + TypeReference<Integer> typeRefTypeVar = createTypeRefWithTypeVar(); + fail("Expected a type reference with type variables to fail"); + // Unreachable. Make the warning about an unused variable go away. + assertFalse(typeRefTypeVar.equals(typeRefInt)); + } catch (IllegalArgumentException e) { + // OK. Expected behavior + } + } + + // Compare the raw type against rawClass + private static <T> void assertRawTypeEquals(TypeReference<T> typeRef, Class<?> rawClass) { + assertEquals("Expected the raw type from " + typeRef + " to match the class " + rawClass, + rawClass, typeRef.getRawType()); + } + + // Compare the normal type against the klass + private static <T> void assertTypeReferenceEquals(TypeReference<T> typeRef, Class<?> klass) { + assertEquals("Expected the type from " + typeRef + " to match the class " + klass, + klass, typeRef.getType()); + } + + @SmallTest + public void testRawTypes() { + TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }}; + assertRawTypeEquals(intToken, Integer.class); + + TypeReference<List<Integer>> listToken = new TypeReference<List<Integer>>() {{ }}; + assertRawTypeEquals(listToken, List.class); + + TypeReference<List<List<Integer>>> listListToken = + new TypeReference<List<List<Integer>>>() {{ }}; + assertRawTypeEquals(listListToken, List.class); + + TypeReference<int[]> intArrayToken = new TypeReference<int[]>() {{ }}; + assertRawTypeEquals(intArrayToken, int[].class); + + TypeReference<Integer[]> integerArrayToken = new TypeReference<Integer[]>() {{ }}; + assertRawTypeEquals(integerArrayToken, Integer[].class); + + TypeReference<List<Integer>[]> listArrayToken = new TypeReference<List<Integer>[]>() {{ }}; + assertRawTypeEquals(listArrayToken, List[].class); + } + + private class IntTokenOne extends TypeReference<Integer> {} + private class IntTokenTwo extends TypeReference<Integer> {} + + private class IntArrayToken1 extends TypeReference<Integer[]> {} + private class IntArrayToken2 extends TypeReference<Integer[]> {} + + private class IntListToken1 extends TypeReference<List<Integer>> {} + private class IntListToken2 extends TypeReference<List<Integer>> {} + + private class IntListArrayToken1 extends TypeReference<List<Integer>[]> {} + private class IntListArrayToken2 extends TypeReference<List<Integer>[]> {} + + + // FIXME: Equality will fail: b/14590652 + @SmallTest + public void testEquals() { + // Not an array. component type should be null. + TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }}; + assertEquals(intToken, intToken); + assertEquals(intToken, new TypeReference<Integer>() {{ }}); + + assertEquals(intToken, new IntTokenOne()); + assertEquals(intToken, new IntTokenTwo()); + assertEquals(new IntTokenOne(), new IntTokenTwo()); + + assertEquals(new IntArrayToken1(), new IntArrayToken2()); + assertEquals(new IntListToken1(), new IntListToken2()); + assertEquals(new IntListArrayToken1(), new IntListArrayToken2()); + } + + @SmallTest + public void testComponentType() { + // Not an array. component type should be null. + TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }}; + assertNull(intToken.getComponentType()); + + TypeReference<List<Integer>> listToken = new TypeReference<List<Integer>>() {{ }}; + assertNull(listToken.getComponentType()); + + TypeReference<List<List<Integer>>> listListToken = + new TypeReference<List<List<Integer>>>() {{ }}; + assertNull(listListToken.getComponentType()); + + // Check arrays. Component types should be what we expect. + TypeReference<int[]> intArrayToken = new TypeReference<int[]>() {{ }}; + assertTypeReferenceEquals(intArrayToken.getComponentType(), int.class); + + TypeReference<Integer[]> integerArrayToken = new TypeReference<Integer[]>() {{ }}; + assertTypeReferenceEquals(integerArrayToken.getComponentType(), Integer.class); + + assertEquals(new IntArrayToken1().getComponentType(), + new IntArrayToken2().getComponentType()); + + assertEquals(new IntListArrayToken1().getComponentType(), + new IntListArrayToken2().getComponentType()); + + // FIXME: Equality will fail: b/14590652 + TypeReference<List<Integer>[]> listArrayToken = new TypeReference<List<Integer>[]>() {{ }}; + assertEquals(listToken, listArrayToken.getComponentType()); + } +} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java index b4308c6..98122fc 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java @@ -231,15 +231,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mLockPatternView.setOnPatternListener(null); } - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - super.onWindowFocusChanged(hasWindowFocus); - if (hasWindowFocus) { - // when timeout dialog closes we want to update our state - reset(); - } - } - private class UnlockPatternListener implements LockPatternView.OnPatternListener { public void onPatternStart() { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index ba67a82..d6351df 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -99,6 +99,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final int MSG_SCREEN_TURNED_ON = 319; private static final int MSG_SCREEN_TURNED_OFF = 320; private static final int MSG_NFC_UNLOCK = 321; + private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322; private static KeyguardUpdateMonitor sInstance; @@ -111,6 +112,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private int mRingMode; private int mPhoneState; private boolean mKeyguardIsVisible; + private boolean mBouncer; private boolean mBootCompleted; // Device provisioning state @@ -155,7 +157,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { handleRingerModeChange(msg.arg1); break; case MSG_PHONE_STATE_CHANGED: - handlePhoneStateChanged((String)msg.obj); + handlePhoneStateChanged((String) msg.obj); break; case MSG_CLOCK_VISIBILITY_CHANGED: handleClockVisibilityChanged(); @@ -167,7 +169,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { handleDevicePolicyManagerStateChanged(); break; case MSG_USER_SWITCHING: - handleUserSwitching(msg.arg1, (IRemoteCallback)msg.obj); + handleUserSwitching(msg.arg1, (IRemoteCallback) msg.obj); break; case MSG_USER_SWITCH_COMPLETE: handleUserSwitchComplete(msg.arg1); @@ -178,6 +180,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { case MSG_KEYGUARD_VISIBILITY_CHANGED: handleKeyguardVisibilityChanged(msg.arg1); break; + case MSG_KEYGUARD_BOUNCER_CHANGED: + handleKeyguardBouncerChanged(msg.arg1); + break; case MSG_BOOT_COMPLETED: handleBootCompleted(); break; @@ -887,6 +892,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } /** + * Handle {@link #MSG_KEYGUARD_BOUNCER_CHANGED} + * @see #sendKeyguardBouncerChanged(boolean) + */ + private void handleKeyguardBouncerChanged(int bouncer) { + if (DEBUG) Log.d(TAG, "handleKeyguardBouncerChanged(" + bouncer + ")"); + boolean isBouncer = (bouncer == 1); + mBouncer = isBouncer; + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onKeyguardBouncerChanged(isBouncer); + } + } + } + + /** * Handle {@link #MSG_REPORT_EMERGENCY_CALL_ACTION} */ private void handleReportEmergencyCallAction() { @@ -902,6 +923,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { return mKeyguardIsVisible; } + /** + * @return if the keyguard is currently in bouncer mode. + */ + public boolean isKeyguardBouncer() { + return mBouncer; + } + public boolean isSwitchingUser() { return mSwitchingUser; } @@ -1021,6 +1049,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { message.sendToTarget(); } + /** + * @see #handleKeyguardBouncerChanged(int) + */ + public void sendKeyguardBouncerChanged(boolean showingBouncer) { + if (DEBUG) Log.d(TAG, "sendKeyguardBouncerChanged(" + showingBouncer + ")"); + Message message = mHandler.obtainMessage(MSG_KEYGUARD_BOUNCER_CHANGED); + message.arg1 = showingBouncer ? 1 : 0; + message.sendToTarget(); + } + public void reportClockVisible(boolean visible) { mClockVisible = visible; mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget(); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 7be4cec..91a024f 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -87,6 +87,12 @@ public class KeyguardUpdateMonitorCallback { } /** + * Called when the keyguard enters or leaves bouncer mode. + * @param bouncer if true, keyguard is now in bouncer mode. + */ + public void onKeyguardBouncerChanged(boolean bouncer) { } + + /** * Called when visibility of lockscreen clock changes, such as when * obscured by a widget. */ diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java index d8e5b8a..a9206e7 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java @@ -297,7 +297,7 @@ public abstract class KeyguardViewBase extends FrameLayout implements SecurityCa * @param event The key event * @return whether the event was consumed as a media key. */ - private boolean interceptMediaKey(KeyEvent event) { + public boolean interceptMediaKey(KeyEvent event) { final int keyCode = event.getKeyCode(); if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (keyCode) { diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 59b486f..bf97fc0 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -189,4 +189,7 @@ <!-- Default for Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 1==on --> <integer name="def_heads_up_enabled">1</integer> + <!-- Default for Settings.Global.DEVICE_NAME $1=BRAND $2=MODEL--> + <string name="def_device_name">%1$s %2$s</string> + </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 58086c4..909c32e 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -31,6 +31,7 @@ import android.database.sqlite.SQLiteStatement; import android.media.AudioManager; import android.media.AudioService; import android.net.ConnectivityManager; +import android.os.Build; import android.os.Environment; import android.os.RemoteException; import android.os.ServiceManager; @@ -69,7 +70,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 101; + private static final int DATABASE_VERSION = 102; private Context mContext; private int mUserHandle; @@ -1614,6 +1615,23 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 101; } + if (upgradeVersion == 101) { + if (mUserHandle == UserHandle.USER_OWNER) { + db.beginTransaction(); + SQLiteStatement stmt = null; + try { + stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)" + + " VALUES(?,?);"); + loadSetting(stmt, Settings.Global.DEVICE_NAME, getDefaultDeviceName()); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + if (stmt != null) stmt.close(); + } + } + upgradeVersion = 102; + } + // *** Remember to update DATABASE_VERSION above! if (upgradeVersion != currentVersion) { @@ -2342,6 +2360,8 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadIntegerSetting(stmt, Global.HEADS_UP_NOTIFICATIONS_ENABLED, R.integer.def_heads_up_enabled); + loadSetting(stmt, Settings.Global.DEVICE_NAME, getDefaultDeviceName()); + // --- New global settings start here } finally { if (stmt != null) stmt.close(); @@ -2398,4 +2418,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { } return defaultValue; } + + private String getDefaultDeviceName() { + return mContext.getResources().getString(R.string.def_device_name, Build.BRAND, + Build.MODEL); + } } diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_auto_rotate.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_auto_rotate.png Binary files differdeleted file mode 100644 index 63acea0..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_qs_auto_rotate.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_rotation_locked.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_rotation_locked.png Binary files differdeleted file mode 100644 index f3dc08f..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_qs_rotation_locked.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_auto_rotate.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_auto_rotate.png Binary files differdeleted file mode 100644 index ac6c1cf..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_qs_auto_rotate.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_rotation_locked.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_rotation_locked.png Binary files differdeleted file mode 100644 index 3b7a284..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_qs_rotation_locked.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_auto_rotate.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_auto_rotate.png Binary files differdeleted file mode 100644 index c553bc2..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_auto_rotate.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_rotation_locked.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_rotation_locked.png Binary files differdeleted file mode 100644 index b6daaf3..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_rotation_locked.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_auto_rotate.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_auto_rotate.png Binary files differdeleted file mode 100644 index b6cfaec..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_auto_rotate.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_rotation_locked.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_rotation_locked.png Binary files differdeleted file mode 100644 index 8e37884..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_rotation_locked.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/ic_qs_airplane.xml b/packages/SystemUI/res/drawable/ic_qs_airplane.xml new file mode 100644 index 0000000..ffe571f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_airplane.xml @@ -0,0 +1,31 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M10.2,9.0"/> + <path + android:fill="#FF000000" + android:pathData="M21.0,16.0l0.0,-2.0l-8.0,-5.0L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5L10.0,9.0l-8.0,5.0l0.0,2.0l8.0,-2.5L10.0,19.0l-2.0,1.5L8.0,22.0l3.5,-1.0l3.5,1.0l0.0,-1.5L13.0,19.0l0.0,-5.5L21.0,16.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth.xml new file mode 100644 index 0000000..22d0dcf --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M17.7,7.7L12.0,2.0l-1.0,0.0l0.0,7.6L6.4,5.0L5.0,6.4l5.6,5.6L5.0,17.6L6.4,19.0l4.6,-4.6L11.0,22.0l1.0,0.0l5.7,-5.7L13.4,12.0L17.7,7.7zM13.0,5.8l1.9,1.9L13.0,9.6L13.0,5.8zM14.9,16.3L13.0,18.2l0.0,-3.8L14.9,16.3z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_bugreport.xml b/packages/SystemUI/res/drawable/ic_qs_bugreport.xml new file mode 100644 index 0000000..2dfe183 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_bugreport.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M20.0,8.0l-2.8,0.0c-0.5,-0.8 -1.1,-1.5 -1.8,-2.0L17.0,4.4L15.6,3.0l-2.2,2.2C13.0,5.1 12.5,5.0 12.0,5.0s-1.0,0.1 -1.4,0.2L8.4,3.0L7.0,4.4L8.6,6.0C7.9,6.5 7.3,7.2 6.8,8.0L4.0,8.0l0.0,2.0l2.1,0.0C6.0,10.3 6.0,10.7 6.0,11.0l0.0,1.0L4.0,12.0l0.0,2.0l2.0,0.0l0.0,1.0c0.0,0.3 0.0,0.7 0.1,1.0L4.0,16.0l0.0,2.0l2.8,0.0c1.0,1.8 3.0,3.0 5.2,3.0s4.2,-1.2 5.2,-3.0L20.0,18.0l0.0,-2.0l-2.1,0.0c0.1,-0.3 0.1,-0.7 0.1,-1.0l0.0,-1.0l2.0,0.0l0.0,-2.0l-2.0,0.0l0.0,-1.0c0.0,-0.3 0.0,-0.7 -0.1,-1.0L20.0,10.0L20.0,8.0zM14.0,16.0l-4.0,0.0l0.0,-2.0l4.0,0.0L14.0,16.0zM14.0,12.0l-4.0,0.0l0.0,-2.0l4.0,0.0L14.0,12.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_cast.xml b/packages/SystemUI/res/drawable/ic_qs_cast.xml new file mode 100644 index 0000000..6f2840b --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_cast.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M21.0,3.0L3.0,3.0C1.9,3.0 1.0,3.9 1.0,5.0l0.0,3.0l2.0,0.0L3.0,5.0l18.0,0.0l0.0,14.0l-7.0,0.0l0.0,2.0l7.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L23.0,5.0C23.0,3.9 22.1,3.0 21.0,3.0zM1.0,18.0l0.0,3.0l3.0,0.0C4.0,19.3 2.7,18.0 1.0,18.0zM1.0,14.0l0.0,2.0c2.8,0.0 5.0,2.2 5.0,5.0l2.0,0.0C8.0,17.1 4.9,14.0 1.0,14.0zM1.0,10.0l0.0,2.0c5.0,0.0 9.0,4.0 9.0,9.0l2.0,0.0C12.0,14.9 7.1,10.0 1.0,10.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_close.xml b/packages/SystemUI/res/drawable/ic_qs_close.xml new file mode 100644 index 0000000..c2c72c8 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_close.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M19.0,6.4l-1.3999996,-1.4000001 -5.6000004,5.6000004 -5.6,-5.6000004 -1.4000001,1.4000001 5.6000004,5.6 -5.6000004,5.6000004 1.4000001,1.3999996 5.6,-5.6000004 5.6000004,5.6000004 1.3999996,-1.3999996 -5.6000004,-5.6000004z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_hotspot.xml b/packages/SystemUI/res/drawable/ic_qs_hotspot.xml new file mode 100644 index 0000000..965e3c1 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_hotspot.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M12.0,11.0c-1.1,0.0 -2.0,0.9 -2.0,2.0c0.0,1.1 0.9,2.0 2.0,2.0c1.1,0.0 2.0,-0.9 2.0,-2.0C14.0,11.9 13.1,11.0 12.0,11.0zM18.0,13.0c0.0,-3.3 -2.7,-6.0 -6.0,-6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,2.2 1.2,4.1 3.0,5.2l1.0,-1.7c-1.2,-0.7 -2.0,-2.0 -2.0,-3.4c0.0,-2.2 1.8,-4.0 4.0,-4.0s4.0,1.8 4.0,4.0c0.0,1.5 -0.8,2.8 -2.0,3.4l1.0,1.7C16.8,17.1 18.0,15.2 18.0,13.0zM12.0,3.0C6.5,3.0 2.0,7.5 2.0,13.0c0.0,3.7 2.0,6.9 5.0,8.6l1.0,-1.7c-2.4,-1.4 -4.0,-4.0 -4.0,-6.9c0.0,-4.4 3.6,-8.0 8.0,-8.0s8.0,3.6 8.0,8.0c0.0,3.0 -1.6,5.5 -4.0,6.9l1.0,1.7c3.0,-1.7 5.0,-5.0 5.0,-8.6C22.0,7.5 17.5,3.0 12.0,3.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_invert_colors.xml b/packages/SystemUI/res/drawable/ic_qs_invert_colors.xml new file mode 100644 index 0000000..7c92052 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_invert_colors.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M18.939,7.244c-5.887,-5.885 -6.214,-6.214 -6.222,-6.222l-0.707,-0.737L5.088,7.207c-2.914,2.915 -3.74,6.629 -2.266,10.19c1.541,3.719 5.312,6.316 9.174,6.317l0.0,0.0c3.861,-0.001 7.636,-2.603 9.179,-6.328C22.646,13.834 21.832,10.138 18.939,7.244zM4.67,16.632c-1.149,-2.776 -0.481,-5.696 1.832,-8.011l5.494,-5.492c0.0,0.002 0.002,0.003 0.003,0.004l0.0,18.582c-0.001,0.0 -0.002,0.0 -0.003,0.0C8.922,21.714 5.91,19.624 4.67,16.632z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location.xml b/packages/SystemUI/res/drawable/ic_qs_location.xml new file mode 100644 index 0000000..e6e98a0 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_location.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M12.0,2.0C8.1,2.0 5.0,5.1 5.0,9.0c0.0,5.2 7.0,13.0 7.0,13.0s7.0,-7.8 7.0,-13.0C19.0,5.1 15.9,2.0 12.0,2.0zM12.0,11.5c-1.4,0.0 -2.5,-1.1 -2.5,-2.5s1.1,-2.5 2.5,-2.5c1.4,0.0 2.5,1.1 2.5,2.5S13.4,11.5 12.0,11.5z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_minus.xml b/packages/SystemUI/res/drawable/ic_qs_minus.xml new file mode 100644 index 0000000..8323e89 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_minus.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M7.0,11.0l0.0,2.0l10.0,0.0l0.0,-2.0L7.0,11.0zM12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.0c-4.4,0.0 -8.0,-3.6 -8.0,-8.0s3.6,-8.0 8.0,-8.0c4.4,0.0 8.0,3.6 8.0,8.0S16.4,20.0 12.0,20.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_plus.xml b/packages/SystemUI/res/drawable/ic_qs_plus.xml new file mode 100644 index 0000000..84cd72a --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_plus.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M13.0,7.0l-2.0,0.0l0.0,4.0L7.0,11.0l0.0,2.0l4.0,0.0l0.0,4.0l2.0,0.0l0.0,-4.0l4.0,0.0l0.0,-2.0l-4.0,0.0L13.0,7.0zM12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.0c-4.4,0.0 -8.0,-3.6 -8.0,-8.0s3.6,-8.0 8.0,-8.0c4.4,0.0 8.0,3.6 8.0,8.0S16.4,20.0 12.0,20.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml new file mode 100644 index 0000000..fa6f20c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M3.0,9.0c0.0,0.0 0.0,6.0 0.0,6.0l4.0,0.0l5.0,5.0L12.0,4.0L7.0,9.0L3.0,9.0zM16.5,12.0c0.0,-1.8 -1.0,-3.3 -2.5,-4.0L14.0,16.0C15.5,15.3 16.5,13.8 16.5,12.0zM14.0,3.2l0.0,2.1c2.9,0.9 5.0,3.5 5.0,6.7s-2.1,5.8 -5.0,6.7l0.0,2.1c4.0,-0.9 7.0,-4.5 7.0,-8.8S18.0,4.1 14.0,3.2z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml new file mode 100644 index 0000000..0665196 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M16.5,12.0c0.0,-1.8 -1.0,-3.3 -2.5,-4.0l0.0,2.2l2.5,2.5C16.5,12.4 16.5,12.2 16.5,12.0zM19.0,12.0c0.0,0.9 -0.2,1.8 -0.5,2.6l1.5,1.5c0.7,-1.2 1.0,-2.7 1.0,-4.2c0.0,-4.3 -3.0,-7.9 -7.0,-8.8l0.0,2.1C16.9,6.2 19.0,8.8 19.0,12.0zM4.3,3.0L3.0,4.3L7.7,9.0L3.0,9.0c0.0,0.0 0.0,6.0 0.0,6.0l4.0,0.0l5.0,5.0l0.0,-6.7l4.3,4.3c-0.7,0.5 -1.4,0.9 -2.3,1.2l0.0,2.1c1.4,-0.3 2.6,-0.9 3.7,-1.8l2.0,2.0l1.3,-1.3l-9.0,-9.0L4.3,3.0zM12.0,4.0L9.9,6.1L12.0,8.2L12.0,4.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml new file mode 100644 index 0000000..299a2ef --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_zen.xml b/packages/SystemUI/res/drawable/ic_qs_zen.xml new file mode 100644 index 0000000..059c068 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_zen.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FF000000" + android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0zM14.0,9.8l-2.8,3.4L14.0,13.200001L14.0,15.0L9.0,15.0l0.0,-1.8l2.8,-3.4L9.0,9.799999L9.0,8.0l5.0,0.0L14.0,9.8z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_01.xml b/packages/SystemUI/res/drawable/ic_rotate_24_01.xml new file mode 100644 index 0000000..a6c2cf8 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_01.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M10.25,1.75c-0.6,-0.6 -1.5,-0.6 -2.1,0.0l-6.4,6.4c-0.6,0.6 -0.6,1.5 0.0,2.1l12.0,12.0c0.6,0.6 1.5,0.6 2.1,0.0l6.4,-6.4c0.6,-0.6 0.6,-1.5 0.0,-2.1L10.25,1.75zM14.85,21.25l-12.0,-12.0l6.4,-6.4l12.0,12.0L14.85,21.25z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M16.55,2.5c3.3,1.5 5.6,4.7 6.0,8.5l1.5,0.0c-0.6,-6.2 -5.7,-11.0 -12.0,-11.0c-0.2,0.0 -0.4,0.0 -0.7,0.0l3.8,3.8L16.55,2.5z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M7.55,21.5c-3.3,-1.5 -5.6,-4.7 -6.0,-8.5l-1.4,0.0c0.5,6.2 5.6,11.0 11.9,11.0c0.2,0.0 0.4,0.0 0.7,0.0l-3.8,-3.8L7.55,21.5z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_02.xml b/packages/SystemUI/res/drawable/ic_rotate_24_02.xml new file mode 100644 index 0000000..4107c46 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_02.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M10.24,2.43C9.67,1.88 8.83,1.88 8.28,2.45L2.34,8.48c-0.56,0.57 -0.55,1.41 0.02,1.96l11.3,11.13c0.57,0.56 1.41,0.55 1.96,-0.02l5.93,-6.03c0.56,-0.57 0.55,-1.41 -0.02,-1.96L10.24,2.43zM14.68,20.62L3.38,9.5l5.93,-6.03l11.3,11.13L14.68,20.62z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M16.91,2.71c3.23,1.64 5.39,4.94 5.62,8.76l1.5,0.07c-0.33,-6.22 -5.21,-11.24 -11.5,-11.52c-0.2,-0.01 -0.4,-0.02 -0.7,-0.03l3.63,3.96L16.91,2.71z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M7.09,21.29c-3.23,-1.64 -5.39,-4.94 -5.62,-8.76l-1.4,-0.06c0.23,6.22 5.11,11.24 11.4,11.51c0.2,0.01 0.4,0.02 0.7,0.03l-3.63,-3.96L7.09,21.29z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_03.xml b/packages/SystemUI/res/drawable/ic_rotate_24_03.xml new file mode 100644 index 0000000..127296c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_03.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M11.07,4.12c-0.43,-0.49 -1.11,-0.53 -1.6,-0.1L4.3,8.57C3.81,9.0 3.77,9.68 4.19,10.17l8.54,9.71c0.43,0.49 1.11,0.53 1.6,0.1l5.18,-4.55c0.49,-0.43 0.53,-1.11 0.1,-1.6L11.07,4.12zM13.61,19.17L5.08,9.46l5.18,-4.55l8.54,9.71L13.61,19.17z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M17.72,3.2c3.06,1.94 4.9,5.43 4.77,9.25l1.49,0.21C24.23,6.43 19.84,0.97 13.6,0.11c-0.2,-0.03 -0.4,-0.06 -0.69,-0.1l3.24,4.29L17.72,3.2z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M6.19,20.78c-3.06,-1.94 -4.9,-5.43 -4.77,-9.25l-1.39,-0.19c-0.36,6.21 4.03,11.67 10.27,12.53c0.2,0.03 0.4,0.06 0.69,0.1l-3.24,-4.29L6.19,20.78z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_04.xml b/packages/SystemUI/res/drawable/ic_rotate_24_04.xml new file mode 100644 index 0000000..d00262a --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_04.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M11.88,5.72c-0.3,-0.42 -0.83,-0.51 -1.25,-0.21L6.19,8.69c-0.42,0.3 -0.51,0.83 -0.21,1.25l5.95,8.34c0.3,0.42 0.83,0.51 1.25,0.21l4.45,-3.17c0.42,-0.3 0.51,-0.83 0.21,-1.25L11.88,5.72zM12.68,17.79L6.73,9.45l4.45,-3.17l5.95,8.34L12.68,17.79z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M18.8,4.01c2.79,2.32 4.16,6.01 3.54,9.78l1.45,0.4c1.06,-6.14 -2.59,-12.11 -8.67,-13.78c-0.19,-0.05 -0.39,-0.11 -0.68,-0.18l2.66,4.67L18.8,4.01z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M5.11,19.96c-2.79,-2.32 -4.16,-6.01 -3.54,-9.78L0.21,9.81c-1.15,6.11 2.5,12.09 8.57,13.75c0.19,0.05 0.39,0.11 0.68,0.18L6.8,19.08L5.11,19.96z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_05.xml b/packages/SystemUI/res/drawable/ic_rotate_24_05.xml new file mode 100644 index 0000000..570f51f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_05.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M15.41,8.14c0.05,-0.42 -0.23,-0.78 -0.65,-0.83l-4.5,-0.54C9.84,6.72 9.48,7.0 9.43,7.42l-1.01,8.44c-0.05,0.42 0.23,0.78 0.65,0.83l4.5,0.54C14.0,17.28 14.35,17.0 14.4,16.58L15.41,8.14zM9.16,15.99l1.01,-8.44l4.5,0.54l-1.01,8.44L9.16,15.99z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M22.35,10.28c0.64,3.57 -0.69,7.28 -3.59,9.77l0.85,1.23c4.76,-4.01 5.82,-10.94 2.24,-16.12c-0.11,-0.16 -0.23,-0.33 -0.4,-0.58l-0.97,5.29L22.35,10.28z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M1.6,13.66c-0.64,-3.57 0.69,-7.28 3.59,-9.77L4.4,2.74c-4.82,3.93 -5.88,10.86 -2.3,16.04c0.11,0.16 0.23,0.33 0.4,0.58l0.97,-5.29L1.6,13.66z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_06.xml b/packages/SystemUI/res/drawable/ic_rotate_24_06.xml new file mode 100644 index 0000000..aaf9356 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_06.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M15.62,13.08c0.29,-0.1 0.44,-0.39 0.34,-0.69L14.9,9.27c-0.1,-0.29 -0.39,-0.44 -0.69,-0.34l-5.86,1.98c-0.29,0.1 -0.44,0.39 -0.34,0.69l1.06,3.12c0.1,0.29 0.39,0.44 0.69,0.34L15.62,13.08zM8.51,11.44l5.86,-1.98l1.06,3.12l-5.86,1.98L8.51,11.44z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M18.53,20.21c-2.8,2.3 -6.69,2.95 -10.27,1.64L7.6,23.2c5.83,2.2 12.39,-0.26 15.16,-5.92c0.09,-0.18 0.18,-0.36 0.31,-0.63l-5.09,1.73L18.53,20.21z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M5.45,3.76c2.8,-2.3 6.69,-2.95 10.27,-1.64l0.62,-1.26C10.56,-1.42 4.0,1.04 1.22,6.69C1.13,6.87 1.05,7.05 0.91,7.32L6.0,5.59L5.45,3.76z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_07.xml b/packages/SystemUI/res/drawable/ic_rotate_24_07.xml new file mode 100644 index 0000000..330ce6a --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_07.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M12.23,14.48c0.13,0.16 0.35,0.17 0.5,0.04l1.66,-1.4c0.16,-0.13 0.17,-0.35 0.04,-0.5l-2.62,-3.11c-0.13,-0.16 -0.35,-0.17 -0.5,-0.04l-1.66,1.4c-0.16,0.13 -0.17,0.35 -0.04,0.5L12.23,14.48zM11.53,9.73l2.62,3.11l-1.66,1.4l-2.62,-3.11L11.53,9.73z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M6.04,20.63c-3.01,-2.02 -4.75,-5.56 -4.52,-9.37l-1.48,-0.25c-0.43,6.21 3.81,11.79 10.02,12.83c0.2,0.03 0.39,0.07 0.69,0.12l-3.12,-4.37L6.04,20.63z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M18.04,3.37c3.01,2.02 4.75,5.56 4.52,9.37l1.38,0.23c0.53,-6.2 -3.71,-11.77 -9.93,-12.81c-0.2,-0.03 -0.39,-0.07 -0.69,-0.12l3.12,4.37L18.04,3.37z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_08.xml b/packages/SystemUI/res/drawable/ic_rotate_24_08.xml new file mode 100644 index 0000000..1c7f1a1 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_08.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M10.55,12.73c-0.06,0.12 -0.02,0.25 0.1,0.32l1.26,0.68c0.12,0.06 0.25,0.02 0.32,-0.1l1.27,-2.36c0.06,-0.12 0.02,-0.25 -0.1,-0.32l-1.26,-0.68c-0.12,-0.06 -0.25,-0.02 -0.32,0.1L10.55,12.73zM13.29,11.15l-1.27,2.36l-1.26,-0.68l1.27,-2.36L13.29,11.15z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M1.55,12.95C1.18,9.34 2.78,5.74 5.86,3.47L5.1,2.18C0.05,5.83 -1.51,12.66 1.67,18.09c0.1,0.17 0.2,0.35 0.35,0.6l1.36,-5.2L1.55,12.95z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M22.5,11.12c0.37,3.61 -1.23,7.21 -4.31,9.47l0.71,1.21c5.1,-3.56 6.67,-10.39 3.48,-15.83c-0.1,-0.17 -0.2,-0.35 -0.35,-0.6l-1.36,5.2L22.5,11.12z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_09.xml b/packages/SystemUI/res/drawable/ic_rotate_24_09.xml new file mode 100644 index 0000000..ebfbad6 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_09.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:pathData="M11.45,12c0,0.14,0.12,0.25,0.25,0.25l0.06,0l0,-0.5l-0.06,0C11.57,11.75,11.45,11.86,11.45,12z" + android:fill="#00000000"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M11.77,11.62l-0.06,0.0c-0.21,0.0 -0.37,0.17 -0.38,0.37c0.0,0.21 0.17,0.37 0.37,0.37l0.06,0.0c0.0,0.07 0.06,0.12 0.12,0.13l0.62,0.0c0.07,0.0 0.12,-0.06 0.13,-0.12l0.0,-0.75c0.0,-0.07 -0.06,-0.12 -0.12,-0.13l-0.62,0.0C11.83,11.5 11.77,11.56 11.77,11.62zM12.33,12.0c0.0,0.07 -0.06,0.12 -0.13,0.12c-0.07,0.0 -0.12,-0.06 -0.12,-0.13c0.0,-0.07 0.06,-0.12 0.13,-0.12C12.28,11.88 12.33,11.93 12.33,12.0zM11.77,11.75l0.0,0.5l-0.06,0.0c-0.14,0.0 -0.26,-0.11 -0.25,-0.25c0.0,-0.14 0.12,-0.25 0.26,-0.25L11.77,11.75z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M3.83,5.44c2.21,-2.87 5.85,-4.38 9.64,-3.9l0.34,-1.46C7.64,-0.75 1.81,3.12 0.37,9.25C0.32,9.45 0.28,9.64 0.21,9.93L4.78,7.1L3.83,5.44zM20.28,18.53c-2.21,2.87 -5.85,4.38 -9.64,3.9l-0.32,1.36c6.15,0.93 11.99,-2.95 13.42,-9.08c0.05,-0.19 0.09,-0.39 0.16,-0.68l-4.57,2.83L20.28,18.53z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_10.xml b/packages/SystemUI/res/drawable/ic_rotate_24_10.xml new file mode 100644 index 0000000..21dda8c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_10.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:pathData="M10.42,9.96c-0.42,0.42,-0.39,1.12,0.03,1.54l0.19,0.19l1.51,-1.53l-0.19,-0.19 C11.54,9.55,10.84,9.54,10.42,9.96z" + android:fill="#00000000"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M12.53,9.78l-0.19,-0.19c-0.63,-0.63 -1.66,-0.62 -2.28,0.02c-0.63,0.63 -0.62,1.66 0.02,2.28l0.19,0.19c-0.21,0.21 -0.21,0.55 0.01,0.76l1.92,1.89c0.21,0.21 0.55,0.21 0.76,-0.01l2.27,-2.3c0.21,-0.21 0.21,-0.55 -0.01,-0.76l-1.92,-1.89C13.08,9.56 12.74,9.56 12.53,9.78zM13.12,12.62c-0.21,0.21 -0.55,0.21 -0.76,0.01c-0.21,-0.21 -0.21,-0.55 -0.01,-0.76c0.21,-0.21 0.55,-0.21 0.76,-0.01C13.33,12.07 13.33,12.41 13.12,12.62zM12.15,10.16l-1.51,1.53l-0.19,-0.19c-0.42,-0.42 -0.45,-1.12 -0.03,-1.54c0.42,-0.42 1.12,-0.41 1.54,0.01L12.15,10.16z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M7.82,2.36c3.3,-1.49 7.23,-1.12 10.35,1.09l0.99,-1.13C14.09,-1.32 7.12,-0.64 2.97,4.1C2.84,4.25 2.71,4.4 2.51,4.62l5.36,-0.36L7.82,2.36zM16.18,21.64c-3.3,1.49 -7.23,1.12 -10.35,-1.09l-0.92,1.05c4.99,3.71 11.97,3.03 16.12,-1.71c0.13,-0.15 0.26,-0.3 0.46,-0.53l-5.36,0.36L16.18,21.64z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_11.xml b/packages/SystemUI/res/drawable/ic_rotate_24_11.xml new file mode 100644 index 0000000..f4186fe --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_11.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:pathData="M10.53,8.85c-0.7,0.37,-0.95,1.28,-0.58,1.98l0.17,0.32l2.55,-1.36L12.5,9.48 C12.12,8.78,11.23,8.48,10.53,8.85z" + android:fill="#00000000"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M13.3,9.46l-0.17,-0.32c-0.56,-1.05 -1.87,-1.45 -2.93,-0.89s-1.45,1.87 -0.89,2.93l0.17,0.32c-0.35,0.19 -0.48,0.62 -0.3,0.98l1.7,3.18c0.19,0.35 0.62,0.48 0.98,0.3l3.82,-2.04c0.35,-0.19 0.48,-0.62 0.3,-0.98l-1.7,-3.18C14.09,9.4 13.65,9.27 13.3,9.46zM12.92,13.34c-0.35,0.19 -0.79,0.06 -0.98,-0.3c-0.19,-0.35 -0.05,-0.79 0.3,-0.98c0.35,-0.19 0.79,-0.05 0.98,0.3C13.41,12.72 13.27,13.15 12.92,13.34zM12.67,9.8l-2.55,1.36l-0.17,-0.32c-0.37,-0.7 -0.13,-1.61 0.58,-1.98c0.7,-0.37 1.59,-0.08 1.97,0.63L12.67,9.8z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M11.48,1.5c3.62,-0.23 7.16,1.5 9.3,4.66l1.32,-0.71C18.65,0.27 11.89,-1.55 6.34,1.42C6.16,1.51 5.98,1.61 5.72,1.75l5.14,1.56L11.48,1.5zM12.52,22.5c-3.62,0.23 -7.16,-1.5 -9.3,-4.66L1.98,18.5c3.37,5.23 10.13,7.06 15.68,4.08c0.18,-0.09 0.35,-0.19 0.62,-0.33l-5.14,-1.56L12.52,22.5z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_12.xml b/packages/SystemUI/res/drawable/ic_rotate_24_12.xml new file mode 100644 index 0000000..d408e28 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_12.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:pathData="M10.83,8.49c-0.91,0.3,-1.39,1.31,-1.09,2.22l0.13,0.41l3.28,-1.08l-0.13,-0.41 C12.72,8.72,11.73,8.19,10.83,8.49z" + android:fill="#00000000"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M13.98,9.77l-0.13,-0.41C13.4,8.0 11.93,7.26 10.57,7.71c-1.36,0.45 -2.1,1.91 -1.65,3.27l0.13,0.41c-0.45,0.15 -0.7,0.64 -0.55,1.09l1.35,4.1c0.15,0.45 0.64,0.7 1.09,0.55l4.92,-1.62c0.45,-0.15 0.7,-0.64 0.55,-1.09l-1.35,-4.1C14.92,9.87 14.43,9.62 13.98,9.77zM12.73,14.27c-0.45,0.15 -0.94,-0.1 -1.09,-0.55c-0.15,-0.45 0.1,-0.94 0.55,-1.09c0.45,-0.15 0.94,0.1 1.09,0.55C13.43,13.64 13.18,14.12 12.73,14.27zM13.16,10.04l-3.28,1.08l-0.13,-0.41c-0.3,-0.91 0.18,-1.92 1.09,-2.22c0.91,-0.3 1.9,0.24 2.19,1.14L13.16,10.04z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M13.55,1.6c3.59,0.48 6.72,2.87 8.21,6.39l1.44,-0.44C20.82,1.8 14.54,-1.31 8.51,0.52c-0.19,0.06 -0.38,0.12 -0.67,0.2l4.74,2.53L13.55,1.6zM10.45,22.4c-3.59,-0.48 -6.72,-2.87 -8.21,-6.39L0.9,16.41c2.28,5.79 8.55,8.9 14.58,7.07c0.19,-0.06 0.38,-0.12 0.67,-0.2l-4.74,-2.53L10.45,22.4z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_13.xml b/packages/SystemUI/res/drawable/ic_rotate_24_13.xml new file mode 100644 index 0000000..1ac6b39 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_13.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:pathData="M11.17,7.71c-1.09,0.19,-1.8,1.28,-1.61,2.37l0.09,0.49l3.94,-0.7l-0.09,-0.49C13.3,8.3,12.26,7.52,11.17,7.71 z" + android:fill="#00000000"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M14.57,9.7l-0.09,-0.49C14.19,7.58 12.63,6.49 11.0,6.78c-1.63,0.29 -2.71,1.85 -2.43,3.48l0.08,0.49c-0.54,0.1 -0.91,0.62 -0.81,1.16l0.87,4.92c0.1,0.54 0.62,0.91 1.16,0.81l5.91,-1.05c0.54,-0.1 0.91,-0.61 0.81,-1.16l-0.87,-4.92C15.63,9.97 15.11,9.61 14.57,9.7zM12.4,14.66c-0.54,0.1 -1.06,-0.27 -1.16,-0.81c-0.1,-0.54 0.27,-1.06 0.81,-1.16s1.06,0.27 1.16,0.81C13.3,14.04 12.94,14.56 12.4,14.66zM13.58,9.88l-3.94,0.7l-0.09,-0.49c-0.19,-1.09 0.52,-2.17 1.61,-2.37s2.13,0.58 2.33,1.67L13.58,9.88z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M14.99,1.92c3.49,0.97 6.26,3.78 7.24,7.48l1.48,-0.23c-1.55,-6.03 -7.32,-9.99 -13.55,-9.02c-0.2,0.03 -0.4,0.06 -0.69,0.11l4.34,3.17L14.99,1.92zM9.01,22.08C5.52,21.1 2.76,18.3 1.78,14.6L0.4,14.82c1.45,6.05 7.22,10.01 13.45,9.04c0.2,-0.03 0.4,-0.06 0.69,-0.11l-4.34,-3.17L9.01,22.08z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_14.xml b/packages/SystemUI/res/drawable/ic_rotate_24_14.xml new file mode 100644 index 0000000..c43e363 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_14.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:pathData="M11.72,7.61c-1.1,0.08,-1.93,1.08,-1.86,2.18l0.03,0.5l3.99,-0.27l-0.03,-0.5 C13.78,8.42,12.82,7.54,11.72,7.61z" + android:fill="#00000000"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M14.88,9.95l-0.03,-0.5c-0.11,-1.65 -1.54,-2.9 -3.2,-2.79C10.0,6.78 8.75,8.21 8.87,9.86l0.03,0.5c-0.55,0.04 -0.97,0.52 -0.93,1.07l0.34,4.99c0.04,0.55 0.52,0.97 1.07,0.93l5.99,-0.41c0.55,-0.04 0.97,-0.51 0.93,-1.07l-0.34,-4.99C15.91,10.33 15.43,9.91 14.88,9.95zM12.2,14.64c-0.55,0.04 -1.03,-0.38 -1.07,-0.93c-0.04,-0.55 0.38,-1.03 0.93,-1.07c0.55,-0.04 1.03,0.38 1.07,0.93C13.16,14.13 12.75,14.61 12.2,14.64zM13.88,10.02l-3.99,0.27l-0.03,-0.5c-0.08,-1.1 0.75,-2.11 1.86,-2.18c1.1,-0.08 2.06,0.81 2.13,1.91L13.88,10.02z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M15.71,2.17c3.41,1.23 5.96,4.23 6.67,7.98l1.5,-0.12c-1.1,-6.13 -6.58,-10.5 -12.86,-9.99c-0.2,0.02 -0.4,0.03 -0.7,0.06l4.1,3.48L15.71,2.17zM8.29,21.83c-3.41,-1.23 -5.96,-4.23 -6.67,-7.98l-1.4,0.11c1.0,6.14 6.48,10.51 12.76,9.99c0.2,-0.02 0.4,-0.03 0.7,-0.06l-4.1,-3.48L8.29,21.83z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_24_15.xml b/packages/SystemUI/res/drawable/ic_rotate_24_15.xml new file mode 100644 index 0000000..22fa428 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_24_15.xml @@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:pathData="M12.05,7.7c-1.1,0,-2,0.94,-2,2.05v0.5h4v-0.5C14.05,8.65,13.15,7.7,12.05,7.7z" + android:fill="#00000000"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M15.05,10.25l0.0,-0.5c0.0,-1.66 -1.34,-3.0 -3.0,-3.0s-2.99,1.34 -2.99,3.0l-0.01,0.5c-0.55,0.0 -1.0,0.45 -1.0,1.0l0.0,5.0c0.0,0.55 0.45,1.0 1.0,1.0l6.0,0.0c0.55,0.0 1.0,-0.45 1.0,-1.0l0.0,-5.0C16.05,10.7 15.6,10.25 15.05,10.25zM12.05,14.75c-0.55,0.0 -1.0,-0.45 -1.0,-1.0c0.0,-0.55 0.45,-1.0 1.0,-1.0s1.0,0.45 1.0,1.0C13.05,14.3 12.6,14.75 12.05,14.75zM14.05,10.25l-4.0,0.0l0.0,-0.5c0.0,-1.1 0.9,-2.05 2.0,-2.05s2.0,0.94 2.0,2.05L14.05,10.25z"/> + <path + android:fill="#FFFFFFFF" + android:pathData="M16.5,2.5c3.3,1.5 5.6,4.7 6.0,8.5L24.0,11.0C23.4,4.8 18.3,0.0 12.0,0.0c-0.2,0.0 -0.4,0.0 -0.7,0.0l3.8,3.8L16.5,2.5zM7.5,21.5c-3.3,-1.5 -5.6,-4.7 -6.0,-8.5L0.1,13.0C0.6,19.2 5.7,24.0 12.0,24.0c0.2,0.0 0.4,0.0 0.7,0.0l-3.8,-3.8L7.5,21.5z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_rotate_locked_anim.xml b/packages/SystemUI/res/drawable/ic_rotate_locked_anim.xml new file mode 100644 index 0000000..e14a1ce --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_locked_anim.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2014 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. +--> +<animation-list + xmlns:android="http://schemas.android.com/apk/res/android" + android:oneshot="true"> + <item android:drawable="@drawable/ic_rotate_24_01" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_02" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_03" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_04" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_05" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_06" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_07" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_08" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_09" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_10" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_11" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_12" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_13" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_14" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_15" android:duration="16" /> +</animation-list> diff --git a/packages/SystemUI/res/drawable/ic_rotate_unlocked_anim.xml b/packages/SystemUI/res/drawable/ic_rotate_unlocked_anim.xml new file mode 100644 index 0000000..63b8c5f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_rotate_unlocked_anim.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2014 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. +--> +<animation-list + xmlns:android="http://schemas.android.com/apk/res/android" + android:oneshot="true"> + <item android:drawable="@drawable/ic_rotate_24_15" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_14" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_13" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_12" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_11" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_10" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_09" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_08" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_07" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_06" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_05" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_04" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_03" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_02" android:duration="16" /> + <item android:drawable="@drawable/ic_rotate_24_01" android:duration="16" /> +</animation-list> diff --git a/packages/SystemUI/res/layout/quick_settings_tile_media.xml b/packages/SystemUI/res/drawable/qs_panel_background.xml index 355176c..c324976 100644 --- a/packages/SystemUI/res/layout/quick_settings_tile_media.xml +++ b/packages/SystemUI/res/drawable/qs_panel_background.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,12 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. --> -<TextView - xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/TextAppearance.QuickSettings.TileView.AllInOne" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center" - android:text="@string/quick_settings_media_device_label" - android:singleLine="true" - /> +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:insetLeft="@dimen/notification_side_padding" + android:insetRight="@dimen/notification_side_padding"> + <shape> + <solid android:color="@color/system_primary_color" /> + <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" /> + </shape> +</inset> diff --git a/packages/SystemUI/res/layout/flip_settings.xml b/packages/SystemUI/res/layout/flip_settings.xml deleted file mode 100644 index 28d9625..0000000 --- a/packages/SystemUI/res/layout/flip_settings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> - -<com.android.systemui.statusbar.phone.QuickSettingsContainerView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/quick_settings_container" - android:padding="@dimen/notification_side_padding" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="#5f000000" - android:animateLayoutChanges="true" - android:columnCount="@integer/quick_settings_num_columns" />
\ No newline at end of file diff --git a/core/res/res/drawable/btn_color_quantum.xml b/packages/SystemUI/res/layout/qs_panel.xml index 2da9226..b24d4ad 100644 --- a/core/res/res/drawable/btn_color_quantum.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -13,18 +13,16 @@ See the License for the specific language governing permissions and limitations under the License. --> - -<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="?attr/colorButtonPressedColored"> - <selector> - <item android:state_enabled="false"> - <nine-patch android:src="@drawable/btn_qntm_alpha" - android:tint="?attr/colorButtonNormal" - android:alpha="?attr/disabledAlpha" /> - </item> - <item> - <nine-patch android:src="@drawable/btn_qntm_alpha" - android:tint="?attr/colorButtonNormalColored" /> - </item> - </selector> -</touch-feedback> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/quick_settings_container" + android:paddingLeft="@dimen/notification_side_padding" + android:paddingRight="@dimen/notification_side_padding" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/qs_panel_background" > + <com.android.systemui.qs.QSPanel + android:id="@+id/quick_settings_panel" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> +</FrameLayout> diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail.xml new file mode 100644 index 0000000..2df6d43 --- /dev/null +++ b/packages/SystemUI/res/layout/qs_zen_mode_detail.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. +--> +<com.android.systemui.qs.tiles.ZenModeDetail xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/system_secondary_color" > + + <com.android.systemui.qs.QSImageView + android:id="@android:id/button1" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_alignParentStart="true" + android:padding="@dimen/quick_settings_panel_padding" /> + + <Switch + android:id="@android:id/checkbox" + android:layout_width="wrap_content" + android:layout_height="64dp" + android:layout_alignParentEnd="true" + android:gravity="center" + android:padding="@dimen/quick_settings_panel_padding" /> + + <TextView + android:id="@android:id/title" + android:layout_width="match_parent" + android:layout_height="64dp" + android:layout_toEndOf="@android:id/button1" + android:layout_toStartOf="@android:id/checkbox" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" + android:gravity="center_vertical" + android:paddingStart="@dimen/quick_settings_panel_padding" + android:text="@string/zen_mode_title" /> + + <View + android:id="@android:id/custom" + android:layout_width="match_parent" + android:layout_height="2dp" + android:layout_below="@android:id/title" + android:background="#888" /> + + <ListView + android:id="@android:id/content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_above="@android:id/button2" + android:layout_below="@android:id/custom" + android:divider="#00000000" + android:dividerHeight="0px" /> + + <TextView + android:id="@android:id/button2" + style="@style/QSBorderless" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" + android:padding="@dimen/quick_settings_panel_padding" + android:text="@string/quick_settings_more_settings" + android:textAllCaps="true" /> + +</com.android.systemui.qs.tiles.ZenModeDetail>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml new file mode 100644 index 0000000..a5c8903 --- /dev/null +++ b/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" > + + <RadioButton + android:id="@android:id/checkbox" + android:layout_width="32dp" + android:layout_height="64dp" + android:layout_alignParentStart="true" + android:layout_marginStart="@dimen/quick_settings_panel_padding" + android:gravity="center" /> + + <TextView + android:id="@android:id/title" + android:layout_width="match_parent" + android:layout_height="64dp" + android:layout_toEndOf="@android:id/checkbox" + android:layout_toStartOf="@android:id/button1" + android:ellipsize="end" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" + android:gravity="center_vertical" + android:maxLines="1" + android:text="@string/accessibility_back" /> + + <com.android.systemui.qs.QSImageView + android:id="@android:id/button1" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_alignParentEnd="true" + android:layout_marginEnd="48dp" + android:padding="@dimen/quick_settings_panel_padding" + android:paddingRight="0px" /> + + <com.android.systemui.qs.QSImageView + android:id="@android:id/button2" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_alignParentEnd="true" + android:padding="@dimen/quick_settings_panel_padding" /> + +</RelativeLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_tile_alarm.xml b/packages/SystemUI/res/layout/quick_settings_tile_alarm.xml deleted file mode 100644 index 493c704..0000000 --- a/packages/SystemUI/res/layout/quick_settings_tile_alarm.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<TextView - xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/TextAppearance.QuickSettings.TileView.AllInOne" - android:id="@+id/alarm_textview" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:gravity="center" - android:drawableTop="@drawable/ic_qs_alarm_on" - /> diff --git a/packages/SystemUI/res/layout/quick_settings_tile_basic.xml b/packages/SystemUI/res/layout/quick_settings_tile_basic.xml deleted file mode 100644 index 16bf49c..0000000 --- a/packages/SystemUI/res/layout/quick_settings_tile_basic.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2013 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="match_parent" - android:layout_height="match_parent" - android:layout_gravity="top" - android:orientation="vertical"> - <ImageView - android:id="@+id/image" - android:layout_marginTop="@dimen/qs_tile_margin_above_icon" - android:layout_marginBottom="@dimen/qs_tile_margin_below_icon" - android:layout_width="@dimen/qs_tile_icon_size" - android:layout_height="@dimen/qs_tile_icon_size" - android:layout_gravity="top|center_horizontal" - android:scaleType="centerInside" - /> - <TextView - style="@style/TextAppearance.QuickSettings.TileView" - android:id="@+id/text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" - android:gravity="top|center_horizontal" - /> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_tile_battery.xml b/packages/SystemUI/res/layout/quick_settings_tile_battery.xml deleted file mode 100644 index 1f39aef..0000000 --- a/packages/SystemUI/res/layout/quick_settings_tile_battery.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2013 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" - xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="top" - android:orientation="vertical"> - <com.android.systemui.BatteryMeterView - android:id="@+id/image" - android:layout_marginTop="@dimen/qs_tile_margin_above_icon" - android:layout_marginBottom="@dimen/qs_tile_margin_below_icon" - android:layout_width="22dp" - android:layout_height="32dp" - android:padding="3dp" - android:layout_gravity="top|center_horizontal" - systemui:frameColor="@color/qs_batterymeter_frame_color" - /> - <TextView - style="@style/TextAppearance.QuickSettings.TileView" - android:id="@+id/text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" - android:gravity="top|center_horizontal" - /> -</LinearLayout> diff --git a/packages/SystemUI/res/layout/quick_settings_tile_ime.xml b/packages/SystemUI/res/layout/quick_settings_tile_ime.xml deleted file mode 100644 index 1a31efa5..0000000 --- a/packages/SystemUI/res/layout/quick_settings_tile_ime.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<TextView - xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/TextAppearance.QuickSettings.TileView.AllInOne" - android:id="@+id/ime_textview" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:gravity="center" - android:drawableTop="@drawable/ic_qs_ime" - android:text="@string/quick_settings_ime_label" - /> diff --git a/packages/SystemUI/res/layout/quick_settings_tile_monitoring.xml b/packages/SystemUI/res/layout/quick_settings_tile_monitoring.xml deleted file mode 100644 index 4fa48eb..0000000 --- a/packages/SystemUI/res/layout/quick_settings_tile_monitoring.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2013 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="match_parent" - android:layout_height="match_parent" - android:layout_gravity="top" - android:orientation="vertical"> - <ImageView - android:id="@+id/image" - android:layout_marginTop="@dimen/qs_tile_margin_above_icon" - android:layout_marginBottom="@dimen/qs_cawarn_tile_margin_below_icon" - android:layout_width="@dimen/qs_tile_icon_size" - android:layout_height="@dimen/qs_tile_icon_size" - android:layout_gravity="top|center_horizontal" - android:scaleType="centerInside" - /> - <TextView - style="@style/TextAppearance.QuickSettings.CaCertWarning" - android:id="@+id/text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" - android:gravity="top|center_horizontal" - /> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_tile_rssi.xml b/packages/SystemUI/res/layout/quick_settings_tile_rssi.xml deleted file mode 100644 index 6bf31e0..0000000 --- a/packages/SystemUI/res/layout/quick_settings_tile_rssi.xml +++ /dev/null @@ -1,71 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<RelativeLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="top"> - <FrameLayout - android:id="@+id/rssi_images" - android:layout_marginTop="@dimen/qs_tile_margin_above_icon" - android:layout_marginBottom="@dimen/qs_tile_margin_below_icon" - android:layout_width="@dimen/qs_tile_icon_size" - android:layout_height="@dimen/qs_tile_icon_size" - android:layout_gravity="top|center_horizontal" - android:layout_centerHorizontal="true" - > - <ImageView - android:id="@+id/rssi_image" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerInParent="true" - /> - <ImageView - android:id="@+id/rssi_overlay_image" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerInParent="true" - /> - </FrameLayout> - <ImageView - android:id="@+id/activity_in" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_qs_signal_in" - android:layout_toRightOf="@id/rssi_images" - android:layout_alignBottom="@id/rssi_images" - /> - <ImageView - android:id="@+id/activity_out" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_qs_signal_out" - android:layout_toRightOf="@id/rssi_images" - android:layout_alignBottom="@id/rssi_images" - /> - <TextView - style="@style/TextAppearance.QuickSettings.TileView" - android:id="@+id/rssi_textview" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" - android:gravity="top|center_horizontal" - android:text="@string/quick_settings_rssi_label" - android:layout_centerHorizontal="true" - android:layout_below="@id/rssi_images" - android:textAllCaps="@bool/quick_settings_rssi_tile_capitalization" - /> -</RelativeLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_tile_user.xml b/packages/SystemUI/res/layout/quick_settings_tile_user.xml deleted file mode 100644 index 80fc685..0000000 --- a/packages/SystemUI/res/layout/quick_settings_tile_user.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <ImageView - android:id="@+id/user_imageview" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:src="@drawable/ic_qs_default_user" - android:scaleType="centerCrop" - /> - <TextView - style="@style/TextAppearance.QuickSettings.TileView.User" - android:id="@+id/user_textview" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal|bottom" - android:gravity="center" - android:text="@string/quick_settings_user_label" - /> -</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_tile_wifi.xml b/packages/SystemUI/res/layout/quick_settings_tile_wifi.xml deleted file mode 100644 index e61c595..0000000 --- a/packages/SystemUI/res/layout/quick_settings_tile_wifi.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2013 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. ---> -<RelativeLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="top"> - <ImageView - android:id="@+id/image" - android:layout_marginTop="@dimen/qs_tile_margin_above_icon" - android:layout_marginBottom="@dimen/qs_tile_margin_below_icon" - android:layout_width="@dimen/qs_tile_icon_size" - android:layout_height="@dimen/qs_tile_icon_size" - android:layout_gravity="top|center_horizontal" - android:layout_centerHorizontal="true" - android:scaleType="centerInside" - /> - <ImageView - android:id="@+id/activity_in" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_qs_wifi_in" - android:layout_toRightOf="@id/image" - android:layout_alignBottom="@id/image" - /> - <ImageView - android:id="@+id/activity_out" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_qs_wifi_out" - android:layout_toRightOf="@id/image" - android:layout_alignBottom="@id/image" - /> - <TextView - style="@style/TextAppearance.QuickSettings.TileView" - android:id="@+id/text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" - android:gravity="top|center_horizontal" - android:layout_centerHorizontal="true" - android:layout_below="@id/image" - /> -</RelativeLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index f045da4..2ec9935 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -67,7 +67,7 @@ android:layout_height="wrap_content" android:orientation="vertical"> <include - layout="@layout/flip_settings" + layout="@layout/qs_panel" android:layout_marginTop="@dimen/status_bar_header_height_expanded" android:layout_width="match_parent" android:layout_height="wrap_content"/> diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml index 0e84762..7671c35 100644 --- a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml +++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml @@ -29,6 +29,7 @@ android:layout_alignParentBottom="true" android:button="@null" android:background="@*android:drawable/switch_track_quantum" + android:visibility="gone" /> <com.android.systemui.settings.ToggleSeekBar android:id="@+id/slider" @@ -36,6 +37,7 @@ android:layout_height="wrap_content" android:layout_toEndOf="@id/toggle" android:layout_centerVertical="true" + android:layout_alignParentStart="true" android:layout_alignParentEnd="true" android:paddingStart="20dp" android:paddingEnd="20dp" @@ -51,5 +53,6 @@ android:paddingTop="26dp" android:textColor="#666666" android:textSize="12sp" + android:visibility="gone" /> </merge> diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml index 7223773..5755029 100644 --- a/packages/SystemUI/res/values-land/config.xml +++ b/packages/SystemUI/res/values-land/config.xml @@ -25,7 +25,7 @@ <integer name="status_bar_recents_bg_gradient_degrees">90</integer> <!-- The number of columns in the QuickSettings --> - <integer name="quick_settings_num_columns">6</integer> + <integer name="quick_settings_num_columns">4</integer> <!-- The maximum number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">2</integer> diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml index fe2224e..6dea81f 100644 --- a/packages/SystemUI/res/values-sw600dp/config.xml +++ b/packages/SystemUI/res/values-sw600dp/config.xml @@ -21,7 +21,7 @@ for different hardware and product builds. --> <resources> <!-- The number of columns in the QuickSettings --> - <integer name="quick_settings_num_columns">3</integer> + <integer name="quick_settings_num_columns">4</integer> <!-- The maximum number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">4</integer> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index c1a4e26..7de1bd0 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -36,6 +36,14 @@ <color name="batterymeter_charge_color">#FFFFFFFF</color> <color name="batterymeter_bolt_color">#FFFFFFFF</color> <color name="qs_batterymeter_frame_color">#FF404040</color> + <color name="system_primary_color">#ff263238</color> + <color name="system_secondary_color">#ff384248</color> + <color name="system_accent_color">#ff7fcac3</color> + <color name="system_error_color">#fff0592b</color> + <color name="quick_settings_tile_icon_enabled">#ffffffff</color> + <color name="quick_settings_tile_icon_disabled">#ffcccccc</color> + <color name="quick_settings_tile_divider">#ff888888</color> + <color name="quick_settings_tile_text">#FFFFFFFF</color> <color name="status_bar_clock_color">#FFFFFFFF</color> <drawable name="notification_item_background_color">#ff111111</drawable> <drawable name="notification_item_background_color_pressed">#ff454545</drawable> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ab34030..79612e0 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -271,6 +271,10 @@ <dimen name="quick_settings_tmp_scrim_stroke_width">8dp</dimen> <dimen name="quick_settings_tmp_scrim_text_size">30dp</dimen> + <dimen name="quick_settings_panel_padding">16dp</dimen> + <dimen name="quick_settings_tile_icon_outline">2dp</dimen> + <dimen name="quick_settings_tile_text_size">12sp</dimen> + <dimen name="quick_settings_tile_divider_height">1dp</dimen> <dimen name="notifications_top_padding">8dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 3d3cdf6..a50a0ac 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -503,9 +503,15 @@ <!-- QuickSettings: Brightness dialog auto brightness button [CHAR LIMIT=NONE] --> <string name="quick_settings_brightness_dialog_auto_brightness_label">AUTO</string> <!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] --> - <string name="quick_settings_inversion_label">Color inversion mode</string> + <string name="quick_settings_inversion_label">Invert colors</string> <!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] --> <string name="quick_settings_color_space_label">Color correction mode</string> + <!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] --> + <string name="quick_settings_more_settings">More settings</string> + <!-- QuickSettings: Tethering. [CHAR LIMIT=NONE] --> + <string name="quick_settings_tethering_label">Tethering</string> + <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] --> + <string name="quick_settings_hotspot_label">Hotspot</string> <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] --> <string name="recents_empty_message">RECENTS</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 4f52870..1273e74 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -180,4 +180,16 @@ <style name="StatusBarHeader"> <item name="android:layout_width">match_parent</item> </style> + + <style name="QSWhiteTheme" parent="@android:style/Theme.DeviceDefault"> + <item name="android:colorControlNormal">#ffffffff</item> + <item name="android:colorControlActivated">#ffffffff</item> + </style> + + <style name="QSAccentTheme" parent="@android:style/Theme.DeviceDefault"> + <item name="android:colorControlNormal">@color/system_accent_color</item> + <item name="android:colorControlActivated">@color/system_accent_color</item> + </style> + + <style name="QSBorderless" parent="@android:style/Widget.Quantum.Button.Borderless" /> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index f812e8c..e73e904 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -807,7 +807,6 @@ public class KeyguardViewMediator extends SystemUI { */ public void setOccluded(boolean isOccluded) { if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded); - mUpdateMonitor.sendKeyguardVisibilityChanged(!isOccluded); mHandler.removeMessages(SET_OCCLUDED); Message msg = mHandler.obtainMessage(SET_OCCLUDED, (isOccluded ? 1 : 0), 0); mHandler.sendMessage(msg); diff --git a/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java b/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java new file mode 100644 index 0000000..16ee3b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.view.View; + +/** Helper for view-level circular clip animations. **/ +public class CircularClipper { + + private final View mTarget; + + private ValueAnimator mAnimator; + + public CircularClipper(View target) { + mTarget = target; + } + + public void animateCircularClip(int x, int y, boolean in, AnimatorListener listener) { + if (mAnimator != null) { + mAnimator.cancel(); + } + final int w = mTarget.getWidth() - x; + final int h = mTarget.getHeight() - y; + int r = (int) Math.ceil(Math.sqrt(x * x + y * y)); + r = (int) Math.max(r, Math.ceil(Math.sqrt(w * w + y * y))); + r = (int) Math.max(r, Math.ceil(Math.sqrt(w * w + h * h))); + r = (int) Math.max(r, Math.ceil(Math.sqrt(x * x + h * h))); + + mAnimator = mTarget.createRevealAnimator(x, y, 0, r); + mAnimator.removeAllListeners(); + if (listener != null) { + mAnimator.addListener(listener); + } + if (in) { + mAnimator.addListener(mVisibleOnStart); + mAnimator.start(); + } else { + mAnimator.addListener(mGoneOnEnd); + mAnimator.reverse(); + } + } + + private final AnimatorListenerAdapter mVisibleOnStart = new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mTarget.setVisibility(View.VISIBLE); + } + }; + + private final AnimatorListenerAdapter mGoneOnEnd = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mTarget.setVisibility(View.GONE); + }; + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java b/packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java new file mode 100644 index 0000000..05c8ee3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; + +/** Canvas that forwards calls to another canvas. Can be subclassed to transform drawing calls. + * Temporary solution to runtime modification of a single drawable shape into two + * enabled & disabled versions. See QSImageView. **/ +public class FilterCanvas extends Canvas { + private final Canvas mCanvas; + + public FilterCanvas(Canvas c) { + mCanvas = c; + } + + @Override + public void drawPath(Path path, Paint paint) { + mCanvas.drawPath(path, paint); + } + + @Override + public int getSaveCount() { + return mCanvas.getSaveCount(); + } + + @Override + public int save() { + return mCanvas.save(); + } + + @Override + public void translate(float dx, float dy) { + mCanvas.translate(dx, dy); + } + + @Override + public boolean clipRect(int left, int top, int right, int bottom) { + return mCanvas.clipRect(left, top, right, bottom); + } + + @Override + public boolean clipRect(Rect rect) { + return mCanvas.clipRect(rect); + } + + @Override + public void concat(Matrix matrix) { + mCanvas.concat(matrix); + } + + @Override + public void restoreToCount(int saveCount) { + mCanvas.restoreToCount(saveCount); + } + + @Override + public void drawRect(Rect r, Paint paint) { + mCanvas.drawRect(r, paint); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java b/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java new file mode 100644 index 0000000..1e15b9f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.provider.Settings.Global; + +import com.android.systemui.statusbar.policy.Disposable; + +/** Helper for managing a global setting. **/ +public abstract class GlobalSetting extends ContentObserver implements Disposable { + private final Context mContext; + private final String mSettingName; + + protected abstract void handleValueChanged(int value); + + public GlobalSetting(Context context, Handler handler, String settingName) { + super(handler); + mContext = context; + mSettingName = settingName; + mContext.getContentResolver().registerContentObserver( + Global.getUriFor(mSettingName), false, this); + } + + public int getValue() { + return Global.getInt(mContext.getContentResolver(), mSettingName, 0); + } + + public void setValue(int value) { + Global.putInt(mContext.getContentResolver(), mSettingName, value); + } + + @Override + public void dispose() { + mContext.getContentResolver().unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + handleValueChanged(getValue()); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImageView.java b/packages/SystemUI/src/com/android/systemui/qs/QSImageView.java new file mode 100644 index 0000000..ed67560 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSImageView.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Path; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; +import android.util.AttributeSet; +import android.widget.ImageView; + +import com.android.systemui.R; + +/** ImageView that performs runtime modification of vector drawables (using FilterCanvas). **/ +public class QSImageView extends ImageView { + + private final int mOutlineWidth; + private final int mColorEnabled; + private final int mColorDisabled; + private FilterCanvas mFilterCanvas; + private Canvas mCanvas; + private boolean mEnabledVersion = true; + private boolean mFilter; + + public QSImageView(Context context) { + this(context, null); + } + + public QSImageView(Context context, AttributeSet attrs) { + super(context, attrs); + final Resources res = context.getResources(); + mOutlineWidth = res.getDimensionPixelSize(R.dimen.quick_settings_tile_icon_outline); + mColorEnabled = res.getColor(R.color.quick_settings_tile_icon_enabled); + mColorDisabled = res.getColor(R.color.quick_settings_tile_icon_disabled); + } + + public void setEnabledVersion(boolean enabledVersion) { + mEnabledVersion = enabledVersion; + invalidate(); + } + + @Override + public void setImageDrawable(Drawable drawable) { + mFilter = drawable instanceof VectorDrawable; + super.setImageDrawable(drawable); + } + + @Override + public void setImageResource(int resId) { + setImageDrawable(mContext.getDrawable(resId)); + } + + @Override + public void draw(Canvas canvas) { + if (mFilter) { + if (canvas != mCanvas) { + mCanvas = canvas; + mFilterCanvas = new QSFilterCanvas(canvas); + } + super.draw(mFilterCanvas); + } else { + super.draw(canvas); + } + } + + private class QSFilterCanvas extends FilterCanvas { + public QSFilterCanvas(Canvas c) { + super(c); + } + + @Override + public void drawPath(Path path, Paint paint) { + if (mEnabledVersion) { + paint.setColor(mColorEnabled); + } else { + paint.setStyle(Style.STROKE); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setColor(mColorDisabled); + paint.setStrokeWidth(mOutlineWidth); + } + super.drawPath(path, paint); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java new file mode 100644 index 0000000..afb5483 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorListenerAdapter; +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.android.systemui.R; + +import java.util.ArrayList; + +/** View that represents the quick settings tile panel. **/ +public class QSPanel extends ViewGroup { + private static final float TILE_ASPECT = 1.4f; + private static final float LARGE_TILE_FACTOR = 1.1f; + + private final Context mContext; + private final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>(); + private final FrameLayout mDetail; + private final CircularClipper mClipper; + private final H mHandler = new H(); + + private int mColumns; + private int mCellWidth; + private int mCellHeight; + private int mLargeCellWidth; + private int mLargeCellHeight; + + private TileRecord mDetailRecord; + + public QSPanel(Context context) { + this(context, null); + } + + public QSPanel(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + + mDetail = new FrameLayout(mContext); + mDetail.setVisibility(GONE); + mDetail.setClickable(true); + addView(mDetail); + mClipper = new CircularClipper(mDetail); + updateResources(); + } + + public void updateResources() { + final int columns = Math.max(1, + mContext.getResources().getInteger(R.integer.quick_settings_num_columns)); + if (mColumns != columns) { + mColumns = columns; + postInvalidate(); + } + } + + public void setExpanded(boolean expanded) { + if (!expanded) { + showDetail(false /*show*/, mDetailRecord); + } + for (TileRecord r : mRecords) { + r.tile.setShown(expanded); + } + } + + private void showDetail(boolean show, TileRecord r) { + mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget(); + } + + private void setTileVisibility(View v, boolean visible) { + mHandler.obtainMessage(H.SET_TILE_VISIBILITY, visible ? 1 : 0, 0, v).sendToTarget(); + } + + private void handleSetTileVisibility(View v, boolean visible) { + v.setVisibility(visible ? VISIBLE : GONE); + } + + public void addTile(final QSTile<?> tile) { + final TileRecord r = new TileRecord(); + r.tile = tile; + r.tileView = tile.createTileView(mContext); + r.tileView.setVisibility(View.GONE); + r.tile.setCallback(new QSTile.Callback() { + @Override + public void onStateChanged(QSTile.State state) { + setTileVisibility(r.tileView, state.visible); + r.tileView.onStateChanged(state); + } + @Override + public void onShowDetail(boolean show) { + QSPanel.this.showDetail(show, r); + } + }); + final View.OnClickListener click = new View.OnClickListener() { + @Override + public void onClick(View v) { + r.tile.click(); + } + }; + final View.OnClickListener clickSecondary = new View.OnClickListener() { + @Override + public void onClick(View v) { + r.tile.secondaryClick(); + } + }; + r.tileView.init(click, clickSecondary); + mRecords.add(r); + + addView(r.tileView); + } + + private void handleShowDetail(TileRecord r, boolean show) { + AnimatorListener listener = null; + if (show) { + if (mDetailRecord != null) return; + final View detail = r.tile.createDetailView(mContext, mDetail); + if (detail == null) return; + mDetailRecord = r; + mDetail.removeAllViews(); + mDetail.bringToFront(); + mDetail.addView(detail); + } else { + if (mDetailRecord == null) return; + listener = mTeardownDetailWhenDone; + } + int x = r.tileView.getLeft() + r.tileView.getWidth() / 2; + int y = r.tileView.getTop() + r.tileView.getHeight() / 2; + mClipper.animateCircularClip(x, y, show, listener); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int width = MeasureSpec.getSize(widthMeasureSpec); + mCellWidth = width / mColumns; + mCellHeight = (int)(mCellWidth / TILE_ASPECT); + mLargeCellWidth = (int)(mCellWidth * LARGE_TILE_FACTOR); + mLargeCellHeight = (int)(mCellHeight * LARGE_TILE_FACTOR); + int r = 0; + int c = 0; + int rows = 0; + for (TileRecord record : mRecords) { + if (record.tileView.getVisibility() == GONE) continue; + record.row = r; + record.col = c; + rows = r + 1; + c++; + if (c == mColumns /*end of normal column*/ || r == 0 && c == 2 /*end of 1st column*/) { + c = 0; + r++; + } + } + + for (TileRecord record : mRecords) { + if (record.tileView.getVisibility() == GONE) continue; + record.tileView.setDual(record.row == 0); + final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth; + final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight; + record.tileView.measure(exactly(cw), exactly(ch)); + } + final int actualHeight = rows == 0 ? 0 : getRowTop(rows); + mDetail.measure(exactly(width), exactly(actualHeight)); + setMeasuredDimension(width, actualHeight); + } + + private static int exactly(int size) { + return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int w = mCellWidth * mColumns; + for (TileRecord record : mRecords) { + if (record.tileView.getVisibility() == GONE) continue; + final int cols = getColumnCount(record.row); + final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth; + final int extra = (w - cw * cols) / (cols + 1); + final int left = record.col * cw + (record.col + 1) * extra; + final int top = getRowTop(record.row); + record.tileView.layout(left, top, + left + record.tileView.getMeasuredWidth(), + top + record.tileView.getMeasuredHeight()); + } + mDetail.layout(0, 0, mDetail.getMeasuredWidth(), mDetail.getMeasuredHeight()); + } + + private int getRowTop(int row) { + if (row <= 0) return 0; + return mLargeCellHeight + (row - 1) * mCellHeight; + } + + private int getColumnCount(int row) { + int cols = 0; + for (TileRecord record : mRecords) { + if (record.tileView.getVisibility() == GONE) continue; + if (record.row == row) cols++; + } + return cols; + } + + private class H extends Handler { + private static final int SHOW_DETAIL = 1; + private static final int SET_TILE_VISIBILITY = 2; + @Override + public void handleMessage(Message msg) { + if (msg.what == SHOW_DETAIL) { + handleShowDetail((TileRecord)msg.obj, msg.arg1 != 0); + } else if (msg.what == SET_TILE_VISIBILITY) { + handleSetTileVisibility((View)msg.obj, msg.arg1 != 0); + } + } + } + + private static final class TileRecord { + QSTile<?> tile; + QSTileView tileView; + int row; + int col; + } + + private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + mDetail.removeAllViews(); + mDetailRecord = null; + }; + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java new file mode 100644 index 0000000..05f308d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import com.android.systemui.qs.QSTile.State; +import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.Disposable; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.TetheringController; +import com.android.systemui.statusbar.policy.ZenModeController; + +import java.util.List; +import java.util.Objects; + +/** + * Base quick-settings tile, extend this to create a new tile. + * + * State management done on a looper provided by the host. Tiles should update state in + * handleUpdateState. Callbacks affecting state should use refreshState to trigger another + * state update pass on tile looper. + */ +public abstract class QSTile<TState extends State> implements Disposable { + private final String TAG = "QSTile." + getClass().getSimpleName(); + + protected final Host mHost; + protected final Context mContext; + protected final H mHandler; + protected final Handler mUiHandler = new Handler(Looper.getMainLooper()); + + private Callback mCallback; + protected final TState mState = newTileState(); + private final TState mTmpState = newTileState(); + + abstract protected TState newTileState(); + abstract protected void handleClick(); + abstract protected void handleUpdateState(TState state, Object arg); + + protected QSTile(Host host) { + mHost = host; + mContext = host.getContext(); + mHandler = new H(host.getLooper()); + } + + public Host getHost() { + return mHost; + } + + public QSTileView createTileView(Context context) { + return new QSTileView(context); + } + + public View createDetailView(Context context, ViewGroup root) { + return null; // optional + } + + // safe to call from any thread + + public void setCallback(Callback callback) { + mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget(); + } + + public void click() { + mHandler.sendEmptyMessage(H.CLICK); + } + + public void secondaryClick() { + mHandler.sendEmptyMessage(H.SECONDARY_CLICK); + } + + public void showDetail(boolean show) { + mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget(); + } + + protected final void refreshState() { + refreshState(null); + } + + protected final void refreshState(Object arg) { + mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget(); + } + + public void userSwitch(int newUserId) { + mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget(); + } + + public void setShown(boolean shown) { + mHandler.obtainMessage(H.SHOWN, shown ? 1 : 0, 0).sendToTarget(); + } + + // call only on tile worker looper + + private void handleSetCallback(Callback callback) { + mCallback = callback; + handleRefreshState(null); + } + + protected void handleSecondaryClick() { + // optional + } + + protected void handleShown(boolean shown) { + // optional, discouraged + } + + protected void handleRefreshState(Object arg) { + handleUpdateState(mTmpState, arg); + final boolean changed = mTmpState.copyTo(mState); + if (changed) { + handleStateChanged(); + } + } + + private void handleStateChanged() { + if (mCallback != null) { + mCallback.onStateChanged(mState); + } + } + + private void handleShowDetail(boolean show) { + if (mCallback != null) { + mCallback.onShowDetail(show); + } + } + + protected void handleUserSwitch(int newUserId) { + handleRefreshState(null); + } + + protected final class H extends Handler { + private static final int SET_CALLBACK = 1; + private static final int CLICK = 2; + private static final int SECONDARY_CLICK = 3; + private static final int REFRESH_STATE = 4; + private static final int SHOW_DETAIL = 5; + private static final int USER_SWITCH = 6; + private static final int SHOWN = 7; + + private H(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + String name = null; + try { + if (msg.what == SET_CALLBACK) { + name = "handleSetCallback"; + handleSetCallback((QSTile.Callback)msg.obj); + } else if (msg.what == CLICK) { + name = "handleClick"; + handleClick(); + } else if (msg.what == SECONDARY_CLICK) { + name = "handleSecondaryClick"; + handleSecondaryClick(); + } else if (msg.what == REFRESH_STATE) { + name = "handleRefreshState"; + handleRefreshState(msg.obj); + } else if (msg.what == SHOW_DETAIL) { + name = "handleShowDetail"; + handleShowDetail(msg.arg1 != 0); + } else if (msg.what == USER_SWITCH) { + name = "handleUserSwitch"; + handleUserSwitch(msg.arg1); + } else if (msg.what == SHOWN) { + name = "handleShown"; + handleShown(msg.arg1 != 0); + } + } catch (Throwable t) { + final String error = "Error in " + name; + Log.w(TAG, error, t); + mHost.warn(error, t); + } + } + } + + public interface Callback { + void onStateChanged(State state); + void onShowDetail(boolean show); + } + + public interface Host { + void startSettingsActivity(Intent intent); + void warn(String message, Throwable t); + void collapsePanels(); + Looper getLooper(); + Context getContext(); + VectorDrawable getVectorDrawable(int resId); + BluetoothController getBluetoothController(); + LocationController getLocationController(); + RotationLockController getRotationLockController(); + List<QSTile<?>> getTiles(); + NetworkController getNetworkController(); + ZenModeController getZenModeController(); + TetheringController getTetheringController(); + CastController getCastController(); + } + + public static class State { + public boolean visible; + public int iconId; + public Drawable icon; + public String label; + public String contentDescription; + + public boolean copyTo(State other) { + if (other == null) throw new IllegalArgumentException(); + if (!other.getClass().equals(getClass())) throw new IllegalArgumentException(); + final boolean changed = other.visible != visible + || other.iconId != iconId + || !Objects.equals(other.icon, icon) + || !Objects.equals(other.label, label) + || !Objects.equals(other.contentDescription, contentDescription); + other.visible = visible; + other.iconId = iconId; + other.icon = icon; + other.label = label; + other.contentDescription = contentDescription; + return changed; + } + + @Override + public String toString() { + return toStringBuilder().toString(); + } + + protected StringBuilder toStringBuilder() { + final StringBuilder sb = new StringBuilder( getClass().getSimpleName()).append('['); + sb.append("visible=").append(visible); + sb.append(",iconId=").append(iconId); + sb.append(",icon=").append(icon); + sb.append(",label=").append(label); + sb.append(",contentDescription=").append(contentDescription); + return sb.append(']'); + } + } + + public static class BooleanState extends State { + public boolean value; + + @Override + public boolean copyTo(State other) { + final BooleanState o = (BooleanState) other; + final boolean changed = super.copyTo(other) || o.value != value; + o.value = value; + return changed; + } + + @Override + protected StringBuilder toStringBuilder() { + final StringBuilder rt = super.toStringBuilder(); + rt.insert(rt.length() - 1, ",value=" + value); + return rt; + } + } + + public static final class SignalState extends State { + public boolean enabled; + public boolean connected; + public boolean activityIn; + public boolean activityOut; + public int overlayIconId; + + @Override + public boolean copyTo(State other) { + final SignalState o = (SignalState) other; + final boolean changed = o.enabled != enabled + || o.connected != connected || o.activityIn != activityIn + || o.activityOut != activityOut + || o.overlayIconId != overlayIconId; + o.enabled = enabled; + o.connected = connected; + o.activityIn = activityIn; + o.activityOut = activityOut; + o.overlayIconId = overlayIconId; + return super.copyTo(other) || changed; + } + + @Override + protected StringBuilder toStringBuilder() { + final StringBuilder rt = super.toStringBuilder(); + rt.insert(rt.length() - 1, ",enabled=" + enabled); + rt.insert(rt.length() - 1, ",connected=" + connected); + rt.insert(rt.length() - 1, ",activityIn=" + activityIn); + rt.insert(rt.length() - 1, ",activityOut=" + activityOut); + rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId); + return rt; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java new file mode 100644 index 0000000..17a95fb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView.ScaleType; +import android.widget.TextView; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile.State; + +/** View that represents a standard quick settings tile. **/ +public class QSTileView extends ViewGroup { + private static final Typeface CONDENSED = Typeface.create("sans-serif-condensed", + Typeface.NORMAL); + private static final int VERTICAL_PADDING_FACTOR = 8; // internal padding 1/8 the cell height + + protected final Context mContext; + private final View mIcon; + private final View mDivider; + private final TextView mLabel; + private final H mHandler = new H(); + + private boolean mDual; + private OnClickListener mClickPrimary; + private OnClickListener mClickSecondary; + + public QSTileView(Context context) { + super(context); + + mContext = context; + final Resources res = context.getResources(); + mLabel = new TextView(mContext); + mLabel.setId(android.R.id.title); + mLabel.setTextColor(res.getColor(R.color.quick_settings_tile_text)); + mLabel.setGravity(Gravity.CENTER); + mLabel.setTypeface(CONDENSED); + mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, + res.getDimensionPixelSize(R.dimen.quick_settings_tile_text_size)); + addView(mLabel); + setClipChildren(false); + + mIcon = createIcon(); + addView(mIcon); + + mDivider = new View(mContext); + mDivider.setBackgroundColor(res.getColor(R.color.quick_settings_tile_divider)); + final int dh = res.getDimensionPixelSize(R.dimen.quick_settings_tile_divider_height); + mDivider.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, dh)); + addView(mDivider); + + setClickable(true); + setBackground(getSelectableBackground()); + } + + public void setDual(boolean dual) { + mDual = dual; + if (mDual) { + setOnClickListener(mClickPrimary); + mLabel.setClickable(true); + mLabel.setOnClickListener(mClickSecondary); + } else { + mLabel.setClickable(false); + setOnClickListener(mClickPrimary); + } + mDivider.setVisibility(dual ? VISIBLE : GONE); + postInvalidate(); + } + + public void init(OnClickListener clickPrimary, OnClickListener clickSecondary) { + mClickPrimary = clickPrimary; + mClickSecondary = clickSecondary; + } + + protected View createIcon() { + QSImageView icon = new QSImageView(mContext); + icon.setId(android.R.id.icon); + icon.setScaleType(ScaleType.CENTER_INSIDE); + return icon; + } + + private Drawable getSelectableBackground() { + final int[] attrs = new int[] { android.R.attr.selectableItemBackground}; + final TypedArray ta = mContext.obtainStyledAttributes(attrs); + final Drawable d = ta.getDrawable(0); + ta.recycle(); + return d; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int w = MeasureSpec.getSize(widthMeasureSpec); + final int h = MeasureSpec.getSize(heightMeasureSpec); + final int p = h / VERTICAL_PADDING_FACTOR; + final int iconSpec = exactly((int)mLabel.getTextSize() * 2); + mIcon.measure(iconSpec, iconSpec); + mLabel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST)); + mLabel.measure(widthMeasureSpec, exactly(mLabel.getMeasuredHeight() + p * 2)); + if (mDual) { + mDivider.measure(widthMeasureSpec, exactly(mDivider.getLayoutParams().height)); + } + setMeasuredDimension(w, h); + } + + private static int exactly(int size) { + return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int w = getMeasuredWidth(); + final int h = getMeasuredHeight(); + final int p = h / VERTICAL_PADDING_FACTOR; + final int contentHeight = p + mIcon.getMeasuredHeight() + mLabel.getMeasuredHeight() + + (mDual ? (p + mDivider.getMeasuredHeight()) : 0); + + int top = (h - contentHeight) / 2 + p; + final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2; + layout(mIcon, iconLeft, top); + top = mIcon.getBottom(); + if (mDual) { + top += p; + layout(mDivider, 0, top); + top = mDivider.getBottom(); + } + layout(mLabel, 0, top); + } + + private static void layout(View child, int left, int top) { + child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); + } + + protected void handleStateChanged(QSTile.State state) { + if (mIcon instanceof QSImageView) { + QSImageView qsiv = (QSImageView) mIcon; + if (state.icon != null) { + qsiv.setImageDrawable(state.icon); + } else if (state.iconId > 0) { + qsiv.setImageResource(state.iconId); + } + if (state.icon != null && state instanceof QSTile.BooleanState) { + qsiv.setEnabledVersion(((QSTile.BooleanState)state).value); + } + } + mLabel.setText(state.label); + setContentDescription(state.contentDescription); + } + + public void onStateChanged(QSTile.State state) { + mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget(); + } + + private class H extends Handler { + private static final int STATE_CHANGED = 1; + public H() { + super(Looper.getMainLooper()); + } + @Override + public void handleMessage(Message msg) { + if (msg.what == STATE_CHANGED) { + handleStateChanged((State) msg.obj); + } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java new file mode 100644 index 0000000..4debaa9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.provider.Settings.Secure; + +import com.android.systemui.statusbar.policy.Disposable; + +/** Helper for managing a secure setting. **/ +public abstract class SecureSetting extends ContentObserver implements Disposable { + private final Context mContext; + private final String mSettingName; + + protected abstract void handleValueChanged(int value); + + public SecureSetting(Context context, Handler handler, String settingName) { + super(handler); + mContext = context; + mSettingName = settingName; + rebindForCurrentUser(); + } + + public void rebindForCurrentUser() { + mContext.getContentResolver().registerContentObserver( + Secure.getUriFor(mSettingName), false, this); + } + + public int getValue() { + return Secure.getInt(mContext.getContentResolver(), mSettingName, 0); + } + + public void setValue(int value) { + Secure.putInt(mContext.getContentResolver(), mSettingName, value); + } + + @Override + public void dispose() { + mContext.getContentResolver().unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + handleValueChanged(getValue()); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java new file mode 100644 index 0000000..7b6c544 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile.SignalState; + +/** View that represents a custom quick settings tile for displaying signal info (wifi/cell). **/ +public final class SignalTileView extends QSTileView { + private static final long DEFAULT_DURATION = new ValueAnimator().getDuration(); + private static final long SHORT_DURATION = DEFAULT_DURATION / 3; + + private FrameLayout mIconFrame; + private ImageView mSignal; + private ImageView mOverlay; + private ImageView mIn; + private ImageView mOut; + + public SignalTileView(Context context) { + super(context); + + mIn = new ImageView(context); + mIn.setImageResource(R.drawable.ic_qs_signal_in); + addView(mIn); + + mOut = new ImageView(context); + mOut.setImageResource(R.drawable.ic_qs_signal_out); + addView(mOut); + } + + @Override + protected View createIcon() { + mIconFrame = new FrameLayout(mContext); + mSignal = new ImageView(mContext); + mIconFrame.addView(mSignal); + mOverlay = new ImageView(mContext); + mIconFrame.addView(mOverlay); + return mIconFrame; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int hs = MeasureSpec.makeMeasureSpec(mIconFrame.getMeasuredHeight(), MeasureSpec.EXACTLY); + int ws = MeasureSpec.makeMeasureSpec(mIconFrame.getMeasuredHeight(), MeasureSpec.AT_MOST); + mIn.measure(ws, hs); + mOut.measure(ws, hs); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + layoutIndicator(mIn); + layoutIndicator(mOut); + } + + private void layoutIndicator(View indicator) { + indicator.layout( + mIconFrame.getRight(), + mIconFrame.getBottom() - indicator.getMeasuredHeight(), + mIconFrame.getRight() + indicator.getMeasuredWidth(), + mIconFrame.getBottom()); + } + + @Override + protected void handleStateChanged(QSTile.State state) { + super.handleStateChanged(state); + final SignalState s = (SignalState) state; + mSignal.setImageDrawable(null); // force refresh + mSignal.setImageResource(s.iconId); + if (s.overlayIconId > 0) { + mOverlay.setVisibility(VISIBLE); + mOverlay.setImageDrawable(null); // force refresh + mOverlay.setImageResource(s.overlayIconId); + } else { + mOverlay.setVisibility(GONE); + } + setVisibility(mIn, s.activityIn); + setVisibility(mOut, s.activityOut); + } + + private void setVisibility(View view, boolean visible) { + final float newAlpha = visible ? 1 : 0; + if (view.getAlpha() != newAlpha) { + view.animate() + .setDuration(visible ? SHORT_DURATION : DEFAULT_DURATION) + .alpha(newAlpha) + .withLayer() + .start(); + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java new file mode 100644 index 0000000..5fe8422 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.provider.Settings.Global; + +import com.android.systemui.R; +import com.android.systemui.qs.GlobalSetting; +import com.android.systemui.qs.QSTile; + +/** Quick settings tile: Airplane mode **/ +public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { + private final GlobalSetting mSetting; + + public AirplaneModeTile(Host host) { + super(host); + + mSetting = new GlobalSetting(mContext, mHandler, Global.AIRPLANE_MODE_ON) { + @Override + protected void handleValueChanged(int value) { + handleRefreshState(value); + } + }; + + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); + mContext.registerReceiver(mReceiver, filter); + refreshState(); + } + + @Override + protected BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void handleClick() { + setEnabled(!mState.value); + } + + private void setEnabled(boolean enabled) { + mSetting.setValue(enabled ? 1 : 0); + final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + intent.putExtra("state", enabled); + mContext.sendBroadcast(intent); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + final int value = arg instanceof Integer ? (Integer)arg : mSetting.getValue(); + final boolean airplaneMode = value != 0; + state.value = airplaneMode; + state.visible = true; + state.label = mContext.getString(R.string.quick_settings_airplane_mode_label); + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_airplane); + if (airplaneMode) { + state.iconId = R.drawable.ic_qs_airplane_on; + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_airplane, + mContext.getString(R.string.accessibility_desc_on)); + } else { + state.iconId = R.drawable.ic_qs_airplane_off; + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_airplane, + mContext.getString(R.string.accessibility_desc_off)); + } + } + + public void dispose() { + mSetting.dispose(); + mContext.unregisterReceiver(mReceiver); + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) { + refreshState(); + } + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java new file mode 100644 index 0000000..60a6047 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback; +import android.content.Intent; +import android.provider.Settings; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.BluetoothController; + +/** Quick settings tile: Bluetooth **/ +public class BluetoothTile extends QSTile<QSTile.BooleanState> { + private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS); + + private final BluetoothController mController; + + public BluetoothTile(Host host) { + super(host); + mController = host.getBluetoothController(); + mController.addStateChangedCallback(mCallback); + } + + @Override + protected BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void dispose() { + mController.removeStateChangedCallback(mCallback); + } + + @Override + protected void handleClick() { + final boolean isEnabled = (Boolean)mState.value; + mController.setBluetoothEnabled(!isEnabled); + } + + @Override + protected void handleSecondaryClick() { + mHost.startSettingsActivity(BLUETOOTH_SETTINGS); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + final boolean supported = mController.isBluetoothSupported(); + final boolean enabled = mController.isBluetoothEnabled(); + final boolean connected = mController.isBluetoothConnected(); + state.visible = supported; + state.value = enabled; + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_bluetooth); + final String stateContentDescription; + if (enabled) { + if (connected) { + state.iconId = R.drawable.ic_qs_bluetooth_on; + stateContentDescription = mContext.getString(R.string.accessibility_desc_connected); + } else { + state.iconId = R.drawable.ic_qs_bluetooth_not_connected; + stateContentDescription = mContext.getString(R.string.accessibility_desc_on); + } + state.label = mContext.getString(R.string.quick_settings_bluetooth_label); + } else { + state.iconId = R.drawable.ic_qs_bluetooth_off; + state.label = mContext.getString(R.string.quick_settings_bluetooth_off_label); + stateContentDescription = mContext.getString(R.string.accessibility_desc_off); + } + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_bluetooth, stateContentDescription); + } + + private final BluetoothStateChangeCallback mCallback = new BluetoothStateChangeCallback() { + @Override + public void onBluetoothStateChange(boolean on) { + refreshState(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java new file mode 100644 index 0000000..0e9b9a7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.app.ActivityManagerNative; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.RemoteException; +import android.provider.Settings.Global; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import com.android.systemui.R; +import com.android.systemui.qs.GlobalSetting; +import com.android.systemui.qs.QSTile; + +/** Quick settings tile: Bug report **/ +public class BugreportTile extends QSTile<QSTile.State> { + + private final GlobalSetting mSetting; + + public BugreportTile(Host host) { + super(host); + mSetting = new GlobalSetting(mContext, mHandler, Global.BUGREPORT_IN_POWER_MENU) { + @Override + protected void handleValueChanged(int value) { + handleRefreshState(null); + } + }; + } + + @Override + protected State newTileState() { + return new State(); + } + + @Override + public void dispose() { + mSetting.dispose(); + } + + @Override + protected void handleClick() { + mHost.collapsePanels(); + mUiHandler.post(mShowDialog); + } + + @Override + protected void handleUpdateState(State state, Object pushArg) { + state.visible = mSetting.getValue() != 0; + state.iconId = com.android.internal.R.drawable.stat_sys_adb; + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_bugreport); + state.label = mContext.getString(com.android.internal.R.string.bugreport_title); + } + + private final Runnable mShowDialog = new Runnable() { + @Override + public void run() { + final AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setPositiveButton(com.android.internal.R.string.report, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + // Add a little delay before executing, to give the + // dialog a chance to go away before it takes a + // screenshot. + mHandler.postDelayed(new Runnable() { + @Override public void run() { + try { + ActivityManagerNative.getDefault().requestBugReport(); + } catch (RemoteException e) { + } + } + }, 500); + } + } + }); + builder.setMessage(com.android.internal.R.string.bugreport_message); + builder.setTitle(com.android.internal.R.string.bugreport_title); + builder.setCancelable(true); + final Dialog dialog = builder.create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + try { + WindowManagerGlobal.getWindowManagerService().dismissKeyguard(); + } catch (RemoteException e) { + } + dialog.show(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java new file mode 100644 index 0000000..a3eaa2c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.app.Dialog; +import android.content.Intent; +import android.media.MediaRouter; +import android.provider.Settings; +import android.view.View; +import android.view.WindowManager; + +import com.android.internal.app.MediaRouteDialogPresenter; +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.CastController; + +/** Quick settings tile: Cast **/ +public class CastTile extends QSTile<QSTile.BooleanState> { + private static final Intent WIFI_DISPLAY_SETTINGS = + new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS); + + private final CastController mController; + + private boolean mShown; + + public CastTile(Host host) { + super(host); + mController = host.getCastController(); + if (mController != null) { + mController.addCallback(mCallback); + } + } + + @Override + protected BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void dispose() { + if (mController == null) return; + mController.removeCallback(mCallback); + } + + @Override + protected void handleUserSwitch(int newUserId) { + super.handleUserSwitch(newUserId); + if (mController == null) return; + mController.setCurrentUserId(newUserId); + } + + @Override + protected void handleShown(boolean shown) { + if (mShown == shown) return; + if (mController == null) return; + mShown = shown; + mController.setDiscovering(mShown); + } + + @Override + protected void handleClick() { + mHost.collapsePanels(); + mUiHandler.post(mShowDialog); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + state.visible = true; + state.label = mContext + .getString(R.string.quick_settings_remote_display_no_connection_label); + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_cast); + if (arg instanceof CallbackInfo) { + final CallbackInfo cb = (CallbackInfo) arg; + if (cb.connectedRouteName != null) { + state.value = !cb.connecting; + } + } + } + + private static class CallbackInfo { + boolean enabled; + boolean connecting; + String connectedRouteName; + } + + private final CastController.Callback mCallback = new CastController.Callback() { + @Override + public void onStateChanged(boolean enabled, boolean connecting, + String connectedRouteName) { + final CallbackInfo info = new CallbackInfo(); // TODO pool + info.enabled = enabled; + info.connecting = connecting; + info.connectedRouteName = connectedRouteName; + refreshState(info); + } + }; + + private final Runnable mShowDialog = new Runnable() { + private Dialog mDialog; + @Override + public void run() { + mDialog = MediaRouteDialogPresenter.createDialog(mContext, + MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, + new View.OnClickListener() { + @Override + public void onClick(View v) { + mDialog.dismiss(); + mHost.startSettingsActivity(WIFI_DISPLAY_SETTINGS); + } + }); + mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); + mDialog.show(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java new file mode 100644 index 0000000..86a4e79 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.qs.QSTileView; +import com.android.systemui.qs.SignalTileView; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; + +/** Quick settings tile: Cellular **/ +public class CellularTile extends QSTile<QSTile.SignalState> { + private static final Intent CELLULAR_SETTINGS = new Intent().setComponent(new ComponentName( + "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity")); + + private final NetworkController mController; + + public CellularTile(Host host) { + super(host); + mController = host.getNetworkController(); + mController.addNetworkSignalChangedCallback(mCallback); + } + + @Override + protected SignalState newTileState() { + return new SignalState(); + } + + @Override + public void dispose() { + mController.removeNetworkSignalChangedCallback(mCallback); + } + + @Override + public QSTileView createTileView(Context context) { + return new SignalTileView(context); + } + + @Override + protected void handleClick() { + mHost.startSettingsActivity(CELLULAR_SETTINGS); + } + + @Override + protected void handleUpdateState(SignalState state, Object arg) { + state.visible = mController.hasMobileDataFeature(); + if (!state.visible) return; + final CallbackInfo cb = (CallbackInfo) arg; + if (cb == null) return; + + final Resources r = mContext.getResources(); + state.iconId = cb.enabled && (cb.mobileSignalIconId > 0) + ? cb.mobileSignalIconId + : R.drawable.ic_qs_signal_no_signal; + state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled + ? cb.dataTypeIconId + : 0; + state.activityIn = cb.enabled && cb.activityIn; + state.activityOut = cb.enabled && cb.activityOut; + + state.label = cb.enabled + ? removeTrailingPeriod(cb.enabledDesc) + : r.getString(R.string.quick_settings_rssi_emergency_only); + + final String signalContentDesc = cb.enabled && (cb.mobileSignalIconId > 0) + ? cb.signalContentDescription + : r.getString(R.string.accessibility_no_signal); + final String dataContentDesc = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled + ? cb.dataContentDescription + : r.getString(R.string.accessibility_no_data); + state.contentDescription = r.getString( + R.string.accessibility_quick_settings_mobile, + signalContentDesc, dataContentDesc, + state.label); + } + + // Remove the period from the network name + public static String removeTrailingPeriod(String string) { + if (string == null) return null; + final int length = string.length(); + if (string.endsWith(".")) { + return string.substring(0, length - 1); + } + return string; + } + + private static final class CallbackInfo { + boolean enabled; + boolean wifiEnabled; + int mobileSignalIconId; + String signalContentDescription; + int dataTypeIconId; + String dataContentDescription; + boolean activityIn; + boolean activityOut; + String enabledDesc; + } + + private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() { + private boolean mWifiEnabled; + + @Override + public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId, + boolean activityIn, boolean activityOut, + String wifiSignalContentDescriptionId, String description) { + mWifiEnabled = enabled; + } + + @Override + public void onMobileDataSignalChanged(boolean enabled, + int mobileSignalIconId, + String mobileSignalContentDescriptionId, int dataTypeIconId, + boolean activityIn, boolean activityOut, + String dataTypeContentDescriptionId, String description) { + final CallbackInfo info = new CallbackInfo(); // TODO pool? + info.enabled = enabled; + info.wifiEnabled = mWifiEnabled; + info.mobileSignalIconId = mobileSignalIconId; + info.signalContentDescription = mobileSignalContentDescriptionId; + info.dataTypeIconId = dataTypeIconId; + info.dataContentDescription = dataTypeContentDescriptionId; + info.activityIn = activityIn; + info.activityOut = activityOut; + info.enabledDesc = description; + refreshState(info); + } + + @Override + public void onAirplaneModeChanged(boolean enabled) { + // noop + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java new file mode 100644 index 0000000..66740af --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.provider.Settings.Secure; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.qs.SecureSetting; + +/** Quick settings tile: Invert colors **/ +public class ColorInversionTile extends QSTile<QSTile.BooleanState> { + + private final SecureSetting mSetting; + + private boolean mVisible; + + public ColorInversionTile(Host host) { + super(host); + + mSetting = new SecureSetting(mContext, mHandler, + Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) { + @Override + protected void handleValueChanged(int value) { + handleRefreshState(value); + } + }; + + refreshState(); + } + + @Override + protected BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void dispose() { + mSetting.dispose(); + } + + @Override + protected void handleUserSwitch(int newUserId) { + mSetting.rebindForCurrentUser(); + } + + @Override + protected void handleClick() { + mSetting.setValue(mState.value ? 0 : 1); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue(); + final boolean enabled = value != 0; + if (enabled) { + mVisible = true; + } + state.visible = mVisible; + state.value = enabled; + state.label = mContext.getString(R.string.quick_settings_inversion_label); + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_invert_colors); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java new file mode 100644 index 0000000..1a67afc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.Intent; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.TetheringController; + +/** Quick settings tile: Hotspot **/ +public class HotspotTile extends QSTile<QSTile.State> { + private static final Intent TETHER_SETTINGS = new Intent() + .setClassName("com.android.settings", "com.android.settings.TetherSettings"); + + // TODO: implement. see com.android.settings.TetherSettings + + private final TetheringController mController; + + public HotspotTile(Host host) { + super(host); + mController = host.getTetheringController(); + } + + @Override + protected State newTileState() { + return new State(); + } + + @Override + public void dispose() { + + } + + @Override + protected void handleClick() { + mHost.startSettingsActivity(TETHER_SETTINGS); + } + + @Override + protected void handleUpdateState(State state, Object arg) { + state.visible = mController != null; + state.label = mContext.getString(R.string.quick_settings_hotspot_label); + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_hotspot); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java new file mode 100644 index 0000000..d32f98f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback; + +/** Quick settings tile: Location **/ +public class LocationTile extends QSTile<QSTile.BooleanState> { + + private final LocationController mController; + + public LocationTile(Host host) { + super(host); + mController = host.getLocationController(); + mController.addSettingsChangedCallback(mCallback); + } + + @Override + protected BooleanState newTileState() { + return new BooleanState(); + } + + public void dispose() { + mController.removeSettingsChangedCallback(mCallback); + } + + @Override + protected void handleClick() { + final boolean wasEnabled = (Boolean) mState.value; + final boolean changed = mController.setLocationEnabled(!wasEnabled); + if (!wasEnabled && changed) { + // If we've successfully switched from location off to on, close the + // notifications tray to show the network location provider consent dialog. + mHost.collapsePanels(); + } + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + final boolean locationEnabled = mController.isLocationEnabled(); + state.visible = true; + state.value = locationEnabled; + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_location); + if (locationEnabled) { + state.iconId = R.drawable.ic_qs_location_on; + state.label = mContext.getString(R.string.quick_settings_location_label); + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_location, + mContext.getString(R.string.accessibility_desc_on)); + } else { + state.iconId = R.drawable.ic_qs_location_off; + state.label = mContext.getString(R.string.quick_settings_location_off_label); + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_location, + mContext.getString(R.string.accessibility_desc_off)); + } + } + + private final LocationSettingsChangeCallback mCallback = new LocationSettingsChangeCallback() { + @Override + public void onLocationSettingsChanged(boolean enabled) { + refreshState(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java new file mode 100644 index 0000000..36a579c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; + +/** Quick settings tile: Ringer mode **/ +public class RingerModeTile extends QSTile<RingerModeTile.IntState> { + + private final AudioManager mAudioManager; + + public RingerModeTile(Host host) { + super(host); + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); + mContext.registerReceiver(mReceiver, filter); + } + + @Override + protected IntState newTileState() { + return new IntState(); + } + + @Override + public void dispose() { + mContext.unregisterReceiver(mReceiver); + } + + @Override + protected void handleClick() { + final int oldValue = (Integer) mState.value; + final int newValue = + oldValue == AudioManager.RINGER_MODE_NORMAL ? AudioManager.RINGER_MODE_VIBRATE + : oldValue == AudioManager.RINGER_MODE_VIBRATE ? AudioManager.RINGER_MODE_SILENT + : AudioManager.RINGER_MODE_NORMAL; + + mAudioManager.setRingerMode(newValue); + } + + @Override + protected void handleUpdateState(IntState state, Object arg) { + final int ringerMode = mAudioManager.getRingerMode(); + state.visible = true; + state.value = ringerMode; + if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_ringer_vibrate); + state.label = "Vibrate"; + } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) { + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_ringer_silent); + state.label = "Silent"; + } else { + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_ringer_audible); + state.label = "Audible"; + } + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) { + refreshState(); + } + } + }; + + public static class IntState extends QSTile.State { + public int value; + + @Override + public boolean copyTo(State other) { + final IntState o = (IntState) other; + final boolean changed = o.value != value; + o.value = value; + return super.copyTo(other) || changed; + } + + @Override + protected StringBuilder toStringBuilder() { + final StringBuilder rt = super.toStringBuilder(); + rt.insert(rt.length() - 1, ",value=" + value); + return rt; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java new file mode 100644 index 0000000..d075299 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.res.Configuration; +import android.graphics.drawable.AnimationDrawable; +import android.graphics.drawable.Drawable; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback; + +/** Quick settings tile: Rotation **/ +public class RotationLockTile extends QSTile<QSTile.BooleanState> { + + private final RotationLockController mController; + + public RotationLockTile(Host host) { + super(host); + mController = host.getRotationLockController(); + if (mController == null) return; + mController.addRotationLockControllerCallback(mCallback); + } + + @Override + protected BooleanState newTileState() { + return new BooleanState(); + } + + public void dispose() { + if (mController == null) return; + mController.removeRotationLockControllerCallback(mCallback); + } + + @Override + protected void handleClick() { + if (mController == null) return; + mController.setRotationLocked(!mState.value); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + if (mController == null) return; + final boolean rotationLocked = mController.isRotationLocked(); + state.visible = mController.isRotationLockAffordanceVisible(); + if (state.value != rotationLocked) { + state.value = rotationLocked; + final AnimationDrawable d = (AnimationDrawable) mContext.getDrawable(rotationLocked + ? R.drawable.ic_rotate_locked_anim + : R.drawable.ic_rotate_unlocked_anim); + state.icon = d; + mUiHandler.post(new Runnable() { + @Override + public void run() { + d.start(); + } + }); + } + if (rotationLocked) { + final int lockOrientation = mController.getRotationLockOrientation(); + final int label = lockOrientation == Configuration.ORIENTATION_PORTRAIT + ? R.string.quick_settings_rotation_locked_portrait_label + : lockOrientation == Configuration.ORIENTATION_LANDSCAPE + ? R.string.quick_settings_rotation_locked_landscape_label + : R.string.quick_settings_rotation_locked_label; + state.label = mContext.getString(label); + if (state.icon == null) { + state.icon = mContext.getDrawable(R.drawable.ic_rotate_24_15); + } + } else { + state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label); + if (state.icon == null) { + state.icon = mContext.getDrawable(R.drawable.ic_rotate_24_01); + } + } + } + + private final RotationLockControllerCallback mCallback = new RotationLockControllerCallback() { + @Override + public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) { + refreshState(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java new file mode 100644 index 0000000..e08a6fa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.provider.Settings; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.qs.QSTileView; +import com.android.systemui.qs.SignalTileView; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; + +/** Quick settings tile: Wifi **/ +public class WifiTile extends QSTile<QSTile.SignalState> { + private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS); + + private final NetworkController mController; + + public WifiTile(Host host) { + super(host); + mController = host.getNetworkController(); + mController.addNetworkSignalChangedCallback(mCallback); + } + + @Override + protected SignalState newTileState() { + return new SignalState(); + } + + @Override + public void dispose() { + mController.removeNetworkSignalChangedCallback(mCallback); + } + + @Override + public QSTileView createTileView(Context context) { + return new SignalTileView(context); + } + + @Override + protected void handleClick() { + mController.setWifiEnabled(!mState.enabled); + } + + @Override + protected void handleSecondaryClick() { + mHost.startSettingsActivity(WIFI_SETTINGS); + } + + @Override + protected void handleUpdateState(SignalState state, Object arg) { + if (arg == null) return; + state.visible = true; + CallbackInfo cb = (CallbackInfo) arg; + + boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null); + boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null); + state.enabled = wifiConnected; + state.connected = wifiConnected; + state.activityIn = cb.enabled && cb.activityIn; + state.activityOut = cb.enabled && cb.activityOut; + final String signalContentDescription; + final Resources r = mContext.getResources(); + if (wifiConnected) { + state.iconId = cb.wifiSignalIconId; + state.label = removeDoubleQuotes(cb.enabledDesc); + signalContentDescription = cb.wifiSignalContentDescription; + } else if (wifiNotConnected) { + state.iconId = R.drawable.ic_qs_wifi_0; + state.label = r.getString(R.string.quick_settings_wifi_label); + signalContentDescription = r.getString(R.string.accessibility_no_wifi); + } else { + state.iconId = R.drawable.ic_qs_wifi_no_network; + state.label = r.getString(R.string.quick_settings_wifi_off_label); + signalContentDescription = r.getString(R.string.accessibility_wifi_off); + } + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_wifi, + signalContentDescription, + state.connected ? state.label : ""); + } + + private static String removeDoubleQuotes(String string) { + if (string == null) return null; + final int length = string.length(); + if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { + return string.substring(1, length - 1); + } + return string; + } + + private static final class CallbackInfo { + boolean enabled; + int wifiSignalIconId; + String enabledDesc; + boolean activityIn; + boolean activityOut; + String wifiSignalContentDescription; + } + + private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() { + @Override + public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId, + boolean activityIn, boolean activityOut, + String wifiSignalContentDescriptionId, String description) { + final CallbackInfo info = new CallbackInfo(); + info.enabled = enabled; + info.wifiSignalIconId = wifiSignalIconId; + info.enabledDesc = description; + info.activityIn = activityIn; + info.activityOut = activityOut; + info.wifiSignalContentDescription = wifiSignalContentDescriptionId; + refreshState(info); + } + + @Override + public void onMobileDataSignalChanged(boolean enabled, + int mobileSignalIconId, + String mobileSignalContentDescriptionId, int dataTypeIconId, + boolean activityIn, boolean activityOut, + String dataTypeContentDescriptionId, String description) { + // noop + } + + @Override + public void onAirplaneModeChanged(boolean enabled) { + // noop + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java new file mode 100644 index 0000000..dceb856 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings; +import android.service.notification.Condition; +import android.util.AttributeSet; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ListView; +import android.widget.RadioButton; +import android.widget.RelativeLayout; +import android.widget.Switch; +import android.widget.TextView; + +import com.android.systemui.R; +import com.android.systemui.qs.QSImageView; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.ZenModeController; + +import java.util.HashSet; + +/** Quick settings control panel: Zen mode **/ +public class ZenModeDetail extends RelativeLayout { + private static final String TAG = "ZenModeDetail"; + private static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS); + private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240 }; + + private final H mHandler = new H(); + + private int mMinutesIndex = 3; + private Context mContext; + private ZenModeTile mTile; + private QSTile.Host mHost; + private ZenModeController mController; + + private Switch mSwitch; + private ConditionAdapter mAdapter; + + public ZenModeDetail(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void init(ZenModeTile tile) { + mTile = tile; + mHost = mTile.getHost(); + mContext = getContext(); + mController = mHost.getZenModeController(); + + final QSImageView close = (QSImageView) findViewById(android.R.id.button1); + close.setImageDrawable(mHost.getVectorDrawable(R.drawable.ic_qs_close)); + close.setEnabledVersion(true); + close.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mTile.showDetail(false); + } + }); + mSwitch = (Switch) findViewById(android.R.id.checkbox); + mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + mController.setZen(isChecked); + } + }); + mSwitch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final boolean isChecked = mSwitch.isChecked(); + mController.setZen(isChecked); + if (!isChecked) { + mTile.showDetail(false); + } + } + }); + + final View moreSettings = findViewById(android.R.id.button2); + moreSettings.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mHost.startSettingsActivity(ZEN_SETTINGS); + mTile.showDetail(false); + } + }); + final ListView conditions = (ListView) findViewById(android.R.id.content); + mAdapter = new ConditionAdapter(mContext); + conditions.setAdapter(mAdapter); + mAdapter.add(updateTimeCondition()); + + updateZen(mController.isZen()); + } + + private Condition updateTimeCondition() { + final int minutes = MINUTES[mMinutesIndex]; + final long millis = System.currentTimeMillis() + minutes * 60 * 1000; + final Uri id = new Uri.Builder().scheme(Condition.SCHEME).authority("android") + .appendPath("countdown").appendPath(Long.toString(millis)).build(); + final int num = minutes < 60 ? minutes : minutes / 60; + final String units = minutes < 60 ? "minutes" : minutes == 60 ? "hour" : "hours"; + return new Condition(id, "For " + num + " " + units, "", "", 0, Condition.STATE_TRUE, + Condition.FLAG_RELEVANT_NOW); + } + + private void editTimeCondition(int delta) { + final int i = mMinutesIndex + delta; + if (i < 0 || i >= MINUTES.length) return; + mMinutesIndex = i; + mAdapter.remove(mAdapter.getItem(0)); + final Condition c = updateTimeCondition(); + mAdapter.insert(c, 0); + select(c); + } + + private void select(Condition condition) { + mController.select(condition); + } + + private void updateZen(boolean zen) { + mHandler.obtainMessage(H.UPDATE_ZEN, zen ? 1 : 0, 0).sendToTarget(); + } + + private void updateConditions(Condition[] conditions) { + if (conditions == null) return; + mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget(); + } + + private void handleUpdateZen(boolean zen) { + mSwitch.setChecked(zen); + } + + private void handleUpdateConditions(Condition[] conditions) { + for (int i = mAdapter.getCount() - 1; i > 0; i--) { + mAdapter.remove(mAdapter.getItem(i)); + } + for (Condition condition : conditions) { + mAdapter.add(condition); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mController.addCallback(mCallback); + mController.requestConditions(true); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mController.removeCallback(mCallback); + mController.requestConditions(false); + } + + private final class H extends Handler { + private static final int UPDATE_ZEN = 1; + private static final int UPDATE_CONDITIONS = 2; + + public H() { + super(Looper.getMainLooper()); + } + + @Override + public void handleMessage(Message msg) { + if (msg.what == UPDATE_ZEN) { + handleUpdateZen(msg.arg1 == 1); + } else if (msg.what == UPDATE_CONDITIONS) { + handleUpdateConditions((Condition[])msg.obj); + } + } + } + + private final ZenModeController.Callback mCallback = new ZenModeController.Callback() { + @Override + public void onZenChanged(boolean zen) { + updateZen(zen); + } + public void onConditionsChanged(Condition[] conditions) { + updateConditions(conditions); + } + }; + + private final class ConditionAdapter extends ArrayAdapter<Condition> { + private final LayoutInflater mInflater; + private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>(); + + public ConditionAdapter(Context context) { + super(context, 0); + mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme)); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final Condition condition = getItem(position); + final boolean enabled = condition.state == Condition.STATE_TRUE; + + final View row = convertView != null ? convertView : mInflater + .inflate(R.layout.qs_zen_mode_detail_condition, parent, false); + final RadioButton rb = (RadioButton) row.findViewById(android.R.id.checkbox); + mRadioButtons.add(rb); + rb.setEnabled(enabled); + rb.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + for (RadioButton otherButton : mRadioButtons) { + if (otherButton == rb) continue; + otherButton.setChecked(false); + } + select(condition); + } + } + }); + final TextView title = (TextView) row.findViewById(android.R.id.title); + title.setText(condition.summary); + title.setEnabled(enabled); + title.setAlpha(enabled ? 1 : .5f); + final QSImageView button1 = (QSImageView) row.findViewById(android.R.id.button1); + button1.setImageDrawable(mHost.getVectorDrawable(R.drawable.ic_qs_minus)); + button1.setEnabledVersion(true); + button1.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + rb.setChecked(true); + editTimeCondition(-1); + } + }); + + final QSImageView button2 = (QSImageView) row.findViewById(android.R.id.button2); + button2.setImageDrawable(mHost.getVectorDrawable(R.drawable.ic_qs_plus)); + button2.setEnabledVersion(true); + button2.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + rb.setChecked(true); + editTimeCondition(1); + } + }); + title.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + rb.setChecked(true); + } + }); + if (position != 0) { + button1.setVisibility(View.GONE); + button2.setVisibility(View.GONE); + } + if (position == 0 && mRadioButtons.size() == 1) { + rb.setChecked(true); + } + return row; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java new file mode 100644 index 0000000..83918e8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.Context; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.ZenModeController; + +/** Quick settings tile: Zen mode **/ +public class ZenModeTile extends QSTile<QSTile.BooleanState> { + private final ZenModeController mController; + + public ZenModeTile(Host host) { + super(host); + mController = host.getZenModeController(); + mController.addCallback(mCallback); + } + + @Override + public View createDetailView(Context context, ViewGroup root) { + final Context themedContext = new ContextThemeWrapper(mContext, R.style.QSAccentTheme); + final ZenModeDetail v = (ZenModeDetail) LayoutInflater.from(themedContext) + .inflate(R.layout.qs_zen_mode_detail, root, false); + v.init(this); + return v; + } + + @Override + protected BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void dispose() { + mController.removeCallback(mCallback); + } + + @Override + protected void handleClick() { + final boolean newZen = !mState.value; + mController.setZen(newZen); + if (newZen) { + showDetail(true); + } + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + final boolean zen = arg instanceof Boolean ? (Boolean)arg : mController.isZen(); + state.value = zen; + state.visible = true; + state.iconId = R.drawable.stat_sys_zen_limited; + state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_zen); + state.label = mContext.getString(R.string.zen_mode_title); + } + + private final ZenModeController.Callback mCallback = new ZenModeController.Callback() { + @Override + public void onZenChanged(boolean zen) { + refreshState(zen); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index 1747e6e..a770f58 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -28,7 +28,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; import android.widget.ImageView; import java.util.ArrayList; @@ -36,6 +35,12 @@ import java.util.ArrayList; public class BrightnessController implements ToggleSlider.Listener { private static final String TAG = "StatusBar.BrightnessController"; + /** + * {@link android.provider.Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ} uses the range [-1, 1]. + * Using this factor, it is converted to [0, BRIGHTNESS_ADJ_RESOLUTION] for the SeekBar. + */ + private static final float BRIGHTNESS_ADJ_RESOLUTION = 100; + private final int mMinimumBacklight; private final int mMaximumBacklight; @@ -51,6 +56,8 @@ public class BrightnessController implements ToggleSlider.Listener { private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks = new ArrayList<BrightnessStateChangeCallback>(); + private boolean mAutomatic; + public interface BrightnessStateChangeCallback { public void onBrightnessLevelChanged(); } @@ -62,6 +69,8 @@ public class BrightnessController implements ToggleSlider.Listener { Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE); private final Uri BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); + private final Uri BRIGHTNESS_ADJ_URI = + Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ); public BrightnessObserver(Handler handler) { super(handler); @@ -77,7 +86,10 @@ public class BrightnessController implements ToggleSlider.Listener { if (selfChange) return; if (BRIGHTNESS_MODE_URI.equals(uri)) { updateMode(); - } else if (BRIGHTNESS_URI.equals(uri)) { + updateSlider(); + } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) { + updateSlider(); + } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) { updateSlider(); } else { updateMode(); @@ -97,6 +109,9 @@ public class BrightnessController implements ToggleSlider.Listener { cr.registerContentObserver( BRIGHTNESS_URI, false, this, UserHandle.USER_ALL); + cr.registerContentObserver( + BRIGHTNESS_ADJ_URI, + false, this, UserHandle.USER_ALL); } public void stopObserving() { @@ -163,10 +178,8 @@ public class BrightnessController implements ToggleSlider.Listener { } public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) { - setMode(automatic ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC - : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); - updateIcon(automatic); - if (!automatic) { + updateIcon(mAutomatic); + if (!mAutomatic) { final int val = value + mMinimumBacklight; setBrightness(val); if (!tracking) { @@ -178,6 +191,18 @@ public class BrightnessController implements ToggleSlider.Listener { } }); } + } else { + final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1; + setBrightnessAdj(adj); + if (!tracking) { + AsyncTask.execute(new Runnable() { + public void run() { + Settings.System.putFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adj, + UserHandle.USER_CURRENT); + } + }); + } } for (BrightnessStateChangeCallback cb : mChangeCallbacks) { @@ -198,6 +223,13 @@ public class BrightnessController implements ToggleSlider.Listener { } } + private void setBrightnessAdj(float adj) { + try { + mPower.setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(adj); + } catch (RemoteException ex) { + } + } + private void updateIcon(boolean automatic) { if (mIcon != null) { mIcon.setImageResource(automatic ? @@ -210,15 +242,12 @@ public class BrightnessController implements ToggleSlider.Listener { private void updateMode() { if (mAutomaticAvailable) { int automatic; - try { - automatic = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - UserHandle.USER_CURRENT); - } catch (SettingNotFoundException snfe) { - automatic = 0; - } - mControl.setChecked(automatic != 0); - updateIcon(automatic != 0); + automatic = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, + UserHandle.USER_CURRENT); + mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL; + updateIcon(mAutomatic); } else { mControl.setChecked(false); updateIcon(false /*automatic*/); @@ -227,16 +256,20 @@ public class BrightnessController implements ToggleSlider.Listener { /** Fetch the brightness from the system settings and update the slider */ private void updateSlider() { - int value; - try { + if (mAutomatic) { + float value = Settings.System.getFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, + UserHandle.USER_CURRENT); + mControl.setMax((int) BRIGHTNESS_ADJ_RESOLUTION); + mControl.setValue((int) ((value + 1) * BRIGHTNESS_ADJ_RESOLUTION / 2f)); + } else { + int value; value = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS, + Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight, UserHandle.USER_CURRENT); - } catch (SettingNotFoundException ex) { - value = mMaximumBacklight; + mControl.setMax(mMaximumBacklight - mMinimumBacklight); + mControl.setValue(value - mMinimumBacklight); } - mControl.setMax(mMaximumBacklight - mMinimumBacklight); - mControl.setValue(value - mMinimumBacklight); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index 89da08f..2bc6f9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -28,19 +28,19 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.systemui.R; -import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkControllerImpl; // Intimately tied to the design of res/layout/signal_cluster_view.xml public class SignalClusterView extends LinearLayout - implements NetworkController.SignalCluster { + implements NetworkControllerImpl.SignalCluster { static final boolean DEBUG = false; static final String TAG = "SignalClusterView"; static final PorterDuffColorFilter PROBLEM_FILTER = new PorterDuffColorFilter(0xffab653b, PorterDuff.Mode.SRC_ATOP); - NetworkController mNC; + NetworkControllerImpl mNC; private boolean mWifiVisible = false; private int mWifiStrengthId = 0; @@ -67,7 +67,7 @@ public class SignalClusterView super(context, attrs, defStyle); } - public void setNetworkController(NetworkController nc) { + public void setNetworkController(NetworkControllerImpl nc) { if (DEBUG) Log.d(TAG, "NetworkController=" + nc); mNC = nc; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index a3cf0f2..869edff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -27,7 +27,7 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.DemoMode; import com.android.systemui.R; import com.android.systemui.statusbar.StatusBarIconView; -import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.LocationControllerImpl; public class DemoStatusIcons extends LinearLayout implements DemoMode { private final LinearLayout mStatusIcons; @@ -74,9 +74,9 @@ public class DemoStatusIcons extends LinearLayout implements DemoMode { } String location = args.getString("location"); if (location != null) { - int iconId = location.equals("show") ? LocationController.LOCATION_STATUS_ICON_ID + int iconId = location.equals("show") ? LocationControllerImpl.LOCATION_STATUS_ICON_ID : 0; - updateSlot(LocationController.LOCATION_STATUS_ICON_PLACEHOLDER, null, iconId); + updateSlot(LocationControllerImpl.LOCATION_STATUS_ICON_PLACEHOLDER, null, iconId); } String alarm = args.getString("alarm"); if (alarm != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 1ffb4ee..d8e1766 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -160,4 +161,9 @@ public class KeyguardBouncer { return false; } } + + public boolean interceptMediaKey(KeyEvent event) { + ensureView(); + return mKeyguardView.interceptMediaKey(event); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 150db63..9054fe3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -45,7 +45,7 @@ public class NotificationPanelView extends PanelView implements PhoneStatusBar mStatusBar; private StatusBarHeaderView mHeader; - private QuickSettingsContainerView mQsContainer; + private View mQsContainer; private View mKeyguardStatusView; private ObservableScrollView mScrollView; private View mStackScrollerContainer; @@ -106,7 +106,7 @@ public class NotificationPanelView extends PanelView implements mHeader.setOverlayParent(this); mKeyguardStatusView = findViewById(R.id.keyguard_status_view); mStackScrollerContainer = findViewById(R.id.notification_container_parent); - mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container); + mQsContainer = findViewById(R.id.quick_settings_container); mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view); mScrollView.setListener(this); mNotificationStackScroller = (NotificationStackScrollLayout) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index e7da4f9..23b0594 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -68,6 +68,7 @@ import android.util.EventLog; import android.util.Log; import android.view.Display; import android.view.Gravity; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; @@ -92,6 +93,8 @@ import com.android.systemui.DemoMode; import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.qs.QSPanel; +import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; @@ -105,13 +108,15 @@ import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.BluetoothControllerImpl; +import com.android.systemui.statusbar.policy.CastControllerImpl; import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.HeadsUpNotificationView; -import com.android.systemui.statusbar.policy.LocationController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.policy.UserInfoController; +import com.android.systemui.statusbar.policy.LocationControllerImpl; +import com.android.systemui.statusbar.policy.NetworkControllerImpl; +import com.android.systemui.statusbar.policy.RotationLockControllerImpl; +import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; import com.android.systemui.statusbar.stack.StackScrollState.ViewState; @@ -180,12 +185,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, PhoneStatusBarPolicy mIconPolicy; // These are no longer handled by the policy, because we need custom strategies for them - BluetoothController mBluetoothController; + BluetoothControllerImpl mBluetoothController; BatteryController mBatteryController; - LocationController mLocationController; - NetworkController mNetworkController; - RotationLockController mRotationLockController; + LocationControllerImpl mLocationController; + NetworkControllerImpl mNetworkController; + RotationLockControllerImpl mRotationLockController; UserInfoController mUserInfoController; + ZenModeControllerImpl mZenModeController; + CastControllerImpl mCastController; int mNaturalBarHeight = -1; int mIconSize = -1; @@ -226,9 +233,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, TextView mNotificationPanelDebugText; // settings - QuickSettings mQS; View mFlipSettingsView; - QuickSettingsContainerView mSettingsContainer; + private QSPanel mQSPanel; // top bar StatusBarHeaderView mHeader; @@ -660,15 +666,16 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, setAreThereNotifications(); // Other icons - mLocationController = new LocationController(mContext); // will post a notification + mLocationController = new LocationControllerImpl(mContext); // will post a notification mBatteryController = new BatteryController(mContext); - mNetworkController = new NetworkController(mContext); - mBluetoothController = new BluetoothController(mContext); - if (mContext.getResources().getBoolean(R.bool.config_showRotationLock) - || QuickSettings.DEBUG_GONE_TILES) { - mRotationLockController = new RotationLockController(mContext); + mNetworkController = new NetworkControllerImpl(mContext); + mBluetoothController = new BluetoothControllerImpl(mContext); + if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) { + mRotationLockController = new RotationLockControllerImpl(mContext); } mUserInfoController = new UserInfoController(mContext); + mZenModeController = new ZenModeControllerImpl(mContext, mHandler); + mCastController = new CastControllerImpl(mContext); final SignalClusterView signalCluster = (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster); @@ -717,18 +724,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // updateCarrierLabelVisibility(false); } - // Quick Settings needs a container to survive - mSettingsContainer = (QuickSettingsContainerView) - mStatusBarWindow.findViewById(R.id.quick_settings_container); - mFlipSettingsView = mSettingsContainer; - if (mSettingsContainer != null) { - mQS = new QuickSettings(mContext, mSettingsContainer); - mQS.setService(this); - mQS.setBar(mStatusBarView); - mQS.setup(mNetworkController, mBluetoothController, mBatteryController, - mLocationController, mRotationLockController); - } else { - mQS = null; // fly away, be free + // Set up the quick settings tile panel + mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); + if (mQSPanel != null) { + final QSTileHost qsh = new QSTileHost(mContext, this, + mBluetoothController, mLocationController, mRotationLockController, + mNetworkController, mZenModeController, null /*tethering*/, + mCastController); + for (QSTile<?> tile : qsh.getTiles()) { + mQSPanel.addTile(tile); + } + mHeader.setQSPanel(mQSPanel); } // User info. Trigger first load. @@ -1471,7 +1477,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - public Handler getHandler() { + private Handler getHandler() { return mHandler; } @@ -1514,6 +1520,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); } + private final Runnable mAnimateCollapsePanels = new Runnable() { + @Override + public void run() { + animateCollapsePanels(); + } + }; + + public void postAnimateCollapsePanels() { + mHandler.post(mAnimateCollapsePanels); + } + public void animateCollapsePanels(int flags) { if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { return; @@ -1591,7 +1608,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final int FLIP_DURATION_IN = 225; final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT); - Animator mScrollViewAnim, mFlipSettingsViewAnim, mClearButtonAnim; + Animator mScrollViewAnim, mClearButtonAnim; @Override public void animateExpandNotificationsPanel() { @@ -1662,7 +1679,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mStatusBarView.collapseAllPanels(/*animate=*/ false); // reset things to their proper state - if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel(); if (mScrollViewAnim != null) mScrollViewAnim.cancel(); if (mClearButtonAnim != null) mClearButtonAnim.cancel(); @@ -1984,7 +2000,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } setNavigationIconHints(flags); - if (mQS != null) mQS.setImeWindowStatus(vis > 0); } @Override @@ -2383,11 +2398,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, * meantime, just update the things that we know change. */ void updateResources() { - final Context context = mContext; - final Resources res = context.getResources(); - - // Update the QuickSettings container - if (mQS != null) mQS.updateResources(); + // Update the quick setting tiles + if (mQSPanel != null) mQSPanel.updateResources(); loadDimens(); } @@ -2546,10 +2558,29 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0; } - public void startSettingsActivity(String action) { - if (mQS != null) { - mQS.startSettingsActivity(action); + public void postStartSettingsActivity(final Intent intent) { + mHandler.post(new Runnable() { + @Override + public void run() { + handleStartSettingsActivity(intent, true /*onlyProvisioned*/); + } + }); + } + + private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) { + if (onlyProvisioned && !isDeviceProvisioned()) return; + try { + // Dismiss the lock screen when Settings starts. + ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); + } catch (RemoteException e) { } + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + animateCollapsePanels(); + } + + public void startSettingsActivity(String action) { + postStartSettingsActivity(new Intent(action)); } private static class FastColorDrawable extends Drawable { @@ -2711,7 +2742,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardStatusView.setVisibility(View.GONE); mKeyguardIndicationTextView.setVisibility(View.GONE); } - mSettingsContainer.setKeyguardShowing(mState == StatusBarState.KEYGUARD); if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { mKeyguardBottomArea.setVisibility(View.VISIBLE); mHeader.setKeyguardShowing(true); @@ -2738,6 +2768,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } + public boolean interceptMediaKey(KeyEvent event) { + return mState == StatusBarState.KEYGUARD + && mStatusBarKeyguardViewManager.interceptMediaKey(event); + } + public boolean onMenuPressed() { return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java new file mode 100644 index 0000000..1fe3be5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.VectorDrawable; +import android.os.HandlerThread; +import android.os.Looper; + +import com.android.systemui.qs.QSTile; +import com.android.systemui.qs.tiles.AirplaneModeTile; +import com.android.systemui.qs.tiles.BluetoothTile; +import com.android.systemui.qs.tiles.BugreportTile; +import com.android.systemui.qs.tiles.CastTile; +import com.android.systemui.qs.tiles.CellularTile; +import com.android.systemui.qs.tiles.ColorInversionTile; +import com.android.systemui.qs.tiles.LocationTile; +import com.android.systemui.qs.tiles.RingerModeTile; +import com.android.systemui.qs.tiles.RotationLockTile; +import com.android.systemui.qs.tiles.HotspotTile; +import com.android.systemui.qs.tiles.WifiTile; +import com.android.systemui.qs.tiles.ZenModeTile; +import com.android.systemui.settings.CurrentUserTracker; +import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.TetheringController; +import com.android.systemui.statusbar.policy.ZenModeController; + +import java.util.ArrayList; +import java.util.List; + +/** Platform implementation of the quick settings tile host **/ +public class QSTileHost implements QSTile.Host { + + private final Context mContext; + private final PhoneStatusBar mStatusBar; + private final BluetoothController mBluetooth; + private final LocationController mLocation; + private final RotationLockController mRotation; + private final NetworkController mNetwork; + private final ZenModeController mZen; + private final TetheringController mTethering; + private final CastController mCast; + private final Looper mLooper; + private final CurrentUserTracker mUserTracker; + private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>(); + + public QSTileHost(Context context, PhoneStatusBar statusBar, + BluetoothController bluetooth, LocationController location, + RotationLockController rotation, NetworkController network, + ZenModeController zen, TetheringController tethering, + CastController cast) { + mContext = context; + mStatusBar = statusBar; + mBluetooth = bluetooth; + mLocation = location; + mRotation = rotation; + mNetwork = network; + mZen = zen; + mTethering = tethering; + mCast = cast; + + final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName()); + ht.start(); + mLooper = ht.getLooper(); + + mTiles.add(new WifiTile(this)); + mTiles.add(new BluetoothTile(this)); + mTiles.add(new ColorInversionTile(this)); + mTiles.add(new CellularTile(this)); + mTiles.add(new AirplaneModeTile(this)); + mTiles.add(new ZenModeTile(this)); + mTiles.add(new RingerModeTile(this)); + mTiles.add(new RotationLockTile(this)); + mTiles.add(new LocationTile(this)); + mTiles.add(new CastTile(this)); + mTiles.add(new HotspotTile(this)); + mTiles.add(new BugreportTile(this)); + + mUserTracker = new CurrentUserTracker(mContext) { + @Override + public void onUserSwitched(int newUserId) { + for (QSTile<?> tile : mTiles) { + tile.userSwitch(newUserId); + } + } + }; + mUserTracker.startTracking(); + } + + @Override + public List<QSTile<?>> getTiles() { + return mTiles; + } + + @Override + public void startSettingsActivity(final Intent intent) { + mStatusBar.postStartSettingsActivity(intent); + } + + @Override + public void warn(String message, Throwable t) { + // already logged + } + + @Override + public void collapsePanels() { + mStatusBar.postAnimateCollapsePanels(); + } + + @Override + public Looper getLooper() { + return mLooper; + } + + @Override + public Context getContext() { + return mContext; + } + + @Override + public VectorDrawable getVectorDrawable(int resId) { + return (VectorDrawable) mContext.getDrawable(resId); + } + + @Override + public BluetoothController getBluetoothController() { + return mBluetooth; + } + + @Override + public LocationController getLocationController() { + return mLocation; + } + + @Override + public RotationLockController getRotationLockController() { + return mRotation; + } + + @Override + public NetworkController getNetworkController() { + return mNetwork; + } + + @Override + public ZenModeController getZenModeController() { + return mZen; + } + + @Override + public TetheringController getTetheringController() { + return mTethering; + } + + @Override + public CastController getCastController() { + return mCast; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java deleted file mode 100644 index 8ce7279..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java +++ /dev/null @@ -1,1119 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.animation.ValueAnimator; -import android.app.ActivityManagerNative; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.PendingIntent; -import android.app.admin.DevicePolicyManager; -import android.bluetooth.BluetoothAdapter; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.UserInfo; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.hardware.display.DisplayManager; -import android.media.MediaRouter; -import android.net.wifi.WifiManager; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Handler; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.AlarmClock; -import android.provider.ContactsContract; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.Profile; -import android.provider.Settings; -import android.security.KeyChain; -import android.text.TextUtils.TruncateAt; -import android.util.Log; -import android.util.Pair; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; -import android.view.WindowManager.LayoutParams; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.internal.app.MediaRouteDialogPresenter; -import com.android.systemui.R; -import com.android.systemui.settings.UserSwitcherHostView; -import com.android.systemui.statusbar.phone.QuickSettingsModel.ActivityState; -import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState; -import com.android.systemui.statusbar.phone.QuickSettingsModel.RSSIState; -import com.android.systemui.statusbar.phone.QuickSettingsModel.State; -import com.android.systemui.statusbar.phone.QuickSettingsModel.UserState; -import com.android.systemui.statusbar.phone.QuickSettingsModel.WifiState; -import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.BluetoothController; -import com.android.systemui.statusbar.policy.LocationController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.RotationLockController; - -import java.util.ArrayList; - -/** - * - */ -class QuickSettings { - static final boolean DEBUG_GONE_TILES = false; - private static final String TAG = "QuickSettings"; - public static final boolean SHOW_IME_TILE = false; - public static final boolean SHOW_ACCESSIBILITY_TILES = true; - - public static final boolean LONG_PRESS_TOGGLES = true; - - private Context mContext; - private PanelBar mBar; - private QuickSettingsModel mModel; - private ViewGroup mContainerView; - - private DevicePolicyManager mDevicePolicyManager; - private PhoneStatusBar mStatusBarService; - private BluetoothState mBluetoothState; - private BluetoothAdapter mBluetoothAdapter; - private WifiManager mWifiManager; - - private BluetoothController mBluetoothController; - private RotationLockController mRotationLockController; - private LocationController mLocationController; - - private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask; - private AsyncTask<Void, Void, Pair<Boolean, Boolean>> mQueryCertTask; - - boolean mTilesSetUp = false; - boolean mUseDefaultAvatar = false; - - private Handler mHandler; - - // The set of QuickSettingsTiles that have dynamic spans (and need to be updated on - // configuration change) - private final ArrayList<QuickSettingsTileView> mDynamicSpannedTiles = - new ArrayList<QuickSettingsTileView>(); - - public QuickSettings(Context context, QuickSettingsContainerView container) { - mDevicePolicyManager - = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); - mContext = context; - mContainerView = container; - mModel = new QuickSettingsModel(context); - mBluetoothState = new QuickSettingsModel.BluetoothState(); - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - - mHandler = new Handler(); - - IntentFilter filter = new IntentFilter(); - filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); - filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); - filter.addAction(Intent.ACTION_USER_SWITCHED); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - filter.addAction(KeyChain.ACTION_STORAGE_CHANGED); - mContext.registerReceiver(mReceiver, filter); - - IntentFilter profileFilter = new IntentFilter(); - profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED); - profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED); - mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter, - null, null); - } - - void setBar(PanelBar bar) { - mBar = bar; - } - - public void setService(PhoneStatusBar phoneStatusBar) { - mStatusBarService = phoneStatusBar; - } - - public PhoneStatusBar getService() { - return mStatusBarService; - } - - public void setImeWindowStatus(boolean visible) { - mModel.onImeWindowStatusChanged(visible); - } - - void setup(NetworkController networkController, BluetoothController bluetoothController, - BatteryController batteryController, LocationController locationController, - RotationLockController rotationLockController) { - mBluetoothController = bluetoothController; - mRotationLockController = rotationLockController; - mLocationController = locationController; - - setupQuickSettings(); - updateResources(); - applyLocationEnabledStatus(); - - networkController.addNetworkSignalChangedCallback(mModel); - bluetoothController.addStateChangedCallback(mModel); - batteryController.addStateChangedCallback(mModel); - locationController.addSettingsChangedCallback(mModel); - if (rotationLockController != null) { - rotationLockController.addRotationLockControllerCallback(mModel); - } - } - - private void queryForSslCaCerts() { - mQueryCertTask = new AsyncTask<Void, Void, Pair<Boolean, Boolean>>() { - @Override - protected Pair<Boolean, Boolean> doInBackground(Void... params) { - boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); - boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null; - - return Pair.create(hasCert, isManaged); - } - @Override - protected void onPostExecute(Pair<Boolean, Boolean> result) { - super.onPostExecute(result); - boolean hasCert = result.first; - boolean isManaged = result.second; - mModel.setSslCaCertWarningTileInfo(hasCert, isManaged); - } - }; - mQueryCertTask.execute(); - } - - private void queryForUserInformation() { - Context currentUserContext = null; - UserInfo userInfo = null; - try { - userInfo = ActivityManagerNative.getDefault().getCurrentUser(); - currentUserContext = mContext.createPackageContextAsUser("android", 0, - new UserHandle(userInfo.id)); - } catch (NameNotFoundException e) { - Log.e(TAG, "Couldn't create user context", e); - throw new RuntimeException(e); - } catch (RemoteException e) { - Log.e(TAG, "Couldn't get user info", e); - } - final int userId = userInfo.id; - final String userName = userInfo.name; - - final Context context = currentUserContext; - mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() { - @Override - protected Pair<String, Drawable> doInBackground(Void... params) { - final UserManager um = UserManager.get(mContext); - - // Fall back to the UserManager nickname if we can't read the name from the local - // profile below. - String name = userName; - Drawable avatar = null; - Bitmap rawAvatar = um.getUserIcon(userId); - if (rawAvatar != null) { - avatar = new BitmapDrawable(mContext.getResources(), rawAvatar); - } else { - avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user); - mUseDefaultAvatar = true; - } - - // If it's a single-user device, get the profile name, since the nickname is not - // usually valid - if (um.getUsers().size() <= 1) { - // Try and read the display name from the local profile - final Cursor cursor = context.getContentResolver().query( - Profile.CONTENT_URI, new String[] {Phone._ID, Phone.DISPLAY_NAME}, - null, null, null); - if (cursor != null) { - try { - if (cursor.moveToFirst()) { - name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME)); - } - } finally { - cursor.close(); - } - } - } - return new Pair<String, Drawable>(name, avatar); - } - - @Override - protected void onPostExecute(Pair<String, Drawable> result) { - super.onPostExecute(result); - mModel.setUserTileInfo(result.first, result.second); - mUserInfoTask = null; - } - }; - mUserInfoTask.execute(); - } - - private void setupQuickSettings() { - // Setup the tiles that we are going to be showing (including the temporary ones) - LayoutInflater inflater = LayoutInflater.from(mContext); - - addUserTiles(mContainerView, inflater); - addSystemTiles(mContainerView, inflater); - addTemporaryTiles(mContainerView, inflater); - addAccessibilityTiles(mContainerView); - - queryForUserInformation(); - queryForSslCaCerts(); - mTilesSetUp = true; - } - - public void startSettingsActivity(String action) { - Intent intent = new Intent(action); - startSettingsActivity(intent); - } - - private void startSettingsActivity(Intent intent) { - startSettingsActivity(intent, true); - } - - private void collapsePanels() { - getService().animateCollapsePanels(); - } - - private void startSettingsActivity(Intent intent, boolean onlyProvisioned) { - if (onlyProvisioned && !getService().isDeviceProvisioned()) return; - try { - // Dismiss the lock screen when Settings starts. - ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); - } catch (RemoteException e) { - } - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); - collapsePanels(); - } - - private void addAccessibilityTiles(ViewGroup parent) { - if (!DEBUG_GONE_TILES && !SHOW_ACCESSIBILITY_TILES) return; - - // Color inversion tile - final SystemSettingTile inversionTile = new SystemSettingTile(mContext); - inversionTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, - SystemSettingTile.TYPE_SECURE); - inversionTile.setFragment("Settings$AccessibilityInversionSettingsActivity"); - mModel.addInversionTile(inversionTile, inversionTile.getRefreshCallback()); - parent.addView(inversionTile); - - // Color space adjustment tile - final SystemSettingTile colorSpaceTile = new SystemSettingTile(mContext); - colorSpaceTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, - SystemSettingTile.TYPE_SECURE); - colorSpaceTile.setFragment("Settings$AccessibilityDaltonizerSettingsActivity"); - mModel.addColorSpaceTile(colorSpaceTile, colorSpaceTile.getRefreshCallback()); - parent.addView(colorSpaceTile); - } - - private void addUserTiles(final ViewGroup parent, final LayoutInflater inflater) { - QuickSettingsTileView userTile = (QuickSettingsTileView) - inflater.inflate(R.layout.quick_settings_tile, parent, false); - userTile.setContent(R.layout.quick_settings_tile_user, inflater); - userTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - final UserManager um = UserManager.get(mContext); - if (um.isUserSwitcherEnabled()) { - final ViewGroup switcherParent = getService().getQuickSettingsOverlayParent(); - final UserSwitcherHostView switcher = (UserSwitcherHostView) inflater.inflate( - R.layout.user_switcher_host, switcherParent, false); - switcher.setFinishRunnable(new Runnable() { - @Override - public void run() { - switcherParent.removeView(switcher); - } - }); - switcher.refreshUsers(); - switcherParent.addView(switcher); - } else { - collapsePanels(); - Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent( - mContext, v, ContactsContract.Profile.CONTENT_URI, - ContactsContract.QuickContact.MODE_LARGE, null); - mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); - } - } - }); - mModel.addUserTile(userTile, new QuickSettingsModel.RefreshCallback() { - @Override - public void refreshView(QuickSettingsTileView view, State state) { - UserState us = (UserState) state; - ImageView iv = (ImageView) view.findViewById(R.id.user_imageview); - TextView tv = (TextView) view.findViewById(R.id.user_textview); - tv.setText(state.label); - iv.setImageDrawable(us.avatar); - view.setContentDescription(mContext.getString( - R.string.accessibility_quick_settings_user, state.label)); - } - }); - parent.addView(userTile); - mDynamicSpannedTiles.add(userTile); - - // Brightness - final QuickSettingsBasicTile brightnessTile - = new QuickSettingsBasicTile(mContext); - brightnessTile.setImageResource(R.drawable.ic_qs_brightness_auto_off); - brightnessTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - collapsePanels(); - showBrightnessDialog(); - } - }); - mModel.addBrightnessTile(brightnessTile, - new QuickSettingsModel.BasicRefreshCallback(brightnessTile)); - parent.addView(brightnessTile); - mDynamicSpannedTiles.add(brightnessTile); - - // Settings tile - final QuickSettingsBasicTile settingsTile = new QuickSettingsBasicTile(mContext); - settingsTile.setImageResource(R.drawable.ic_qs_settings); - settingsTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startSettingsActivity(android.provider.Settings.ACTION_SETTINGS); - } - }); - mModel.addSettingsTile(settingsTile, - new QuickSettingsModel.BasicRefreshCallback(settingsTile)); - parent.addView(settingsTile); - mDynamicSpannedTiles.add(settingsTile); - } - - private void addSystemTiles(ViewGroup parent, LayoutInflater inflater) { - // Wi-fi - final QuickSettingsTileView wifiTile = (QuickSettingsTileView) - inflater.inflate(R.layout.quick_settings_tile, parent, false); - wifiTile.setContent(R.layout.quick_settings_tile_wifi, inflater); - wifiTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startSettingsActivity(android.provider.Settings.ACTION_WIFI_SETTINGS); - } - }); - if (LONG_PRESS_TOGGLES) { - wifiTile.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - final boolean enable = - (mWifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED); - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... args) { - // Disable tethering if enabling Wifi - final int wifiApState = mWifiManager.getWifiApState(); - if (enable && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || - (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { - mWifiManager.setWifiApEnabled(null, false); - } - - mWifiManager.setWifiEnabled(enable); - return null; - } - }.execute(); - wifiTile.setPressed(false); - return true; - }} ); - } - mModel.addWifiTile(wifiTile, new NetworkActivityCallback() { - @Override - public void refreshView(QuickSettingsTileView view, State state) { - WifiState wifiState = (WifiState) state; - ImageView iv = (ImageView) view.findViewById(R.id.image); - iv.setImageResource(wifiState.iconId); - setActivity(view, wifiState); - TextView tv = (TextView) view.findViewById(R.id.text); - tv.setText(wifiState.label); - wifiTile.setContentDescription(mContext.getString( - R.string.accessibility_quick_settings_wifi, - wifiState.signalContentDescription, - (wifiState.connected) ? wifiState.label : "")); - } - }); - parent.addView(wifiTile); - - if (mModel.deviceHasMobileData()) { - // RSSI - QuickSettingsTileView rssiTile = (QuickSettingsTileView) - inflater.inflate(R.layout.quick_settings_tile, parent, false); - rssiTile.setContent(R.layout.quick_settings_tile_rssi, inflater); - rssiTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(); - intent.setComponent(new ComponentName( - "com.android.settings", - "com.android.settings.Settings$DataUsageSummaryActivity")); - startSettingsActivity(intent); - } - }); - mModel.addRSSITile(rssiTile, new NetworkActivityCallback() { - @Override - public void refreshView(QuickSettingsTileView view, State state) { - RSSIState rssiState = (RSSIState) state; - ImageView iv = (ImageView) view.findViewById(R.id.rssi_image); - ImageView iov = (ImageView) view.findViewById(R.id.rssi_overlay_image); - TextView tv = (TextView) view.findViewById(R.id.rssi_textview); - // Force refresh - iv.setImageDrawable(null); - iv.setImageResource(rssiState.signalIconId); - - if (rssiState.dataTypeIconId > 0) { - iov.setImageResource(rssiState.dataTypeIconId); - } else { - iov.setImageDrawable(null); - } - setActivity(view, rssiState); - - tv.setText(state.label); - view.setContentDescription(mContext.getResources().getString( - R.string.accessibility_quick_settings_mobile, - rssiState.signalContentDescription, rssiState.dataContentDescription, - state.label)); - } - }); - parent.addView(rssiTile); - } - - // Rotation Lock - if (mRotationLockController != null) { - final QuickSettingsBasicTile rotationLockTile - = new QuickSettingsBasicTile(mContext); - rotationLockTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - final boolean locked = mRotationLockController.isRotationLocked(); - mRotationLockController.setRotationLocked(!locked); - } - }); - mModel.addRotationLockTile(rotationLockTile, mRotationLockController, - new QuickSettingsModel.RefreshCallback() { - @Override - public void refreshView(QuickSettingsTileView view, State state) { - QuickSettingsModel.RotationLockState rotationLockState = - (QuickSettingsModel.RotationLockState) state; - view.setVisibility(rotationLockState.visible - ? View.VISIBLE : View.GONE); - if (state.iconId != 0) { - // needed to flush any cached IDs - rotationLockTile.setImageDrawable(null); - rotationLockTile.setImageResource(state.iconId); - } - if (state.label != null) { - rotationLockTile.setText(state.label); - } - } - }); - parent.addView(rotationLockTile); - } - - // Battery - final QuickSettingsTileView batteryTile = (QuickSettingsTileView) - inflater.inflate(R.layout.quick_settings_tile, parent, false); - batteryTile.setContent(R.layout.quick_settings_tile_battery, inflater); - batteryTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startSettingsActivity(Intent.ACTION_POWER_USAGE_SUMMARY); - } - }); - mModel.addBatteryTile(batteryTile, new QuickSettingsModel.RefreshCallback() { - @Override - public void refreshView(QuickSettingsTileView unused, State state) { - QuickSettingsModel.BatteryState batteryState = - (QuickSettingsModel.BatteryState) state; - String t; - if (batteryState.batteryLevel == 100) { - t = mContext.getString(R.string.quick_settings_battery_charged_label); - } else { - t = batteryState.pluggedIn - ? mContext.getString(R.string.quick_settings_battery_charging_label, - batteryState.batteryLevel) - : mContext.getString(R.string.status_bar_settings_battery_meter_format, - batteryState.batteryLevel); - } - ((TextView)batteryTile.findViewById(R.id.text)).setText(t); - batteryTile.setContentDescription( - mContext.getString(R.string.accessibility_quick_settings_battery, t)); - } - }); - parent.addView(batteryTile); - - // Bluetooth - if (mModel.deviceSupportsBluetooth() - || DEBUG_GONE_TILES) { - final QuickSettingsBasicTile bluetoothTile - = new QuickSettingsBasicTile(mContext); - bluetoothTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startSettingsActivity(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS); - } - }); - if (LONG_PRESS_TOGGLES) { - bluetoothTile.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mBluetoothAdapter.isEnabled()) { - mBluetoothAdapter.disable(); - } else { - mBluetoothAdapter.enable(); - } - bluetoothTile.setPressed(false); - return true; - }}); - } - mModel.addBluetoothTile(bluetoothTile, new QuickSettingsModel.RefreshCallback() { - @Override - public void refreshView(QuickSettingsTileView unused, State state) { - BluetoothState bluetoothState = (BluetoothState) state; - bluetoothTile.setImageResource(state.iconId); - - /* - Resources r = mContext.getResources(); - //TODO: Show connected bluetooth device label - Set<BluetoothDevice> btDevices = - mBluetoothController.getBondedBluetoothDevices(); - if (btDevices.size() == 1) { - // Show the name of the bluetooth device you are connected to - label = btDevices.iterator().next().getName(); - } else if (btDevices.size() > 1) { - // Show a generic label about the number of bluetooth devices - label = r.getString(R.string.quick_settings_bluetooth_multiple_devices_label, - btDevices.size()); - } - */ - bluetoothTile.setContentDescription(mContext.getString( - R.string.accessibility_quick_settings_bluetooth, - bluetoothState.stateContentDescription)); - bluetoothTile.setText(state.label); - } - }); - parent.addView(bluetoothTile); - } - - // Location - final QuickSettingsBasicTile locationTile - = new QuickSettingsBasicTile(mContext); - locationTile.setImageResource(R.drawable.ic_qs_location_on); - locationTile.setTextResource(R.string.quick_settings_location_label); - locationTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startSettingsActivity(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); - } - }); - if (LONG_PRESS_TOGGLES) { - locationTile.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - boolean newLocationEnabledState = !mLocationController.isLocationEnabled(); - if (mLocationController.setLocationEnabled(newLocationEnabledState) - && newLocationEnabledState) { - // If we've successfully switched from location off to on, close the - // notifications tray to show the network location provider consent dialog. - Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - mContext.sendBroadcast(closeDialog); - } - return true; // Consume click - }} ); - } - mModel.addLocationTile(locationTile, new QuickSettingsModel.RefreshCallback() { - @Override - public void refreshView(QuickSettingsTileView unused, State state) { - locationTile.setImageResource(state.iconId); - String locationState = mContext.getString( - (state.enabled) ? R.string.accessibility_desc_on - : R.string.accessibility_desc_off); - locationTile.setContentDescription(mContext.getString( - R.string.accessibility_quick_settings_location, - locationState)); - locationTile.setText(state.label); - } - }); - parent.addView(locationTile); - - // Airplane Mode - final QuickSettingsBasicTile airplaneTile - = new QuickSettingsBasicTile(mContext); - mModel.addAirplaneModeTile(airplaneTile, new QuickSettingsModel.RefreshCallback() { - @Override - public void refreshView(QuickSettingsTileView unused, State state) { - airplaneTile.setImageResource(state.iconId); - - String airplaneState = mContext.getString( - (state.enabled) ? R.string.accessibility_desc_on - : R.string.accessibility_desc_off); - airplaneTile.setContentDescription( - mContext.getString(R.string.accessibility_quick_settings_airplane, - airplaneState)); - airplaneTile.setText(state.label); - } - }); - parent.addView(airplaneTile); - - // Zen Mode - final QuickSettingsBasicTile zenModeTile = new QuickSettingsBasicTile(mContext); - zenModeTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showZenModeDialog(); - } - }); - mModel.addZenModeTile(zenModeTile, new QuickSettingsModel.RefreshCallback() { - @Override - public void refreshView(QuickSettingsTileView unused, State state) { - zenModeTile.setImageResource(state.iconId); - // TODO cut new assets - zenModeTile.getImageView().setAlpha(state.enabled ? 1 : .2f); - zenModeTile.getImageView().setScaleX(1.5f); - zenModeTile.getImageView().setScaleY(1.5f); - // for landscape version - zenModeTile.getTextView().setMaxLines(2); - zenModeTile.getTextView().setEllipsize(TruncateAt.END); - // TODO content description - zenModeTile.setText(state.label); - } - }); - parent.addView(zenModeTile); - } - - private void addTemporaryTiles(final ViewGroup parent, final LayoutInflater inflater) { - // Alarm tile - final QuickSettingsBasicTile alarmTile - = new QuickSettingsBasicTile(mContext); - alarmTile.setImageResource(R.drawable.ic_qs_alarm_on); - alarmTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startSettingsActivity(AlarmClock.ACTION_SHOW_ALARMS); - } - }); - mModel.addAlarmTile(alarmTile, new QuickSettingsModel.RefreshCallback() { - @Override - public void refreshView(QuickSettingsTileView unused, State alarmState) { - alarmTile.setText(alarmState.label); - alarmTile.setVisibility(alarmState.enabled ? View.VISIBLE : View.GONE); - alarmTile.setContentDescription(mContext.getString( - R.string.accessibility_quick_settings_alarm, alarmState.label)); - } - }); - parent.addView(alarmTile); - - // Remote Display - QuickSettingsBasicTile remoteDisplayTile - = new QuickSettingsBasicTile(mContext); - remoteDisplayTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - collapsePanels(); - - final Dialog[] dialog = new Dialog[1]; - dialog[0] = MediaRouteDialogPresenter.createDialog(mContext, - MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, - new View.OnClickListener() { - @Override - public void onClick(View v) { - dialog[0].dismiss(); - startSettingsActivity( - android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS); - } - }); - dialog[0].getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); - dialog[0].show(); - } - }); - mModel.addRemoteDisplayTile(remoteDisplayTile, - new QuickSettingsModel.BasicRefreshCallback(remoteDisplayTile) - .setShowWhenEnabled(true)); - parent.addView(remoteDisplayTile); - - if (SHOW_IME_TILE || DEBUG_GONE_TILES) { - // IME - final QuickSettingsBasicTile imeTile - = new QuickSettingsBasicTile(mContext); - imeTile.setImageResource(R.drawable.ic_qs_ime); - imeTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - try { - collapsePanels(); - Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER); - PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); - pendingIntent.send(); - } catch (Exception e) {} - } - }); - mModel.addImeTile(imeTile, - new QuickSettingsModel.BasicRefreshCallback(imeTile) - .setShowWhenEnabled(true)); - parent.addView(imeTile); - } - - // Bug reports - final QuickSettingsBasicTile bugreportTile - = new QuickSettingsBasicTile(mContext); - bugreportTile.setImageResource(com.android.internal.R.drawable.stat_sys_adb); - bugreportTile.setTextResource(com.android.internal.R.string.bugreport_title); - bugreportTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - collapsePanels(); - showBugreportDialog(); - } - }); - mModel.addBugreportTile(bugreportTile, new QuickSettingsModel.RefreshCallback() { - @Override - public void refreshView(QuickSettingsTileView view, State state) { - view.setVisibility(state.enabled ? View.VISIBLE : View.GONE); - } - }); - parent.addView(bugreportTile); - /* - QuickSettingsTileView mediaTile = (QuickSettingsTileView) - inflater.inflate(R.layout.quick_settings_tile, parent, false); - mediaTile.setContent(R.layout.quick_settings_tile_media, inflater); - parent.addView(mediaTile); - QuickSettingsTileView imeTile = (QuickSettingsTileView) - inflater.inflate(R.layout.quick_settings_tile, parent, false); - imeTile.setContent(R.layout.quick_settings_tile_ime, inflater); - imeTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - parent.removeViewAt(0); - } - }); - parent.addView(imeTile); - */ - - // SSL CA Cert Warning. - final QuickSettingsBasicTile sslCaCertWarningTile = - new QuickSettingsBasicTile(mContext, null, R.layout.quick_settings_tile_monitoring); - sslCaCertWarningTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - collapsePanels(); - startSettingsActivity(Settings.ACTION_MONITORING_CERT_INFO); - } - }); - - sslCaCertWarningTile.setImageResource( - com.android.internal.R.drawable.indicator_input_error); - sslCaCertWarningTile.setTextResource(R.string.ssl_ca_cert_warning); - - mModel.addSslCaCertWarningTile(sslCaCertWarningTile, - new QuickSettingsModel.BasicRefreshCallback(sslCaCertWarningTile) - .setShowWhenEnabled(true)); - parent.addView(sslCaCertWarningTile); - } - - void updateResources() { - Resources r = mContext.getResources(); - - // Update the model - mModel.updateResources(); - - // Update the User, Time, and Settings tiles spans, and reset everything else - int span = r.getInteger(R.integer.quick_settings_user_time_settings_tile_span); - for (QuickSettingsTileView v : mDynamicSpannedTiles) { - v.setColumnSpan(span); - } - ((QuickSettingsContainerView)mContainerView).updateResources(); - mContainerView.requestLayout(); - } - - - private void showBrightnessDialog() { - Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG); - mContext.sendBroadcast(intent); - } - - private void showBugreportDialog() { - final AlertDialog.Builder builder = new AlertDialog.Builder(mContext); - builder.setPositiveButton(com.android.internal.R.string.report, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (which == DialogInterface.BUTTON_POSITIVE) { - // Add a little delay before executing, to give the - // dialog a chance to go away before it takes a - // screenshot. - mHandler.postDelayed(new Runnable() { - @Override public void run() { - try { - ActivityManagerNative.getDefault() - .requestBugReport(); - } catch (RemoteException e) { - } - } - }, 500); - } - } - }); - builder.setMessage(com.android.internal.R.string.bugreport_message); - builder.setTitle(com.android.internal.R.string.bugreport_title); - builder.setCancelable(true); - final Dialog dialog = builder.create(); - dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); - try { - WindowManagerGlobal.getWindowManagerService().dismissKeyguard(); - } catch (RemoteException e) { - } - dialog.show(); - } - - private void showZenModeDialog() { - final Dialog d = new Dialog(mContext); - d.requestWindowFeature(Window.FEATURE_NO_TITLE); - d.setCancelable(true); - d.setCanceledOnTouchOutside(true); - final ZenModeView v = new ZenModeView(mContext) { - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - WindowManager.LayoutParams lp = d.getWindow().getAttributes(); - lp.width = mContext.getResources() - .getDimensionPixelSize(R.dimen.zen_mode_dialog_width); - d.getWindow().setAttributes(lp); - } - }; - v.setAutoActivate(true); - v.setAdapter(new ZenModeViewAdapter(mContext) { - @Override - public void configure() { - if (mStatusBarService != null) { - mStatusBarService.startSettingsActivity(Settings.ACTION_ZEN_MODE_SETTINGS); - } - d.dismiss(); - } - @Override - public void close() { - d.dismiss(); - } - }); - d.setContentView(v); - d.create(); - d.getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); - WindowManager.LayoutParams lp = d.getWindow().getAttributes(); - lp.width = mContext.getResources().getDimensionPixelSize(R.dimen.zen_mode_dialog_width); - d.getWindow().setAttributes(lp); - d.show(); - } - - private void applyBluetoothStatus() { - mModel.onBluetoothStateChange(mBluetoothState); - } - - private void applyLocationEnabledStatus() { - mModel.onLocationSettingsChanged(mLocationController.isLocationEnabled()); - } - - void reloadUserInfo() { - if (mUserInfoTask != null) { - mUserInfoTask.cancel(false); - mUserInfoTask = null; - } - if (mTilesSetUp) { - queryForUserInformation(); - queryForSslCaCerts(); - } - } - - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { - int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, - BluetoothAdapter.ERROR); - mBluetoothState.enabled = (state == BluetoothAdapter.STATE_ON); - applyBluetoothStatus(); - } else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) { - int status = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, - BluetoothAdapter.STATE_DISCONNECTED); - mBluetoothState.connected = (status == BluetoothAdapter.STATE_CONNECTED); - applyBluetoothStatus(); - } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { - reloadUserInfo(); - } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { - if (mUseDefaultAvatar) { - queryForUserInformation(); - } - } else if (KeyChain.ACTION_STORAGE_CHANGED.equals(action)) { - queryForSslCaCerts(); - } - } - }; - - private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) || - Intent.ACTION_USER_INFO_CHANGED.equals(action)) { - try { - final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id; - final int changedUser = - intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); - if (changedUser == currentUser) { - reloadUserInfo(); - } - } catch (RemoteException e) { - Log.e(TAG, "Couldn't get current user id for profile change", e); - } - } - - } - }; - - private abstract static class NetworkActivityCallback - implements QuickSettingsModel.RefreshCallback { - private final long mDefaultDuration = new ValueAnimator().getDuration(); - private final long mShortDuration = mDefaultDuration / 3; - - public void setActivity(View view, ActivityState state) { - setVisibility(view.findViewById(R.id.activity_in), state.activityIn); - setVisibility(view.findViewById(R.id.activity_out), state.activityOut); - } - - private void setVisibility(View view, boolean visible) { - final float newAlpha = visible ? 1 : 0; - if (view.getAlpha() != newAlpha) { - view.animate() - .setDuration(visible ? mShortDuration : mDefaultDuration) - .alpha(newAlpha) - .start(); - } - } - } - - /** - * Quick Setting tile that represents a secure setting. This type of tile - * can toggle a URI within Settings.Secure on click and launch a Settings - * fragment on long-click. - */ - public class SystemSettingTile extends QuickSettingsBasicTile { - private static final int TYPE_GLOBAL = 0; - private static final int TYPE_SECURE = 1; - private static final int TYPE_SYSTEM = 2; - - private final QuickSettingsModel.BasicRefreshCallback mRefreshCallback; - - private String mFragment; - private String mName; - private int mType; - - public SystemSettingTile(Context context) { - super(context); - - mRefreshCallback = new QuickSettingsModel.BasicRefreshCallback(this); - mRefreshCallback.setShowWhenEnabled(true); - } - - @Override - public boolean performLongClick() { - if (mFragment != null) { - collapsePanels(); - - final Intent intent = new Intent(); - intent.setComponent(new ComponentName( - "com.android.settings", "com.android.settings." + mFragment)); - startSettingsActivity(intent); - return true; - } - return false; - } - - @Override - public boolean performClick() { - if (mName != null) { - collapsePanels(); - - final ContentResolver cr = mContext.getContentResolver(); - switch (mType) { - case TYPE_GLOBAL: { - final boolean enable = Settings.Global.getInt(cr, mName, 0) == 0; - Settings.Global.putInt(cr, mName, enable ? 1 : 0); - } break; - case TYPE_SECURE: { - final boolean enable = Settings.Secure.getIntForUser( - cr, mName, 0, UserHandle.USER_CURRENT) == 0; - Settings.Secure.putIntForUser( - cr, mName, enable ? 1 : 0, UserHandle.USER_CURRENT); - } break; - case TYPE_SYSTEM: { - final boolean enable = Settings.System.getIntForUser( - cr, mName, 0, UserHandle.USER_CURRENT) == 0; - Settings.System.putIntForUser( - cr, mName, enable ? 1 : 0, UserHandle.USER_CURRENT); - } break; - } - return true; - } - return false; - } - - /** - * Specifies the fragment within the com.android.settings package to - * launch when this tile is long-clicked. - * - * @param fragment a fragment name within the com.android.settings - * package - */ - public void setFragment(String fragment) { - mFragment = fragment; - setLongClickable(fragment != null); - } - - /** - * Specifies the setting name and type to toggle when this tile is - * clicked. - * - * @param name a setting name - * @param type the type of setting, one of: - * <ul> - * <li>{@link #TYPE_GLOBAL} - * <li>{@link #TYPE_SECURE} - * <li>{@link #TYPE_SYSTEM} - * </ul> - */ - public void setUri(String name, int type) { - mName = name; - mType = type; - setClickable(mName != null); - } - - /** - * @return the refresh callback for this tile - */ - public QuickSettingsModel.BasicRefreshCallback getRefreshCallback() { - return mRefreshCallback; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java deleted file mode 100644 index 099780c..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.systemui.R; - -class QuickSettingsBasicTile extends QuickSettingsTileView { - private final TextView mTextView; - private final ImageView mImageView; - - public QuickSettingsBasicTile(Context context) { - this(context, null); - } - - public QuickSettingsBasicTile(Context context, AttributeSet attrs) { - this(context, attrs, R.layout.quick_settings_tile_basic); - } - - public QuickSettingsBasicTile(Context context, AttributeSet attrs, int layoutId) { - super(context, attrs); - - setLayoutParams(new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - context.getResources().getDimensionPixelSize(R.dimen.quick_settings_cell_height) - )); - setBackgroundResource(R.drawable.qs_tile_background); - addView(LayoutInflater.from(context).inflate(layoutId, null), - new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT)); - mTextView = (TextView) findViewById(R.id.text); - mImageView = (ImageView) findViewById(R.id.image); - } - - @Override - void setContent(int layoutId, LayoutInflater inflater) { - throw new RuntimeException("why?"); - } - - public ImageView getImageView() { - return mImageView; - } - - public TextView getTextView() { - return mTextView; - } - - public void setImageDrawable(Drawable drawable) { - mImageView.setImageDrawable(drawable); - } - - public void setImageResource(int resId) { - mImageView.setImageResource(resId); - } - - public void setText(CharSequence text) { - mTextView.setText(text); - } - - public void setTextResource(int resId) { - mTextView.setText(resId); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java deleted file mode 100644 index c44cb0c..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.animation.LayoutTransition; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import com.android.systemui.R; - -/** - * - */ -class QuickSettingsContainerView extends FrameLayout { - - private static boolean sShowScrim = true; - - private final Context mContext; - - // The number of columns in the QuickSettings grid - private int mNumColumns; - - private boolean mKeyguardShowing; - private int mMaxRows; - private int mMaxRowsOnKeyguard; - - // The gap between tiles in the QuickSettings grid - private float mCellGap; - - private ScrimView mScrim; - - public QuickSettingsContainerView(Context context, AttributeSet attrs) { - super(context, attrs); - mContext = context; - updateResources(); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - if (sShowScrim) { - mScrim = new ScrimView(mContext); - addView(mScrim); - } - // TODO: Setup the layout transitions - LayoutTransition transitions = getLayoutTransition(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mScrim != null) { - sShowScrim = false; - removeView(mScrim); - } - return super.onTouchEvent(event); - } - - void updateResources() { - Resources r = getContext().getResources(); - mCellGap = r.getDimension(R.dimen.quick_settings_cell_gap); - mNumColumns = r.getInteger(R.integer.quick_settings_num_columns); - mMaxRows = r.getInteger(R.integer.quick_settings_max_rows); - mMaxRowsOnKeyguard = r.getInteger(R.integer.quick_settings_max_rows_keyguard); - requestLayout(); - } - - void setKeyguardShowing(boolean showing) { - mKeyguardShowing = showing; - requestLayout(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // Calculate the cell width dynamically - int width = MeasureSpec.getSize(widthMeasureSpec); - int height = MeasureSpec.getSize(heightMeasureSpec); - int availableWidth = (int) (width - getPaddingLeft() - getPaddingRight() - - (mNumColumns - 1) * mCellGap); - float cellWidth = (float) Math.ceil(((float) availableWidth) / mNumColumns); - - // Update each of the children's widths accordingly to the cell width - final int N = getChildCount(); - int cellHeight = 0; - int cursor = 0; - int maxRows = mKeyguardShowing ? mMaxRowsOnKeyguard : mMaxRows; - - for (int i = 0; i < N; ++i) { - if (getChildAt(i).equals(mScrim)) { - continue; - } - // Update the child's width - QuickSettingsTileView v = (QuickSettingsTileView) getChildAt(i); - if (v.getVisibility() != View.GONE) { - int row = (int) (cursor / mNumColumns); - if (row >= maxRows) continue; - - ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams(); - int colSpan = v.getColumnSpan(); - lp.width = (int) ((colSpan * cellWidth) + (colSpan - 1) * mCellGap); - - // Measure the child - int newWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); - int newHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); - v.measure(newWidthSpec, newHeightSpec); - - // Save the cell height - if (cellHeight <= 0) { - cellHeight = v.getMeasuredHeight(); - } - cursor += colSpan; - } - } - - // Set the measured dimensions. We always fill the tray width, but wrap to the height of - // all the tiles. - int numRows = (int) Math.ceil((float) cursor / mNumColumns); - int newHeight = (int) ((numRows * cellHeight) + ((numRows - 1) * mCellGap)) + - getPaddingTop() + getPaddingBottom(); - setMeasuredDimension(width, newHeight); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (mScrim != null) { - mScrim.bringToFront(); - } - final int N = getChildCount(); - final boolean isLayoutRtl = isLayoutRtl(); - final int width = getWidth(); - - int x = getPaddingStart(); - int y = getPaddingTop(); - int cursor = 0; - int maxRows = mKeyguardShowing ? mMaxRowsOnKeyguard : mMaxRows; - - for (int i = 0; i < N; ++i) { - if (getChildAt(i).equals(mScrim)) { - int w = right - left - getPaddingLeft() - getPaddingRight(); - int h = bottom - top - getPaddingTop() - getPaddingBottom(); - mScrim.measure( - MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY)); - mScrim.layout(getPaddingLeft(), getPaddingTop(), right, bottom); - continue; - } - QuickSettingsTileView child = (QuickSettingsTileView) getChildAt(i); - ViewGroup.LayoutParams lp = child.getLayoutParams(); - if (child.getVisibility() != GONE) { - final int col = cursor % mNumColumns; - final int colSpan = child.getColumnSpan(); - - final int childWidth = lp.width; - final int childHeight = lp.height; - - int row = (int) (cursor / mNumColumns); - if (row >= maxRows) continue; - - // Push the item to the next row if it can't fit on this one - if ((col + colSpan) > mNumColumns) { - x = getPaddingStart(); - y += childHeight + mCellGap; - row++; - } - - final int childLeft = (isLayoutRtl) ? width - x - childWidth : x; - final int childRight = childLeft + childWidth; - - final int childTop = y; - final int childBottom = childTop + childHeight; - - // Layout the container - child.layout(childLeft, childTop, childRight, childBottom); - - // Offset the position by the cell gap or reset the position and cursor when we - // reach the end of the row - cursor += child.getColumnSpan(); - if (cursor < (((row + 1) * mNumColumns))) { - x += childWidth + mCellGap; - } else { - x = getPaddingStart(); - y += childHeight + mCellGap; - } - } - } - } - - private static final class ScrimView extends View { - private static final int SCRIM = 0x4f000000; - private static final int COLOR = 0xaf4285f4; - - private final Paint mLinePaint; - private final int mStrokeWidth; - private final Rect mTmp = new Rect(); - private final Paint mTextPaint; - private final int mTextSize; - - public ScrimView(Context context) { - super(context); - setFocusable(false); - final Resources res = context.getResources(); - mStrokeWidth = res.getDimensionPixelSize(R.dimen.quick_settings_tmp_scrim_stroke_width); - mTextSize = res.getDimensionPixelSize(R.dimen.quick_settings_tmp_scrim_text_size); - - mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLinePaint.setColor(COLOR); - mLinePaint.setStrokeWidth(mStrokeWidth); - mLinePaint.setStrokeJoin(Paint.Join.ROUND); - mLinePaint.setStrokeCap(Paint.Cap.ROUND); - mLinePaint.setStyle(Paint.Style.STROKE); - - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mTextPaint.setColor(COLOR); - mTextPaint.setTextSize(mTextSize); - mTextPaint.setTypeface(Typeface.create("sans-serif-condensed", Typeface.BOLD)); - } - - @Override - protected void onDraw(Canvas canvas) { - final int w = getMeasuredWidth(); - final int h = getMeasuredHeight(); - final int f = mStrokeWidth * 3 / 4; - - canvas.drawColor(SCRIM); - canvas.drawPath(line(f, h / 2, w - f, h / 2), mLinePaint); - canvas.drawPath(line(w / 2, f, w / 2, h - f), mLinePaint); - - final int s = mStrokeWidth; - mTextPaint.setTextAlign(Paint.Align.RIGHT); - canvas.drawText("FUTURE", w / 2 - s, h / 2 - s, mTextPaint); - mTextPaint.setTextAlign(Paint.Align.LEFT); - canvas.drawText("SITE OF", w / 2 + s, h / 2 - s , mTextPaint); - mTextPaint.setTextAlign(Paint.Align.RIGHT); - drawUnder(canvas, "QUANTUM", w / 2 - s, h / 2 + s); - mTextPaint.setTextAlign(Paint.Align.LEFT); - drawUnder(canvas, "SETTINGS", w / 2 + s, h / 2 + s); - } - - private void drawUnder(Canvas c, String text, float x, float y) { - if (mTmp.isEmpty()) { - mTextPaint.getTextBounds(text, 0, text.length(), mTmp); - } - c.drawText(text, x, y + mTmp.height() * .85f, mTextPaint); - } - - private Path line(float x1, float y1, float x2, float y2) { - final int a = mStrokeWidth * 2; - final Path p = new Path(); - p.moveTo(x1, y1); - p.lineTo(x2, y2); - if (y1 == y2) { - p.moveTo(x1 + a, y1 + a); - p.lineTo(x1, y1); - p.lineTo(x1 + a, y1 - a); - - p.moveTo(x2 - a, y2 - a); - p.lineTo(x2, y2); - p.lineTo(x2 - a, y2 + a); - } - if (x1 == x2) { - p.moveTo(x1 - a, y1 + a); - p.lineTo(x1, y1); - p.lineTo(x1 + a, y1 + a); - - p.moveTo(x2 - a, y2 - a); - p.lineTo(x2, y2); - p.lineTo(x2 + a, y2 - a); - } - return p; - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java deleted file mode 100644 index 9b90d3d..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java +++ /dev/null @@ -1,1047 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.graphics.drawable.Drawable; -import android.media.MediaRouter; -import android.media.MediaRouter.RouteInfo; -import android.net.ConnectivityManager; -import android.os.Handler; -import android.os.UserHandle; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; - -import com.android.systemui.R; -import com.android.systemui.settings.BrightnessController.BrightnessStateChangeCallback; -import com.android.systemui.settings.CurrentUserTracker; -import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; -import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback; -import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; -import com.android.systemui.statusbar.policy.RotationLockController; -import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback; - -import java.util.List; - -class QuickSettingsModel implements BluetoothStateChangeCallback, - NetworkSignalChangedCallback, - BatteryStateChangeCallback, - BrightnessStateChangeCallback, - RotationLockControllerCallback, - LocationSettingsChangeCallback { - // Sett InputMethoManagerService - private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher"; - - /** Represents the state of a given attribute. */ - static class State { - int iconId; - String label; - boolean enabled = false; - } - static class BatteryState extends State { - int batteryLevel; - boolean pluggedIn; - } - static class ActivityState extends State { - boolean activityIn; - boolean activityOut; - } - static class RSSIState extends ActivityState { - int signalIconId; - String signalContentDescription; - int dataTypeIconId; - String dataContentDescription; - } - static class WifiState extends ActivityState { - String signalContentDescription; - boolean connected; - } - static class UserState extends State { - Drawable avatar; - } - static class BrightnessState extends State { - boolean autoBrightness; - } - static class InversionState extends State { - boolean toggled; - } - static class ColorSpaceState extends State { - boolean toggled; - int type; - } - public static class BluetoothState extends State { - boolean connected = false; - String stateContentDescription; - } - public static class RotationLockState extends State { - boolean visible = false; - } - public static class ZenModeState extends State { - int zenMode = Settings.Global.ZEN_MODE_OFF; - } - - /** The callback to update a given tile. */ - interface RefreshCallback { - public void refreshView(QuickSettingsTileView view, State state); - } - - public static class BasicRefreshCallback implements RefreshCallback { - private final QuickSettingsBasicTile mView; - private boolean mShowWhenEnabled; - - public BasicRefreshCallback(QuickSettingsBasicTile v) { - mView = v; - } - public void refreshView(QuickSettingsTileView ignored, State state) { - if (mShowWhenEnabled) { - mView.setVisibility(state.enabled ? View.VISIBLE : View.GONE); - } - if (state.iconId != 0) { - mView.setImageDrawable(null); // needed to flush any cached IDs - mView.setImageResource(state.iconId); - } - if (state.label != null) { - mView.setText(state.label); - } - } - public BasicRefreshCallback setShowWhenEnabled(boolean swe) { - mShowWhenEnabled = swe; - return this; - } - } - - /** Broadcast receive to determine if there is an alarm set. */ - private BroadcastReceiver mAlarmIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(Intent.ACTION_ALARM_CHANGED)) { - onAlarmChanged(intent); - onNextAlarmChanged(); - } - } - }; - - /** ContentObserver to determine the next alarm */ - private class NextAlarmObserver extends ContentObserver { - public NextAlarmObserver(Handler handler) { - super(handler); - } - - @Override public void onChange(boolean selfChange) { - onNextAlarmChanged(); - } - - public void startObserving() { - final ContentResolver cr = mContext.getContentResolver(); - cr.registerContentObserver( - Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED), false, this, - UserHandle.USER_ALL); - } - } - - /** ContentObserver to watch adb */ - private class BugreportObserver extends ContentObserver { - public BugreportObserver(Handler handler) { - super(handler); - } - - @Override public void onChange(boolean selfChange) { - onBugreportChanged(); - } - - public void startObserving() { - final ContentResolver cr = mContext.getContentResolver(); - cr.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.BUGREPORT_IN_POWER_MENU), false, this); - } - } - - /** ContentObserver to watch brightness **/ - private class BrightnessObserver extends ContentObserver { - public BrightnessObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - onBrightnessLevelChanged(); - } - - public void startObserving() { - final ContentResolver cr = mContext.getContentResolver(); - cr.unregisterContentObserver(this); - cr.registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), - false, this, mUserTracker.getCurrentUserId()); - cr.registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS), - false, this, mUserTracker.getCurrentUserId()); - } - } - - /** ContentObserver to watch display inversion */ - private class DisplayInversionObserver extends ContentObserver { - public DisplayInversionObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - onInversionChanged(); - } - - public void startObserving() { - final ContentResolver cr = mContext.getContentResolver(); - cr.unregisterContentObserver(this); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED), - false, this, mUserTracker.getCurrentUserId()); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED), - false, this, mUserTracker.getCurrentUserId()); - } - } - - /** ContentObserver to watch display color space adjustment */ - private class DisplayColorSpaceObserver extends ContentObserver { - public DisplayColorSpaceObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - onColorSpaceChanged(); - } - - public void startObserving() { - final ContentResolver cr = mContext.getContentResolver(); - cr.unregisterContentObserver(this); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED), - false, this, mUserTracker.getCurrentUserId()); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_QUICK_SETTING_ENABLED), - false, this, mUserTracker.getCurrentUserId()); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER), - false, this, mUserTracker.getCurrentUserId()); - } - } - - /** ContentObserver to watch display color space adjustment */ - private class ZenModeObserver extends ContentObserver { - public ZenModeObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - onZenModeChanged(); - } - - public void startObserving() { - final ContentResolver cr = mContext.getContentResolver(); - cr.unregisterContentObserver(this); - cr.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, this); - } - } - - /** Callback for changes to remote display routes. */ - private class RemoteDisplayRouteCallback extends MediaRouter.SimpleCallback { - @Override - public void onRouteAdded(MediaRouter router, RouteInfo route) { - updateRemoteDisplays(); - } - @Override - public void onRouteChanged(MediaRouter router, RouteInfo route) { - updateRemoteDisplays(); - } - @Override - public void onRouteRemoved(MediaRouter router, RouteInfo route) { - updateRemoteDisplays(); - } - @Override - public void onRouteSelected(MediaRouter router, int type, RouteInfo route) { - updateRemoteDisplays(); - } - @Override - public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) { - updateRemoteDisplays(); - } - } - - private final Context mContext; - private final Handler mHandler; - private final CurrentUserTracker mUserTracker; - private final NextAlarmObserver mNextAlarmObserver; - private final BugreportObserver mBugreportObserver; - private final BrightnessObserver mBrightnessObserver; - private final DisplayInversionObserver mInversionObserver; - private final DisplayColorSpaceObserver mColorSpaceObserver; - private final ZenModeObserver mZenModeObserver; - - private final MediaRouter mMediaRouter; - private final RemoteDisplayRouteCallback mRemoteDisplayRouteCallback; - - private final boolean mHasMobileData; - - private QuickSettingsTileView mUserTile; - private RefreshCallback mUserCallback; - private UserState mUserState = new UserState(); - - private QuickSettingsTileView mTimeTile; - private RefreshCallback mTimeCallback; - private State mTimeState = new State(); - - private QuickSettingsTileView mAlarmTile; - private RefreshCallback mAlarmCallback; - private State mAlarmState = new State(); - - private QuickSettingsTileView mAirplaneModeTile; - private RefreshCallback mAirplaneModeCallback; - private State mAirplaneModeState = new State(); - - private QuickSettingsTileView mZenModeTile; - private RefreshCallback mZenModeCallback; - private ZenModeState mZenModeState = new ZenModeState(); - - private QuickSettingsTileView mWifiTile; - private RefreshCallback mWifiCallback; - private WifiState mWifiState = new WifiState(); - - private QuickSettingsTileView mRemoteDisplayTile; - private RefreshCallback mRemoteDisplayCallback; - private State mRemoteDisplayState = new State(); - - private QuickSettingsTileView mRSSITile; - private RefreshCallback mRSSICallback; - private RSSIState mRSSIState = new RSSIState(); - - private QuickSettingsTileView mBluetoothTile; - private RefreshCallback mBluetoothCallback; - private BluetoothState mBluetoothState = new BluetoothState(); - - private QuickSettingsTileView mBatteryTile; - private RefreshCallback mBatteryCallback; - private BatteryState mBatteryState = new BatteryState(); - - private QuickSettingsTileView mLocationTile; - private RefreshCallback mLocationCallback; - private State mLocationState = new State(); - - private QuickSettingsTileView mImeTile; - private RefreshCallback mImeCallback = null; - private State mImeState = new State(); - - private QuickSettingsTileView mRotationLockTile; - private RefreshCallback mRotationLockCallback; - private RotationLockState mRotationLockState = new RotationLockState(); - - private QuickSettingsTileView mBrightnessTile; - private RefreshCallback mBrightnessCallback; - private BrightnessState mBrightnessState = new BrightnessState(); - - private QuickSettingsTileView mInversionTile; - private RefreshCallback mInversionCallback; - private InversionState mInversionState = new InversionState(); - - private QuickSettingsTileView mColorSpaceTile; - private RefreshCallback mColorSpaceCallback; - private ColorSpaceState mColorSpaceState = new ColorSpaceState(); - - private QuickSettingsTileView mBugreportTile; - private RefreshCallback mBugreportCallback; - private State mBugreportState = new State(); - - private QuickSettingsTileView mSettingsTile; - private RefreshCallback mSettingsCallback; - private State mSettingsState = new State(); - - private QuickSettingsTileView mSslCaCertWarningTile; - private RefreshCallback mSslCaCertWarningCallback; - private State mSslCaCertWarningState = new State(); - - private RotationLockController mRotationLockController; - private int mRotationLockedLabel; - - public QuickSettingsModel(Context context) { - mContext = context; - mHandler = new Handler(); - mUserTracker = new CurrentUserTracker(mContext) { - @Override - public void onUserSwitched(int newUserId) { - mBrightnessObserver.startObserving(); - mInversionObserver.startObserving(); - mColorSpaceObserver.startObserving(); - refreshRotationLockTile(); - onBrightnessLevelChanged(); - onInversionChanged(); - onColorSpaceChanged(); - onNextAlarmChanged(); - onBugreportChanged(); - rebindMediaRouterAsCurrentUser(); - } - }; - mUserTracker.startTracking(); - - mNextAlarmObserver = new NextAlarmObserver(mHandler); - mNextAlarmObserver.startObserving(); - mBugreportObserver = new BugreportObserver(mHandler); - mBugreportObserver.startObserving(); - mBrightnessObserver = new BrightnessObserver(mHandler); - mBrightnessObserver.startObserving(); - mInversionObserver = new DisplayInversionObserver(mHandler); - mInversionObserver.startObserving(); - mColorSpaceObserver = new DisplayColorSpaceObserver(mHandler); - mColorSpaceObserver.startObserving(); - mZenModeObserver = new ZenModeObserver(mHandler); - mZenModeObserver.startObserving(); - - mMediaRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE); - rebindMediaRouterAsCurrentUser(); - - mRemoteDisplayRouteCallback = new RemoteDisplayRouteCallback(); - - ConnectivityManager cm = (ConnectivityManager) - context.getSystemService(Context.CONNECTIVITY_SERVICE); - mHasMobileData = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); - - IntentFilter alarmIntentFilter = new IntentFilter(); - alarmIntentFilter.addAction(Intent.ACTION_ALARM_CHANGED); - context.registerReceiver(mAlarmIntentReceiver, alarmIntentFilter); - } - - void updateResources() { - refreshSettingsTile(); - refreshBatteryTile(); - refreshBluetoothTile(); - refreshBrightnessTile(); - refreshRotationLockTile(); - refreshRssiTile(); - refreshLocationTile(); - } - - // Settings - void addSettingsTile(QuickSettingsTileView view, RefreshCallback cb) { - mSettingsTile = view; - mSettingsCallback = cb; - refreshSettingsTile(); - } - void refreshSettingsTile() { - Resources r = mContext.getResources(); - mSettingsState.label = r.getString(R.string.quick_settings_settings_label); - mSettingsCallback.refreshView(mSettingsTile, mSettingsState); - } - - // User - void addUserTile(QuickSettingsTileView view, RefreshCallback cb) { - mUserTile = view; - mUserCallback = cb; - mUserCallback.refreshView(mUserTile, mUserState); - } - void setUserTileInfo(String name, Drawable avatar) { - mUserState.label = name; - mUserState.avatar = avatar; - mUserCallback.refreshView(mUserTile, mUserState); - } - - // Time - void addTimeTile(QuickSettingsTileView view, RefreshCallback cb) { - mTimeTile = view; - mTimeCallback = cb; - mTimeCallback.refreshView(view, mTimeState); - } - - // Alarm - void addAlarmTile(QuickSettingsTileView view, RefreshCallback cb) { - mAlarmTile = view; - mAlarmCallback = cb; - mAlarmCallback.refreshView(view, mAlarmState); - } - void onAlarmChanged(Intent intent) { - mAlarmState.enabled = intent.getBooleanExtra("alarmSet", false); - mAlarmCallback.refreshView(mAlarmTile, mAlarmState); - } - void onNextAlarmChanged() { - final String alarmText = Settings.System.getStringForUser(mContext.getContentResolver(), - Settings.System.NEXT_ALARM_FORMATTED, - UserHandle.USER_CURRENT); - mAlarmState.label = alarmText; - - // When switching users, this is the only clue we're going to get about whether the - // alarm is actually set, since we won't get the ACTION_ALARM_CHANGED broadcast - mAlarmState.enabled = ! TextUtils.isEmpty(alarmText); - - mAlarmCallback.refreshView(mAlarmTile, mAlarmState); - } - - // Airplane Mode - void addAirplaneModeTile(QuickSettingsTileView view, RefreshCallback cb) { - mAirplaneModeTile = view; - mAirplaneModeTile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mAirplaneModeState.enabled) { - setAirplaneModeState(false); - } else { - setAirplaneModeState(true); - } - } - }); - mAirplaneModeCallback = cb; - int airplaneMode = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0); - onAirplaneModeChanged(airplaneMode != 0); - } - private void setAirplaneModeState(boolean enabled) { - // TODO: Sets the view to be "awaiting" if not already awaiting - - // Change the system setting - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, - enabled ? 1 : 0); - - // Post the intent - Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); - intent.putExtra("state", enabled); - mContext.sendBroadcast(intent); - } - // NetworkSignalChanged callback - @Override - public void onAirplaneModeChanged(boolean enabled) { - // TODO: If view is in awaiting state, disable - Resources r = mContext.getResources(); - mAirplaneModeState.enabled = enabled; - mAirplaneModeState.iconId = (enabled ? - R.drawable.ic_qs_airplane_on : - R.drawable.ic_qs_airplane_off); - mAirplaneModeState.label = r.getString(R.string.quick_settings_airplane_mode_label); - mAirplaneModeCallback.refreshView(mAirplaneModeTile, mAirplaneModeState); - } - - // Zen Mode - void addZenModeTile(QuickSettingsTileView view, RefreshCallback cb) { - mZenModeTile = view; - mZenModeCallback = cb; - onZenModeChanged(); - } - private void onZenModeChanged() { - final int mode = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); - mZenModeState.enabled = mode != Settings.Global.ZEN_MODE_OFF; - mZenModeState.zenMode = mode; - mZenModeState.label = mContext.getString(R.string.zen_mode_title); - mZenModeState.iconId = R.drawable.stat_sys_zen_limited; - mZenModeCallback.refreshView(mZenModeTile, mZenModeState); - } - - // Wifi - void addWifiTile(QuickSettingsTileView view, RefreshCallback cb) { - mWifiTile = view; - mWifiCallback = cb; - mWifiCallback.refreshView(mWifiTile, mWifiState); - } - // Remove the double quotes that the SSID may contain - public static String removeDoubleQuotes(String string) { - if (string == null) return null; - final int length = string.length(); - if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { - return string.substring(1, length - 1); - } - return string; - } - // Remove the period from the network name - public static String removeTrailingPeriod(String string) { - if (string == null) return null; - final int length = string.length(); - if (string.endsWith(".")) { - return string.substring(0, length - 1); - } - return string; - } - // NetworkSignalChanged callback - @Override - public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId, - boolean activityIn, boolean activityOut, - String wifiSignalContentDescription, String enabledDesc) { - // TODO: If view is in awaiting state, disable - Resources r = mContext.getResources(); - - boolean wifiConnected = enabled && (wifiSignalIconId > 0) && (enabledDesc != null); - boolean wifiNotConnected = (wifiSignalIconId > 0) && (enabledDesc == null); - mWifiState.enabled = enabled; - mWifiState.connected = wifiConnected; - mWifiState.activityIn = enabled && activityIn; - mWifiState.activityOut = enabled && activityOut; - if (wifiConnected) { - mWifiState.iconId = wifiSignalIconId; - mWifiState.label = removeDoubleQuotes(enabledDesc); - mWifiState.signalContentDescription = wifiSignalContentDescription; - } else if (wifiNotConnected) { - mWifiState.iconId = R.drawable.ic_qs_wifi_0; - mWifiState.label = r.getString(R.string.quick_settings_wifi_label); - mWifiState.signalContentDescription = r.getString(R.string.accessibility_no_wifi); - } else { - mWifiState.iconId = R.drawable.ic_qs_wifi_no_network; - mWifiState.label = r.getString(R.string.quick_settings_wifi_off_label); - mWifiState.signalContentDescription = r.getString(R.string.accessibility_wifi_off); - } - mWifiCallback.refreshView(mWifiTile, mWifiState); - } - - boolean deviceHasMobileData() { - return mHasMobileData; - } - - // RSSI - void addRSSITile(QuickSettingsTileView view, RefreshCallback cb) { - mRSSITile = view; - mRSSICallback = cb; - mRSSICallback.refreshView(mRSSITile, mRSSIState); - } - // NetworkSignalChanged callback - @Override - public void onMobileDataSignalChanged( - boolean enabled, int mobileSignalIconId, String signalContentDescription, - int dataTypeIconId, boolean activityIn, boolean activityOut, - String dataContentDescription,String enabledDesc) { - if (deviceHasMobileData()) { - // TODO: If view is in awaiting state, disable - Resources r = mContext.getResources(); - mRSSIState.signalIconId = enabled && (mobileSignalIconId > 0) - ? mobileSignalIconId - : R.drawable.ic_qs_signal_no_signal; - mRSSIState.signalContentDescription = enabled && (mobileSignalIconId > 0) - ? signalContentDescription - : r.getString(R.string.accessibility_no_signal); - mRSSIState.dataTypeIconId = enabled && (dataTypeIconId > 0) && !mWifiState.enabled - ? dataTypeIconId - : 0; - mRSSIState.activityIn = enabled && activityIn; - mRSSIState.activityOut = enabled && activityOut; - mRSSIState.dataContentDescription = enabled && (dataTypeIconId > 0) && !mWifiState.enabled - ? dataContentDescription - : r.getString(R.string.accessibility_no_data); - mRSSIState.label = enabled - ? removeTrailingPeriod(enabledDesc) - : r.getString(R.string.quick_settings_rssi_emergency_only); - mRSSICallback.refreshView(mRSSITile, mRSSIState); - } - } - - void refreshRssiTile() { - if (mRSSITile != null) { - // We reinflate the original view due to potential styling changes that may have - // taken place due to a configuration change. - mRSSITile.reinflateContent(LayoutInflater.from(mContext)); - } - } - - // Bluetooth - void addBluetoothTile(QuickSettingsTileView view, RefreshCallback cb) { - mBluetoothTile = view; - mBluetoothCallback = cb; - - final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - mBluetoothState.enabled = adapter.isEnabled(); - mBluetoothState.connected = - (adapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED); - onBluetoothStateChange(mBluetoothState); - } - boolean deviceSupportsBluetooth() { - return (BluetoothAdapter.getDefaultAdapter() != null); - } - // BluetoothController callback - @Override - public void onBluetoothStateChange(boolean on) { - mBluetoothState.enabled = on; - onBluetoothStateChange(mBluetoothState); - } - public void onBluetoothStateChange(BluetoothState bluetoothStateIn) { - // TODO: If view is in awaiting state, disable - Resources r = mContext.getResources(); - mBluetoothState.enabled = bluetoothStateIn.enabled; - mBluetoothState.connected = bluetoothStateIn.connected; - if (mBluetoothState.enabled) { - if (mBluetoothState.connected) { - mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_on; - mBluetoothState.stateContentDescription = r.getString(R.string.accessibility_desc_connected); - } else { - mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_not_connected; - mBluetoothState.stateContentDescription = r.getString(R.string.accessibility_desc_on); - } - mBluetoothState.label = r.getString(R.string.quick_settings_bluetooth_label); - } else { - mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_off; - mBluetoothState.label = r.getString(R.string.quick_settings_bluetooth_off_label); - mBluetoothState.stateContentDescription = r.getString(R.string.accessibility_desc_off); - } - mBluetoothCallback.refreshView(mBluetoothTile, mBluetoothState); - } - void refreshBluetoothTile() { - if (mBluetoothTile != null) { - onBluetoothStateChange(mBluetoothState.enabled); - } - } - - // Battery - void addBatteryTile(QuickSettingsTileView view, RefreshCallback cb) { - mBatteryTile = view; - mBatteryCallback = cb; - mBatteryCallback.refreshView(mBatteryTile, mBatteryState); - } - // BatteryController callback - @Override - public void onBatteryLevelChanged(int level, boolean pluggedIn) { - mBatteryState.batteryLevel = level; - mBatteryState.pluggedIn = pluggedIn; - mBatteryCallback.refreshView(mBatteryTile, mBatteryState); - } - void refreshBatteryTile() { - mBatteryCallback.refreshView(mBatteryTile, mBatteryState); - } - - // Location - void addLocationTile(QuickSettingsTileView view, RefreshCallback cb) { - mLocationTile = view; - mLocationCallback = cb; - mLocationCallback.refreshView(mLocationTile, mLocationState); - } - - void refreshLocationTile() { - if (mLocationTile != null) { - onLocationSettingsChanged(mLocationState.enabled); - } - } - - @Override - public void onLocationSettingsChanged(boolean locationEnabled) { - int textResId = locationEnabled ? R.string.quick_settings_location_label - : R.string.quick_settings_location_off_label; - String label = mContext.getText(textResId).toString(); - int locationIconId = locationEnabled - ? R.drawable.ic_qs_location_on : R.drawable.ic_qs_location_off; - mLocationState.enabled = locationEnabled; - mLocationState.label = label; - mLocationState.iconId = locationIconId; - mLocationCallback.refreshView(mLocationTile, mLocationState); - } - - // Bug report - void addBugreportTile(QuickSettingsTileView view, RefreshCallback cb) { - mBugreportTile = view; - mBugreportCallback = cb; - onBugreportChanged(); - } - // SettingsObserver callback - public void onBugreportChanged() { - final ContentResolver cr = mContext.getContentResolver(); - boolean enabled = false; - try { - enabled = (Settings.Global.getInt(cr, Settings.Global.BUGREPORT_IN_POWER_MENU) != 0); - } catch (SettingNotFoundException e) { - } - - mBugreportState.enabled = enabled && mUserTracker.isCurrentUserOwner(); - mBugreportCallback.refreshView(mBugreportTile, mBugreportState); - } - - // Remote Display - void addRemoteDisplayTile(QuickSettingsTileView view, RefreshCallback cb) { - mRemoteDisplayTile = view; - mRemoteDisplayCallback = cb; - mRemoteDisplayTile.setOnPrepareListener(new QuickSettingsTileView.OnPrepareListener() { - @Override - public void onPrepare() { - mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, - mRemoteDisplayRouteCallback, - MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); - updateRemoteDisplays(); - } - @Override - public void onUnprepare() { - mMediaRouter.removeCallback(mRemoteDisplayRouteCallback); - } - }); - - updateRemoteDisplays(); - } - - private void rebindMediaRouterAsCurrentUser() { - mMediaRouter.rebindAsUser(mUserTracker.getCurrentUserId()); - } - - private void updateRemoteDisplays() { - MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute( - MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY); - boolean enabled = connectedRoute != null - && connectedRoute.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY); - boolean connecting; - if (enabled) { - connecting = connectedRoute.isConnecting(); - } else { - connectedRoute = null; - connecting = false; - enabled = mMediaRouter.isRouteAvailable(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, - MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); - } - - mRemoteDisplayState.enabled = enabled; - if (connectedRoute != null) { - mRemoteDisplayState.label = connectedRoute.getName().toString(); - mRemoteDisplayState.iconId = connecting ? - R.drawable.ic_qs_cast_connecting : R.drawable.ic_qs_cast_connected; - } else { - mRemoteDisplayState.label = mContext.getString( - R.string.quick_settings_remote_display_no_connection_label); - mRemoteDisplayState.iconId = R.drawable.ic_qs_cast_available; - } - mRemoteDisplayCallback.refreshView(mRemoteDisplayTile, mRemoteDisplayState); - } - - // IME - void addImeTile(QuickSettingsTileView view, RefreshCallback cb) { - mImeTile = view; - mImeCallback = cb; - mImeCallback.refreshView(mImeTile, mImeState); - } - /* This implementation is taken from - InputMethodManagerService.needsToShowImeSwitchOngoingNotification(). */ - private boolean needsToShowImeSwitchOngoingNotification(InputMethodManager imm) { - List<InputMethodInfo> imis = imm.getEnabledInputMethodList(); - final int N = imis.size(); - if (N > 2) return true; - if (N < 1) return false; - int nonAuxCount = 0; - int auxCount = 0; - InputMethodSubtype nonAuxSubtype = null; - InputMethodSubtype auxSubtype = null; - for(int i = 0; i < N; ++i) { - final InputMethodInfo imi = imis.get(i); - final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(imi, - true); - final int subtypeCount = subtypes.size(); - if (subtypeCount == 0) { - ++nonAuxCount; - } else { - for (int j = 0; j < subtypeCount; ++j) { - final InputMethodSubtype subtype = subtypes.get(j); - if (!subtype.isAuxiliary()) { - ++nonAuxCount; - nonAuxSubtype = subtype; - } else { - ++auxCount; - auxSubtype = subtype; - } - } - } - } - if (nonAuxCount > 1 || auxCount > 1) { - return true; - } else if (nonAuxCount == 1 && auxCount == 1) { - if (nonAuxSubtype != null && auxSubtype != null - && (nonAuxSubtype.getLocale().equals(auxSubtype.getLocale()) - || auxSubtype.overridesImplicitlyEnabledSubtype() - || nonAuxSubtype.overridesImplicitlyEnabledSubtype()) - && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) { - return false; - } - return true; - } - return false; - } - void onImeWindowStatusChanged(boolean visible) { - InputMethodManager imm = - (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); - List<InputMethodInfo> imis = imm.getInputMethodList(); - - mImeState.enabled = (visible && needsToShowImeSwitchOngoingNotification(imm)); - mImeState.label = getCurrentInputMethodName(mContext, mContext.getContentResolver(), - imm, imis, mContext.getPackageManager()); - if (mImeCallback != null) { - mImeCallback.refreshView(mImeTile, mImeState); - } - } - private static String getCurrentInputMethodName(Context context, ContentResolver resolver, - InputMethodManager imm, List<InputMethodInfo> imis, PackageManager pm) { - if (resolver == null || imis == null) return null; - final String currentInputMethodId = Settings.Secure.getString(resolver, - Settings.Secure.DEFAULT_INPUT_METHOD); - if (TextUtils.isEmpty(currentInputMethodId)) return null; - for (InputMethodInfo imi : imis) { - if (currentInputMethodId.equals(imi.getId())) { - final InputMethodSubtype subtype = imm.getCurrentInputMethodSubtype(); - final CharSequence summary = subtype != null - ? subtype.getDisplayName(context, imi.getPackageName(), - imi.getServiceInfo().applicationInfo) - : context.getString(R.string.quick_settings_ime_label); - return summary.toString(); - } - } - return null; - } - - // Rotation lock - void addRotationLockTile(QuickSettingsTileView view, - RotationLockController rotationLockController, - RefreshCallback cb) { - mRotationLockTile = view; - mRotationLockCallback = cb; - mRotationLockController = rotationLockController; - final int lockOrientation = mRotationLockController.getRotationLockOrientation(); - mRotationLockedLabel = lockOrientation == Configuration.ORIENTATION_PORTRAIT - ? R.string.quick_settings_rotation_locked_portrait_label - : lockOrientation == Configuration.ORIENTATION_LANDSCAPE - ? R.string.quick_settings_rotation_locked_landscape_label - : R.string.quick_settings_rotation_locked_label; - onRotationLockChanged(); - } - void onRotationLockChanged() { - onRotationLockStateChanged(mRotationLockController.isRotationLocked(), - mRotationLockController.isRotationLockAffordanceVisible()); - } - @Override - public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) { - mRotationLockState.visible = affordanceVisible; - mRotationLockState.enabled = rotationLocked; - mRotationLockState.iconId = rotationLocked - ? R.drawable.ic_qs_rotation_locked - : R.drawable.ic_qs_auto_rotate; - mRotationLockState.label = rotationLocked - ? mContext.getString(mRotationLockedLabel) - : mContext.getString(R.string.quick_settings_rotation_unlocked_label); - mRotationLockCallback.refreshView(mRotationLockTile, mRotationLockState); - } - void refreshRotationLockTile() { - if (mRotationLockTile != null) { - onRotationLockChanged(); - } - } - - // Brightness - void addBrightnessTile(QuickSettingsTileView view, RefreshCallback cb) { - mBrightnessTile = view; - mBrightnessCallback = cb; - onBrightnessLevelChanged(); - } - @Override - public void onBrightnessLevelChanged() { - Resources r = mContext.getResources(); - int mode = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, - mUserTracker.getCurrentUserId()); - mBrightnessState.autoBrightness = - (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mBrightnessState.iconId = mBrightnessState.autoBrightness - ? R.drawable.ic_qs_brightness_auto_on - : R.drawable.ic_qs_brightness_auto_off; - mBrightnessState.label = r.getString(R.string.quick_settings_brightness_label); - mBrightnessCallback.refreshView(mBrightnessTile, mBrightnessState); - } - void refreshBrightnessTile() { - onBrightnessLevelChanged(); - } - - // Color inversion - void addInversionTile(QuickSettingsTileView view, RefreshCallback cb) { - mInversionTile = view; - mInversionCallback = cb; - onInversionChanged(); - } - public void onInversionChanged() { - final Resources res = mContext.getResources(); - final ContentResolver cr = mContext.getContentResolver(); - final int currentUserId = mUserTracker.getCurrentUserId(); - final boolean quickSettingEnabled = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED, 0, - currentUserId) == 1; - final boolean enabled = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, currentUserId) == 1; - mInversionState.enabled = quickSettingEnabled; - mInversionState.toggled = enabled; - // TODO: Add real icon assets. - mInversionState.iconId = enabled ? R.drawable.ic_qs_inversion_on - : R.drawable.ic_qs_inversion_off; - mInversionState.label = res.getString(R.string.quick_settings_inversion_label); - mInversionCallback.refreshView(mInversionTile, mInversionState); - } - - // Color space adjustment - void addColorSpaceTile(QuickSettingsTileView view, RefreshCallback cb) { - mColorSpaceTile = view; - mColorSpaceCallback = cb; - onColorSpaceChanged(); - } - public void onColorSpaceChanged() { - final Resources res = mContext.getResources(); - final ContentResolver cr = mContext.getContentResolver(); - final int currentUserId = mUserTracker.getCurrentUserId(); - final boolean quickSettingEnabled = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_QUICK_SETTING_ENABLED, 0, - currentUserId) == 1; - final boolean enabled = Settings.Secure.getIntForUser(cr, - Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, currentUserId) == 1; - final int type = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, 0, currentUserId); - mColorSpaceState.enabled = quickSettingEnabled; - mColorSpaceState.toggled = enabled; - mColorSpaceState.type = type; - // TODO: Add real icon assets. - mColorSpaceState.iconId = enabled ? R.drawable.ic_qs_color_space_on - : R.drawable.ic_qs_color_space_off; - mColorSpaceState.label = res.getString(R.string.quick_settings_color_space_label); - mColorSpaceCallback.refreshView(mColorSpaceTile, mColorSpaceState); - } - - // SSL CA Cert warning. - public void addSslCaCertWarningTile(QuickSettingsTileView view, RefreshCallback cb) { - mSslCaCertWarningTile = view; - mSslCaCertWarningCallback = cb; - // Set a sane default while we wait for the AsyncTask to finish (no cert). - setSslCaCertWarningTileInfo(false, true); - } - public void setSslCaCertWarningTileInfo(boolean hasCert, boolean isManaged) { - Resources r = mContext.getResources(); - mSslCaCertWarningState.enabled = hasCert; - if (isManaged) { - mSslCaCertWarningState.iconId = R.drawable.ic_qs_certificate_info; - } else { - mSslCaCertWarningState.iconId = android.R.drawable.stat_notify_error; - } - mSslCaCertWarningState.label = r.getString(R.string.ssl_ca_cert_warning); - mSslCaCertWarningCallback.refreshView(mSslCaCertWarningTile, mSslCaCertWarningState); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java deleted file mode 100644 index 175805a..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.widget.ScrollView; - -public class QuickSettingsScrollView extends ScrollView { - - public QuickSettingsScrollView(Context context) { - super(context); - } - - public QuickSettingsScrollView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public QuickSettingsScrollView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - // Y U NO PROTECTED - private int getScrollRange() { - int scrollRange = 0; - if (getChildCount() > 0) { - View child = getChildAt(0); - scrollRange = Math.max(0, - child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop())); - } - return scrollRange; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - final int range = getScrollRange(); - if (range == 0) { - return false; - } - - return super.onTouchEvent(ev); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java deleted file mode 100644 index ad18294..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.content.Context; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewParent; -import android.widget.FrameLayout; - -/** - * - */ -class QuickSettingsTileView extends FrameLayout { - private static final String TAG = "QuickSettingsTileView"; - - private int mContentLayoutId; - private int mColSpan; - private boolean mPrepared; - private OnPrepareListener mOnPrepareListener; - - public QuickSettingsTileView(Context context, AttributeSet attrs) { - super(context, attrs); - - mContentLayoutId = -1; - mColSpan = 1; - } - - void setColumnSpan(int span) { - mColSpan = span; - } - - int getColumnSpan() { - return mColSpan; - } - - void setContent(int layoutId, LayoutInflater inflater) { - mContentLayoutId = layoutId; - inflater.inflate(layoutId, this); - } - - void reinflateContent(LayoutInflater inflater) { - if (mContentLayoutId != -1) { - removeAllViews(); - setContent(mContentLayoutId, inflater); - } else { - Log.e(TAG, "Not reinflating content: No layoutId set"); - } - } - - @Override - public void setVisibility(int vis) { - if (QuickSettings.DEBUG_GONE_TILES) { - if (vis == View.GONE) { - vis = View.VISIBLE; - setAlpha(0.25f); - setEnabled(false); - } else { - setAlpha(1f); - setEnabled(true); - } - } - super.setVisibility(vis); - } - - public void setOnPrepareListener(OnPrepareListener listener) { - if (mOnPrepareListener != listener) { - mOnPrepareListener = listener; - mPrepared = false; - post(new Runnable() { - @Override - public void run() { - updatePreparedState(); - } - }); - } - } - - @Override - protected void onVisibilityChanged(View changedView, int visibility) { - super.onVisibilityChanged(changedView, visibility); - updatePreparedState(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - updatePreparedState(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - updatePreparedState(); - } - - private void updatePreparedState() { - if (mOnPrepareListener != null) { - if (isParentVisible()) { - if (!mPrepared) { - mPrepared = true; - mOnPrepareListener.onPrepare(); - } - } else if (mPrepared) { - mPrepared = false; - mOnPrepareListener.onUnprepare(); - } - } - } - - private boolean isParentVisible() { - if (!isAttachedToWindow()) { - return false; - } - for (ViewParent current = getParent(); current instanceof View; - current = current.getParent()) { - View view = (View)current; - if (view.getVisibility() != VISIBLE) { - return false; - } - } - return true; - } - - /** - * Called when the view's parent becomes visible or invisible to provide - * an opportunity for the client to provide new content. - */ - public interface OnPrepareListener { - void onPrepare(); - void onUnprepare(); - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java index 5527473..2305445 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -28,6 +28,7 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import com.android.systemui.R; +import com.android.systemui.qs.QSPanel; import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.ToggleSlider; import com.android.systemui.statusbar.policy.UserInfoController; @@ -60,6 +61,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL private ActivityStarter mActivityStarter; private BrightnessController mBrightnessController; + private QSPanel mQSPanel; private final Rect mClipBounds = new Rect(); private final Outline mOutline = new Outline(); @@ -115,6 +117,9 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL updateVisibilities(); updateSystemIconsLayoutParams(); updateBrightnessControllerState(); + if (mQSPanel != null) { + mQSPanel.setExpanded(expanded); + } } } @@ -259,4 +264,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL private void startSettingsActivity() { mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS)); } + + public void setQSPanel(QSPanel qsp) { + mQSPanel = qsp; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 48c54fc..77b760e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -20,11 +20,13 @@ import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import android.util.Slog; +import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import com.android.internal.policy.IKeyguardShowCallback; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; /** @@ -50,6 +52,12 @@ public class StatusBarKeyguardViewManager { private boolean mShowing; private boolean mOccluded; + private boolean mFirstUpdate = true; + private boolean mLastShowing; + private boolean mLastOccluded; + private boolean mLastBouncerShowing; + private boolean mLastBouncerDismissible; + public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils) { mContext = context; @@ -207,23 +215,55 @@ public class StatusBarKeyguardViewManager { private void updateStates() { int vis = mContainer.getSystemUiVisibility(); - boolean bouncerDismissable = mBouncer.isShowing() && !mBouncer.needsFullscreenBouncer(); - if (bouncerDismissable || !mShowing) { - mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK); - } else { - mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK); - } - if (mPhoneStatusBar.getNavigationBarView() != null) { - if (!(mShowing && !mOccluded) || mBouncer.isShowing()) { - mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE); + boolean showing = mShowing; + boolean occluded = mOccluded; + boolean bouncerShowing = mBouncer.isShowing(); + boolean bouncerDismissible = bouncerShowing && !mBouncer.needsFullscreenBouncer(); + + if ((bouncerDismissible || !showing) != (mLastBouncerDismissible || !mLastShowing) + || mFirstUpdate) { + if (bouncerDismissible || !showing) { + mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK); } else { - mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE); + mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK); } } - mPhoneStatusBar.setBouncerShowing(mBouncer.isShowing()); + if ((!(showing && !occluded) || bouncerShowing) + != (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing) || mFirstUpdate) { + if (mPhoneStatusBar.getNavigationBarView() != null) { + if (!(showing && !occluded) || bouncerShowing) { + mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE); + } else { + mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE); + } + } + } + + if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) { + mStatusBarWindowManager.setBouncerShowing(bouncerShowing); + mPhoneStatusBar.setBouncerShowing(bouncerShowing); + } + + KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); + if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) { + updateMonitor.sendKeyguardVisibilityChanged(showing && !occluded); + } + if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) { + updateMonitor.sendKeyguardBouncerChanged(bouncerShowing); + } + + mFirstUpdate = false; + mLastShowing = showing; + mLastOccluded = occluded; + mLastBouncerShowing = bouncerShowing; + mLastBouncerDismissible = bouncerDismissible; } public boolean onMenuPressed() { return mBouncer.onMenuPressed(); } + + public boolean interceptMediaKey(KeyEvent event) { + return mBouncer.interceptMediaKey(event); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index 8809d18..46a637b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -111,7 +111,8 @@ public class StatusBarWindowManager { } private void applyFocusableFlag(State state) { - if (state.isKeyguardShowingAndNotOccluded() && state.keyguardNeedsInput) { + if (state.isKeyguardShowingAndNotOccluded() && state.keyguardNeedsInput + && state.bouncerShowing) { mLp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mLp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } else if (state.isKeyguardShowingAndNotOccluded() || state.statusBarFocusable) { @@ -196,6 +197,11 @@ public class StatusBarWindowManager { apply(mCurrentState); } + public void setBouncerShowing(boolean showing) { + mCurrentState.bouncerShowing = showing; + apply(mCurrentState); + } + /** * @param state The {@link StatusBarState} of the status bar. */ @@ -211,6 +217,7 @@ public class StatusBarWindowManager { boolean statusBarExpanded; boolean statusBarFocusable; long keyguardUserActivityTimeout; + boolean bouncerShowing; /** * The {@link BaseStatusBar} state from the status bar. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index e802d18..b51626d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -103,6 +103,9 @@ public class StatusBarWindowView extends FrameLayout { return mService.onMenuPressed(); } } + if (mService.interceptMediaKey(event)) { + return true; + } return super.dispatchKeyEvent(event); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java deleted file mode 100644 index ff921cd..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.PorterDuff.Mode; -import android.graphics.Typeface; -import android.graphics.drawable.ShapeDrawable; -import android.graphics.drawable.shapes.OvalShape; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextPaint; -import android.text.method.LinkMovementMethod; -import android.text.style.URLSpan; -import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.Switch; -import android.widget.TextView; -import android.widget.Toast; - -import com.android.systemui.R; -import com.android.systemui.statusbar.phone.ZenModeView.Adapter.ExitCondition; - -public class ZenModeView extends RelativeLayout { - private static final String TAG = ZenModeView.class.getSimpleName(); - private static final boolean DEBUG = false; - - public static final int BACKGROUND = 0xff282828; - - private static final Typeface CONDENSED = - Typeface.create("sans-serif-condensed", Typeface.NORMAL); - private static final int GRAY = 0xff999999; //TextAppearance.StatusBar.Expanded.Network - private static final int DARK_GRAY = 0xff333333; - - private static final long DURATION = new ValueAnimator().getDuration(); - private static final long PAGER_DURATION = DURATION / 2; - private static final long CLOSE_DELAY = 600; - private static final long AUTO_ACTIVATE_DELAY = 100; - - private final Context mContext; - private final TextView mModeText; - private final Switch mModeSwitch; - private final View mDivider; - private final UntilPager mUntilPager; - private final ProgressDots mProgressDots; - private final View mDivider2; - private final TextView mSettingsButton; - - private Adapter mAdapter; - private boolean mInit; - private boolean mAutoActivate; - - public ZenModeView(Context context) { - this(context, null); - } - - public ZenModeView(Context context, AttributeSet attrs) { - super(context, attrs); - if (DEBUG) log("new %s()", getClass().getSimpleName()); - mContext = context; - - final int iconSize = mContext.getResources() - .getDimensionPixelSize(com.android.internal.R.dimen.notification_large_icon_width); - final int topRowSize = iconSize * 2 / 3; - final int p = topRowSize / 3; - - LayoutParams lp = null; - - mModeText = new TextView(mContext); - mModeText.setText(R.string.zen_mode_title); - mModeText.setId(android.R.id.title); - mModeText.setTextColor(GRAY); - mModeText.setTypeface(CONDENSED); - mModeText.setAllCaps(true); - mModeText.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); - mModeText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mModeText.getTextSize() * 1.5f); - lp = new LayoutParams(LayoutParams.WRAP_CONTENT, topRowSize); - lp.leftMargin = p; - addView(mModeText, lp); - - mModeSwitch = new Switch(mContext); - mModeSwitch.setSwitchPadding(0); - mModeSwitch.setSwitchTypeface(CONDENSED); - lp = new LayoutParams(LayoutParams.WRAP_CONTENT, topRowSize); - lp.topMargin = p; - lp.rightMargin = p; - lp.addRule(ALIGN_PARENT_RIGHT); - lp.addRule(ALIGN_BASELINE, mModeText.getId()); - addView(mModeSwitch, lp); - mModeSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mAdapter.setMode(isChecked); - if (!mInit) return; - postDelayed(new Runnable(){ - @Override - public void run() { - mAdapter.close(); - } - }, CLOSE_DELAY); - } - }); - - mDivider = new View(mContext); - mDivider.setId(android.R.id.empty); - mDivider.setBackgroundColor(GRAY); - lp = new LayoutParams(LayoutParams.MATCH_PARENT, 2); - lp.addRule(BELOW, mModeText.getId()); - lp.bottomMargin = p; - addView(mDivider, lp); - - mUntilPager = new UntilPager(mContext, iconSize * 3 / 4); - mUntilPager.setId(android.R.id.tabhost); - lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - lp.leftMargin = lp.rightMargin = iconSize / 2; - lp.addRule(CENTER_HORIZONTAL); - lp.addRule(BELOW, mDivider.getId()); - addView(mUntilPager, lp); - - mProgressDots = new ProgressDots(mContext, iconSize / 5); - mProgressDots.setId(android.R.id.progress); - lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - lp.addRule(CENTER_HORIZONTAL); - lp.addRule(BELOW, mUntilPager.getId()); - addView(mProgressDots, lp); - - mDivider2 = new View(mContext); - mDivider2.setId(android.R.id.widget_frame); - mDivider2.setBackgroundColor(GRAY); - lp = new LayoutParams(LayoutParams.MATCH_PARENT, 2); - lp.addRule(BELOW, mProgressDots.getId()); - addView(mDivider2, lp); - - mSettingsButton = new TextView(mContext); - mSettingsButton.setTypeface(CONDENSED); - mSettingsButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSettingsButton.getTextSize() * 1.3f); - mSettingsButton.setPadding(p, p, p, p); - mSettingsButton.setText("More settings..."); - lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - lp.addRule(BELOW, mDivider2.getId()); - addView(mSettingsButton, lp); - mSettingsButton.setOnTouchListener(new OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - mSettingsButton.setBackgroundColor(DARK_GRAY); - } else if (event.getAction() == MotionEvent.ACTION_UP) { - mSettingsButton.setBackground(null); - if (mAdapter != null) { - mAdapter.configure(); - } - } - return true; - } - }); - } - - public void setAdapter(Adapter adapter) { - mAdapter = adapter; - mAdapter.setCallbacks(new Adapter.Callbacks() { - @Override - public void onChanged() { - post(new Runnable() { - @Override - public void run() { - updateState(true); - } - }); - } - }); - updateState(false); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (mAutoActivate) { - mAutoActivate = false; - postDelayed(new Runnable() { - @Override - public void run() { - if (!mModeSwitch.isChecked()) { - mInit = false; - mModeSwitch.setChecked(true); - } - } - }, AUTO_ACTIVATE_DELAY); - } - } - - @Override - protected void onDetachedFromWindow() { - if (mAdapter != null) { - mAdapter.dispose(); - } - } - - public void setAutoActivate(boolean value) { - mAutoActivate = value; - } - - private void updateState(boolean animate) { - mUntilPager.updateState(); - mModeSwitch.setChecked(mAdapter.getMode()); - mInit = true; - } - - private static void log(String msg, Object... args) { - Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args)); - } - - private final class UntilView extends FrameLayout { - private static final boolean SUPPORT_LINKS = false; - - private final TextView mText; - public UntilView(Context context) { - super(context); - mText = new TextView(mContext); - mText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mText.getTextSize() * 1.3f); - mText.setTypeface(CONDENSED); - mText.setTextColor(GRAY); - mText.setGravity(Gravity.CENTER); - addView(mText); - } - - public void setExitCondition(final ExitCondition ec) { - SpannableStringBuilder ss = new SpannableStringBuilder(ec.summary); - if (SUPPORT_LINKS && ec.action != null) { - ss.setSpan(new CustomLinkSpan() { - @Override - public void onClick() { - // TODO wire up links - Toast.makeText(mContext, ec.action, Toast.LENGTH_SHORT).show(); - } - }, 0, ss.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - mText.setMovementMethod(LinkMovementMethod.getInstance()); - } else { - mText.setMovementMethod(null); - } - mText.setText(ss); - } - } - - private final class ProgressDots extends LinearLayout { - private final int mDotSize; - public ProgressDots(Context context, int dotSize) { - super(context); - setOrientation(HORIZONTAL); - mDotSize = dotSize; - } - - private void updateState(int current, int count) { - while (getChildCount() < count) { - View dot = new View(mContext); - OvalShape s = new OvalShape(); - ShapeDrawable sd = new ShapeDrawable(s); - - dot.setBackground(sd); - LayoutParams lp = new LayoutParams(mDotSize, mDotSize); - lp.leftMargin = lp.rightMargin = mDotSize / 2; - lp.topMargin = lp.bottomMargin = mDotSize * 2 / 3; - addView(dot, lp); - } - while (getChildCount() > count) { - removeViewAt(getChildCount() - 1); - } - final int N = getChildCount(); - for (int i = 0; i < N; i++) { - final int color = current == i ? GRAY : DARK_GRAY; - ((ShapeDrawable)getChildAt(i).getBackground()).setColorFilter(color, Mode.ADD); - } - } - } - - private final class UntilPager extends RelativeLayout { - private final UntilView[] mViews; - private int mCurrent; - private float mDownX; - - public UntilPager(Context context, int iconSize) { - super(context); - mViews = new UntilView[3]; - for (int i = 0; i < mViews.length; i++) { - UntilView v = new UntilView(mContext); - LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, iconSize); - addView(v, lp); - mViews[i] = v; - } - updateState(); - addOnLayoutChangeListener(new OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, - int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - if (left != oldLeft || right != oldRight) { - updateState(); - } - } - }); - setBackgroundColor(DARK_GRAY); - } - - private void updateState() { - if (mAdapter == null) { - return; - } - UntilView current = mViews[mCurrent]; - current.setExitCondition(mAdapter.getExitCondition(0)); - UntilView next = mViews[mCurrent + 1 % 3]; - next.setExitCondition(mAdapter.getExitCondition(1)); - UntilView prev = mViews[mCurrent + 2 % 3]; - prev.setExitCondition(mAdapter.getExitCondition(-1)); - position(0, false); - mProgressDots.updateState(mAdapter.getExitConditionIndex(), - mAdapter.getExitConditionCount()); - } - - private void position(float dx, boolean animate) { - int w = getWidth(); - UntilView current = mViews[mCurrent]; - UntilView next = mViews[mCurrent + 1 % 3]; - UntilView prev = mViews[mCurrent + 2 % 3]; - if (animate) { - current.animate().setDuration(PAGER_DURATION).translationX(dx).start(); - next.animate().setDuration(PAGER_DURATION).translationX(w + dx).start(); - prev.animate().setDuration(PAGER_DURATION).translationX(-w + dx).start(); - } else { - current.setTranslationX(dx); - next.setTranslationX(w + dx); - prev.setTranslationX(-w + dx); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (DEBUG) log("onTouchEvent " + MotionEvent.actionToString(event.getAction())); - if (event.getAction() == MotionEvent.ACTION_DOWN) { - mDownX = event.getX(); - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - float dx = event.getX() - mDownX; - position(dx, false); - } else if (event.getAction() == MotionEvent.ACTION_UP - || event.getAction() == MotionEvent.ACTION_CANCEL) { - float dx = event.getX() - mDownX; - int d = Math.abs(dx) < getWidth() / 3 ? 0 : Math.signum(dx) > 0 ? -1 : 1; - if (d != 0 && mAdapter.getExitConditionCount() > 1) { - mAdapter.select(mAdapter.getExitCondition(d)); - } else { - position(0, true); - } - } - return true; - } - } - - private abstract static class CustomLinkSpan extends URLSpan { - abstract public void onClick(); - - public CustomLinkSpan() { - super("#"); - } - - @Override - public void updateDrawState(TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.bgColor = BACKGROUND; - } - - @Override - public void onClick(View widget) { - onClick(); - } - } - - public interface Adapter { - void configure(); - void close(); - boolean getMode(); - void setMode(boolean mode); - void select(ExitCondition ec); - void init(); - void dispose(); - void setCallbacks(Callbacks callbacks); - ExitCondition getExitCondition(int d); - int getExitConditionCount(); - int getExitConditionIndex(); - - public static class ExitCondition { - public String summary; - public String line1; - public String line2; - public String action; - public Object tag; - } - - public interface Callbacks { - void onChanged(); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java deleted file mode 100644 index 8748888..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.app.INotificationManager; -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Handler; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.provider.Settings; -import android.service.notification.Condition; -import android.service.notification.IConditionListener; -import android.util.ArrayMap; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public abstract class ZenModeViewAdapter implements ZenModeView.Adapter { - private static final String TAG = "ZenModeViewAdapter"; - - private final Context mContext; - private final ContentResolver mResolver; - private final Handler mHandler = new Handler(); - private final SettingsObserver mObserver; - private final List<ExitCondition> mExits = new ArrayList<ExitCondition>(Arrays.asList( - newExit("Until you turn this off", "Until", "You turn this off", null))); - private final INotificationManager mNoMan; - private final ArrayMap<Uri, Condition> mConditions = new ArrayMap<Uri, Condition>(); - - private Callbacks mCallbacks; - private int mExitIndex; - private boolean mMode; - - public ZenModeViewAdapter(Context context) { - mContext = context; - mResolver = mContext.getContentResolver(); - mObserver = new SettingsObserver(mHandler); - mNoMan = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - try { - mNoMan.requestZenModeConditions(mListener, Condition.FLAG_RELEVANT_NOW); - } catch (RemoteException e) { - // noop - } - mObserver.init(); - init(); - } - - @Override - public boolean getMode() { - return mMode; - } - - @Override - public void setMode(boolean mode) { - if (mode == mMode) return; - mMode = mode; - final int v = mMode ? Settings.Global.ZEN_MODE_ON : Settings.Global.ZEN_MODE_OFF; - AsyncTask.execute(new Runnable() { - @Override - public void run() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.ZEN_MODE, v); - } - }); - dispatchChanged(); - } - - @Override - public void init() { - if (mExitIndex != 0) { - mExitIndex = 0; - dispatchChanged(); - } - setZenModeCondition(); - } - - @Override - public void dispose() { - try { - mNoMan.requestZenModeConditions(mListener, 0 /*none*/); - } catch (RemoteException e) { - // noop - } - } - - private void dispatchChanged() { - mHandler.removeCallbacks(mChanged); - mHandler.post(mChanged); - } - - @Override - public void setCallbacks(final Callbacks callbacks) { - mHandler.post(new Runnable() { - @Override - public void run() { - mCallbacks = callbacks; - } - }); - } - - @Override - public ExitCondition getExitCondition(int d) { - final int n = mExits.size(); - final int i = (n + (mExitIndex + (int)Math.signum(d))) % n; - return mExits.get(i); - } - - @Override - public int getExitConditionCount() { - return mExits.size(); - } - - @Override - public int getExitConditionIndex() { - return mExitIndex; - } - - @Override - public void select(ExitCondition ec) { - final int i = mExits.indexOf(ec); - if (i == -1 || i == mExitIndex) { - return; - } - mExitIndex = i; - dispatchChanged(); - setZenModeCondition(); - } - - private void setZenModeCondition() { - if (mExitIndex < 0 || mExitIndex >= mExits.size()) { - Log.w(TAG, "setZenModeCondition to bad index " + mExitIndex + " of " + mExits.size()); - return; - } - final Uri conditionUri = (Uri) mExits.get(mExitIndex).tag; - try { - mNoMan.setZenModeCondition(conditionUri); - } catch (RemoteException e) { - // noop - } - } - - private static ExitCondition newExit(String summary, String line1, String line2, Object tag) { - final ExitCondition rt = new ExitCondition(); - rt.summary = summary; - rt.line1 = line1; - rt.line2 = line2; - rt.tag = tag; - return rt; - } - - private final Runnable mChanged = new Runnable() { - public void run() { - if (mCallbacks == null) { - return; - } - try { - mCallbacks.onChanged(); - } catch (Throwable t) { - Log.w(TAG, "Error dispatching onChanged to " + mCallbacks, t); - } - } - }; - - private final class SettingsObserver extends ContentObserver { - public SettingsObserver(Handler handler) { - super(handler); - } - - public void init() { - loadSettings(); - mResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ZEN_MODE), - false, this); - } - - @Override - public void onChange(boolean selfChange) { - loadSettings(); - mChanged.run(); // already on handler - } - - private void loadSettings() { - mMode = getModeFromSetting(); - } - - private boolean getModeFromSetting() { - final int v = Settings.Global.getInt(mResolver, - Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); - return v != Settings.Global.ZEN_MODE_OFF; - } - } - - private final IConditionListener mListener = new IConditionListener.Stub() { - @Override - public void onConditionsReceived(Condition[] conditions) { - if (conditions == null || conditions.length == 0) return; - for (Condition c : conditions) { - mConditions.put(c.id, c); - } - for (int i = mExits.size() - 1; i > 0; i--) { - mExits.remove(i); - } - for (Condition c : mConditions.values()) { - mExits.add(newExit(c.summary, c.line1, c.line2, c.id)); - } - dispatchChanged(); - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java index 0e53f0d..f4145cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2014 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. @@ -16,87 +16,14 @@ package com.android.systemui.statusbar.policy; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback; -import android.bluetooth.BluetoothDevice; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; +public interface BluetoothController { + void addStateChangedCallback(BluetoothStateChangeCallback callback); + void removeStateChangedCallback(BluetoothStateChangeCallback callback); -public class BluetoothController extends BroadcastReceiver { - private static final String TAG = "StatusBar.BluetoothController"; - - private boolean mEnabled = false; - - private Set<BluetoothDevice> mBondedDevices = new HashSet<BluetoothDevice>(); - - private ArrayList<BluetoothStateChangeCallback> mChangeCallbacks = - new ArrayList<BluetoothStateChangeCallback>(); - - public BluetoothController(Context context) { - - IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); - filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); - context.registerReceiver(this, filter); - - final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null) { - handleAdapterStateChange(adapter.getState()); - } - fireCallbacks(); - updateBondedBluetoothDevices(); - } - - public void addStateChangedCallback(BluetoothStateChangeCallback cb) { - mChangeCallbacks.add(cb); - } - - public Set<BluetoothDevice> getBondedBluetoothDevices() { - return mBondedDevices; - } - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - - if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { - handleAdapterStateChange( - intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)); - } - fireCallbacks(); - updateBondedBluetoothDevices(); - } - - private void updateBondedBluetoothDevices() { - mBondedDevices.clear(); - - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null) { - Set<BluetoothDevice> devices = adapter.getBondedDevices(); - if (devices != null) { - for (BluetoothDevice device : devices) { - if (device.getBondState() != BluetoothDevice.BOND_NONE) { - mBondedDevices.add(device); - } - } - } - } - } - - private void handleAdapterStateChange(int adapterState) { - mEnabled = (adapterState == BluetoothAdapter.STATE_ON); - } - - private void fireCallbacks() { - for (BluetoothStateChangeCallback cb : mChangeCallbacks) { - cb.onBluetoothStateChange(mEnabled); - } - } + boolean isBluetoothSupported(); + boolean isBluetoothEnabled(); + boolean isBluetoothConnected(); + void setBluetoothEnabled(boolean enabled); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java new file mode 100644 index 0000000..1c7119f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +public class BluetoothControllerImpl extends BroadcastReceiver implements BluetoothController { + private static final String TAG = "StatusBar.BluetoothController"; + + private final BluetoothAdapter mAdapter; + + private boolean mEnabled = false; + + private Set<BluetoothDevice> mBondedDevices = new HashSet<BluetoothDevice>(); + + private ArrayList<BluetoothStateChangeCallback> mChangeCallbacks = + new ArrayList<BluetoothStateChangeCallback>(); + + public BluetoothControllerImpl(Context context) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + context.registerReceiver(this, filter); + + final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + handleAdapterStateChange(adapter.getState()); + } + fireCallbacks(); + updateBondedBluetoothDevices(); + } + + public void addStateChangedCallback(BluetoothStateChangeCallback cb) { + mChangeCallbacks.add(cb); + } + + @Override + public void removeStateChangedCallback(BluetoothStateChangeCallback cb) { + mChangeCallbacks.remove(cb); + } + + @Override + public boolean isBluetoothEnabled() { + return mAdapter != null && mAdapter.isEnabled(); + } + + @Override + public boolean isBluetoothConnected() { + return mAdapter != null + && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED; + } + + @Override + public void setBluetoothEnabled(boolean enabled) { + if (mAdapter != null) { + if (enabled) { + mAdapter.enable(); + } else { + mAdapter.disable(); + } + } + } + + @Override + public boolean isBluetoothSupported() { + return mAdapter != null; + } + + public Set<BluetoothDevice> getBondedBluetoothDevices() { + return mBondedDevices; + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + + if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + handleAdapterStateChange( + intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)); + } + fireCallbacks(); + updateBondedBluetoothDevices(); + } + + private void updateBondedBluetoothDevices() { + mBondedDevices.clear(); + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + Set<BluetoothDevice> devices = adapter.getBondedDevices(); + if (devices != null) { + for (BluetoothDevice device : devices) { + if (device.getBondState() != BluetoothDevice.BOND_NONE) { + mBondedDevices.add(device); + } + } + } + } + } + + private void handleAdapterStateChange(int adapterState) { + mEnabled = (adapterState == BluetoothAdapter.STATE_ON); + } + + private void fireCallbacks() { + for (BluetoothStateChangeCallback cb : mChangeCallbacks) { + cb.onBluetoothStateChange(mEnabled); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java new file mode 100644 index 0000000..54041e1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +public interface CastController { + void addCallback(Callback callback); + void removeCallback(Callback callback); + void setDiscovering(boolean request); + void setCurrentUserId(int currentUserId); + + public interface Callback { + void onStateChanged(boolean enabled, boolean connecting, String connectedRouteName); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java new file mode 100644 index 0000000..33a85b1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.content.Context; +import android.media.MediaRouter; +import android.media.MediaRouter.RouteInfo; + +import java.util.ArrayList; + +/** Platform implementation of the cast controller. **/ +public class CastControllerImpl implements CastController { + + private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); + private final MediaRouter mMediaRouter; + + public CastControllerImpl(Context context) { + mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); + } + + @Override + public void addCallback(Callback callback) { + mCallbacks.add(callback); + } + + @Override + public void removeCallback(Callback callback) { + mCallbacks.remove(callback); + } + + @Override + public void setDiscovering(boolean request) { + if (request) { + mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, + mMediaCallback, + MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); + } else { + mMediaRouter.removeCallback(mMediaCallback); + } + } + + @Override + public void setCurrentUserId(int currentUserId) { + mMediaRouter.rebindAsUser(currentUserId); + } + + private void updateRemoteDisplays() { + final MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute( + MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY); + boolean enabled = connectedRoute != null + && connectedRoute.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY); + boolean connecting; + if (enabled) { + connecting = connectedRoute.isConnecting(); + } else { + connecting = false; + enabled = mMediaRouter.isRouteAvailable(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, + MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); + } + + String connectedRouteName = null; + if (connectedRoute != null) { + connectedRouteName = connectedRoute.getName().toString(); + } + fireStateChanged(enabled, connecting, connectedRouteName); + } + + private void fireStateChanged(boolean enabled, boolean connecting, String connectedRouteName) { + for (Callback callback : mCallbacks) { + callback.onStateChanged(enabled, connecting, connectedRouteName); + } + } + + private final MediaRouter.SimpleCallback mMediaCallback = new MediaRouter.SimpleCallback() { + @Override + public void onRouteAdded(MediaRouter router, RouteInfo route) { + updateRemoteDisplays(); + } + @Override + public void onRouteChanged(MediaRouter router, RouteInfo route) { + updateRemoteDisplays(); + } + @Override + public void onRouteRemoved(MediaRouter router, RouteInfo route) { + updateRemoteDisplays(); + } + @Override + public void onRouteSelected(MediaRouter router, int type, RouteInfo route) { + updateRemoteDisplays(); + } + @Override + public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) { + updateRemoteDisplays(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java new file mode 100644 index 0000000..158e9c1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +/** Common interface for items requiring manual cleanup. **/ +public interface Disposable { + void dispose(); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java index f5ee95b..29a8981 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2014 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. @@ -16,47 +16,11 @@ package com.android.systemui.statusbar.policy; -import android.app.ActivityManager; -import android.app.AppOpsManager; -import android.app.StatusBarManager; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.location.LocationManager; -import android.os.Handler; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; - -import com.android.systemui.R; - -import java.util.ArrayList; -import java.util.List; - -/** - * A controller to manage changes of location related states and update the views accordingly. - */ -public class LocationController extends BroadcastReceiver { - // The name of the placeholder corresponding to the location request status icon. - // This string corresponds to config_statusBarIcons in core/res/res/values/config.xml. - public static final String LOCATION_STATUS_ICON_PLACEHOLDER = "location"; - public static final int LOCATION_STATUS_ICON_ID - = R.drawable.stat_sys_device_access_location_found; - - private static final int[] mHighPowerRequestAppOpArray - = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION}; - - private Context mContext; - - private AppOpsManager mAppOpsManager; - private StatusBarManager mStatusBarManager; - - private boolean mAreActiveLocationRequests; - - private ArrayList<LocationSettingsChangeCallback> mSettingsChangeCallbacks = - new ArrayList<LocationSettingsChangeCallback>(); +public interface LocationController { + boolean isLocationEnabled(); + boolean setLocationEnabled(boolean enabled); + void addSettingsChangedCallback(LocationSettingsChangeCallback cb); + void removeSettingsChangedCallback(LocationSettingsChangeCallback cb); /** * A callback for change in location settings (the user has enabled/disabled location). @@ -68,156 +32,6 @@ public class LocationController extends BroadcastReceiver { * @param locationEnabled A value of true indicates that at least one type of location * is enabled in settings. */ - public void onLocationSettingsChanged(boolean locationEnabled); - } - - public LocationController(Context context) { - mContext = context; - - IntentFilter filter = new IntentFilter(); - filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); - context.registerReceiver(this, filter); - - mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - mStatusBarManager - = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); - - // Register to listen for changes in location settings. - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION); - context.registerReceiverAsUser(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (LocationManager.MODE_CHANGED_ACTION.equals(action)) { - locationSettingsChanged(); - } - } - }, UserHandle.ALL, intentFilter, null, new Handler()); - - // Examine the current location state and initialize the status view. - updateActiveLocationRequests(); - refreshViews(); - } - - /** - * Add a callback to listen for changes in location settings. - */ - public void addSettingsChangedCallback(LocationSettingsChangeCallback cb) { - mSettingsChangeCallbacks.add(cb); - } - - /** - * Enable or disable location in settings. - * - * <p>This will attempt to enable/disable every type of location setting - * (e.g. high and balanced power). - * - * <p>If enabling, a user consent dialog will pop up prompting the user to accept. - * If the user doesn't accept, network location won't be enabled. - * - * @return true if attempt to change setting was successful. - */ - public boolean setLocationEnabled(boolean enabled) { - int currentUserId = ActivityManager.getCurrentUser(); - if (isUserLocationRestricted(currentUserId)) { - return false; - } - final ContentResolver cr = mContext.getContentResolver(); - // When enabling location, a user consent dialog will pop up, and the - // setting won't be fully enabled until the user accepts the agreement. - int mode = enabled - ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY : Settings.Secure.LOCATION_MODE_OFF; - // QuickSettings always runs as the owner, so specifically set the settings - // for the current foreground user. - return Settings.Secure - .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, currentUserId); - } - - /** - * Returns true if location isn't disabled in settings. - */ - public boolean isLocationEnabled() { - ContentResolver resolver = mContext.getContentResolver(); - // QuickSettings always runs as the owner, so specifically retrieve the settings - // for the current foreground user. - int mode = Settings.Secure.getIntForUser(resolver, Settings.Secure.LOCATION_MODE, - Settings.Secure.LOCATION_MODE_OFF, ActivityManager.getCurrentUser()); - return mode != Settings.Secure.LOCATION_MODE_OFF; - } - - /** - * Returns true if the current user is restricted from using location. - */ - private boolean isUserLocationRestricted(int userId) { - final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - return um.hasUserRestriction( - UserManager.DISALLOW_SHARE_LOCATION, - new UserHandle(userId)); - } - - /** - * Returns true if there currently exist active high power location requests. - */ - private boolean areActiveHighPowerLocationRequests() { - List<AppOpsManager.PackageOps> packages - = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray); - // AppOpsManager can return null when there is no requested data. - if (packages != null) { - final int numPackages = packages.size(); - for (int packageInd = 0; packageInd < numPackages; packageInd++) { - AppOpsManager.PackageOps packageOp = packages.get(packageInd); - List<AppOpsManager.OpEntry> opEntries = packageOp.getOps(); - if (opEntries != null) { - final int numOps = opEntries.size(); - for (int opInd = 0; opInd < numOps; opInd++) { - AppOpsManager.OpEntry opEntry = opEntries.get(opInd); - // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because - // of the mHighPowerRequestAppOpArray filter, but checking defensively. - if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { - if (opEntry.isRunning()) { - return true; - } - } - } - } - } - } - - return false; - } - - // Updates the status view based on the current state of location requests. - private void refreshViews() { - if (mAreActiveLocationRequests) { - mStatusBarManager.setIcon(LOCATION_STATUS_ICON_PLACEHOLDER, LOCATION_STATUS_ICON_ID, 0, - mContext.getString(R.string.accessibility_location_active)); - } else { - mStatusBarManager.removeIcon(LOCATION_STATUS_ICON_PLACEHOLDER); - } - } - - // Reads the active location requests and updates the status view if necessary. - private void updateActiveLocationRequests() { - boolean hadActiveLocationRequests = mAreActiveLocationRequests; - mAreActiveLocationRequests = areActiveHighPowerLocationRequests(); - if (mAreActiveLocationRequests != hadActiveLocationRequests) { - refreshViews(); - } - } - - private void locationSettingsChanged() { - boolean isEnabled = isLocationEnabled(); - for (LocationSettingsChangeCallback cb : mSettingsChangeCallbacks) { - cb.onLocationSettingsChanged(isEnabled); - } - } - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) { - updateActiveLocationRequests(); - } + void onLocationSettingsChanged(boolean locationEnabled); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java new file mode 100644 index 0000000..9e5ad18 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.location.LocationManager; +import android.os.Handler; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; + +import com.android.systemui.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * A controller to manage changes of location related states and update the views accordingly. + */ +public class LocationControllerImpl extends BroadcastReceiver implements LocationController { + // The name of the placeholder corresponding to the location request status icon. + // This string corresponds to config_statusBarIcons in core/res/res/values/config.xml. + public static final String LOCATION_STATUS_ICON_PLACEHOLDER = "location"; + public static final int LOCATION_STATUS_ICON_ID + = R.drawable.stat_sys_device_access_location_found; + + private static final int[] mHighPowerRequestAppOpArray + = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION}; + + private Context mContext; + + private AppOpsManager mAppOpsManager; + private StatusBarManager mStatusBarManager; + + private boolean mAreActiveLocationRequests; + + private ArrayList<LocationSettingsChangeCallback> mSettingsChangeCallbacks = + new ArrayList<LocationSettingsChangeCallback>(); + + public LocationControllerImpl(Context context) { + mContext = context; + + IntentFilter filter = new IntentFilter(); + filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); + context.registerReceiver(this, filter); + + mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mStatusBarManager + = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); + + // Register to listen for changes in location settings. + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION); + context.registerReceiverAsUser(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (LocationManager.MODE_CHANGED_ACTION.equals(action)) { + locationSettingsChanged(); + } + } + }, UserHandle.ALL, intentFilter, null, new Handler()); + + // Examine the current location state and initialize the status view. + updateActiveLocationRequests(); + refreshViews(); + } + + /** + * Add a callback to listen for changes in location settings. + */ + public void addSettingsChangedCallback(LocationSettingsChangeCallback cb) { + mSettingsChangeCallbacks.add(cb); + } + + public void removeSettingsChangedCallback(LocationSettingsChangeCallback cb) { + mSettingsChangeCallbacks.remove(cb); + } + + /** + * Enable or disable location in settings. + * + * <p>This will attempt to enable/disable every type of location setting + * (e.g. high and balanced power). + * + * <p>If enabling, a user consent dialog will pop up prompting the user to accept. + * If the user doesn't accept, network location won't be enabled. + * + * @return true if attempt to change setting was successful. + */ + public boolean setLocationEnabled(boolean enabled) { + int currentUserId = ActivityManager.getCurrentUser(); + if (isUserLocationRestricted(currentUserId)) { + return false; + } + final ContentResolver cr = mContext.getContentResolver(); + // When enabling location, a user consent dialog will pop up, and the + // setting won't be fully enabled until the user accepts the agreement. + int mode = enabled + ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY : Settings.Secure.LOCATION_MODE_OFF; + // QuickSettings always runs as the owner, so specifically set the settings + // for the current foreground user. + return Settings.Secure + .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, currentUserId); + } + + /** + * Returns true if location isn't disabled in settings. + */ + public boolean isLocationEnabled() { + ContentResolver resolver = mContext.getContentResolver(); + // QuickSettings always runs as the owner, so specifically retrieve the settings + // for the current foreground user. + int mode = Settings.Secure.getIntForUser(resolver, Settings.Secure.LOCATION_MODE, + Settings.Secure.LOCATION_MODE_OFF, ActivityManager.getCurrentUser()); + return mode != Settings.Secure.LOCATION_MODE_OFF; + } + + /** + * Returns true if the current user is restricted from using location. + */ + private boolean isUserLocationRestricted(int userId) { + final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + return um.hasUserRestriction( + UserManager.DISALLOW_SHARE_LOCATION, + new UserHandle(userId)); + } + + /** + * Returns true if there currently exist active high power location requests. + */ + private boolean areActiveHighPowerLocationRequests() { + List<AppOpsManager.PackageOps> packages + = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray); + // AppOpsManager can return null when there is no requested data. + if (packages != null) { + final int numPackages = packages.size(); + for (int packageInd = 0; packageInd < numPackages; packageInd++) { + AppOpsManager.PackageOps packageOp = packages.get(packageInd); + List<AppOpsManager.OpEntry> opEntries = packageOp.getOps(); + if (opEntries != null) { + final int numOps = opEntries.size(); + for (int opInd = 0; opInd < numOps; opInd++) { + AppOpsManager.OpEntry opEntry = opEntries.get(opInd); + // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because + // of the mHighPowerRequestAppOpArray filter, but checking defensively. + if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { + if (opEntry.isRunning()) { + return true; + } + } + } + } + } + } + + return false; + } + + // Updates the status view based on the current state of location requests. + private void refreshViews() { + if (mAreActiveLocationRequests) { + mStatusBarManager.setIcon(LOCATION_STATUS_ICON_PLACEHOLDER, LOCATION_STATUS_ICON_ID, 0, + mContext.getString(R.string.accessibility_location_active)); + } else { + mStatusBarManager.removeIcon(LOCATION_STATUS_ICON_PLACEHOLDER); + } + } + + // Reads the active location requests and updates the status view if necessary. + private void updateActiveLocationRequests() { + boolean hadActiveLocationRequests = mAreActiveLocationRequests; + mAreActiveLocationRequests = areActiveHighPowerLocationRequests(); + if (mAreActiveLocationRequests != hadActiveLocationRequests) { + refreshViews(); + } + } + + private void locationSettingsChanged() { + boolean isEnabled = isLocationEnabled(); + for (LocationSettingsChangeCallback cb : mSettingsChangeCallbacks) { + cb.onLocationSettingsChanged(isEnabled); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) { + updateActiveLocationRequests(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 92c008e..dc8f315 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2014 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. @@ -16,153 +16,12 @@ package com.android.systemui.statusbar.policy; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.net.wimax.WimaxManagerConstants; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.provider.Settings; -import android.telephony.PhoneStateListener; -import android.telephony.ServiceState; -import android.telephony.SignalStrength; -import android.telephony.TelephonyManager; -import android.util.Log; -import android.view.View; -import android.widget.TextView; +public interface NetworkController { -import com.android.internal.telephony.IccCardConstants; -import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.telephony.cdma.EriInfo; -import com.android.internal.util.AsyncChannel; -import com.android.systemui.DemoMode; -import com.android.systemui.R; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -public class NetworkController extends BroadcastReceiver implements DemoMode { - // debug - static final String TAG = "StatusBar.NetworkController"; - static final boolean DEBUG = false; - static final boolean CHATTY = false; // additional diagnostics, but not logspew - - private static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_signal_flightmode; - - // telephony - boolean mHspaDataDistinguishable; - final TelephonyManager mPhone; - boolean mDataConnected; - IccCardConstants.State mSimState = IccCardConstants.State.READY; - int mPhoneState = TelephonyManager.CALL_STATE_IDLE; - int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN; - int mDataState = TelephonyManager.DATA_DISCONNECTED; - int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; - ServiceState mServiceState; - SignalStrength mSignalStrength; - int[] mDataIconList = TelephonyIcons.DATA_G[0]; - String mNetworkName; - String mNetworkNameDefault; - String mNetworkNameSeparator; - int mPhoneSignalIconId; - int mQSPhoneSignalIconId; - int mDataDirectionIconId; // data + data direction on phones - int mDataSignalIconId; - int mDataTypeIconId; - int mQSDataTypeIconId; - int mAirplaneIconId; - boolean mDataActive; - int mLastSignalLevel; - boolean mShowPhoneRSSIForData = false; - boolean mShowAtLeastThreeGees = false; - boolean mAlwaysShowCdmaRssi = false; - - String mContentDescriptionPhoneSignal; - String mContentDescriptionWifi; - String mContentDescriptionWimax; - String mContentDescriptionCombinedSignal; - String mContentDescriptionDataType; - - // wifi - final WifiManager mWifiManager; - AsyncChannel mWifiChannel; - boolean mWifiEnabled, mWifiConnected; - int mWifiRssi, mWifiLevel; - String mWifiSsid; - int mWifiIconId = 0; - int mQSWifiIconId = 0; - int mWifiActivity = WifiManager.DATA_ACTIVITY_NONE; - - // bluetooth - private boolean mBluetoothTethered = false; - private int mBluetoothTetherIconId = - com.android.internal.R.drawable.stat_sys_tether_bluetooth; - - //wimax - private boolean mWimaxSupported = false; - private boolean mIsWimaxEnabled = false; - private boolean mWimaxConnected = false; - private boolean mWimaxIdle = false; - private int mWimaxIconId = 0; - private int mWimaxSignal = 0; - private int mWimaxState = 0; - private int mWimaxExtraState = 0; - - // data connectivity (regardless of state, can we access the internet?) - // state of inet connection - 0 not connected, 100 connected - private boolean mConnected = false; - private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE; - private String mConnectedNetworkTypeName; - private int mInetCondition = 0; - private int mLastInetCondition = 0; - private static final int INET_CONDITION_THRESHOLD = 50; - - private boolean mAirplaneMode = false; - private boolean mLastAirplaneMode = true; - - private Locale mLocale = null; - private Locale mLastLocale = null; - - // our ui - Context mContext; - ArrayList<TextView> mCombinedLabelViews = new ArrayList<TextView>(); - ArrayList<TextView> mMobileLabelViews = new ArrayList<TextView>(); - ArrayList<TextView> mWifiLabelViews = new ArrayList<TextView>(); - ArrayList<TextView> mEmergencyLabelViews = new ArrayList<TextView>(); - ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>(); - ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks = - new ArrayList<NetworkSignalChangedCallback>(); - int mLastPhoneSignalIconId = -1; - int mLastDataDirectionIconId = -1; - int mLastWifiIconId = -1; - int mLastWimaxIconId = -1; - int mLastCombinedSignalIconId = -1; - int mLastDataTypeIconId = -1; - String mLastCombinedLabel = ""; - - private boolean mHasMobileDataFeature; - - boolean mDataAndWifiStacked = false; - - public interface SignalCluster { - void setWifiIndicators(boolean visible, int strengthIcon, boolean problem, - String contentDescription); - void setMobileDataIndicators(boolean visible, int strengthIcon, boolean problem, - int typeIcon, String contentDescription, String typeContentDescription); - void setIsAirplaneMode(boolean is, int airplaneIcon); - } + boolean hasMobileDataFeature(); + void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb); + void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb); + void setWifiEnabled(boolean enabled); public interface NetworkSignalChangedCallback { void onWifiSignalChanged(boolean enabled, int wifiSignalIconId, @@ -174,1304 +33,4 @@ public class NetworkController extends BroadcastReceiver implements DemoMode { String dataTypeContentDescriptionId, String description); void onAirplaneModeChanged(boolean enabled); } - - /** - * Construct this controller object and register for updates. - */ - public NetworkController(Context context) { - mContext = context; - final Resources res = context.getResources(); - - ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - mHasMobileDataFeature = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); - - mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData); - mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G); - mAlwaysShowCdmaRssi = res.getBoolean( - com.android.internal.R.bool.config_alwaysUseCdmaRssi); - - // set up the default wifi icon, used when no radios have ever appeared - updateWifiIcons(); - updateWimaxIcons(); - - // telephony - mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); - mPhone.listen(mPhoneStateListener, - PhoneStateListener.LISTEN_SERVICE_STATE - | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS - | PhoneStateListener.LISTEN_CALL_STATE - | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE - | PhoneStateListener.LISTEN_DATA_ACTIVITY); - mHspaDataDistinguishable = mContext.getResources().getBoolean( - R.bool.config_hspa_data_distinguishable); - mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator); - mNetworkNameDefault = mContext.getString( - com.android.internal.R.string.lockscreen_carrier_default); - mNetworkName = mNetworkNameDefault; - - // wifi - mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - Handler handler = new WifiHandler(); - mWifiChannel = new AsyncChannel(); - Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); - if (wifiMessenger != null) { - mWifiChannel.connect(mContext, handler, wifiMessenger); - } - - // broadcasts - IntentFilter filter = new IntentFilter(); - filter.addAction(WifiManager.RSSI_CHANGED_ACTION); - filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); - filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - filter.addAction(ConnectivityManager.INET_CONDITION_ACTION); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); - mWimaxSupported = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_wimaxEnabled); - if(mWimaxSupported) { - filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION); - filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION); - filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION); - } - context.registerReceiver(this, filter); - - // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it - updateAirplaneMode(); - - mLastLocale = mContext.getResources().getConfiguration().locale; - } - - public boolean hasMobileDataFeature() { - return mHasMobileDataFeature; - } - - public boolean hasVoiceCallingFeature() { - return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; - } - - public boolean isEmergencyOnly() { - return (mServiceState != null && mServiceState.isEmergencyOnly()); - } - - public void addCombinedLabelView(TextView v) { - mCombinedLabelViews.add(v); - } - - public void addMobileLabelView(TextView v) { - mMobileLabelViews.add(v); - } - - public void addWifiLabelView(TextView v) { - mWifiLabelViews.add(v); - } - - public void addEmergencyLabelView(TextView v) { - mEmergencyLabelViews.add(v); - } - - public void addSignalCluster(SignalCluster cluster) { - mSignalClusters.add(cluster); - refreshSignalCluster(cluster); - } - - public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { - mSignalsChangedCallbacks.add(cb); - notifySignalsChangedCallbacks(cb); - } - - public void refreshSignalCluster(SignalCluster cluster) { - if (mDemoMode) return; - cluster.setWifiIndicators( - // only show wifi in the cluster if connected or if wifi-only - mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature), - mWifiIconId, - mInetCondition == 0, - mContentDescriptionWifi); - - if (mIsWimaxEnabled && mWimaxConnected) { - // wimax is special - cluster.setMobileDataIndicators( - true, - mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId, - mInetCondition == 0, - mDataTypeIconId, - mContentDescriptionWimax, - mContentDescriptionDataType); - } else { - // normal mobile data - cluster.setMobileDataIndicators( - mHasMobileDataFeature, - mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId, - mInetCondition == 0, - mDataTypeIconId, - mContentDescriptionPhoneSignal, - mContentDescriptionDataType); - } - cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId); - } - - void notifySignalsChangedCallbacks(NetworkSignalChangedCallback cb) { - // only show wifi in the cluster if connected or if wifi-only - boolean wifiEnabled = mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature); - String wifiDesc = wifiEnabled ? - mWifiSsid : null; - boolean wifiIn = wifiEnabled && mWifiSsid != null - && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT - || mWifiActivity == WifiManager.DATA_ACTIVITY_IN); - boolean wifiOut = wifiEnabled && mWifiSsid != null - && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT - || mWifiActivity == WifiManager.DATA_ACTIVITY_OUT); - cb.onWifiSignalChanged(wifiEnabled, mQSWifiIconId, wifiIn, wifiOut, - mContentDescriptionWifi, wifiDesc); - - boolean mobileIn = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT - || mDataActivity == TelephonyManager.DATA_ACTIVITY_IN); - boolean mobileOut = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT - || mDataActivity == TelephonyManager.DATA_ACTIVITY_OUT); - if (isEmergencyOnly()) { - cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId, - mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, null); - } else { - if (mIsWimaxEnabled && mWimaxConnected) { - // Wimax is special - cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId, - mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, mNetworkName); - } else { - // Normal mobile data - cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId, - mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, mNetworkName); - } - } - cb.onAirplaneModeChanged(mAirplaneMode); - } - - public void setStackedMode(boolean stacked) { - mDataAndWifiStacked = true; - } - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (action.equals(WifiManager.RSSI_CHANGED_ACTION) - || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) - || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - updateWifiState(intent); - refreshViews(); - } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { - updateSimState(intent); - updateDataIcon(); - refreshViews(); - } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { - updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), - intent.getStringExtra(TelephonyIntents.EXTRA_SPN), - intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), - intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); - refreshViews(); - } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || - action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { - updateConnectivity(intent); - refreshViews(); - } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { - refreshLocale(); - refreshViews(); - } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { - refreshLocale(); - updateAirplaneMode(); - refreshViews(); - } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) || - action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) || - action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) { - updateWimaxState(intent); - refreshViews(); - } - } - - - // ===== Telephony ============================================================== - - PhoneStateListener mPhoneStateListener = new PhoneStateListener() { - @Override - public void onSignalStrengthsChanged(SignalStrength signalStrength) { - if (DEBUG) { - Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength + - ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); - } - mSignalStrength = signalStrength; - updateTelephonySignalStrength(); - refreshViews(); - } - - @Override - public void onServiceStateChanged(ServiceState state) { - if (DEBUG) { - Log.d(TAG, "onServiceStateChanged voiceState=" + state.getVoiceRegState() - + " dataState=" + state.getDataRegState()); - } - mServiceState = state; - updateTelephonySignalStrength(); - updateDataNetType(); - updateDataIcon(); - refreshViews(); - } - - @Override - public void onCallStateChanged(int state, String incomingNumber) { - if (DEBUG) { - Log.d(TAG, "onCallStateChanged state=" + state); - } - // In cdma, if a voice call is made, RSSI should switch to 1x. - if (isCdma()) { - updateTelephonySignalStrength(); - refreshViews(); - } - } - - @Override - public void onDataConnectionStateChanged(int state, int networkType) { - if (DEBUG) { - Log.d(TAG, "onDataConnectionStateChanged: state=" + state - + " type=" + networkType); - } - mDataState = state; - mDataNetType = networkType; - updateDataNetType(); - updateDataIcon(); - refreshViews(); - } - - @Override - public void onDataActivity(int direction) { - if (DEBUG) { - Log.d(TAG, "onDataActivity: direction=" + direction); - } - mDataActivity = direction; - updateDataIcon(); - refreshViews(); - } - }; - - private final void updateSimState(Intent intent) { - String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); - if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { - mSimState = IccCardConstants.State.ABSENT; - } - else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { - mSimState = IccCardConstants.State.READY; - } - else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { - final String lockedReason = - intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); - if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { - mSimState = IccCardConstants.State.PIN_REQUIRED; - } - else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { - mSimState = IccCardConstants.State.PUK_REQUIRED; - } - else { - mSimState = IccCardConstants.State.NETWORK_LOCKED; - } - } else { - mSimState = IccCardConstants.State.UNKNOWN; - } - } - - private boolean isCdma() { - return (mSignalStrength != null) && !mSignalStrength.isGsm(); - } - - private boolean hasService() { - if (mServiceState != null) { - // Consider the device to be in service if either voice or data service is available. - // Some SIM cards are marketed as data-only and do not support voice service, and on - // these SIM cards, we want to show signal bars for data service as well as the "no - // service" or "emergency calls only" text that indicates that voice is not available. - switch(mServiceState.getVoiceRegState()) { - case ServiceState.STATE_POWER_OFF: - return false; - case ServiceState.STATE_OUT_OF_SERVICE: - case ServiceState.STATE_EMERGENCY_ONLY: - return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE; - default: - return true; - } - } else { - return false; - } - } - - private void updateAirplaneMode() { - mAirplaneMode = (Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0) == 1); - } - - private void refreshLocale() { - mLocale = mContext.getResources().getConfiguration().locale; - } - - private final void updateTelephonySignalStrength() { - if (!hasService()) { - if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: !hasService()"); - mPhoneSignalIconId = R.drawable.stat_sys_signal_null; - mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; - mDataSignalIconId = R.drawable.stat_sys_signal_null; - } else { - if (mSignalStrength == null) { - if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null"); - mPhoneSignalIconId = R.drawable.stat_sys_signal_null; - mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; - mDataSignalIconId = R.drawable.stat_sys_signal_null; - mContentDescriptionPhoneSignal = mContext.getString( - AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]); - } else { - int iconLevel; - int[] iconList; - if (isCdma() && mAlwaysShowCdmaRssi) { - mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel(); - if(DEBUG) Log.d(TAG, "mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi - + " set to cdmaLevel=" + mSignalStrength.getCdmaLevel() - + " instead of level=" + mSignalStrength.getLevel()); - } else { - mLastSignalLevel = iconLevel = mSignalStrength.getLevel(); - } - - if (isCdma()) { - if (isCdmaEri()) { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; - } else { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; - } - } else { - // Though mPhone is a Manager, this call is not an IPC - if (mPhone.isNetworkRoaming()) { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; - } else { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; - } - } - mPhoneSignalIconId = iconList[iconLevel]; - mQSPhoneSignalIconId = - TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mInetCondition][iconLevel]; - mContentDescriptionPhoneSignal = mContext.getString( - AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]); - mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel]; - } - } - } - - private final void updateDataNetType() { - if (mIsWimaxEnabled && mWimaxConnected) { - // wimax is a special 4g network not handled by telephony - mDataIconList = TelephonyIcons.DATA_4G[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_4g); - } else { - switch (mDataNetType) { - case TelephonyManager.NETWORK_TYPE_UNKNOWN: - if (!mShowAtLeastThreeGees) { - mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; - mDataTypeIconId = 0; - mQSDataTypeIconId = 0; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_gprs); - break; - } else { - // fall through - } - case TelephonyManager.NETWORK_TYPE_EDGE: - if (!mShowAtLeastThreeGees) { - mDataIconList = TelephonyIcons.DATA_E[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_e; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_E[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_edge); - break; - } else { - // fall through - } - case TelephonyManager.NETWORK_TYPE_UMTS: - mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3g); - break; - case TelephonyManager.NETWORK_TYPE_HSDPA: - case TelephonyManager.NETWORK_TYPE_HSUPA: - case TelephonyManager.NETWORK_TYPE_HSPA: - case TelephonyManager.NETWORK_TYPE_HSPAP: - if (mHspaDataDistinguishable) { - mDataIconList = TelephonyIcons.DATA_H[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_h; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_H[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3_5g); - } else { - mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3g); - } - break; - case TelephonyManager.NETWORK_TYPE_CDMA: - if (!mShowAtLeastThreeGees) { - // display 1xRTT for IS95A/B - mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_1x; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_cdma); - break; - } else { - // fall through - } - case TelephonyManager.NETWORK_TYPE_1xRTT: - if (!mShowAtLeastThreeGees) { - mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_1x; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_cdma); - break; - } else { - // fall through - } - case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through - case TelephonyManager.NETWORK_TYPE_EVDO_A: - case TelephonyManager.NETWORK_TYPE_EVDO_B: - case TelephonyManager.NETWORK_TYPE_EHRPD: - mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3g); - break; - case TelephonyManager.NETWORK_TYPE_LTE: - boolean show4GforLTE = mContext.getResources().getBoolean(R.bool.config_show4GForLTE); - if (show4GforLTE) { - mDataIconList = TelephonyIcons.DATA_4G[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_4g); - } else { - mDataIconList = TelephonyIcons.DATA_LTE[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_lte; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_LTE[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_lte); - } - break; - default: - if (!mShowAtLeastThreeGees) { - mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_g; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_G[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_gprs); - } else { - mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3g); - } - break; - } - } - - if (isCdma()) { - if (isCdmaEri()) { - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; - } - } else if (mPhone.isNetworkRoaming()) { - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; - } - } - - boolean isCdmaEri() { - if (mServiceState != null) { - final int iconIndex = mServiceState.getCdmaEriIconIndex(); - if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { - final int iconMode = mServiceState.getCdmaEriIconMode(); - if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL - || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { - return true; - } - } - } - return false; - } - - private final void updateDataIcon() { - int iconId; - boolean visible = true; - - if (!isCdma()) { - // GSM case, we have to check also the sim state - if (mSimState == IccCardConstants.State.READY || - mSimState == IccCardConstants.State.UNKNOWN) { - if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { - switch (mDataActivity) { - case TelephonyManager.DATA_ACTIVITY_IN: - iconId = mDataIconList[1]; - break; - case TelephonyManager.DATA_ACTIVITY_OUT: - iconId = mDataIconList[2]; - break; - case TelephonyManager.DATA_ACTIVITY_INOUT: - iconId = mDataIconList[3]; - break; - default: - iconId = mDataIconList[0]; - break; - } - mDataDirectionIconId = iconId; - } else { - iconId = 0; - visible = false; - } - } else { - iconId = R.drawable.stat_sys_no_sim; - visible = false; // no SIM? no data - } - } else { - // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT - if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { - switch (mDataActivity) { - case TelephonyManager.DATA_ACTIVITY_IN: - iconId = mDataIconList[1]; - break; - case TelephonyManager.DATA_ACTIVITY_OUT: - iconId = mDataIconList[2]; - break; - case TelephonyManager.DATA_ACTIVITY_INOUT: - iconId = mDataIconList[3]; - break; - case TelephonyManager.DATA_ACTIVITY_DORMANT: - default: - iconId = mDataIconList[0]; - break; - } - } else { - iconId = 0; - visible = false; - } - } - - mDataDirectionIconId = iconId; - mDataConnected = visible; - } - - void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { - if (false) { - Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn - + " showPlmn=" + showPlmn + " plmn=" + plmn); - } - StringBuilder str = new StringBuilder(); - boolean something = false; - if (showPlmn && plmn != null) { - str.append(plmn); - something = true; - } - if (showSpn && spn != null) { - if (something) { - str.append(mNetworkNameSeparator); - } - str.append(spn); - something = true; - } - if (something) { - mNetworkName = str.toString(); - } else { - mNetworkName = mNetworkNameDefault; - } - } - - // ===== Wifi =================================================================== - - class WifiHandler extends Handler { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - mWifiChannel.sendMessage(Message.obtain(this, - AsyncChannel.CMD_CHANNEL_FULL_CONNECTION)); - } else { - Log.e(TAG, "Failed to connect to wifi"); - } - break; - case WifiManager.DATA_ACTIVITY_NOTIFICATION: - if (msg.arg1 != mWifiActivity) { - mWifiActivity = msg.arg1; - refreshViews(); - } - break; - default: - //Ignore - break; - } - } - } - - private void updateWifiState(Intent intent) { - final String action = intent.getAction(); - if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { - mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; - - } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - final NetworkInfo networkInfo = (NetworkInfo) - intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - boolean wasConnected = mWifiConnected; - mWifiConnected = networkInfo != null && networkInfo.isConnected(); - // If we just connected, grab the inintial signal strength and ssid - if (mWifiConnected && !wasConnected) { - // try getting it out of the intent first - WifiInfo info = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); - if (info == null) { - info = mWifiManager.getConnectionInfo(); - } - if (info != null) { - mWifiSsid = huntForSsid(info); - } else { - mWifiSsid = null; - } - } else if (!mWifiConnected) { - mWifiSsid = null; - } - } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { - mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); - mWifiLevel = WifiManager.calculateSignalLevel( - mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT); - } - - updateWifiIcons(); - } - - private void updateWifiIcons() { - if (mWifiConnected) { - mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; - mQSWifiIconId = WifiIcons.QS_WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; - mContentDescriptionWifi = mContext.getString( - AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]); - } else { - if (mDataAndWifiStacked) { - mWifiIconId = 0; - mQSWifiIconId = 0; - } else { - mWifiIconId = mWifiEnabled ? R.drawable.stat_sys_wifi_signal_null : 0; - mQSWifiIconId = mWifiEnabled ? R.drawable.ic_qs_wifi_no_network : 0; - } - mContentDescriptionWifi = mContext.getString(R.string.accessibility_no_wifi); - } - } - - private String huntForSsid(WifiInfo info) { - String ssid = info.getSSID(); - if (ssid != null) { - return ssid; - } - // OK, it's not in the connectionInfo; we have to go hunting for it - List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks(); - for (WifiConfiguration net : networks) { - if (net.networkId == info.getNetworkId()) { - return net.SSID; - } - } - return null; - } - - - // ===== Wimax =================================================================== - private final void updateWimaxState(Intent intent) { - final String action = intent.getAction(); - boolean wasConnected = mWimaxConnected; - if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION)) { - int wimaxStatus = intent.getIntExtra(WimaxManagerConstants.EXTRA_4G_STATE, - WimaxManagerConstants.NET_4G_STATE_UNKNOWN); - mIsWimaxEnabled = (wimaxStatus == - WimaxManagerConstants.NET_4G_STATE_ENABLED); - } else if (action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION)) { - mWimaxSignal = intent.getIntExtra(WimaxManagerConstants.EXTRA_NEW_SIGNAL_LEVEL, 0); - } else if (action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) { - mWimaxState = intent.getIntExtra(WimaxManagerConstants.EXTRA_WIMAX_STATE, - WimaxManagerConstants.NET_4G_STATE_UNKNOWN); - mWimaxExtraState = intent.getIntExtra( - WimaxManagerConstants.EXTRA_WIMAX_STATE_DETAIL, - WimaxManagerConstants.NET_4G_STATE_UNKNOWN); - mWimaxConnected = (mWimaxState == - WimaxManagerConstants.WIMAX_STATE_CONNECTED); - mWimaxIdle = (mWimaxExtraState == WimaxManagerConstants.WIMAX_IDLE); - } - updateDataNetType(); - updateWimaxIcons(); - } - - private void updateWimaxIcons() { - if (mIsWimaxEnabled) { - if (mWimaxConnected) { - if (mWimaxIdle) - mWimaxIconId = WimaxIcons.WIMAX_IDLE; - else - mWimaxIconId = WimaxIcons.WIMAX_SIGNAL_STRENGTH[mInetCondition][mWimaxSignal]; - mContentDescriptionWimax = mContext.getString( - AccessibilityContentDescriptions.WIMAX_CONNECTION_STRENGTH[mWimaxSignal]); - } else { - mWimaxIconId = WimaxIcons.WIMAX_DISCONNECTED; - mContentDescriptionWimax = mContext.getString(R.string.accessibility_no_wimax); - } - } else { - mWimaxIconId = 0; - } - } - - // ===== Full or limited Internet connectivity ================================== - - private void updateConnectivity(Intent intent) { - if (CHATTY) { - Log.d(TAG, "updateConnectivity: intent=" + intent); - } - - final ConnectivityManager connManager = (ConnectivityManager) mContext - .getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo info = connManager.getActiveNetworkInfo(); - - // Are we connected at all, by any interface? - mConnected = info != null && info.isConnected(); - if (mConnected) { - mConnectedNetworkType = info.getType(); - mConnectedNetworkTypeName = info.getTypeName(); - } else { - mConnectedNetworkType = ConnectivityManager.TYPE_NONE; - mConnectedNetworkTypeName = null; - } - - int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0); - - if (CHATTY) { - Log.d(TAG, "updateConnectivity: networkInfo=" + info); - Log.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus); - } - - mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0); - - if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) { - mBluetoothTethered = info.isConnected(); - } else { - mBluetoothTethered = false; - } - - // We want to update all the icons, all at once, for any condition change - updateDataNetType(); - updateWimaxIcons(); - updateDataIcon(); - updateTelephonySignalStrength(); - updateWifiIcons(); - } - - - // ===== Update the views ======================================================= - - void refreshViews() { - Context context = mContext; - - int combinedSignalIconId = 0; - String combinedLabel = ""; - String wifiLabel = ""; - String mobileLabel = ""; - int N; - final boolean emergencyOnly = isEmergencyOnly(); - - if (!mHasMobileDataFeature) { - mDataSignalIconId = mPhoneSignalIconId = 0; - mQSPhoneSignalIconId = 0; - mobileLabel = ""; - } else { - // We want to show the carrier name if in service and either: - // - We are connected to mobile data, or - // - We are not connected to mobile data, as long as the *reason* packets are not - // being routed over that link is that we have better connectivity via wifi. - // If data is disconnected for some other reason but wifi (or ethernet/bluetooth) - // is connected, we show nothing. - // Otherwise (nothing connected) we show "No internet connection". - - if (mDataConnected) { - mobileLabel = mNetworkName; - } else if (mConnected || emergencyOnly) { - if (hasService() || emergencyOnly) { - // The isEmergencyOnly test covers the case of a phone with no SIM - mobileLabel = mNetworkName; - } else { - // Tablets, basically - mobileLabel = ""; - } - } else { - mobileLabel - = context.getString(R.string.status_bar_settings_signal_meter_disconnected); - } - - // Now for things that should only be shown when actually using mobile data. - if (mDataConnected) { - combinedSignalIconId = mDataSignalIconId; - - combinedLabel = mobileLabel; - combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon() - mContentDescriptionCombinedSignal = mContentDescriptionDataType; - } - } - - if (mWifiConnected) { - if (mWifiSsid == null) { - wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid); - } else { - wifiLabel = mWifiSsid; - if (DEBUG) { - wifiLabel += "xxxxXXXXxxxxXXXX"; - } - } - - combinedLabel = wifiLabel; - combinedSignalIconId = mWifiIconId; // set by updateWifiIcons() - mContentDescriptionCombinedSignal = mContentDescriptionWifi; - } else { - if (mHasMobileDataFeature) { - wifiLabel = ""; - } else { - wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); - } - } - - if (mBluetoothTethered) { - combinedLabel = mContext.getString(R.string.bluetooth_tethered); - combinedSignalIconId = mBluetoothTetherIconId; - mContentDescriptionCombinedSignal = mContext.getString( - R.string.accessibility_bluetooth_tether); - } - - final boolean ethernetConnected = (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET); - if (ethernetConnected) { - combinedLabel = context.getString(R.string.ethernet_label); - } - - if (mAirplaneMode && - (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) { - // Only display the flight-mode icon if not in "emergency calls only" mode. - - // look again; your radios are now airplanes - mContentDescriptionPhoneSignal = mContext.getString( - R.string.accessibility_airplane_mode); - mAirplaneIconId = FLIGHT_MODE_ICON; - mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = mQSDataTypeIconId = 0; - mQSPhoneSignalIconId = 0; - - // combined values from connected wifi take precedence over airplane mode - if (mWifiConnected) { - // Suppress "No internet connection." from mobile if wifi connected. - mobileLabel = ""; - } else { - if (mHasMobileDataFeature) { - // let the mobile icon show "No internet connection." - wifiLabel = ""; - } else { - wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); - combinedLabel = wifiLabel; - } - mContentDescriptionCombinedSignal = mContentDescriptionPhoneSignal; - combinedSignalIconId = mDataSignalIconId; - } - } - else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected && !ethernetConnected) { - // pretty much totally disconnected - - combinedLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); - // On devices without mobile radios, we want to show the wifi icon - combinedSignalIconId = - mHasMobileDataFeature ? mDataSignalIconId : mWifiIconId; - mContentDescriptionCombinedSignal = mHasMobileDataFeature - ? mContentDescriptionDataType : mContentDescriptionWifi; - - mDataTypeIconId = 0; - mQSDataTypeIconId = 0; - if (isCdma()) { - if (isCdmaEri()) { - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; - } - } else if (mPhone.isNetworkRoaming()) { - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; - } - } - - if (DEBUG) { - Log.d(TAG, "refreshViews connected={" - + (mWifiConnected?" wifi":"") - + (mDataConnected?" data":"") - + " } level=" - + ((mSignalStrength == null)?"??":Integer.toString(mSignalStrength.getLevel())) - + " combinedSignalIconId=0x" - + Integer.toHexString(combinedSignalIconId) - + "/" + getResourceName(combinedSignalIconId) - + " mobileLabel=" + mobileLabel - + " wifiLabel=" + wifiLabel - + " emergencyOnly=" + emergencyOnly - + " combinedLabel=" + combinedLabel - + " mAirplaneMode=" + mAirplaneMode - + " mDataActivity=" + mDataActivity - + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId) - + " mQSPhoneSignalIconId=0x" + Integer.toHexString(mQSPhoneSignalIconId) - + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId) - + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId) - + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId) - + " mQSDataTypeIconId=0x" + Integer.toHexString(mQSDataTypeIconId) - + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId) - + " mQSWifiIconId=0x" + Integer.toHexString(mQSWifiIconId) - + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId)); - } - - // update QS - for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) { - notifySignalsChangedCallbacks(cb); - } - - if (mLastPhoneSignalIconId != mPhoneSignalIconId - || mLastWifiIconId != mWifiIconId - || mLastInetCondition != mInetCondition - || mLastWimaxIconId != mWimaxIconId - || mLastDataTypeIconId != mDataTypeIconId - || mLastAirplaneMode != mAirplaneMode - || mLastLocale != mLocale) - { - // NB: the mLast*s will be updated later - for (SignalCluster cluster : mSignalClusters) { - refreshSignalCluster(cluster); - } - } - - if (mLastAirplaneMode != mAirplaneMode) { - mLastAirplaneMode = mAirplaneMode; - } - - if (mLastLocale != mLocale) { - mLastLocale = mLocale; - } - - // the phone icon on phones - if (mLastPhoneSignalIconId != mPhoneSignalIconId) { - mLastPhoneSignalIconId = mPhoneSignalIconId; - } - - // the data icon on phones - if (mLastDataDirectionIconId != mDataDirectionIconId) { - mLastDataDirectionIconId = mDataDirectionIconId; - } - - // the wifi icon on phones - if (mLastWifiIconId != mWifiIconId) { - mLastWifiIconId = mWifiIconId; - } - - if (mLastInetCondition != mInetCondition) { - mLastInetCondition = mInetCondition; - } - - // the wimax icon on phones - if (mLastWimaxIconId != mWimaxIconId) { - mLastWimaxIconId = mWimaxIconId; - } - // the combined data signal icon - if (mLastCombinedSignalIconId != combinedSignalIconId) { - mLastCombinedSignalIconId = combinedSignalIconId; - } - - // the data network type overlay - if (mLastDataTypeIconId != mDataTypeIconId) { - mLastDataTypeIconId = mDataTypeIconId; - } - - // the combinedLabel in the notification panel - if (!mLastCombinedLabel.equals(combinedLabel)) { - mLastCombinedLabel = combinedLabel; - N = mCombinedLabelViews.size(); - for (int i=0; i<N; i++) { - TextView v = mCombinedLabelViews.get(i); - v.setText(combinedLabel); - } - } - - // wifi label - N = mWifiLabelViews.size(); - for (int i=0; i<N; i++) { - TextView v = mWifiLabelViews.get(i); - v.setText(wifiLabel); - if ("".equals(wifiLabel)) { - v.setVisibility(View.GONE); - } else { - v.setVisibility(View.VISIBLE); - } - } - - // mobile label - N = mMobileLabelViews.size(); - for (int i=0; i<N; i++) { - TextView v = mMobileLabelViews.get(i); - v.setText(mobileLabel); - if ("".equals(mobileLabel)) { - v.setVisibility(View.GONE); - } else { - v.setVisibility(View.VISIBLE); - } - } - - // e-call label - N = mEmergencyLabelViews.size(); - for (int i=0; i<N; i++) { - TextView v = mEmergencyLabelViews.get(i); - if (!emergencyOnly) { - v.setVisibility(View.GONE); - } else { - v.setText(mobileLabel); // comes from the telephony stack - v.setVisibility(View.VISIBLE); - } - } - } - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("NetworkController state:"); - pw.println(String.format(" %s network type %d (%s)", - mConnected?"CONNECTED":"DISCONNECTED", - mConnectedNetworkType, mConnectedNetworkTypeName)); - pw.println(" - telephony ------"); - pw.print(" hasVoiceCallingFeature()="); - pw.println(hasVoiceCallingFeature()); - pw.print(" hasService()="); - pw.println(hasService()); - pw.print(" mHspaDataDistinguishable="); - pw.println(mHspaDataDistinguishable); - pw.print(" mDataConnected="); - pw.println(mDataConnected); - pw.print(" mSimState="); - pw.println(mSimState); - pw.print(" mPhoneState="); - pw.println(mPhoneState); - pw.print(" mDataState="); - pw.println(mDataState); - pw.print(" mDataActivity="); - pw.println(mDataActivity); - pw.print(" mDataNetType="); - pw.print(mDataNetType); - pw.print("/"); - pw.println(TelephonyManager.getNetworkTypeName(mDataNetType)); - pw.print(" mServiceState="); - pw.println(mServiceState); - pw.print(" mSignalStrength="); - pw.println(mSignalStrength); - pw.print(" mLastSignalLevel="); - pw.println(mLastSignalLevel); - pw.print(" mNetworkName="); - pw.println(mNetworkName); - pw.print(" mNetworkNameDefault="); - pw.println(mNetworkNameDefault); - pw.print(" mNetworkNameSeparator="); - pw.println(mNetworkNameSeparator.replace("\n","\\n")); - pw.print(" mPhoneSignalIconId=0x"); - pw.print(Integer.toHexString(mPhoneSignalIconId)); - pw.print("/"); - pw.print(" mQSPhoneSignalIconId=0x"); - pw.print(Integer.toHexString(mQSPhoneSignalIconId)); - pw.print("/"); - pw.println(getResourceName(mPhoneSignalIconId)); - pw.print(" mDataDirectionIconId="); - pw.print(Integer.toHexString(mDataDirectionIconId)); - pw.print("/"); - pw.println(getResourceName(mDataDirectionIconId)); - pw.print(" mDataSignalIconId="); - pw.print(Integer.toHexString(mDataSignalIconId)); - pw.print("/"); - pw.println(getResourceName(mDataSignalIconId)); - pw.print(" mDataTypeIconId="); - pw.print(Integer.toHexString(mDataTypeIconId)); - pw.print("/"); - pw.println(getResourceName(mDataTypeIconId)); - pw.print(" mQSDataTypeIconId="); - pw.print(Integer.toHexString(mQSDataTypeIconId)); - pw.print("/"); - pw.println(getResourceName(mQSDataTypeIconId)); - - pw.println(" - wifi ------"); - pw.print(" mWifiEnabled="); - pw.println(mWifiEnabled); - pw.print(" mWifiConnected="); - pw.println(mWifiConnected); - pw.print(" mWifiRssi="); - pw.println(mWifiRssi); - pw.print(" mWifiLevel="); - pw.println(mWifiLevel); - pw.print(" mWifiSsid="); - pw.println(mWifiSsid); - pw.println(String.format(" mWifiIconId=0x%08x/%s", - mWifiIconId, getResourceName(mWifiIconId))); - pw.println(String.format(" mQSWifiIconId=0x%08x/%s", - mQSWifiIconId, getResourceName(mQSWifiIconId))); - pw.print(" mWifiActivity="); - pw.println(mWifiActivity); - - if (mWimaxSupported) { - pw.println(" - wimax ------"); - pw.print(" mIsWimaxEnabled="); pw.println(mIsWimaxEnabled); - pw.print(" mWimaxConnected="); pw.println(mWimaxConnected); - pw.print(" mWimaxIdle="); pw.println(mWimaxIdle); - pw.println(String.format(" mWimaxIconId=0x%08x/%s", - mWimaxIconId, getResourceName(mWimaxIconId))); - pw.println(String.format(" mWimaxSignal=%d", mWimaxSignal)); - pw.println(String.format(" mWimaxState=%d", mWimaxState)); - pw.println(String.format(" mWimaxExtraState=%d", mWimaxExtraState)); - } - - pw.println(" - Bluetooth ----"); - pw.print(" mBtReverseTethered="); - pw.println(mBluetoothTethered); - - pw.println(" - connectivity ------"); - pw.print(" mInetCondition="); - pw.println(mInetCondition); - - pw.println(" - icons ------"); - pw.print(" mLastPhoneSignalIconId=0x"); - pw.print(Integer.toHexString(mLastPhoneSignalIconId)); - pw.print("/"); - pw.println(getResourceName(mLastPhoneSignalIconId)); - pw.print(" mLastDataDirectionIconId=0x"); - pw.print(Integer.toHexString(mLastDataDirectionIconId)); - pw.print("/"); - pw.println(getResourceName(mLastDataDirectionIconId)); - pw.print(" mLastWifiIconId=0x"); - pw.print(Integer.toHexString(mLastWifiIconId)); - pw.print("/"); - pw.println(getResourceName(mLastWifiIconId)); - pw.print(" mLastCombinedSignalIconId=0x"); - pw.print(Integer.toHexString(mLastCombinedSignalIconId)); - pw.print("/"); - pw.println(getResourceName(mLastCombinedSignalIconId)); - pw.print(" mLastDataTypeIconId=0x"); - pw.print(Integer.toHexString(mLastDataTypeIconId)); - pw.print("/"); - pw.println(getResourceName(mLastDataTypeIconId)); - pw.print(" mLastCombinedLabel="); - pw.print(mLastCombinedLabel); - pw.println(""); - } - - private String getResourceName(int resId) { - if (resId != 0) { - final Resources res = mContext.getResources(); - try { - return res.getResourceName(resId); - } catch (android.content.res.Resources.NotFoundException ex) { - return "(unknown)"; - } - } else { - return "(null)"; - } - } - - private boolean mDemoMode; - private int mDemoInetCondition; - private int mDemoWifiLevel; - private int mDemoDataTypeIconId; - private int mDemoMobileLevel; - - @Override - public void dispatchDemoCommand(String command, Bundle args) { - if (!mDemoMode && command.equals(COMMAND_ENTER)) { - mDemoMode = true; - mDemoWifiLevel = mWifiLevel; - mDemoInetCondition = mInetCondition; - mDemoDataTypeIconId = mDataTypeIconId; - mDemoMobileLevel = mLastSignalLevel; - } else if (mDemoMode && command.equals(COMMAND_EXIT)) { - mDemoMode = false; - for (SignalCluster cluster : mSignalClusters) { - refreshSignalCluster(cluster); - } - } else if (mDemoMode && command.equals(COMMAND_NETWORK)) { - String airplane = args.getString("airplane"); - if (airplane != null) { - boolean show = airplane.equals("show"); - for (SignalCluster cluster : mSignalClusters) { - cluster.setIsAirplaneMode(show, FLIGHT_MODE_ICON); - } - } - String fully = args.getString("fully"); - if (fully != null) { - mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0; - } - String wifi = args.getString("wifi"); - if (wifi != null) { - boolean show = wifi.equals("show"); - String level = args.getString("level"); - if (level != null) { - mDemoWifiLevel = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); - } - int iconId = mDemoWifiLevel < 0 ? R.drawable.stat_sys_wifi_signal_null - : WifiIcons.WIFI_SIGNAL_STRENGTH[mDemoInetCondition][mDemoWifiLevel]; - for (SignalCluster cluster : mSignalClusters) { - cluster.setWifiIndicators( - show, - iconId, - mDemoInetCondition == 0, - "Demo"); - } - } - String mobile = args.getString("mobile"); - if (mobile != null) { - boolean show = mobile.equals("show"); - String datatype = args.getString("datatype"); - if (datatype != null) { - mDemoDataTypeIconId = - datatype.equals("1x") ? R.drawable.stat_sys_data_fully_connected_1x : - datatype.equals("3g") ? R.drawable.stat_sys_data_fully_connected_3g : - datatype.equals("4g") ? R.drawable.stat_sys_data_fully_connected_4g : - datatype.equals("e") ? R.drawable.stat_sys_data_fully_connected_e : - datatype.equals("g") ? R.drawable.stat_sys_data_fully_connected_g : - datatype.equals("h") ? R.drawable.stat_sys_data_fully_connected_h : - datatype.equals("lte") ? R.drawable.stat_sys_data_fully_connected_lte : - datatype.equals("roam") - ? R.drawable.stat_sys_data_fully_connected_roam : - 0; - } - int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH; - String level = args.getString("level"); - if (level != null) { - mDemoMobileLevel = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), icons[0].length - 1); - } - int iconId = mDemoMobileLevel < 0 ? R.drawable.stat_sys_signal_null : - icons[mDemoInetCondition][mDemoMobileLevel]; - for (SignalCluster cluster : mSignalClusters) { - cluster.setMobileDataIndicators( - show, - iconId, - mDemoInetCondition == 0, - mDemoDataTypeIconId, - "Demo", - "Demo"); - } - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java new file mode 100644 index 0000000..966c0b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -0,0 +1,1491 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.net.wimax.WimaxManagerConstants; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.provider.Settings; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.cdma.EriInfo; +import com.android.internal.util.AsyncChannel; +import com.android.systemui.DemoMode; +import com.android.systemui.R; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** Platform implementation of the network controller. **/ +public class NetworkControllerImpl extends BroadcastReceiver + implements NetworkController, DemoMode { + // debug + static final String TAG = "StatusBar.NetworkController"; + static final boolean DEBUG = false; + static final boolean CHATTY = false; // additional diagnostics, but not logspew + + private static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_signal_flightmode; + + // telephony + boolean mHspaDataDistinguishable; + final TelephonyManager mPhone; + boolean mDataConnected; + IccCardConstants.State mSimState = IccCardConstants.State.READY; + int mPhoneState = TelephonyManager.CALL_STATE_IDLE; + int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + int mDataState = TelephonyManager.DATA_DISCONNECTED; + int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; + ServiceState mServiceState; + SignalStrength mSignalStrength; + int[] mDataIconList = TelephonyIcons.DATA_G[0]; + String mNetworkName; + String mNetworkNameDefault; + String mNetworkNameSeparator; + int mPhoneSignalIconId; + int mQSPhoneSignalIconId; + int mDataDirectionIconId; // data + data direction on phones + int mDataSignalIconId; + int mDataTypeIconId; + int mQSDataTypeIconId; + int mAirplaneIconId; + boolean mDataActive; + int mLastSignalLevel; + boolean mShowPhoneRSSIForData = false; + boolean mShowAtLeastThreeGees = false; + boolean mAlwaysShowCdmaRssi = false; + + String mContentDescriptionPhoneSignal; + String mContentDescriptionWifi; + String mContentDescriptionWimax; + String mContentDescriptionCombinedSignal; + String mContentDescriptionDataType; + + // wifi + final WifiManager mWifiManager; + AsyncChannel mWifiChannel; + boolean mWifiEnabled, mWifiConnected; + int mWifiRssi, mWifiLevel; + String mWifiSsid; + int mWifiIconId = 0; + int mQSWifiIconId = 0; + int mWifiActivity = WifiManager.DATA_ACTIVITY_NONE; + + // bluetooth + private boolean mBluetoothTethered = false; + private int mBluetoothTetherIconId = + com.android.internal.R.drawable.stat_sys_tether_bluetooth; + + //wimax + private boolean mWimaxSupported = false; + private boolean mIsWimaxEnabled = false; + private boolean mWimaxConnected = false; + private boolean mWimaxIdle = false; + private int mWimaxIconId = 0; + private int mWimaxSignal = 0; + private int mWimaxState = 0; + private int mWimaxExtraState = 0; + + // data connectivity (regardless of state, can we access the internet?) + // state of inet connection - 0 not connected, 100 connected + private boolean mConnected = false; + private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE; + private String mConnectedNetworkTypeName; + private int mInetCondition = 0; + private int mLastInetCondition = 0; + private static final int INET_CONDITION_THRESHOLD = 50; + + private boolean mAirplaneMode = false; + private boolean mLastAirplaneMode = true; + + private Locale mLocale = null; + private Locale mLastLocale = null; + + // our ui + Context mContext; + ArrayList<TextView> mCombinedLabelViews = new ArrayList<TextView>(); + ArrayList<TextView> mMobileLabelViews = new ArrayList<TextView>(); + ArrayList<TextView> mWifiLabelViews = new ArrayList<TextView>(); + ArrayList<TextView> mEmergencyLabelViews = new ArrayList<TextView>(); + ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>(); + ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks = + new ArrayList<NetworkSignalChangedCallback>(); + int mLastPhoneSignalIconId = -1; + int mLastDataDirectionIconId = -1; + int mLastWifiIconId = -1; + int mLastWimaxIconId = -1; + int mLastCombinedSignalIconId = -1; + int mLastDataTypeIconId = -1; + String mLastCombinedLabel = ""; + + private boolean mHasMobileDataFeature; + + boolean mDataAndWifiStacked = false; + + public interface SignalCluster { + void setWifiIndicators(boolean visible, int strengthIcon, boolean problem, + String contentDescription); + void setMobileDataIndicators(boolean visible, int strengthIcon, boolean problem, + int typeIcon, String contentDescription, String typeContentDescription); + void setIsAirplaneMode(boolean is, int airplaneIcon); + } + + /** + * Construct this controller object and register for updates. + */ + public NetworkControllerImpl(Context context) { + mContext = context; + final Resources res = context.getResources(); + + ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + mHasMobileDataFeature = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + + mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData); + mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G); + mAlwaysShowCdmaRssi = res.getBoolean( + com.android.internal.R.bool.config_alwaysUseCdmaRssi); + + // set up the default wifi icon, used when no radios have ever appeared + updateWifiIcons(); + updateWimaxIcons(); + + // telephony + mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); + mPhone.listen(mPhoneStateListener, + PhoneStateListener.LISTEN_SERVICE_STATE + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS + | PhoneStateListener.LISTEN_CALL_STATE + | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE + | PhoneStateListener.LISTEN_DATA_ACTIVITY); + mHspaDataDistinguishable = mContext.getResources().getBoolean( + R.bool.config_hspa_data_distinguishable); + mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator); + mNetworkNameDefault = mContext.getString( + com.android.internal.R.string.lockscreen_carrier_default); + mNetworkName = mNetworkNameDefault; + + // wifi + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + Handler handler = new WifiHandler(); + mWifiChannel = new AsyncChannel(); + Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); + if (wifiMessenger != null) { + mWifiChannel.connect(mContext, handler, wifiMessenger); + } + + // broadcasts + IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.RSSI_CHANGED_ACTION); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); + filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(ConnectivityManager.INET_CONDITION_ACTION); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); + mWimaxSupported = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_wimaxEnabled); + if(mWimaxSupported) { + filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION); + filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION); + } + context.registerReceiver(this, filter); + + // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it + updateAirplaneMode(); + + mLastLocale = mContext.getResources().getConfiguration().locale; + } + + public boolean hasMobileDataFeature() { + return mHasMobileDataFeature; + } + + public boolean hasVoiceCallingFeature() { + return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; + } + + public boolean isEmergencyOnly() { + return (mServiceState != null && mServiceState.isEmergencyOnly()); + } + + public void addCombinedLabelView(TextView v) { + mCombinedLabelViews.add(v); + } + + public void addMobileLabelView(TextView v) { + mMobileLabelViews.add(v); + } + + public void addWifiLabelView(TextView v) { + mWifiLabelViews.add(v); + } + + public void addEmergencyLabelView(TextView v) { + mEmergencyLabelViews.add(v); + } + + public void addSignalCluster(SignalCluster cluster) { + mSignalClusters.add(cluster); + refreshSignalCluster(cluster); + } + + public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { + mSignalsChangedCallbacks.add(cb); + notifySignalsChangedCallbacks(cb); + } + + public void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { + mSignalsChangedCallbacks.remove(cb); + } + + @Override + public void setWifiEnabled(final boolean enabled) { + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... args) { + // Disable tethering if enabling Wifi + final int wifiApState = mWifiManager.getWifiApState(); + if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || + (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { + mWifiManager.setWifiApEnabled(null, false); + } + + mWifiManager.setWifiEnabled(enabled); + return null; + } + }.execute(); + } + + public void refreshSignalCluster(SignalCluster cluster) { + if (mDemoMode) return; + cluster.setWifiIndicators( + // only show wifi in the cluster if connected or if wifi-only + mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature), + mWifiIconId, + mInetCondition == 0, + mContentDescriptionWifi); + + if (mIsWimaxEnabled && mWimaxConnected) { + // wimax is special + cluster.setMobileDataIndicators( + true, + mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId, + mInetCondition == 0, + mDataTypeIconId, + mContentDescriptionWimax, + mContentDescriptionDataType); + } else { + // normal mobile data + cluster.setMobileDataIndicators( + mHasMobileDataFeature, + mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId, + mInetCondition == 0, + mDataTypeIconId, + mContentDescriptionPhoneSignal, + mContentDescriptionDataType); + } + cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId); + } + + void notifySignalsChangedCallbacks(NetworkSignalChangedCallback cb) { + // only show wifi in the cluster if connected or if wifi-only + boolean wifiEnabled = mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature); + String wifiDesc = wifiEnabled ? + mWifiSsid : null; + boolean wifiIn = wifiEnabled && mWifiSsid != null + && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT + || mWifiActivity == WifiManager.DATA_ACTIVITY_IN); + boolean wifiOut = wifiEnabled && mWifiSsid != null + && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT + || mWifiActivity == WifiManager.DATA_ACTIVITY_OUT); + cb.onWifiSignalChanged(wifiEnabled, mQSWifiIconId, wifiIn, wifiOut, + mContentDescriptionWifi, wifiDesc); + + boolean mobileIn = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT + || mDataActivity == TelephonyManager.DATA_ACTIVITY_IN); + boolean mobileOut = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT + || mDataActivity == TelephonyManager.DATA_ACTIVITY_OUT); + if (isEmergencyOnly()) { + cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId, + mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, + mContentDescriptionDataType, null); + } else { + if (mIsWimaxEnabled && mWimaxConnected) { + // Wimax is special + cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId, + mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, + mContentDescriptionDataType, mNetworkName); + } else { + // Normal mobile data + cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId, + mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, + mContentDescriptionDataType, mNetworkName); + } + } + cb.onAirplaneModeChanged(mAirplaneMode); + } + + public void setStackedMode(boolean stacked) { + mDataAndWifiStacked = true; + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action.equals(WifiManager.RSSI_CHANGED_ACTION) + || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) + || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + updateWifiState(intent); + refreshViews(); + } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { + updateSimState(intent); + updateDataIcon(); + refreshViews(); + } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { + updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), + intent.getStringExtra(TelephonyIntents.EXTRA_SPN), + intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), + intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); + refreshViews(); + } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || + action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { + updateConnectivity(intent); + refreshViews(); + } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { + refreshLocale(); + refreshViews(); + } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { + refreshLocale(); + updateAirplaneMode(); + refreshViews(); + } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) || + action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) || + action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) { + updateWimaxState(intent); + refreshViews(); + } + } + + + // ===== Telephony ============================================================== + + PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @Override + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + if (DEBUG) { + Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength + + ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); + } + mSignalStrength = signalStrength; + updateTelephonySignalStrength(); + refreshViews(); + } + + @Override + public void onServiceStateChanged(ServiceState state) { + if (DEBUG) { + Log.d(TAG, "onServiceStateChanged voiceState=" + state.getVoiceRegState() + + " dataState=" + state.getDataRegState()); + } + mServiceState = state; + updateTelephonySignalStrength(); + updateDataNetType(); + updateDataIcon(); + refreshViews(); + } + + @Override + public void onCallStateChanged(int state, String incomingNumber) { + if (DEBUG) { + Log.d(TAG, "onCallStateChanged state=" + state); + } + // In cdma, if a voice call is made, RSSI should switch to 1x. + if (isCdma()) { + updateTelephonySignalStrength(); + refreshViews(); + } + } + + @Override + public void onDataConnectionStateChanged(int state, int networkType) { + if (DEBUG) { + Log.d(TAG, "onDataConnectionStateChanged: state=" + state + + " type=" + networkType); + } + mDataState = state; + mDataNetType = networkType; + updateDataNetType(); + updateDataIcon(); + refreshViews(); + } + + @Override + public void onDataActivity(int direction) { + if (DEBUG) { + Log.d(TAG, "onDataActivity: direction=" + direction); + } + mDataActivity = direction; + updateDataIcon(); + refreshViews(); + } + }; + + private final void updateSimState(Intent intent) { + String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); + if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { + mSimState = IccCardConstants.State.ABSENT; + } + else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { + mSimState = IccCardConstants.State.READY; + } + else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { + final String lockedReason = + intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); + if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { + mSimState = IccCardConstants.State.PIN_REQUIRED; + } + else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { + mSimState = IccCardConstants.State.PUK_REQUIRED; + } + else { + mSimState = IccCardConstants.State.NETWORK_LOCKED; + } + } else { + mSimState = IccCardConstants.State.UNKNOWN; + } + } + + private boolean isCdma() { + return (mSignalStrength != null) && !mSignalStrength.isGsm(); + } + + private boolean hasService() { + if (mServiceState != null) { + // Consider the device to be in service if either voice or data service is available. + // Some SIM cards are marketed as data-only and do not support voice service, and on + // these SIM cards, we want to show signal bars for data service as well as the "no + // service" or "emergency calls only" text that indicates that voice is not available. + switch(mServiceState.getVoiceRegState()) { + case ServiceState.STATE_POWER_OFF: + return false; + case ServiceState.STATE_OUT_OF_SERVICE: + case ServiceState.STATE_EMERGENCY_ONLY: + return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE; + default: + return true; + } + } else { + return false; + } + } + + private void updateAirplaneMode() { + mAirplaneMode = (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1); + } + + private void refreshLocale() { + mLocale = mContext.getResources().getConfiguration().locale; + } + + private final void updateTelephonySignalStrength() { + if (!hasService()) { + if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: !hasService()"); + mPhoneSignalIconId = R.drawable.stat_sys_signal_null; + mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; + mDataSignalIconId = R.drawable.stat_sys_signal_null; + } else { + if (mSignalStrength == null) { + if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null"); + mPhoneSignalIconId = R.drawable.stat_sys_signal_null; + mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; + mDataSignalIconId = R.drawable.stat_sys_signal_null; + mContentDescriptionPhoneSignal = mContext.getString( + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]); + } else { + int iconLevel; + int[] iconList; + if (isCdma() && mAlwaysShowCdmaRssi) { + mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel(); + if(DEBUG) Log.d(TAG, "mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi + + " set to cdmaLevel=" + mSignalStrength.getCdmaLevel() + + " instead of level=" + mSignalStrength.getLevel()); + } else { + mLastSignalLevel = iconLevel = mSignalStrength.getLevel(); + } + + if (isCdma()) { + if (isCdmaEri()) { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; + } else { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; + } + } else { + // Though mPhone is a Manager, this call is not an IPC + if (mPhone.isNetworkRoaming()) { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; + } else { + iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; + } + } + mPhoneSignalIconId = iconList[iconLevel]; + mQSPhoneSignalIconId = + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mInetCondition][iconLevel]; + mContentDescriptionPhoneSignal = mContext.getString( + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]); + mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel]; + } + } + } + + private final void updateDataNetType() { + if (mIsWimaxEnabled && mWimaxConnected) { + // wimax is a special 4g network not handled by telephony + mDataIconList = TelephonyIcons.DATA_4G[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_4g); + } else { + switch (mDataNetType) { + case TelephonyManager.NETWORK_TYPE_UNKNOWN: + if (!mShowAtLeastThreeGees) { + mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; + mDataTypeIconId = 0; + mQSDataTypeIconId = 0; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_gprs); + break; + } else { + // fall through + } + case TelephonyManager.NETWORK_TYPE_EDGE: + if (!mShowAtLeastThreeGees) { + mDataIconList = TelephonyIcons.DATA_E[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_e; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_E[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_edge); + break; + } else { + // fall through + } + case TelephonyManager.NETWORK_TYPE_UMTS: + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_3g); + break; + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_HSPAP: + if (mHspaDataDistinguishable) { + mDataIconList = TelephonyIcons.DATA_H[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_h; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_H[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_3_5g); + } else { + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_3g); + } + break; + case TelephonyManager.NETWORK_TYPE_CDMA: + if (!mShowAtLeastThreeGees) { + // display 1xRTT for IS95A/B + mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_1x; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_cdma); + break; + } else { + // fall through + } + case TelephonyManager.NETWORK_TYPE_1xRTT: + if (!mShowAtLeastThreeGees) { + mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_1x; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_cdma); + break; + } else { + // fall through + } + case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_3g); + break; + case TelephonyManager.NETWORK_TYPE_LTE: + boolean show4GforLTE = mContext.getResources().getBoolean(R.bool.config_show4GForLTE); + if (show4GforLTE) { + mDataIconList = TelephonyIcons.DATA_4G[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_4g); + } else { + mDataIconList = TelephonyIcons.DATA_LTE[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_lte; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_LTE[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_lte); + } + break; + default: + if (!mShowAtLeastThreeGees) { + mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_g; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_G[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_gprs); + } else { + mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_3g; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[mInetCondition]; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_3g); + } + break; + } + } + + if (isCdma()) { + if (isCdmaEri()) { + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; + } + } else if (mPhone.isNetworkRoaming()) { + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; + } + } + + boolean isCdmaEri() { + if (mServiceState != null) { + final int iconIndex = mServiceState.getCdmaEriIconIndex(); + if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { + final int iconMode = mServiceState.getCdmaEriIconMode(); + if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL + || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { + return true; + } + } + } + return false; + } + + private final void updateDataIcon() { + int iconId; + boolean visible = true; + + if (!isCdma()) { + // GSM case, we have to check also the sim state + if (mSimState == IccCardConstants.State.READY || + mSimState == IccCardConstants.State.UNKNOWN) { + if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { + switch (mDataActivity) { + case TelephonyManager.DATA_ACTIVITY_IN: + iconId = mDataIconList[1]; + break; + case TelephonyManager.DATA_ACTIVITY_OUT: + iconId = mDataIconList[2]; + break; + case TelephonyManager.DATA_ACTIVITY_INOUT: + iconId = mDataIconList[3]; + break; + default: + iconId = mDataIconList[0]; + break; + } + mDataDirectionIconId = iconId; + } else { + iconId = 0; + visible = false; + } + } else { + iconId = R.drawable.stat_sys_no_sim; + visible = false; // no SIM? no data + } + } else { + // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT + if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { + switch (mDataActivity) { + case TelephonyManager.DATA_ACTIVITY_IN: + iconId = mDataIconList[1]; + break; + case TelephonyManager.DATA_ACTIVITY_OUT: + iconId = mDataIconList[2]; + break; + case TelephonyManager.DATA_ACTIVITY_INOUT: + iconId = mDataIconList[3]; + break; + case TelephonyManager.DATA_ACTIVITY_DORMANT: + default: + iconId = mDataIconList[0]; + break; + } + } else { + iconId = 0; + visible = false; + } + } + + mDataDirectionIconId = iconId; + mDataConnected = visible; + } + + void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { + if (false) { + Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn + + " showPlmn=" + showPlmn + " plmn=" + plmn); + } + StringBuilder str = new StringBuilder(); + boolean something = false; + if (showPlmn && plmn != null) { + str.append(plmn); + something = true; + } + if (showSpn && spn != null) { + if (something) { + str.append(mNetworkNameSeparator); + } + str.append(spn); + something = true; + } + if (something) { + mNetworkName = str.toString(); + } else { + mNetworkName = mNetworkNameDefault; + } + } + + // ===== Wifi =================================================================== + + class WifiHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + mWifiChannel.sendMessage(Message.obtain(this, + AsyncChannel.CMD_CHANNEL_FULL_CONNECTION)); + } else { + Log.e(TAG, "Failed to connect to wifi"); + } + break; + case WifiManager.DATA_ACTIVITY_NOTIFICATION: + if (msg.arg1 != mWifiActivity) { + mWifiActivity = msg.arg1; + refreshViews(); + } + break; + default: + //Ignore + break; + } + } + } + + private void updateWifiState(Intent intent) { + final String action = intent.getAction(); + if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; + + } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + final NetworkInfo networkInfo = (NetworkInfo) + intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + boolean wasConnected = mWifiConnected; + mWifiConnected = networkInfo != null && networkInfo.isConnected(); + // If we just connected, grab the inintial signal strength and ssid + if (mWifiConnected && !wasConnected) { + // try getting it out of the intent first + WifiInfo info = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); + if (info == null) { + info = mWifiManager.getConnectionInfo(); + } + if (info != null) { + mWifiSsid = huntForSsid(info); + } else { + mWifiSsid = null; + } + } else if (!mWifiConnected) { + mWifiSsid = null; + } + } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { + mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); + mWifiLevel = WifiManager.calculateSignalLevel( + mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT); + } + + updateWifiIcons(); + } + + private void updateWifiIcons() { + if (mWifiConnected) { + mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; + mQSWifiIconId = WifiIcons.QS_WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; + mContentDescriptionWifi = mContext.getString( + AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]); + } else { + if (mDataAndWifiStacked) { + mWifiIconId = 0; + mQSWifiIconId = 0; + } else { + mWifiIconId = mWifiEnabled ? R.drawable.stat_sys_wifi_signal_null : 0; + mQSWifiIconId = mWifiEnabled ? R.drawable.ic_qs_wifi_no_network : 0; + } + mContentDescriptionWifi = mContext.getString(R.string.accessibility_no_wifi); + } + } + + private String huntForSsid(WifiInfo info) { + String ssid = info.getSSID(); + if (ssid != null) { + return ssid; + } + // OK, it's not in the connectionInfo; we have to go hunting for it + List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks(); + for (WifiConfiguration net : networks) { + if (net.networkId == info.getNetworkId()) { + return net.SSID; + } + } + return null; + } + + + // ===== Wimax =================================================================== + private final void updateWimaxState(Intent intent) { + final String action = intent.getAction(); + boolean wasConnected = mWimaxConnected; + if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION)) { + int wimaxStatus = intent.getIntExtra(WimaxManagerConstants.EXTRA_4G_STATE, + WimaxManagerConstants.NET_4G_STATE_UNKNOWN); + mIsWimaxEnabled = (wimaxStatus == + WimaxManagerConstants.NET_4G_STATE_ENABLED); + } else if (action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION)) { + mWimaxSignal = intent.getIntExtra(WimaxManagerConstants.EXTRA_NEW_SIGNAL_LEVEL, 0); + } else if (action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) { + mWimaxState = intent.getIntExtra(WimaxManagerConstants.EXTRA_WIMAX_STATE, + WimaxManagerConstants.NET_4G_STATE_UNKNOWN); + mWimaxExtraState = intent.getIntExtra( + WimaxManagerConstants.EXTRA_WIMAX_STATE_DETAIL, + WimaxManagerConstants.NET_4G_STATE_UNKNOWN); + mWimaxConnected = (mWimaxState == + WimaxManagerConstants.WIMAX_STATE_CONNECTED); + mWimaxIdle = (mWimaxExtraState == WimaxManagerConstants.WIMAX_IDLE); + } + updateDataNetType(); + updateWimaxIcons(); + } + + private void updateWimaxIcons() { + if (mIsWimaxEnabled) { + if (mWimaxConnected) { + if (mWimaxIdle) + mWimaxIconId = WimaxIcons.WIMAX_IDLE; + else + mWimaxIconId = WimaxIcons.WIMAX_SIGNAL_STRENGTH[mInetCondition][mWimaxSignal]; + mContentDescriptionWimax = mContext.getString( + AccessibilityContentDescriptions.WIMAX_CONNECTION_STRENGTH[mWimaxSignal]); + } else { + mWimaxIconId = WimaxIcons.WIMAX_DISCONNECTED; + mContentDescriptionWimax = mContext.getString(R.string.accessibility_no_wimax); + } + } else { + mWimaxIconId = 0; + } + } + + // ===== Full or limited Internet connectivity ================================== + + private void updateConnectivity(Intent intent) { + if (CHATTY) { + Log.d(TAG, "updateConnectivity: intent=" + intent); + } + + final ConnectivityManager connManager = (ConnectivityManager) mContext + .getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo info = connManager.getActiveNetworkInfo(); + + // Are we connected at all, by any interface? + mConnected = info != null && info.isConnected(); + if (mConnected) { + mConnectedNetworkType = info.getType(); + mConnectedNetworkTypeName = info.getTypeName(); + } else { + mConnectedNetworkType = ConnectivityManager.TYPE_NONE; + mConnectedNetworkTypeName = null; + } + + int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0); + + if (CHATTY) { + Log.d(TAG, "updateConnectivity: networkInfo=" + info); + Log.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus); + } + + mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0); + + if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) { + mBluetoothTethered = info.isConnected(); + } else { + mBluetoothTethered = false; + } + + // We want to update all the icons, all at once, for any condition change + updateDataNetType(); + updateWimaxIcons(); + updateDataIcon(); + updateTelephonySignalStrength(); + updateWifiIcons(); + } + + + // ===== Update the views ======================================================= + + void refreshViews() { + Context context = mContext; + + int combinedSignalIconId = 0; + String combinedLabel = ""; + String wifiLabel = ""; + String mobileLabel = ""; + int N; + final boolean emergencyOnly = isEmergencyOnly(); + + if (!mHasMobileDataFeature) { + mDataSignalIconId = mPhoneSignalIconId = 0; + mQSPhoneSignalIconId = 0; + mobileLabel = ""; + } else { + // We want to show the carrier name if in service and either: + // - We are connected to mobile data, or + // - We are not connected to mobile data, as long as the *reason* packets are not + // being routed over that link is that we have better connectivity via wifi. + // If data is disconnected for some other reason but wifi (or ethernet/bluetooth) + // is connected, we show nothing. + // Otherwise (nothing connected) we show "No internet connection". + + if (mDataConnected) { + mobileLabel = mNetworkName; + } else if (mConnected || emergencyOnly) { + if (hasService() || emergencyOnly) { + // The isEmergencyOnly test covers the case of a phone with no SIM + mobileLabel = mNetworkName; + } else { + // Tablets, basically + mobileLabel = ""; + } + } else { + mobileLabel + = context.getString(R.string.status_bar_settings_signal_meter_disconnected); + } + + // Now for things that should only be shown when actually using mobile data. + if (mDataConnected) { + combinedSignalIconId = mDataSignalIconId; + + combinedLabel = mobileLabel; + combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon() + mContentDescriptionCombinedSignal = mContentDescriptionDataType; + } + } + + if (mWifiConnected) { + if (mWifiSsid == null) { + wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid); + } else { + wifiLabel = mWifiSsid; + if (DEBUG) { + wifiLabel += "xxxxXXXXxxxxXXXX"; + } + } + + combinedLabel = wifiLabel; + combinedSignalIconId = mWifiIconId; // set by updateWifiIcons() + mContentDescriptionCombinedSignal = mContentDescriptionWifi; + } else { + if (mHasMobileDataFeature) { + wifiLabel = ""; + } else { + wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); + } + } + + if (mBluetoothTethered) { + combinedLabel = mContext.getString(R.string.bluetooth_tethered); + combinedSignalIconId = mBluetoothTetherIconId; + mContentDescriptionCombinedSignal = mContext.getString( + R.string.accessibility_bluetooth_tether); + } + + final boolean ethernetConnected = (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET); + if (ethernetConnected) { + combinedLabel = context.getString(R.string.ethernet_label); + } + + if (mAirplaneMode && + (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) { + // Only display the flight-mode icon if not in "emergency calls only" mode. + + // look again; your radios are now airplanes + mContentDescriptionPhoneSignal = mContext.getString( + R.string.accessibility_airplane_mode); + mAirplaneIconId = FLIGHT_MODE_ICON; + mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = mQSDataTypeIconId = 0; + mQSPhoneSignalIconId = 0; + + // combined values from connected wifi take precedence over airplane mode + if (mWifiConnected) { + // Suppress "No internet connection." from mobile if wifi connected. + mobileLabel = ""; + } else { + if (mHasMobileDataFeature) { + // let the mobile icon show "No internet connection." + wifiLabel = ""; + } else { + wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); + combinedLabel = wifiLabel; + } + mContentDescriptionCombinedSignal = mContentDescriptionPhoneSignal; + combinedSignalIconId = mDataSignalIconId; + } + } + else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected && !ethernetConnected) { + // pretty much totally disconnected + + combinedLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); + // On devices without mobile radios, we want to show the wifi icon + combinedSignalIconId = + mHasMobileDataFeature ? mDataSignalIconId : mWifiIconId; + mContentDescriptionCombinedSignal = mHasMobileDataFeature + ? mContentDescriptionDataType : mContentDescriptionWifi; + + mDataTypeIconId = 0; + mQSDataTypeIconId = 0; + if (isCdma()) { + if (isCdmaEri()) { + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; + } + } else if (mPhone.isNetworkRoaming()) { + mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam; + mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; + } + } + + if (DEBUG) { + Log.d(TAG, "refreshViews connected={" + + (mWifiConnected?" wifi":"") + + (mDataConnected?" data":"") + + " } level=" + + ((mSignalStrength == null)?"??":Integer.toString(mSignalStrength.getLevel())) + + " combinedSignalIconId=0x" + + Integer.toHexString(combinedSignalIconId) + + "/" + getResourceName(combinedSignalIconId) + + " mobileLabel=" + mobileLabel + + " wifiLabel=" + wifiLabel + + " emergencyOnly=" + emergencyOnly + + " combinedLabel=" + combinedLabel + + " mAirplaneMode=" + mAirplaneMode + + " mDataActivity=" + mDataActivity + + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId) + + " mQSPhoneSignalIconId=0x" + Integer.toHexString(mQSPhoneSignalIconId) + + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId) + + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId) + + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId) + + " mQSDataTypeIconId=0x" + Integer.toHexString(mQSDataTypeIconId) + + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId) + + " mQSWifiIconId=0x" + Integer.toHexString(mQSWifiIconId) + + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId)); + } + + // update QS + for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) { + notifySignalsChangedCallbacks(cb); + } + + if (mLastPhoneSignalIconId != mPhoneSignalIconId + || mLastWifiIconId != mWifiIconId + || mLastInetCondition != mInetCondition + || mLastWimaxIconId != mWimaxIconId + || mLastDataTypeIconId != mDataTypeIconId + || mLastAirplaneMode != mAirplaneMode + || mLastLocale != mLocale) + { + // NB: the mLast*s will be updated later + for (SignalCluster cluster : mSignalClusters) { + refreshSignalCluster(cluster); + } + } + + if (mLastAirplaneMode != mAirplaneMode) { + mLastAirplaneMode = mAirplaneMode; + } + + if (mLastLocale != mLocale) { + mLastLocale = mLocale; + } + + // the phone icon on phones + if (mLastPhoneSignalIconId != mPhoneSignalIconId) { + mLastPhoneSignalIconId = mPhoneSignalIconId; + } + + // the data icon on phones + if (mLastDataDirectionIconId != mDataDirectionIconId) { + mLastDataDirectionIconId = mDataDirectionIconId; + } + + // the wifi icon on phones + if (mLastWifiIconId != mWifiIconId) { + mLastWifiIconId = mWifiIconId; + } + + if (mLastInetCondition != mInetCondition) { + mLastInetCondition = mInetCondition; + } + + // the wimax icon on phones + if (mLastWimaxIconId != mWimaxIconId) { + mLastWimaxIconId = mWimaxIconId; + } + // the combined data signal icon + if (mLastCombinedSignalIconId != combinedSignalIconId) { + mLastCombinedSignalIconId = combinedSignalIconId; + } + + // the data network type overlay + if (mLastDataTypeIconId != mDataTypeIconId) { + mLastDataTypeIconId = mDataTypeIconId; + } + + // the combinedLabel in the notification panel + if (!mLastCombinedLabel.equals(combinedLabel)) { + mLastCombinedLabel = combinedLabel; + N = mCombinedLabelViews.size(); + for (int i=0; i<N; i++) { + TextView v = mCombinedLabelViews.get(i); + v.setText(combinedLabel); + } + } + + // wifi label + N = mWifiLabelViews.size(); + for (int i=0; i<N; i++) { + TextView v = mWifiLabelViews.get(i); + v.setText(wifiLabel); + if ("".equals(wifiLabel)) { + v.setVisibility(View.GONE); + } else { + v.setVisibility(View.VISIBLE); + } + } + + // mobile label + N = mMobileLabelViews.size(); + for (int i=0; i<N; i++) { + TextView v = mMobileLabelViews.get(i); + v.setText(mobileLabel); + if ("".equals(mobileLabel)) { + v.setVisibility(View.GONE); + } else { + v.setVisibility(View.VISIBLE); + } + } + + // e-call label + N = mEmergencyLabelViews.size(); + for (int i=0; i<N; i++) { + TextView v = mEmergencyLabelViews.get(i); + if (!emergencyOnly) { + v.setVisibility(View.GONE); + } else { + v.setText(mobileLabel); // comes from the telephony stack + v.setVisibility(View.VISIBLE); + } + } + } + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("NetworkController state:"); + pw.println(String.format(" %s network type %d (%s)", + mConnected?"CONNECTED":"DISCONNECTED", + mConnectedNetworkType, mConnectedNetworkTypeName)); + pw.println(" - telephony ------"); + pw.print(" hasVoiceCallingFeature()="); + pw.println(hasVoiceCallingFeature()); + pw.print(" hasService()="); + pw.println(hasService()); + pw.print(" mHspaDataDistinguishable="); + pw.println(mHspaDataDistinguishable); + pw.print(" mDataConnected="); + pw.println(mDataConnected); + pw.print(" mSimState="); + pw.println(mSimState); + pw.print(" mPhoneState="); + pw.println(mPhoneState); + pw.print(" mDataState="); + pw.println(mDataState); + pw.print(" mDataActivity="); + pw.println(mDataActivity); + pw.print(" mDataNetType="); + pw.print(mDataNetType); + pw.print("/"); + pw.println(TelephonyManager.getNetworkTypeName(mDataNetType)); + pw.print(" mServiceState="); + pw.println(mServiceState); + pw.print(" mSignalStrength="); + pw.println(mSignalStrength); + pw.print(" mLastSignalLevel="); + pw.println(mLastSignalLevel); + pw.print(" mNetworkName="); + pw.println(mNetworkName); + pw.print(" mNetworkNameDefault="); + pw.println(mNetworkNameDefault); + pw.print(" mNetworkNameSeparator="); + pw.println(mNetworkNameSeparator.replace("\n","\\n")); + pw.print(" mPhoneSignalIconId=0x"); + pw.print(Integer.toHexString(mPhoneSignalIconId)); + pw.print("/"); + pw.print(" mQSPhoneSignalIconId=0x"); + pw.print(Integer.toHexString(mQSPhoneSignalIconId)); + pw.print("/"); + pw.println(getResourceName(mPhoneSignalIconId)); + pw.print(" mDataDirectionIconId="); + pw.print(Integer.toHexString(mDataDirectionIconId)); + pw.print("/"); + pw.println(getResourceName(mDataDirectionIconId)); + pw.print(" mDataSignalIconId="); + pw.print(Integer.toHexString(mDataSignalIconId)); + pw.print("/"); + pw.println(getResourceName(mDataSignalIconId)); + pw.print(" mDataTypeIconId="); + pw.print(Integer.toHexString(mDataTypeIconId)); + pw.print("/"); + pw.println(getResourceName(mDataTypeIconId)); + pw.print(" mQSDataTypeIconId="); + pw.print(Integer.toHexString(mQSDataTypeIconId)); + pw.print("/"); + pw.println(getResourceName(mQSDataTypeIconId)); + + pw.println(" - wifi ------"); + pw.print(" mWifiEnabled="); + pw.println(mWifiEnabled); + pw.print(" mWifiConnected="); + pw.println(mWifiConnected); + pw.print(" mWifiRssi="); + pw.println(mWifiRssi); + pw.print(" mWifiLevel="); + pw.println(mWifiLevel); + pw.print(" mWifiSsid="); + pw.println(mWifiSsid); + pw.println(String.format(" mWifiIconId=0x%08x/%s", + mWifiIconId, getResourceName(mWifiIconId))); + pw.println(String.format(" mQSWifiIconId=0x%08x/%s", + mQSWifiIconId, getResourceName(mQSWifiIconId))); + pw.print(" mWifiActivity="); + pw.println(mWifiActivity); + + if (mWimaxSupported) { + pw.println(" - wimax ------"); + pw.print(" mIsWimaxEnabled="); pw.println(mIsWimaxEnabled); + pw.print(" mWimaxConnected="); pw.println(mWimaxConnected); + pw.print(" mWimaxIdle="); pw.println(mWimaxIdle); + pw.println(String.format(" mWimaxIconId=0x%08x/%s", + mWimaxIconId, getResourceName(mWimaxIconId))); + pw.println(String.format(" mWimaxSignal=%d", mWimaxSignal)); + pw.println(String.format(" mWimaxState=%d", mWimaxState)); + pw.println(String.format(" mWimaxExtraState=%d", mWimaxExtraState)); + } + + pw.println(" - Bluetooth ----"); + pw.print(" mBtReverseTethered="); + pw.println(mBluetoothTethered); + + pw.println(" - connectivity ------"); + pw.print(" mInetCondition="); + pw.println(mInetCondition); + + pw.println(" - icons ------"); + pw.print(" mLastPhoneSignalIconId=0x"); + pw.print(Integer.toHexString(mLastPhoneSignalIconId)); + pw.print("/"); + pw.println(getResourceName(mLastPhoneSignalIconId)); + pw.print(" mLastDataDirectionIconId=0x"); + pw.print(Integer.toHexString(mLastDataDirectionIconId)); + pw.print("/"); + pw.println(getResourceName(mLastDataDirectionIconId)); + pw.print(" mLastWifiIconId=0x"); + pw.print(Integer.toHexString(mLastWifiIconId)); + pw.print("/"); + pw.println(getResourceName(mLastWifiIconId)); + pw.print(" mLastCombinedSignalIconId=0x"); + pw.print(Integer.toHexString(mLastCombinedSignalIconId)); + pw.print("/"); + pw.println(getResourceName(mLastCombinedSignalIconId)); + pw.print(" mLastDataTypeIconId=0x"); + pw.print(Integer.toHexString(mLastDataTypeIconId)); + pw.print("/"); + pw.println(getResourceName(mLastDataTypeIconId)); + pw.print(" mLastCombinedLabel="); + pw.print(mLastCombinedLabel); + pw.println(""); + } + + private String getResourceName(int resId) { + if (resId != 0) { + final Resources res = mContext.getResources(); + try { + return res.getResourceName(resId); + } catch (android.content.res.Resources.NotFoundException ex) { + return "(unknown)"; + } + } else { + return "(null)"; + } + } + + private boolean mDemoMode; + private int mDemoInetCondition; + private int mDemoWifiLevel; + private int mDemoDataTypeIconId; + private int mDemoMobileLevel; + + @Override + public void dispatchDemoCommand(String command, Bundle args) { + if (!mDemoMode && command.equals(COMMAND_ENTER)) { + mDemoMode = true; + mDemoWifiLevel = mWifiLevel; + mDemoInetCondition = mInetCondition; + mDemoDataTypeIconId = mDataTypeIconId; + mDemoMobileLevel = mLastSignalLevel; + } else if (mDemoMode && command.equals(COMMAND_EXIT)) { + mDemoMode = false; + for (SignalCluster cluster : mSignalClusters) { + refreshSignalCluster(cluster); + } + } else if (mDemoMode && command.equals(COMMAND_NETWORK)) { + String airplane = args.getString("airplane"); + if (airplane != null) { + boolean show = airplane.equals("show"); + for (SignalCluster cluster : mSignalClusters) { + cluster.setIsAirplaneMode(show, FLIGHT_MODE_ICON); + } + } + String fully = args.getString("fully"); + if (fully != null) { + mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0; + } + String wifi = args.getString("wifi"); + if (wifi != null) { + boolean show = wifi.equals("show"); + String level = args.getString("level"); + if (level != null) { + mDemoWifiLevel = level.equals("null") ? -1 + : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); + } + int iconId = mDemoWifiLevel < 0 ? R.drawable.stat_sys_wifi_signal_null + : WifiIcons.WIFI_SIGNAL_STRENGTH[mDemoInetCondition][mDemoWifiLevel]; + for (SignalCluster cluster : mSignalClusters) { + cluster.setWifiIndicators( + show, + iconId, + mDemoInetCondition == 0, + "Demo"); + } + } + String mobile = args.getString("mobile"); + if (mobile != null) { + boolean show = mobile.equals("show"); + String datatype = args.getString("datatype"); + if (datatype != null) { + mDemoDataTypeIconId = + datatype.equals("1x") ? R.drawable.stat_sys_data_fully_connected_1x : + datatype.equals("3g") ? R.drawable.stat_sys_data_fully_connected_3g : + datatype.equals("4g") ? R.drawable.stat_sys_data_fully_connected_4g : + datatype.equals("e") ? R.drawable.stat_sys_data_fully_connected_e : + datatype.equals("g") ? R.drawable.stat_sys_data_fully_connected_g : + datatype.equals("h") ? R.drawable.stat_sys_data_fully_connected_h : + datatype.equals("lte") ? R.drawable.stat_sys_data_fully_connected_lte : + datatype.equals("roam") + ? R.drawable.stat_sys_data_fully_connected_roam : + 0; + } + int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH; + String level = args.getString("level"); + if (level != null) { + mDemoMobileLevel = level.equals("null") ? -1 + : Math.min(Integer.parseInt(level), icons[0].length - 1); + } + int iconId = mDemoMobileLevel < 0 ? R.drawable.stat_sys_signal_null : + icons[mDemoInetCondition][mDemoMobileLevel]; + for (SignalCluster cluster : mSignalClusters) { + cluster.setMobileDataIndicators( + show, + iconId, + mDemoInetCondition == 0, + mDemoDataTypeIconId, + "Demo", + "Demo"); + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java index 98d205a..1eb678d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2014 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. @@ -16,65 +16,15 @@ package com.android.systemui.statusbar.policy; -import android.content.Context; -import android.content.res.Configuration; -import android.os.UserHandle; - -import com.android.internal.view.RotationPolicy; - -import java.util.concurrent.CopyOnWriteArrayList; - -public final class RotationLockController { - private final Context mContext; - private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks = - new CopyOnWriteArrayList<RotationLockControllerCallback>(); - - private final RotationPolicy.RotationPolicyListener mRotationPolicyListener = - new RotationPolicy.RotationPolicyListener() { - @Override - public void onChange() { - notifyChanged(); - } - }; +public interface RotationLockController extends Disposable { + int getRotationLockOrientation(); + boolean isRotationLockAffordanceVisible(); + boolean isRotationLocked(); + void setRotationLocked(boolean locked); + void addRotationLockControllerCallback(RotationLockControllerCallback callback); + void removeRotationLockControllerCallback(RotationLockControllerCallback callback); public interface RotationLockControllerCallback { - public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible); - } - - public RotationLockController(Context context) { - mContext = context; - RotationPolicy.registerRotationPolicyListener(mContext, - mRotationPolicyListener, UserHandle.USER_ALL); - } - - public void addRotationLockControllerCallback(RotationLockControllerCallback callback) { - mCallbacks.add(callback); - } - - public int getRotationLockOrientation() { - return RotationPolicy.getRotationLockOrientation(mContext); - } - - public boolean isRotationLocked() { - return RotationPolicy.isRotationLocked(mContext); - } - - public void setRotationLocked(boolean locked) { - RotationPolicy.setRotationLock(mContext, locked); - } - - public boolean isRotationLockAffordanceVisible() { - return RotationPolicy.isRotationLockToggleVisible(mContext); - } - - public void release() { - RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener); - } - - private void notifyChanged() { - for (RotationLockControllerCallback callback : mCallbacks) { - callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext), - RotationPolicy.isRotationLockToggleVisible(mContext)); - } + void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java new file mode 100644 index 0000000..caa07ef --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.content.Context; +import android.os.UserHandle; + +import com.android.internal.view.RotationPolicy; + +import java.util.concurrent.CopyOnWriteArrayList; + +/** Platform implementation of the rotation lock controller. **/ +public final class RotationLockControllerImpl implements RotationLockController { + private final Context mContext; + private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks = + new CopyOnWriteArrayList<RotationLockControllerCallback>(); + + private final RotationPolicy.RotationPolicyListener mRotationPolicyListener = + new RotationPolicy.RotationPolicyListener() { + @Override + public void onChange() { + notifyChanged(); + } + }; + + public RotationLockControllerImpl(Context context) { + mContext = context; + RotationPolicy.registerRotationPolicyListener(mContext, + mRotationPolicyListener, UserHandle.USER_ALL); + } + + public void addRotationLockControllerCallback(RotationLockControllerCallback callback) { + mCallbacks.add(callback); + } + + public void removeRotationLockControllerCallback(RotationLockControllerCallback callback) { + mCallbacks.remove(callback); + } + + public int getRotationLockOrientation() { + return RotationPolicy.getRotationLockOrientation(mContext); + } + + public boolean isRotationLocked() { + return RotationPolicy.isRotationLocked(mContext); + } + + public void setRotationLocked(boolean locked) { + RotationPolicy.setRotationLock(mContext, locked); + } + + public boolean isRotationLockAffordanceVisible() { + return RotationPolicy.isRotationLockToggleVisible(mContext); + } + + @Override + public void dispose() { + RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener); + } + + private void notifyChanged() { + for (RotationLockControllerCallback callback : mCallbacks) { + callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext), + RotationPolicy.isRotationLockToggleVisible(mContext)); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java new file mode 100644 index 0000000..143ebaa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +public interface TetheringController { + void addCallback(Callback callback); + void removeCallback(Callback callback); + boolean isHotspotEnabled(); + boolean isHotspotSupported(); + + public interface Callback { + void onHotspotChanged(boolean hotspot); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java new file mode 100644 index 0000000..6225c12 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.service.notification.Condition; + +public interface ZenModeController { + void addCallback(Callback callback); + void removeCallback(Callback callback); + void setZen(boolean zen); + boolean isZen(); + void requestConditions(boolean request); + void select(Condition condition); + + public static class Callback { + public void onZenChanged(boolean zen) {} + public void onConditionsChanged(Condition[] conditions) {} + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java new file mode 100644 index 0000000..d760f78 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.app.INotificationManager; +import android.content.Context; +import android.net.Uri; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings.Global; +import android.service.notification.Condition; +import android.service.notification.IConditionListener; +import android.util.Slog; + +import com.android.systemui.qs.GlobalSetting; + +import java.util.ArrayList; +import java.util.LinkedHashMap; + +/** Platform implementation of the zen mode controller. **/ +public class ZenModeControllerImpl implements ZenModeController { + private static final String TAG = "ZenModeControllerImpl"; + + private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); + private final Context mContext; + private final GlobalSetting mSetting; + private final INotificationManager mNoMan; + private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>(); + + private boolean mRequesting; + + public ZenModeControllerImpl(Context context, Handler handler) { + mContext = context; + mSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) { + @Override + protected void handleValueChanged(int value) { + fireZenChanged(value != 0); + } + }; + mNoMan = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + } + + @Override + public void addCallback(Callback callback) { + mCallbacks.add(callback); + } + + @Override + public void removeCallback(Callback callback) { + mCallbacks.remove(callback); + } + + @Override + public boolean isZen() { + return mSetting.getValue() != 0; + } + + @Override + public void setZen(boolean zen) { + mSetting.setValue(zen ? 1 : 0); + } + + @Override + public void requestConditions(boolean request) { + mRequesting = request; + try { + mNoMan.requestZenModeConditions(mListener, request ? Condition.FLAG_RELEVANT_NOW : 0); + } catch (RemoteException e) { + // noop + } + if (!mRequesting) { + mConditions.clear(); + } + } + + @Override + public void select(Condition condition) { + try { + mNoMan.setZenModeCondition(condition == null ? null : condition.id); + } catch (RemoteException e) { + // noop + } + } + + private void fireZenChanged(boolean zen) { + for (Callback cb : mCallbacks) { + cb.onZenChanged(zen); + } + } + + private void fireConditionsChanged(Condition[] conditions) { + for (Callback cb : mCallbacks) { + cb.onConditionsChanged(conditions); + } + } + + private void updateConditions(Condition[] conditions) { + if (conditions == null || conditions.length == 0) return; + for (Condition c : conditions) { + if ((c.flags & Condition.FLAG_RELEVANT_NOW) == 0) continue; + mConditions.put(c.id, c); + } + fireConditionsChanged( + mConditions.values().toArray(new Condition[mConditions.values().size()])); + } + + private final IConditionListener mListener = new IConditionListener.Stub() { + @Override + public void onConditionsReceived(Condition[] conditions) { + Slog.d(TAG, "onConditionsReceived " + (conditions == null ? 0 : conditions.length) + + " mRequesting=" + mRequesting); + if (!mRequesting) return; + updateConditions(conditions); + } + }; +} diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index b1fea03..6341a0c 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -44,6 +44,7 @@ import android.app.ActivityOptions; import android.app.KeyguardManager; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -301,6 +302,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { throw new AndroidRuntimeException( "You cannot combine swipe dismissal and the action bar."); } + + if (featureId == FEATURE_INDETERMINATE_PROGRESS && + getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { + throw new AndroidRuntimeException("You cannot use indeterminate progress on a watch."); + } return super.requestFeature(featureId); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index ae6aeee..4ee8103 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -4777,9 +4777,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHandler.post(new Runnable() { @Override public void run() { if (mBootMsgDialog == null) { - int theme = mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WATCH) ? - com.android.internal.R.style.Theme_Micro_Dialog_Alert : 0; + int theme; + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WATCH)) { + theme = com.android.internal.R.style.Theme_Micro_Dialog_Alert; + } else if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELEVISION)) { + theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert; + } else { + theme = 0; + } mBootMsgDialog = new ProgressDialog(mContext, theme) { // This dialog will consume all events coming in to diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index ce2aefb..0708e55 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -20,15 +20,26 @@ import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; +import static android.net.ConnectivityManager.NetworkCallbacks; import static android.net.ConnectivityManager.TYPE_BLUETOOTH; import static android.net.ConnectivityManager.TYPE_DUMMY; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; +import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL; +import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; +import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; +import static android.net.ConnectivityManager.TYPE_MOBILE_IMS; +import static android.net.ConnectivityManager.TYPE_MOBILE_CBS; +import static android.net.ConnectivityManager.TYPE_MOBILE_IA; +import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; +import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.ConnectivityManager.TYPE_PROXY; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; +import static android.net.ConnectivityServiceProtocol.NetworkFactoryProtocol; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; @@ -66,10 +77,13 @@ import android.net.LinkProperties.CompareResult; import android.net.LinkQualityInfo; import android.net.MobileDataStateTracker; import android.net.Network; +import android.net.NetworkAgent; +import android.net.NetworkCapabilities; import android.net.NetworkConfig; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkQuotaInfo; +import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkStateTracker; import android.net.NetworkUtils; @@ -79,7 +93,6 @@ import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.SamplingDataTracker; import android.net.Uri; -import android.net.wifi.WifiStateTracker; import android.net.wimax.WimaxManagerConstants; import android.os.AsyncTask; import android.os.Binder; @@ -119,11 +132,14 @@ import com.android.internal.telephony.DctConstants; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.util.AsyncChannel; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.Nat464Xlat; +import com.android.server.connectivity.NetworkAgentInfo; +import com.android.server.connectivity.NetworkMonitor; import com.android.server.connectivity.PacManager; import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; @@ -175,7 +191,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final String TAG = "ConnectivityService"; private static final boolean DBG = true; - private static final boolean VDBG = false; + private static final boolean VDBG = true; // STOPSHIP private static final boolean LOGD_RULES = false; @@ -241,6 +257,17 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private NetworkStateTracker mNetTrackers[]; + /** + * Holds references to all NetworkAgentInfos claiming to support the legacy + * NetworkType. We used to have a static set of of NetworkStateTrackers + * for each network type. This is the new model. + * Supports synchronous inspection of state. + * These are built out at startup such that an unsupported network + * doesn't get an ArrayList instance, making this a tristate: + * unsupported, supported but not active and active. + */ + private ArrayList<NetworkAgentInfo> mNetworkAgentInfoForType[]; + /* Handles captive portal check on a network */ private CaptivePortalTracker mCaptivePortalTracker; @@ -302,12 +329,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2; /** - * used internally to change our network preference setting - * arg1 = networkType to prefer - */ - private static final int EVENT_SET_NETWORK_PREFERENCE = 3; - - /** * used internally to synchronize inet condition reports * arg1 = networkType * arg2 = condition (0 bad, 100 good) @@ -363,7 +384,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14; /** - * user internally to indicate that data sampling interval is up + * used internally to indicate that data sampling interval is up */ private static final int EVENT_SAMPLE_INTERVAL_ELAPSED = 15; @@ -372,10 +393,48 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private static final int EVENT_PROXY_HAS_CHANGED = 16; + /** + * used internally when registering NetworkFactories + * obj = Messenger + */ + private static final int EVENT_REGISTER_NETWORK_FACTORY = 17; + + /** + * used internally when registering NetworkAgents + * obj = Messenger + */ + private static final int EVENT_REGISTER_NETWORK_AGENT = 18; + + /** + * used to add a network request + * includes a NetworkRequestInfo + */ + private static final int EVENT_REGISTER_NETWORK_REQUEST = 19; + + /** + * indicates a timeout period is over - check if we had a network yet or not + * and if not, call the timeout calback (but leave the request live until they + * cancel it. + * includes a NetworkRequestInfo + */ + private static final int EVENT_TIMEOUT_NETWORK_REQUEST = 20; + + /** + * used to add a network listener - no request + * includes a NetworkRequestInfo + */ + private static final int EVENT_REGISTER_NETWORK_LISTENER = 21; + + /** + * used to remove a network request, either a listener or a real request + * includes a NetworkRequest + */ + private static final int EVENT_RELEASE_NETWORK_REQUEST = 22; + /** Handler used for internal events. */ - private InternalHandler mHandler; + final private InternalHandler mHandler; /** Handler used for incoming {@link NetworkStateTracker} events. */ - private NetworkStateTrackerHandler mTrackerHandler; + final private NetworkStateTrackerHandler mTrackerHandler; // list of DeathRecipients used to make sure features are turned off when // a process dies @@ -461,6 +520,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkFactory netFactory) { if (DBG) log("ConnectivityService starting up"); + NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + mDefaultRequest = new NetworkRequest(netCap, true); + NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(), + NetworkRequestInfo.REQUEST); + mNetworkRequests.put(mDefaultRequest, nri); + HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread"); handlerThread.start(); mHandler = new InternalHandler(handlerThread.getLooper()); @@ -512,6 +579,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTransitionWakeLockTimeout = mContext.getResources().getInteger( com.android.internal.R.integer.config_networkTransitionTimeout); + mNetworkAgentInfoForType = (ArrayList<NetworkAgentInfo>[]) + new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1]; + mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1]; mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1]; @@ -566,6 +636,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { "radio " + n.radio + " in network type " + n.type); continue; } + mNetworkAgentInfoForType[n.type] = new ArrayList<NetworkAgentInfo>(); + mNetConfigs[n.type] = n; mNetworksDefined++; } catch(Exception e) { @@ -608,21 +680,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - // Update mNetworkPreference according to user mannually first then overlay config.xml - mNetworkPreference = getPersistedNetworkPreference(); - if (mNetworkPreference == -1) { - for (int n : mPriorityList) { - if (mNetConfigs[n].isDefault() && ConnectivityManager.isNetworkTypeValid(n)) { - mNetworkPreference = n; - break; - } - } - if (mNetworkPreference == -1) { - throw new IllegalStateException( - "You should set at least one default Network in config.xml!"); - } - } - mNetRequestersPids = (List<Integer> [])new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1]; for (int i : mPriorityList) { @@ -722,6 +779,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { /** * Factory that creates {@link NetworkStateTracker} instances using given * {@link NetworkConfig}. + * + * TODO - this is obsolete and will be deleted. It's replaced by the + * registerNetworkFactory call and protocol. + * @Deprecated in favor of registerNetworkFactory dynamic bindings */ public interface NetworkFactory { public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config); @@ -739,10 +800,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) { switch (config.radio) { - case TYPE_WIFI: - return new WifiStateTracker(targetNetworkType, config.name); - case TYPE_MOBILE: - return new MobileDataStateTracker(targetNetworkType, config.name); case TYPE_DUMMY: return new DummyDataStateTracker(targetNetworkType, config.name); case TYPE_BLUETOOTH: @@ -843,41 +900,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { return wimaxStateTracker; } - /** - * Sets the preferred network. - * @param preference the new preference - */ - public void setNetworkPreference(int preference) { - enforceChangePermission(); - - mHandler.sendMessage( - mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0)); - } - - public int getNetworkPreference() { - enforceAccessPermission(); - int preference; - synchronized(this) { - preference = mNetworkPreference; - } - return preference; - } - - private void handleSetNetworkPreference(int preference) { - if (ConnectivityManager.isNetworkTypeValid(preference) && - mNetConfigs[preference] != null && - mNetConfigs[preference].isDefault()) { - if (mNetworkPreference != preference) { - final ContentResolver cr = mContext.getContentResolver(); - Settings.Global.putInt(cr, Settings.Global.NETWORK_PREFERENCE, preference); - synchronized(this) { - mNetworkPreference = preference; - } - enforcePreference(); - } - } - } - private int getConnectivityChangeDelay() { final ContentResolver cr = mContext.getContentResolver(); @@ -889,41 +911,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { defaultDelay); } - private int getPersistedNetworkPreference() { - final ContentResolver cr = mContext.getContentResolver(); - - final int networkPrefSetting = Settings.Global - .getInt(cr, Settings.Global.NETWORK_PREFERENCE, -1); - - return networkPrefSetting; - } - - /** - * Make the state of network connectivity conform to the preference settings - * In this method, we only tear down a non-preferred network. Establishing - * a connection to the preferred network is taken care of when we handle - * the disconnect event from the non-preferred network - * (see {@link #handleDisconnect(NetworkInfo)}). - */ - private void enforcePreference() { - if (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected()) - return; - - if (!mNetTrackers[mNetworkPreference].isAvailable()) - return; - - for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) { - if (t != mNetworkPreference && mNetTrackers[t] != null && - mNetTrackers[t].getNetworkInfo().isConnected()) { - if (DBG) { - log("tearing down " + mNetTrackers[t].getNetworkInfo() + - " in enforcePreference"); - } - teardown(mNetTrackers[t]); - } - } - } - private boolean teardown(NetworkStateTracker netTracker) { if (netTracker.teardown()) { netTracker.setTeardownRequested(true); @@ -937,11 +924,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { * Check if UID should be blocked from using the network represented by the * given {@link NetworkStateTracker}. */ - private boolean isNetworkBlocked(NetworkStateTracker tracker, int uid) { - final String iface = tracker.getLinkProperties().getInterfaceName(); - + private boolean isNetworkBlocked(int networkType, int uid) { final boolean networkCostly; final int uidRules; + + LinkProperties lp = getLinkPropertiesForType(networkType); + final String iface = (lp == null ? "" : lp.getInterfaceName()); synchronized (mRulesLock) { networkCostly = mMeteredIfaces.contains(iface); uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); @@ -958,11 +946,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { /** * Return a filtered {@link NetworkInfo}, potentially marked * {@link DetailedState#BLOCKED} based on - * {@link #isNetworkBlocked(NetworkStateTracker, int)}. + * {@link #isNetworkBlocked}. */ - private NetworkInfo getFilteredNetworkInfo(NetworkStateTracker tracker, int uid) { - NetworkInfo info = tracker.getNetworkInfo(); - if (isNetworkBlocked(tracker, uid)) { + private NetworkInfo getFilteredNetworkInfo(int networkType, int uid) { + NetworkInfo info = getNetworkInfoForType(networkType); + if (isNetworkBlocked(networkType, uid)) { // network is blocked; clone and override state info = new NetworkInfo(info); info.setDetailedState(DetailedState.BLOCKED, null, null); @@ -987,6 +975,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { return getNetworkInfo(mActiveDefaultNetwork, uid); } + // only called when the default request is satisfied + private void updateActiveDefaultNetwork(NetworkAgentInfo nai) { + if (nai != null) { + mActiveDefaultNetwork = nai.networkInfo.getType(); + } else { + mActiveDefaultNetwork = TYPE_NONE; + } + } + /** * Find the first Provisioning network. * @@ -1029,10 +1026,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { public NetworkInfo getActiveNetworkInfoUnfiltered() { enforceAccessPermission(); if (isNetworkTypeValid(mActiveDefaultNetwork)) { - final NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork]; - if (tracker != null) { - return tracker.getNetworkInfo(); - } + return getNetworkInfoForType(mActiveDefaultNetwork); } return null; } @@ -1053,9 +1047,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { private NetworkInfo getNetworkInfo(int networkType, int uid) { NetworkInfo info = null; if (isNetworkTypeValid(networkType)) { - final NetworkStateTracker tracker = mNetTrackers[networkType]; - if (tracker != null) { - info = getFilteredNetworkInfo(tracker, uid); + if (getNetworkInfoForType(networkType) != null) { + info = getFilteredNetworkInfo(networkType, uid); } } return info; @@ -1067,9 +1060,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { final int uid = Binder.getCallingUid(); final ArrayList<NetworkInfo> result = Lists.newArrayList(); synchronized (mRulesLock) { - for (NetworkStateTracker tracker : mNetTrackers) { - if (tracker != null) { - result.add(getFilteredNetworkInfo(tracker, uid)); + for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE; + networkType++) { + if (getNetworkInfoForType(networkType) != null) { + result.add(getFilteredNetworkInfo(networkType, uid)); } } } @@ -1079,7 +1073,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override public boolean isNetworkSupported(int networkType) { enforceAccessPermission(); - return (isNetworkTypeValid(networkType) && (mNetTrackers[networkType] != null)); + return (isNetworkTypeValid(networkType) && (getNetworkInfoForType(networkType) != null)); } /** @@ -1092,32 +1086,48 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ @Override public LinkProperties getActiveLinkProperties() { - return getLinkProperties(mActiveDefaultNetwork); + return getLinkPropertiesForType(mActiveDefaultNetwork); } @Override - public LinkProperties getLinkProperties(int networkType) { + public LinkProperties getLinkPropertiesForType(int networkType) { enforceAccessPermission(); if (isNetworkTypeValid(networkType)) { - final NetworkStateTracker tracker = mNetTrackers[networkType]; - if (tracker != null) { - return tracker.getLinkProperties(); - } + return getLinkPropertiesForTypeInternal(networkType); } return null; } + // TODO - this should be ALL networks + @Override + public LinkProperties getLinkProperties(Network network) { + enforceAccessPermission(); + NetworkAgentInfo nai = mNetworkForNetId.get(network.netId); + if (nai != null) return new LinkProperties(nai.linkProperties); + return null; + } + + @Override + public NetworkCapabilities getNetworkCapabilities(Network network) { + enforceAccessPermission(); + NetworkAgentInfo nai = mNetworkForNetId.get(network.netId); + if (nai != null) return new NetworkCapabilities(nai.networkCapabilities); + return null; + } + @Override public NetworkState[] getAllNetworkState() { enforceAccessPermission(); final int uid = Binder.getCallingUid(); final ArrayList<NetworkState> result = Lists.newArrayList(); synchronized (mRulesLock) { - for (NetworkStateTracker tracker : mNetTrackers) { - if (tracker != null) { - final NetworkInfo info = getFilteredNetworkInfo(tracker, uid); - result.add(new NetworkState( - info, tracker.getLinkProperties(), tracker.getNetworkCapabilities())); + for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE; + networkType++) { + if (getNetworkInfoForType(networkType) != null) { + final NetworkInfo info = getFilteredNetworkInfo(networkType, uid); + final LinkProperties lp = getLinkPropertiesForTypeInternal(networkType); + final NetworkCapabilities netcap = getNetworkCapabilitiesForType(networkType); + result.add(new NetworkState(info, lp, netcap)); } } } @@ -1126,10 +1136,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { private NetworkState getNetworkStateUnchecked(int networkType) { if (isNetworkTypeValid(networkType)) { - final NetworkStateTracker tracker = mNetTrackers[networkType]; - if (tracker != null) { - return new NetworkState(tracker.getNetworkInfo(), tracker.getLinkProperties(), - tracker.getNetworkCapabilities()); + NetworkInfo info = getNetworkInfoForType(networkType); + if (info != null) { + return new NetworkState(info, + getLinkPropertiesForTypeInternal(networkType), + getNetworkCapabilitiesForType(networkType)); } } return null; @@ -1176,24 +1187,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { return false; } - public boolean setRadios(boolean turnOn) { - boolean result = true; - enforceChangePermission(); - for (NetworkStateTracker t : mNetTrackers) { - if (t != null) result = t.setRadio(turnOn) && result; - } - return result; - } - - public boolean setRadio(int netType, boolean turnOn) { - enforceChangePermission(); - if (!ConnectivityManager.isNetworkTypeValid(netType)) { - return false; - } - NetworkStateTracker tracker = mNetTrackers[netType]; - return tracker != null && tracker.setRadio(turnOn); - } - private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() { @Override public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos) { @@ -1675,7 +1668,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { final long token = Binder.clearCallingIdentity(); try { LinkProperties lp = tracker.getLinkProperties(); - boolean ok = addRouteToAddress(lp, addr, exempt); + boolean ok = addRouteToAddress(lp, addr, exempt, tracker.getNetwork().netId); if (DBG) log("requestRouteToHostAddress ok=" + ok); return ok; } finally { @@ -1684,24 +1677,25 @@ public class ConnectivityService extends IConnectivityManager.Stub { } private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable, - boolean exempt) { - return modifyRoute(p, r, 0, ADD, toDefaultTable, exempt); + boolean exempt, int netId) { + return modifyRoute(p, r, 0, ADD, toDefaultTable, exempt, netId); } - private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable) { - return modifyRoute(p, r, 0, REMOVE, toDefaultTable, UNEXEMPT); + private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable, int netId) { + return modifyRoute(p, r, 0, REMOVE, toDefaultTable, UNEXEMPT, netId); } - private boolean addRouteToAddress(LinkProperties lp, InetAddress addr, boolean exempt) { - return modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt); + private boolean addRouteToAddress(LinkProperties lp, InetAddress addr, boolean exempt, + int netId) { + return modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt, netId); } - private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr) { - return modifyRouteToAddress(lp, addr, REMOVE, TO_DEFAULT_TABLE, UNEXEMPT); + private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr, int netId) { + return modifyRouteToAddress(lp, addr, REMOVE, TO_DEFAULT_TABLE, UNEXEMPT, netId); } private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd, - boolean toDefaultTable, boolean exempt) { + boolean toDefaultTable, boolean exempt, int netId) { RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), addr); if (bestRoute == null) { bestRoute = RouteInfo.makeHostRoute(addr, lp.getInterfaceName()); @@ -1716,11 +1710,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface); } } - return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable, exempt); + return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable, exempt, netId); } private boolean modifyRoute(LinkProperties lp, RouteInfo r, int cycleCount, boolean doAdd, - boolean toDefaultTable, boolean exempt) { + boolean toDefaultTable, boolean exempt, int netId) { if ((lp == null) || (r == null)) { if (DBG) log("modifyRoute got unexpected null: " + lp + ", " + r); return false; @@ -1749,7 +1743,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { bestRoute.getGateway(), ifaceName); } - modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable, exempt); + modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable, exempt, netId); } } if (doAdd) { @@ -1759,7 +1753,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { synchronized (mRoutesLock) { // only track default table - only one apps can effect mAddedRoutes.add(r); - mNetd.addRoute(ifaceName, r); + mNetd.addRoute(netId, r); if (exempt) { LinkAddress dest = r.getDestination(); if (!mExemptAddresses.contains(dest)) { @@ -1769,7 +1763,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } } else { - mNetd.addSecondaryRoute(ifaceName, r); + mNetd.addRoute(netId, r); } } catch (Exception e) { // never crash - catch them all @@ -1785,7 +1779,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (mAddedRoutes.contains(r) == false) { if (VDBG) log("Removing " + r + " for interface " + ifaceName); try { - mNetd.removeRoute(ifaceName, r); + mNetd.removeRoute(netId, r); LinkAddress dest = r.getDestination(); if (mExemptAddresses.contains(dest)) { mNetd.clearHostExemption(dest); @@ -1803,7 +1797,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } else { if (VDBG) log("Removing " + r + " for interface " + ifaceName); try { - mNetd.removeSecondaryRoute(ifaceName, r); + mNetd.removeRoute(netId, r); } catch (Exception e) { // never crash - catch them all if (VDBG) loge("Exception trying to remove a route: " + e); @@ -1912,18 +1906,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } private void handleSetMobileData(boolean enabled) { - if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) { - if (VDBG) { - log(mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled); - } - mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled); - } - if (mNetTrackers[ConnectivityManager.TYPE_WIMAX] != null) { - if (VDBG) { - log(mNetTrackers[ConnectivityManager.TYPE_WIMAX].toString() + enabled); - } - mNetTrackers[ConnectivityManager.TYPE_WIMAX].setUserDataEnable(enabled); - } + // TODO - handle this - probably generalize passing in a transport type and send to the + // factories? } @Override @@ -1936,12 +1920,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { } private void handleSetPolicyDataEnable(int networkType, boolean enabled) { - if (isNetworkTypeValid(networkType)) { - final NetworkStateTracker tracker = mNetTrackers[networkType]; - if (tracker != null) { - tracker.setPolicyDataEnable(enabled); - } - } + // TODO - handle this passing to factories +// if (isNetworkTypeValid(networkType)) { +// final NetworkStateTracker tracker = mNetTrackers[networkType]; +// if (tracker != null) { +// tracker.setPolicyDataEnable(enabled); +// } +// } } private void enforceAccessPermission() { @@ -2000,9 +1985,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { int thisNetId = mNetTrackers[prevNetType].getNetwork().netId; // Remove idletimer previously setup in {@code handleConnect} - if (mNetConfigs[prevNetType].isDefault()) { - removeDataActivityTracking(prevNetType); - } +// Already in place in new function. This is dead code. +// if (mNetConfigs[prevNetType].isDefault()) { +// removeDataActivityTracking(prevNetType); +// } /* * If the disconnected network is not the active one, then don't report @@ -2069,7 +2055,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } // do this before we broadcast the change - handleConnectivityChange(prevNetType, doReset); +// Already done in new function. This is dead code. +// handleConnectivityChange(prevNetType, doReset); final Intent immediateIntent = new Intent(intent); immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE); @@ -2104,6 +2091,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { log("tryFailover: set mActiveDefaultNetwork=-1, prevNetType=" + prevNetType); } mActiveDefaultNetwork = -1; + try { + mNetd.clearDefaultNetId(); + } catch (Exception e) { + loge("Exception clearing default network :" + e); + } } // don't signal a reconnect for anything lower or equal priority than our @@ -2204,67 +2196,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - /** - * Called when an attempt to fail over to another network has failed. - * @param info the {@link NetworkInfo} for the failed network - */ - private void handleConnectionFailure(NetworkInfo info) { - mNetTrackers[info.getType()].setTeardownRequested(false); - - String reason = info.getReason(); - String extraInfo = info.getExtraInfo(); - - String reasonText; - if (reason == null) { - reasonText = "."; - } else { - reasonText = " (" + reason + ")."; - } - loge("Attempt to connect to " + info.getTypeName() + " failed" + reasonText); - - Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, new NetworkInfo(info)); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); - if (getActiveNetworkInfo() == null) { - intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); - } - if (reason != null) { - intent.putExtra(ConnectivityManager.EXTRA_REASON, reason); - } - if (extraInfo != null) { - intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo); - } - if (info.isFailover()) { - intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); - info.setFailover(false); - } - - if (mNetConfigs[info.getType()].isDefault()) { - tryFailover(info.getType()); - if (mActiveDefaultNetwork != -1) { - NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(); - intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo); - } else { - mDefaultInetConditionPublished = 0; - intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); - } - } - - intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished); - - final Intent immediateIntent = new Intent(intent); - immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE); - sendStickyBroadcast(immediateIntent); - sendStickyBroadcast(intent); - /* - * If the failover network is already connected, then immediately send - * out a followup broadcast indicating successful failover - */ - if (mActiveDefaultNetwork != -1) { - sendConnectedBroadcast(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo()); - } - } - private void sendStickyBroadcast(Intent intent) { synchronized(this) { if (!mSystemReady) { @@ -2394,7 +2325,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { teardown(thisNet); return; } - setupDataActivityTracking(newNetType); +// Already in place in new function. This is dead code. +// setupDataActivityTracking(newNetType); synchronized (ConnectivityService.this) { // have a new default network, release the transition wakelock in a second // if it's held. The second pause is to allow apps to reconnect over the @@ -2407,6 +2339,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } mActiveDefaultNetwork = newNetType; + try { + mNetd.setDefaultNetId(thisNetId); + } catch (Exception e) { + loge("Exception setting default network :" + e); + } // this will cause us to come up initially as unconnected and switching // to connected after our normal pause unless somebody reports us as reall // disconnected @@ -2428,8 +2365,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } thisNet.setTeardownRequested(false); - updateMtuSizeSettings(thisNet); - handleConnectivityChange(newNetType, false); +// Already in place in new function. This is dead code. +// updateMtuSizeSettings(thisNet); +// handleConnectivityChange(newNetType, false); sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay()); // notify battery stats service about this network @@ -2447,37 +2385,39 @@ public class ConnectivityService extends IConnectivityManager.Stub { public void captivePortalCheckCompleted(NetworkInfo info, boolean isCaptivePortal) { enforceConnectivityInternalPermission(); if (DBG) log("captivePortalCheckCompleted: ni=" + info + " captive=" + isCaptivePortal); - mNetTrackers[info.getType()].captivePortalCheckCompleted(isCaptivePortal); +// mNetTrackers[info.getType()].captivePortalCheckCompleted(isCaptivePortal); } /** - * Setup data activity tracking for the given network interface. + * Setup data activity tracking for the given network. * * Every {@code setupDataActivityTracking} should be paired with a * {@link #removeDataActivityTracking} for cleanup. */ - private void setupDataActivityTracking(int type) { - final NetworkStateTracker thisNet = mNetTrackers[type]; - final String iface = thisNet.getLinkProperties().getInterfaceName(); + private void setupDataActivityTracking(NetworkAgentInfo networkAgent) { + final String iface = networkAgent.linkProperties.getInterfaceName(); final int timeout; + int type = ConnectivityManager.TYPE_NONE; - if (ConnectivityManager.isNetworkTypeMobile(type)) { + if (networkAgent.networkCapabilities.hasTransport( + NetworkCapabilities.TRANSPORT_CELLULAR)) { timeout = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE, 5); - // Canonicalize mobile network type type = ConnectivityManager.TYPE_MOBILE; - } else if (ConnectivityManager.TYPE_WIFI == type) { + } else if (networkAgent.networkCapabilities.hasTransport( + NetworkCapabilities.TRANSPORT_WIFI)) { timeout = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI, 0); + type = ConnectivityManager.TYPE_WIFI; } else { // do not track any other networks timeout = 0; } - if (timeout > 0 && iface != null) { + if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) { try { mNetd.addIdleTimer(iface, timeout, type); } catch (Exception e) { @@ -2490,12 +2430,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { /** * Remove data activity tracking when network disconnects. */ - private void removeDataActivityTracking(int type) { - final NetworkStateTracker net = mNetTrackers[type]; - final String iface = net.getLinkProperties().getInterfaceName(); + private void removeDataActivityTracking(NetworkAgentInfo networkAgent) { + final String iface = networkAgent.linkProperties.getInterfaceName(); + final NetworkCapabilities caps = networkAgent.networkCapabilities; - if (iface != null && (ConnectivityManager.isNetworkTypeMobile(type) || - ConnectivityManager.TYPE_WIFI == type)) { + if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || + caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) { try { // the call fails silently if no idletimer setup for this interface mNetd.removeIdleTimer(iface); @@ -2510,8 +2450,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { * concerned with making sure that the list of DNS servers is set up * according to which networks are connected, and ensuring that the * right routing table entries exist. + * + * TODO - delete when we're sure all this functionallity is captured. */ - private void handleConnectivityChange(int netType, boolean doReset) { + private void handleConnectivityChange(int netType, LinkProperties curLp, boolean doReset) { int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0; boolean exempt = ConnectivityManager.isNetworkTypeExempt(netType); if (VDBG) { @@ -2525,7 +2467,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ handleDnsConfigurationChange(netType); - LinkProperties curLp = mCurrentLinkProperties[netType]; LinkProperties newLp = null; if (mNetTrackers[netType].getNetworkInfo().isConnected()) { @@ -2582,7 +2523,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } mCurrentLinkProperties[netType] = newLp; - boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault(), exempt); + boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault(), exempt, + mNetTrackers[netType].getNetwork().netId); if (resetMask != 0 || resetDns) { if (VDBG) log("handleConnectivityChange: resetting"); @@ -2604,40 +2546,20 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } } - if (resetDns) { - flushVmDnsCache(); - if (VDBG) log("resetting DNS cache for " + iface); - try { - mNetd.flushInterfaceDnsCache(iface); - } catch (Exception e) { - // never crash - catch them all - if (DBG) loge("Exception resetting dns cache: " + e); - } - } } else { loge("Can't reset connection for type "+netType); } } - } - } - - // Update 464xlat state. - NetworkStateTracker tracker = mNetTrackers[netType]; - if (mClat.requiresClat(netType, tracker)) { - - // If the connection was previously using clat, but is not using it now, stop the clat - // daemon. Normally, this happens automatically when the connection disconnects, but if - // the disconnect is not reported, or if the connection's LinkProperties changed for - // some other reason (e.g., handoff changes the IP addresses on the link), it would - // still be running. If it's not running, then stopping it is a no-op. - if (Nat464Xlat.isRunningClat(curLp) && !Nat464Xlat.isRunningClat(newLp)) { - mClat.stopClat(); - } - // If the link requires clat to be running, then start the daemon now. - if (mNetTrackers[netType].getNetworkInfo().isConnected()) { - mClat.startClat(tracker); - } else { - mClat.stopClat(); + if (resetDns) { + flushVmDnsCache(); + if (VDBG) log("resetting DNS cache for type " + netType); + try { + mNetd.flushNetworkDnsCache(mNetTrackers[netType].getNetwork().netId); + } catch (Exception e) { + // never crash - catch them all + if (DBG) loge("Exception resetting dns cache: " + e); + } + } } } @@ -2661,7 +2583,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { * returns a boolean indicating the routes changed */ private boolean updateRoutes(LinkProperties newLp, LinkProperties curLp, - boolean isLinkDefault, boolean exempt) { + boolean isLinkDefault, boolean exempt, int netId) { Collection<RouteInfo> routesToAdd = null; CompareResult<InetAddress> dnsDiff = new CompareResult<InetAddress>(); CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>(); @@ -2679,45 +2601,20 @@ public class ConnectivityService extends IConnectivityManager.Stub { for (RouteInfo r : routeDiff.removed) { if (isLinkDefault || ! r.isDefaultRoute()) { if (VDBG) log("updateRoutes: default remove route r=" + r); - removeRoute(curLp, r, TO_DEFAULT_TABLE); + removeRoute(curLp, r, TO_DEFAULT_TABLE, netId); } if (isLinkDefault == false) { // remove from a secondary route table - removeRoute(curLp, r, TO_SECONDARY_TABLE); - } - } - - if (!isLinkDefault) { - // handle DNS routes - if (routesChanged) { - // 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, exempt); - } - } - } else { - // no change in routes, check for change in dns themselves - for (InetAddress oldDns : dnsDiff.removed) { - removeRouteToAddress(curLp, oldDns); - } - for (InetAddress newDns : dnsDiff.added) { - addRouteToAddress(newLp, newDns, exempt); - } + removeRoute(curLp, r, TO_SECONDARY_TABLE, netId); } } for (RouteInfo r : routeDiff.added) { if (isLinkDefault || ! r.isDefaultRoute()) { - addRoute(newLp, r, TO_DEFAULT_TABLE, exempt); + addRoute(newLp, r, TO_DEFAULT_TABLE, exempt, netId); } else { // add to a secondary route table - addRoute(newLp, r, TO_SECONDARY_TABLE, UNEXEMPT); + addRoute(newLp, r, TO_SECONDARY_TABLE, UNEXEMPT, netId); // many radios add a default route even when we don't want one. // remove the default route unless somebody else has asked for it @@ -2726,7 +2623,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (!TextUtils.isEmpty(ifaceName) && !mAddedRoutes.contains(r)) { if (VDBG) log("Removing " + r + " for interface " + ifaceName); try { - mNetd.removeRoute(ifaceName, r); + mNetd.removeRoute(netId, r); } catch (Exception e) { // never crash - catch them all if (DBG) loge("Exception trying to remove a route: " + e); @@ -2739,26 +2636,30 @@ public class ConnectivityService extends IConnectivityManager.Stub { return routesChanged; } - /** + /** * Reads the network specific MTU size from reources. * and set it on it's iface. */ - private void updateMtuSizeSettings(NetworkStateTracker nt) { - final String iface = nt.getLinkProperties().getInterfaceName(); - final int mtu = nt.getLinkProperties().getMtu(); - - if (mtu < 68 || mtu > 10000) { - loge("Unexpected mtu value: " + mtu + ", " + nt); - return; - } - - try { - if (VDBG) log("Setting MTU size: " + iface + ", " + mtu); - mNetd.setMtu(iface, mtu); - } catch (Exception e) { - Slog.e(TAG, "exception in setMtu()" + e); - } - } + private void updateMtu(LinkProperties newLp, LinkProperties oldLp) { + final String iface = newLp.getInterfaceName(); + final int mtu = newLp.getMtu(); + if (oldLp != null && newLp.isIdenticalMtu(oldLp)) { + if (VDBG) log("identical MTU - not setting"); + return; + } + + if (mtu < 68 || mtu > 10000) { + loge("Unexpected mtu value: " + mtu + ", " + iface); + return; + } + + try { + if (VDBG) log("Setting MTU size: " + iface + ", " + mtu); + mNetd.setMtu(iface, mtu); + } catch (Exception e) { + Slog.e(TAG, "exception in setMtu()" + e); + } + } /** * Reads the network specific TCP buffer sizes from SystemProperties @@ -2843,7 +2744,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (p == null) continue; if (mNetRequestersPids[i].contains(myPid)) { try { - mNetd.setDnsInterfaceForPid(p.getInterfaceName(), pid); + // TODO: Reimplement this via local variable in bionic. + // mNetd.setDnsNetworkForPid(nt.getNetwork().netId, pid); } catch (Exception e) { Slog.e(TAG, "exception reasseses pid dns: " + e); } @@ -2853,7 +2755,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } // nothing found - delete try { - mNetd.clearDnsInterfaceForPid(pid); + // TODO: Reimplement this via local variable in bionic. + // mNetd.clearDnsNetworkForPid(pid); } catch (Exception e) { Slog.e(TAG, "exception clear interface from pid: " + e); } @@ -2878,8 +2781,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } // Caller must grab mDnsLock. - private void updateDnsLocked(String network, String iface, - Collection<InetAddress> dnses, String domains, boolean defaultDns) { + private void updateDnsLocked(String network, int netId, + Collection<InetAddress> dnses, String domains) { int last = 0; if (dnses.size() == 0 && mDefaultDns != null) { dnses = new ArrayList(); @@ -2890,10 +2793,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } try { - mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses), domains); - if (defaultDns) { - mNetd.setDefaultInterfaceForDns(iface); - } + mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses), domains); for (InetAddress dns : dnses) { ++last; @@ -2918,14 +2818,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { LinkProperties p = nt.getLinkProperties(); if (p == null) return; Collection<InetAddress> dnses = p.getDnses(); + int netId = nt.getNetwork().netId; if (mNetConfigs[netType].isDefault()) { String network = nt.getNetworkInfo().getTypeName(); synchronized (mDnsLock) { - updateDnsLocked(network, p.getInterfaceName(), dnses, p.getDomains(), true); + updateDnsLocked(network, netId, dnses, p.getDomains()); } } else { try { - mNetd.setDnsServersForInterface(p.getInterfaceName(), + mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses), p.getDomains()); } catch (Exception e) { if (DBG) loge("exception setting dns servers: " + e); @@ -2934,7 +2835,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { List<Integer> pids = mNetRequestersPids[netType]; for (Integer pid : pids) { try { - mNetd.setDnsInterfaceForPid(p.getInterfaceName(), pid); + // TODO: Reimplement this via local variable in bionic. + // mNetd.setDnsNetworkForPid(netId, pid); } catch (Exception e) { Slog.e(TAG, "exception setting interface for pid: " + e); } @@ -2976,44 +2878,39 @@ public class ConnectivityService extends IConnectivityManager.Stub { return; } - // TODO: add locking to get atomic snapshot - pw.println(); - for (int i = 0; i < mNetTrackers.length; i++) { - final NetworkStateTracker nst = mNetTrackers[i]; - if (nst != null) { - pw.println("NetworkStateTracker for " + getNetworkTypeName(i) + ":"); - pw.increaseIndent(); - if (nst.getNetworkInfo().isConnected()) { - pw.println("Active network: " + nst.getNetworkInfo(). - getTypeName()); - } - pw.println(nst.getNetworkInfo()); - pw.println(nst.getLinkProperties()); - pw.println(nst); - pw.println(); - pw.decreaseIndent(); - } + NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId); + pw.print("Active default network: "); + if (defaultNai == null) { + pw.println("none"); + } else { + pw.println(defaultNai.network.netId); } - - pw.print("Active default network: "); pw.println(getNetworkTypeName(mActiveDefaultNetwork)); pw.println(); - pw.println("Network Requester Pids:"); + pw.println("Current Networks:"); pw.increaseIndent(); - for (int net : mPriorityList) { - String pidString = net + ": "; - for (Integer pid : mNetRequestersPids[net]) { - pidString = pidString + pid.toString() + ", "; + for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + pw.println(nai.toString()); + pw.increaseIndent(); + pw.println("Requests:"); + pw.increaseIndent(); + for (int i = 0; i < nai.networkRequests.size(); i++) { + pw.println(nai.networkRequests.valueAt(i).toString()); } - pw.println(pidString); + pw.decreaseIndent(); + pw.println("Lingered:"); + pw.increaseIndent(); + for (NetworkRequest nr : nai.networkLingered) pw.println(nr.toString()); + pw.decreaseIndent(); + pw.decreaseIndent(); } - pw.println(); pw.decreaseIndent(); + pw.println(); - pw.println("FeatureUsers:"); + pw.println("Network Requests:"); pw.increaseIndent(); - for (Object requester : mFeatureUsers) { - pw.println(requester.toString()); + for (NetworkRequestInfo nri : mNetworkRequests.values()) { + pw.println(nri.toString()); } pw.println(); pw.decreaseIndent(); @@ -3048,6 +2945,59 @@ public class ConnectivityService extends IConnectivityManager.Stub { public void handleMessage(Message msg) { NetworkInfo info; switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { + handleAsyncChannelHalfConnect(msg); + break; + } + case AsyncChannel.CMD_CHANNEL_DISCONNECT: { + NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); + if (nai != null) nai.asyncChannel.disconnect(); + break; + } + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { + handleAsyncChannelDisconnected(msg); + break; + } + case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: { + NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); + if (nai == null) { + loge("EVENT_NETWORK_CAPABILITIES_CHANGED from unknown NetworkAgent"); + } else { + updateCapabilities(nai, (NetworkCapabilities)msg.obj); + } + break; + } + case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: { + NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); + if (nai == null) { + loge("NetworkAgent not found for EVENT_NETWORK_PROPERTIES_CHANGED"); + } else { + LinkProperties oldLp = nai.linkProperties; + nai.linkProperties = (LinkProperties)msg.obj; + updateLinkProperties(nai, oldLp); + } + break; + } + case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: { + NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); + if (nai == null) { + loge("EVENT_NETWORK_INFO_CHANGED from unknown NetworkAgent"); + break; + } + info = (NetworkInfo) msg.obj; + updateNetworkInfo(nai, info); + break; + } + case NetworkMonitor.EVENT_NETWORK_VALIDATED: { + NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj; + handleConnectionValidated(nai); + break; + } + case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: { + NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj; + handleLingerComplete(nai); + break; + } case NetworkStateTracker.EVENT_STATE_CHANGED: { info = (NetworkInfo) msg.obj; NetworkInfo.State state = info.getState(); @@ -3080,10 +3030,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { EventLogTags.writeConnectivityStateChanged( info.getType(), info.getSubtype(), info.getDetailedState().ordinal()); - if (info.getDetailedState() == - NetworkInfo.DetailedState.FAILED) { - handleConnectionFailure(info); - } else if (info.isConnectedToProvisioningNetwork()) { + if (info.isConnectedToProvisioningNetwork()) { /** * TODO: Create ConnectivityManager.TYPE_MOBILE_PROVISIONING * for now its an in between network, its a network that @@ -3094,7 +3041,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { * to the link that may have incorrectly setup by the lower * levels. */ - LinkProperties lp = getLinkProperties(info.getType()); + LinkProperties lp = getLinkPropertiesForTypeInternal(info.getType()); if (DBG) { log("EVENT_STATE_CHANGED: connected to provisioning network, lp=" + lp); } @@ -3104,21 +3051,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { // connection will fail until the provisioning network // is enabled. for (RouteInfo r : lp.getRoutes()) { - removeRoute(lp, r, TO_DEFAULT_TABLE); + removeRoute(lp, r, TO_DEFAULT_TABLE, + mNetTrackers[info.getType()].getNetwork().netId); } } else if (state == NetworkInfo.State.DISCONNECTED) { - handleDisconnect(info); } else if (state == NetworkInfo.State.SUSPENDED) { - // TODO: need to think this over. - // the logic here is, handle SUSPENDED the same as - // DISCONNECTED. The only difference being we are - // broadcasting an intent with NetworkInfo that's - // suspended. This allows the applications an - // opportunity to handle DISCONNECTED and SUSPENDED - // differently, or not. - handleDisconnect(info); } else if (state == NetworkInfo.State.CONNECTED) { - handleConnect(info); + // handleConnect(info); } if (mLockdownTracker != null) { mLockdownTracker.onNetworkInfoChanged(info); @@ -3130,7 +3069,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { // TODO: Temporary allowing network configuration // change not resetting sockets. // @see bug/4455071 - handleConnectivityChange(info.getType(), false); + handleConnectivityChange(info.getType(), mCurrentLinkProperties[info.getType()], + false); break; } case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: { @@ -3143,6 +3083,173 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + private void handleAsyncChannelHalfConnect(Message msg) { + AsyncChannel ac = (AsyncChannel) msg.obj; + if (mNetworkFactories.contains(ac)) { + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + if (VDBG) log("NetworkFactory connected"); + // A network factory has connected. Send it all current NetworkRequests. + for (NetworkRequestInfo nri : mNetworkRequests.values()) { + NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); + ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, + (nai != null ? nai.currentScore : 0), 0, nri.request); + } + } else { + loge("Error connecting NetworkFactory"); + mNetworkFactories.remove(ac); + } + } else if (mNetworkAgentInfos.containsKey(msg.replyTo)) { + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + if (VDBG) log("NetworkAgent connected"); + // A network agent has requested a connection. Establish the connection. + mNetworkAgentInfos.get(msg.replyTo).asyncChannel. + sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + } else { + loge("Error connecting NetworkAgent"); + NetworkAgentInfo nai = mNetworkAgentInfos.remove(msg.replyTo); + try { + mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai); + } catch (NullPointerException e) {} + if (nai != null) { + mNetworkForNetId.remove(nai.network.netId); + } + } + } + } + private void handleAsyncChannelDisconnected(Message msg) { + NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); + if (nai != null) { + if (DBG) { + log(nai.name() + " got DISCONNECTED, was satisfying " + nai.networkRequests.size()); + } + // A network agent has disconnected. + // Tell netd to clean up the configuration for this network + // (routing rules, DNS, etc). + try { + mNetd.removeNetwork(nai.network.netId); + } catch (Exception e) { + loge("Exception removing network: " + e); + } + notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST); + nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED); + mNetworkAgentInfos.remove(msg.replyTo); + updateClat(null, nai.linkProperties, nai); + try { + mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai); + } catch (NullPointerException e) {} + + mNetworkForNetId.remove(nai.network.netId); + // Since we've lost the network, go through all the requests that + // it was satisfying and see if any other factory can satisfy them. + final ArrayList<NetworkAgentInfo> toActivate = new ArrayList<NetworkAgentInfo>(); + for (int i = 0; i < nai.networkRequests.size(); i++) { + NetworkRequest request = nai.networkRequests.valueAt(i); + NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId); + if (VDBG) { + log(" checking request " + request + ", currentNetwork = " + + currentNetwork != null ? currentNetwork.name() : "null"); + } + if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) { + mNetworkForRequestId.remove(request.requestId); + sendUpdatedScoreToFactories(request, 0); + NetworkAgentInfo alternative = null; + for (Map.Entry entry : mNetworkAgentInfos.entrySet()) { + NetworkAgentInfo existing = (NetworkAgentInfo)entry.getValue(); + if (existing.networkInfo.isConnected() && + request.networkCapabilities.satisfiedByNetworkCapabilities( + existing.networkCapabilities) && + (alternative == null || + alternative.currentScore < existing.currentScore)) { + alternative = existing; + } + } + if (alternative != null && !toActivate.contains(alternative)) { + toActivate.add(alternative); + } + } + } + if (nai.networkRequests.get(mDefaultRequest.requestId) != null) { + removeDataActivityTracking(nai); + mActiveDefaultNetwork = ConnectivityManager.TYPE_NONE; + } + for (NetworkAgentInfo networkToActivate : toActivate) { + networkToActivate.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED); + } + } + } + + private void handleRegisterNetworkRequest(Message msg) { + final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj); + final NetworkCapabilities newCap = nri.request.networkCapabilities; + int score = 0; + + // Check for the best currently alive network that satisfies this request + NetworkAgentInfo bestNetwork = null; + for (NetworkAgentInfo network : mNetworkAgentInfos.values()) { + if (VDBG) log("handleRegisterNetworkRequest checking " + network.name()); + if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) { + if (VDBG) log("apparently satisfied. currentScore=" + network.currentScore); + if ((bestNetwork == null) || bestNetwork.currentScore < network.currentScore) { + bestNetwork = network; + } + } + } + if (bestNetwork != null) { + if (VDBG) log("using " + bestNetwork.name()); + bestNetwork.networkRequests.put(nri.request.requestId, nri.request); + notifyNetworkCallback(bestNetwork, nri); + score = bestNetwork.currentScore; + } + mNetworkRequests.put(nri.request, nri); + if (msg.what == EVENT_REGISTER_NETWORK_REQUEST) { + if (DBG) log("sending new NetworkRequest to factories"); + for (AsyncChannel ac : mNetworkFactories) { + ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request); + } + } + } + + private void handleReleaseNetworkRequest(NetworkRequest request) { + if (DBG) log("releasing NetworkRequest " + request); + NetworkRequestInfo nri = mNetworkRequests.remove(request); + if (nri != null) { + // tell the network currently servicing this that it's no longer interested + NetworkAgentInfo affectedNetwork = mNetworkForRequestId.get(nri.request.requestId); + if (affectedNetwork != null) { + affectedNetwork.networkRequests.remove(nri.request.requestId); + if (VDBG) { + log(" Removing from current network " + affectedNetwork.name() + ", leaving " + + affectedNetwork.networkRequests.size() + " requests."); + } + } + + if (nri.isRequest) { + for (AsyncChannel factory : mNetworkFactories) { + factory.sendMessage(NetworkFactoryProtocol.CMD_CANCEL_REQUEST, nri.request); + } + + if (affectedNetwork != null) { + // check if this network still has live requests - otherwise, tear down + // TODO - probably push this to the NF/NA + boolean keep = false; + for (int i = 0; i < affectedNetwork.networkRequests.size(); i++) { + NetworkRequest r = affectedNetwork.networkRequests.valueAt(i); + if (mNetworkRequests.get(r).isRequest) { + keep = true; + break; + } + } + if (keep == false) { + if (DBG) log("no live requests for " + affectedNetwork.name() + + "; disconnecting"); + affectedNetwork.asyncChannel.disconnect(); + } + } + } + callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED); + } + } + private class InternalHandler extends Handler { public InternalHandler(Looper looper) { super(looper); @@ -3183,11 +3290,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { handleInetConditionHoldEnd(netType, sequence); break; } - case EVENT_SET_NETWORK_PREFERENCE: { - int preference = msg.arg1; - handleSetNetworkPreference(preference); - break; - } case EVENT_SET_MOBILE_DATA: { boolean enabled = (msg.arg1 == ENABLED); handleSetMobileData(enabled); @@ -3241,6 +3343,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { handleApplyDefaultProxy((ProxyInfo)msg.obj); break; } + case EVENT_REGISTER_NETWORK_FACTORY: { + handleRegisterNetworkFactory((Messenger)msg.obj); + break; + } + case EVENT_REGISTER_NETWORK_AGENT: { + handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj); + break; + } + case EVENT_REGISTER_NETWORK_REQUEST: + case EVENT_REGISTER_NETWORK_LISTENER: { + handleRegisterNetworkRequest(msg); + break; + } + case EVENT_RELEASE_NETWORK_REQUEST: { + handleReleaseNetworkRequest((NetworkRequest) msg.obj); + break; + } } } } @@ -3387,6 +3506,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { EVENT_INET_CONDITION_CHANGE, networkType, percentage)); } + public void reportBadNetwork(Network network) { + //TODO + } + private void handleInetConditionChange(int netType, int condition) { if (mActiveDefaultNetwork == -1) { if (DBG) log("handleInetConditionChange: no active default network - ignore"); @@ -3446,7 +3569,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { // if (DBG) log("no change in condition - aborting"); // return; //} - NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(); + NetworkInfo networkInfo = getNetworkInfoForType(mActiveDefaultNetwork); if (networkInfo.isConnected() == false) { if (DBG) log("handleInetConditionHoldEnd: default network not connected - ignoring"); return; @@ -3867,7 +3990,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { // Apply DNS changes. synchronized (mDnsLock) { - updateDnsLocked("VPN", iface, addresses, domains, false); + // TODO: Re-enable this when the netId of the VPN is known. + // updateDnsLocked("VPN", netId, addresses, domains); } // Temporarily disable the default proxy (not global). @@ -3935,21 +4059,21 @@ public class ConnectivityService extends IConnectivityManager.Stub { public void addUidForwarding(String interfaze, int uidStart, int uidEnd, boolean forwardDns) { - try { - mNetd.setUidRangeRoute(interfaze,uidStart, uidEnd); - if (forwardDns) mNetd.setDnsInterfaceForUidRange(interfaze, uidStart, uidEnd); - } catch (RemoteException e) { - } + // TODO: Re-enable this when the netId of the VPN is known. + // try { + // mNetd.setUidRangeRoute(netId, uidStart, uidEnd, forwardDns); + // } catch (RemoteException e) { + // } } public void clearUidForwarding(String interfaze, int uidStart, int uidEnd, boolean forwardDns) { - try { - mNetd.clearUidRangeRoute(interfaze, uidStart, uidEnd); - if (forwardDns) mNetd.clearDnsInterfaceForUidRange(interfaze, uidStart, uidEnd); - } catch (RemoteException e) { - } + // TODO: Re-enable this when the netId of the VPN is known. + // try { + // mNetd.clearUidRangeRoute(interfaze, uidStart, uidEnd); + // } catch (RemoteException e) { + // } } } @@ -4210,7 +4334,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { CheckMp.Params params = new CheckMp.Params(checkMp.getDefaultUrl(), timeOutMs, cb); if (DBG) log("checkMobileProvisioning: params=" + params); - checkMp.execute(params); + // TODO: Reenable when calls to the now defunct + // MobileDataStateTracker.isProvisioningNetwork() are removed. + // This code should be moved to the Telephony code. + // checkMp.execute(params); } finally { Binder.restoreCallingIdentity(token); if (DBG) log("checkMobileProvisioning: X"); @@ -4453,7 +4580,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { log("isMobileOk: addresses=" + inetAddressesToString(addresses)); // Get the type of addresses supported by this link - LinkProperties lp = mCs.getLinkProperties( + LinkProperties lp = mCs.getLinkPropertiesForTypeInternal( ConnectivityManager.TYPE_MOBILE_HIPRI); boolean linkHasIpv4 = lp.hasIPv4Address(); boolean linkHasIpv6 = lp.hasIPv6Address(); @@ -4698,11 +4825,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { // otherwise launch browser with the intent directly. if (mIsProvisioningNetwork.get()) { if (DBG) log("handleMobileProvisioningAction: on prov network enable then launch"); - mIsStartingProvisioning.set(true); - MobileDataStateTracker mdst = (MobileDataStateTracker) - mNetTrackers[ConnectivityManager.TYPE_MOBILE]; - mdst.setEnableFailFastMobileData(DctConstants.ENABLED); - mdst.enableMobileProvisioning(url); +// mIsStartingProvisioning.set(true); +// MobileDataStateTracker mdst = (MobileDataStateTracker) +// mNetTrackers[ConnectivityManager.TYPE_MOBILE]; +// mdst.setEnableFailFastMobileData(DctConstants.ENABLED); +// mdst.enableMobileProvisioning(url); } else { if (DBG) log("handleMobileProvisioningAction: not prov network"); // Check for apps that can handle provisioning first @@ -4998,7 +5125,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override public LinkQualityInfo getLinkQualityInfo(int networkType) { enforceAccessPermission(); - if (isNetworkTypeValid(networkType)) { + if (isNetworkTypeValid(networkType) && mNetTrackers[networkType] != null) { return mNetTrackers[networkType].getLinkQualityInfo(); } else { return null; @@ -5008,7 +5135,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override public LinkQualityInfo getActiveLinkQualityInfo() { enforceAccessPermission(); - if (isNetworkTypeValid(mActiveDefaultNetwork)) { + if (isNetworkTypeValid(mActiveDefaultNetwork) && + mNetTrackers[mActiveDefaultNetwork] != null) { return mNetTrackers[mActiveDefaultNetwork].getLinkQualityInfo(); } else { return null; @@ -5081,4 +5209,615 @@ public class ConnectivityService extends IConnectivityManager.Stub { long wakeupTime = SystemClock.elapsedRealtime() + timeoutInMilliseconds; mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, wakeupTime, intent); } + + private final ArrayList<AsyncChannel> mNetworkFactories = new ArrayList<AsyncChannel>(); + private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = + new HashMap<NetworkRequest, NetworkRequestInfo>(); + + + private class NetworkRequestInfo implements IBinder.DeathRecipient { + static final boolean REQUEST = true; + static final boolean LISTEN = false; + + final NetworkRequest request; + IBinder mBinder; + final int mPid; + final int mUid; + final Messenger messenger; + final boolean isRequest; + + NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, boolean isRequest) { + super(); + messenger = m; + request = r; + mBinder = binder; + mPid = getCallingPid(); + mUid = getCallingUid(); + this.isRequest = isRequest; + + try { + mBinder.linkToDeath(this, 0); + } catch (RemoteException e) { + binderDied(); + } + } + + void unlinkDeathRecipient() { + mBinder.unlinkToDeath(this, 0); + } + + public void binderDied() { + log("ConnectivityService NetworkRequestInfo binderDied(" + + request + ", " + mBinder + ")"); + releaseNetworkRequest(request); + } + } + + @Override + public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, + Messenger messenger, int timeoutSec, IBinder binder) { + if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + == false) { + enforceConnectivityInternalPermission(); + } else { + enforceChangePermission(); + } + + if (timeoutSec < 0 || timeoutSec > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_SEC) { + throw new IllegalArgumentException("Bad timeout specified"); + } + NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities( + networkCapabilities)); + if (DBG) log("requestNetwork for " + networkRequest); + NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, + NetworkRequestInfo.REQUEST); + + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri)); + if (timeoutSec > 0) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST, + nri), timeoutSec * 1000); + } + return networkRequest; + } + + @Override + public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities, + PendingIntent operation) { + // TODO + return null; + } + + @Override + public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities, + Messenger messenger, IBinder binder) { + enforceAccessPermission(); + + NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities( + networkCapabilities)); + if (DBG) log("listenForNetwork for " + networkRequest); + NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, + NetworkRequestInfo.LISTEN); + + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); + return networkRequest; + } + + @Override + public void pendingListenForNetwork(NetworkCapabilities networkCapabilities, + PendingIntent operation) { + } + + @Override + public void releaseNetworkRequest(NetworkRequest networkRequest) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST, + networkRequest)); + } + + @Override + public void registerNetworkFactory(Messenger messenger) { + enforceConnectivityInternalPermission(); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, messenger)); + } + + private void handleRegisterNetworkFactory(Messenger messenger) { + if (VDBG) log("Got NetworkFactory Messenger"); + AsyncChannel ac = new AsyncChannel(); + mNetworkFactories.add(ac); + ac.connect(mContext, mTrackerHandler, messenger); + for (NetworkRequestInfo nri : mNetworkRequests.values()) { + if (nri.isRequest) { + int score = 0; + NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); + if (currentNetwork != null) score = currentNetwork.currentScore; + ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request); + } + } + } + + /** + * NetworkAgentInfo supporting a request by requestId. + * These have already been vetted (their Capabilities satisfy the request) + * and the are the highest scored network available. + * the are keyed off the Requests requestId. + */ + private final SparseArray<NetworkAgentInfo> mNetworkForRequestId = + new SparseArray<NetworkAgentInfo>(); + + private final SparseArray<NetworkAgentInfo> mNetworkForNetId = + new SparseArray<NetworkAgentInfo>(); + + // NetworkAgentInfo keyed off its connecting messenger + // TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays + private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos = + new HashMap<Messenger, NetworkAgentInfo>(); + + private final NetworkRequest mDefaultRequest; + + public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, + LinkProperties linkProperties, NetworkCapabilities networkCapabilities, + int currentScore) { + enforceConnectivityInternalPermission(); + + NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(), + new NetworkInfo(networkInfo), new LinkProperties(linkProperties), + new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler); + + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai)); + } + + private void handleRegisterNetworkAgent(NetworkAgentInfo na) { + if (VDBG) log("Got NetworkAgent Messenger"); + mNetworkAgentInfos.put(na.messenger, na); + try { + mNetworkAgentInfoForType[na.networkInfo.getType()].add(na); + } catch (NullPointerException e) { + loge("registered NetworkAgent for unsupported type: " + na); + } + mNetworkForNetId.put(na.network.netId, na); + na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger); + NetworkInfo networkInfo = na.networkInfo; + na.networkInfo = null; + updateNetworkInfo(na, networkInfo); + } + + private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) { + LinkProperties newLp = networkAgent.linkProperties; + int netId = networkAgent.network.netId; + + updateInterfaces(newLp, oldLp, netId); + updateMtu(newLp, oldLp); + // TODO - figure out what to do for clat +// for (LinkProperties lp : newLp.getStackedLinks()) { +// updateMtu(lp, null); +// } + updateRoutes(newLp, oldLp, netId); + updateDnses(newLp, oldLp, netId); + updateClat(newLp, oldLp, networkAgent); + } + + private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo na) { + // Update 464xlat state. + if (mClat.requiresClat(na)) { + + // If the connection was previously using clat, but is not using it now, stop the clat + // daemon. Normally, this happens automatically when the connection disconnects, but if + // the disconnect is not reported, or if the connection's LinkProperties changed for + // some other reason (e.g., handoff changes the IP addresses on the link), it would + // still be running. If it's not running, then stopping it is a no-op. + if (Nat464Xlat.isRunningClat(oldLp) && !Nat464Xlat.isRunningClat(newLp)) { + mClat.stopClat(); + } + // If the link requires clat to be running, then start the daemon now. + if (newLp != null && na.networkInfo.isConnected()) { + mClat.startClat(na); + } else { + mClat.stopClat(); + } + } + } + + private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId) { + CompareResult<String> interfaceDiff = new CompareResult<String>(); + if (oldLp != null) { + interfaceDiff = oldLp.compareAllInterfaceNames(newLp); + } else if (newLp != null) { + interfaceDiff.added = newLp.getAllInterfaceNames(); + } + for (String iface : interfaceDiff.added) { + try { + mNetd.addInterfaceToNetwork(iface, netId); + } catch (Exception e) { + loge("Exception adding interface: " + e); + } + } + for (String iface : interfaceDiff.removed) { + try { + mNetd.removeInterfaceFromNetwork(iface, netId); + } catch (Exception e) { + loge("Exception removing interface: " + e); + } + } + } + + private void updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) { + CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>(); + if (oldLp != null) { + routeDiff = oldLp.compareAllRoutes(newLp); + } else if (newLp != null) { + routeDiff.added = newLp.getAllRoutes(); + } + + // add routes before removing old in case it helps with continuous connectivity + + // do this twice, adding non-nexthop routes first, then routes they are dependent on + for (RouteInfo route : routeDiff.added) { + if (route.hasGateway()) continue; + try { + mNetd.addRoute(netId, route); + } catch (Exception e) { + loge("Exception in addRoute for non-gateway: " + e); + } + } + for (RouteInfo route : routeDiff.added) { + if (route.hasGateway() == false) continue; + try { + mNetd.addRoute(netId, route); + } catch (Exception e) { + loge("Exception in addRoute for gateway: " + e); + } + } + + for (RouteInfo route : routeDiff.removed) { + try { + mNetd.removeRoute(netId, route); + } catch (Exception e) { + loge("Exception in removeRoute: " + e); + } + } + } + private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) { + if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) { + Collection<InetAddress> dnses = newLp.getDnses(); + if (dnses.size() == 0 && mDefaultDns != null) { + dnses = new ArrayList(); + dnses.add(mDefaultDns); + if (DBG) { + loge("no dns provided for netId " + netId + ", so using defaults"); + } + } + try { + mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses), + newLp.getDomains()); + } catch (Exception e) { + loge("Exception in setDnsServersForNetwork: " + e); + } + // TODO - setprop "net.dnsX" + } + } + + private void updateCapabilities(NetworkAgentInfo networkAgent, + NetworkCapabilities networkCapabilities) { + // TODO - what else here? Verify still satisfies everybody? + // Check if satisfies somebody new? call callbacks? + networkAgent.networkCapabilities = networkCapabilities; + } + + private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) { + if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString()); + for (AsyncChannel ac : mNetworkFactories) { + ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, networkRequest); + } + } + + private void callCallbackForRequest(NetworkRequestInfo nri, + NetworkAgentInfo networkAgent, int notificationType) { + if (nri.messenger == null) return; // Default request has no msgr + Object o; + int a1 = 0; + int a2 = 0; + switch (notificationType) { + case ConnectivityManager.CALLBACK_LOSING: + a1 = 30; // TODO - read this from NetworkMonitor + // fall through + case ConnectivityManager.CALLBACK_PRECHECK: + case ConnectivityManager.CALLBACK_AVAILABLE: + case ConnectivityManager.CALLBACK_LOST: + case ConnectivityManager.CALLBACK_CAP_CHANGED: + case ConnectivityManager.CALLBACK_IP_CHANGED: { + o = new NetworkRequest(nri.request); + a2 = networkAgent.network.netId; + break; + } + case ConnectivityManager.CALLBACK_UNAVAIL: + case ConnectivityManager.CALLBACK_RELEASED: { + o = new NetworkRequest(nri.request); + break; + } + default: { + loge("Unknown notificationType " + notificationType); + return; + } + } + Message msg = Message.obtain(); + msg.arg1 = a1; + msg.arg2 = a2; + msg.obj = o; + msg.what = notificationType; + try { + if (VDBG) log("sending notification " + notificationType + " for " + nri.request); + nri.messenger.send(msg); + } catch (RemoteException e) { + // may occur naturally in the race of binder death. + loge("RemoteException caught trying to send a callback msg for " + nri.request); + } + } + + private void handleLingerComplete(NetworkAgentInfo oldNetwork) { + if (oldNetwork == null) { + loge("Unknown NetworkAgentInfo in handleLingerComplete"); + return; + } + if (DBG) log("handleLingerComplete for " + oldNetwork.name()); + if (DBG) { + if (oldNetwork.networkRequests.size() != 0) { + loge("Dead network still had " + oldNetwork.networkRequests.size() + " requests"); + } + } + oldNetwork.asyncChannel.disconnect(); + } + + private void handleConnectionValidated(NetworkAgentInfo newNetwork) { + if (newNetwork == null) { + loge("Unknown NetworkAgentInfo in handleConnectionValidated"); + return; + } + boolean keep = false; + boolean isNewDefault = false; + if (DBG) log("handleConnectionValidated for "+newNetwork.name()); + // check if any NetworkRequest wants this NetworkAgent + // first check if it satisfies the NetworkCapabilities + ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>(); + if (VDBG) log(" new Network has: " + newNetwork.networkCapabilities); + for (NetworkRequestInfo nri : mNetworkRequests.values()) { + if (VDBG) log(" checking if request is satisfied: " + nri.request); + if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities( + newNetwork.networkCapabilities)) { + // next check if it's better than any current network we're using for + // this request + NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); + if (VDBG) { + log("currentScore = " + + (currentNetwork != null ? currentNetwork.currentScore : 0) + + ", newScore = " + newNetwork.currentScore); + } + if (currentNetwork == null || + currentNetwork.currentScore < newNetwork.currentScore) { + if (currentNetwork != null) { + if (VDBG) log(" accepting network in place of " + currentNetwork.name()); + currentNetwork.networkRequests.remove(nri.request.requestId); + currentNetwork.networkLingered.add(nri.request); + affectedNetworks.add(currentNetwork); + } else { + if (VDBG) log(" accepting network in place of null"); + } + mNetworkForRequestId.put(nri.request.requestId, newNetwork); + newNetwork.networkRequests.put(nri.request.requestId, nri.request); + keep = true; + // TODO - this could get expensive if we have alot of requests for this + // network. Think about if there is a way to reduce this. Push + // netid->request mapping to each factory? + sendUpdatedScoreToFactories(nri.request, newNetwork.currentScore); + if (mDefaultRequest.requestId == nri.request.requestId) { + isNewDefault = true; + updateActiveDefaultNetwork(newNetwork); + } + } + } + } + for (NetworkAgentInfo nai : affectedNetworks) { + boolean teardown = true; + for (int i = 0; i < nai.networkRequests.size(); i++) { + NetworkRequest nr = nai.networkRequests.valueAt(i); + try { + if (mNetworkRequests.get(nr).isRequest) { + teardown = false; + } + } catch (Exception e) { + loge("Request " + nr + " not found in mNetworkRequests."); + loge(" it came from request list of " + nai.name()); + } + } + if (teardown) { + nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER); + notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING); + } else { + // not going to linger, so kill the list of linger networks.. only + // notify them of linger if it happens as the result of gaining another, + // but if they transition and old network stays up, don't tell them of linger + // or very delayed loss + nai.networkLingered.clear(); + if (VDBG) log("Lingered for " + nai.name() + " cleared"); + } + } + if (keep) { + if (isNewDefault) { + if (VDBG) log("Switching to new default network: " + newNetwork); + setupDataActivityTracking(newNetwork); + try { + mNetd.setDefaultNetId(newNetwork.network.netId); + } catch (Exception e) { + loge("Exception setting default network :" + e); + } + if (newNetwork.equals(mNetworkForRequestId.get(mDefaultRequest.requestId))) { + handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy()); + } + synchronized (ConnectivityService.this) { + // have a new default network, release the transition wakelock in + // a second if it's held. The second pause is to allow apps + // to reconnect over the new network + if (mNetTransitionWakeLock.isHeld()) { + mHandler.sendMessageDelayed(mHandler.obtainMessage( + EVENT_CLEAR_NET_TRANSITION_WAKELOCK, + mNetTransitionWakeLockSerialNumber, 0), + 1000); + } + } + + // this will cause us to come up initially as unconnected and switching + // to connected after our normal pause unless somebody reports us as + // really disconnected + mDefaultInetConditionPublished = 0; + mDefaultConnectionSequence++; + mInetConditionChangeInFlight = false; + // TODO - read the tcp buffer size config string from somewhere + // updateNetworkSettings(); + } + // notify battery stats service about this network +// try { + // TODO + //BatteryStatsService.getService().noteNetworkInterfaceType(iface, netType); +// } catch (RemoteException e) { } + notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE); + } else { + if (DBG && newNetwork.networkRequests.size() != 0) { + loge("tearing down network with live requests:"); + for (int i=0; i < newNetwork.networkRequests.size(); i++) { + loge(" " + newNetwork.networkRequests.valueAt(i)); + } + } + if (VDBG) log("Validated network turns out to be unwanted. Tear it down."); + newNetwork.asyncChannel.disconnect(); + } + } + + + private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) { + NetworkInfo.State state = newInfo.getState(); + NetworkInfo oldInfo = networkAgent.networkInfo; + networkAgent.networkInfo = newInfo; + + if (oldInfo != null && oldInfo.getState() == state) { + if (VDBG) log("ignoring duplicate network state non-change"); + return; + } + if (DBG) { + log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " + + (oldInfo == null ? "null" : oldInfo.getState()) + + " to " + state); + } + + if (state == NetworkInfo.State.CONNECTED) { + // TODO - check if we want it (optimization) + try { + mNetd.createNetwork(networkAgent.network.netId); + } catch (Exception e) { + loge("Error creating Network " + networkAgent.network.netId); + } + updateLinkProperties(networkAgent, null); + notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK); + networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED); + } else if (state == NetworkInfo.State.DISCONNECTED || + state == NetworkInfo.State.SUSPENDED) { + networkAgent.asyncChannel.disconnect(); + } + } + + // notify only this one new request of the current state + protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) { + int notifyType = ConnectivityManager.CALLBACK_AVAILABLE; + // TODO - read state from monitor to decide what to send. +// if (nai.networkMonitor.isLingering()) { +// notifyType = NetworkCallbacks.LOSING; +// } else if (nai.networkMonitor.isEvaluating()) { +// notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType); +// } + if (nri.request.needsBroadcasts) { + // TODO +// sendNetworkBroadcast(nai, notifyType); + } + callCallbackForRequest(nri, nai, notifyType); + } + + protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) { + if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name()); + boolean needsBroadcasts = false; + for (int i = 0; i < networkAgent.networkRequests.size(); i++) { + NetworkRequest nr = networkAgent.networkRequests.valueAt(i); + NetworkRequestInfo nri = mNetworkRequests.get(nr); + if (VDBG) log(" sending notification for " + nr); + if (nr.needsBroadcasts) needsBroadcasts = true; + callCallbackForRequest(nri, networkAgent, notifyType); + } + if (needsBroadcasts) { + if (notifyType == ConnectivityManager.CALLBACK_AVAILABLE) { + sendConnectedBroadcastDelayed(networkAgent.networkInfo, + getConnectivityChangeDelay()); + } else if (notifyType == ConnectivityManager.CALLBACK_LOST) { + NetworkInfo info = new NetworkInfo(networkAgent.networkInfo); + Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); + if (info.isFailover()) { + intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); + networkAgent.networkInfo.setFailover(false); + } + if (info.getReason() != null) { + intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason()); + } + if (info.getExtraInfo() != null) { + intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo()); + } + NetworkAgentInfo newDefaultAgent = null; + if (networkAgent.networkRequests.get(mDefaultRequest.requestId) != null) { + newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId); + if (newDefaultAgent != null) { + intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, + newDefaultAgent.networkInfo); + } else { + intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); + } + } + intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, + mDefaultInetConditionPublished); + final Intent immediateIntent = new Intent(intent); + immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE); + sendStickyBroadcast(immediateIntent); + sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay()); + if (newDefaultAgent != null) { + sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo, + getConnectivityChangeDelay()); + } + } + } + } + + private LinkProperties getLinkPropertiesForTypeInternal(int networkType) { + ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType]; + if (list == null) return null; + try { + return new LinkProperties(list.get(0).linkProperties); + } catch (IndexOutOfBoundsException e) { + return new LinkProperties(); + } + } + + private NetworkInfo getNetworkInfoForType(int networkType) { + ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType]; + if (list == null) return null; + try { + return new NetworkInfo(list.get(0).networkInfo); + } catch (IndexOutOfBoundsException e) { + return new NetworkInfo(networkType, 0, "Unknown", ""); + } + } + + private NetworkCapabilities getNetworkCapabilitiesForType(int networkType) { + ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType]; + if (list == null) return null; + try { + return new NetworkCapabilities(list.get(0).networkCapabilities); + } catch (IndexOutOfBoundsException e) { + return new NetworkCapabilities(); + } + } } diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index b9c86dc..cf91782 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.Manifest.permission.CHANGE_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.DUMP; import static android.Manifest.permission.SHUTDOWN; @@ -867,46 +868,25 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void addRoute(String interfaceName, RouteInfo route) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - modifyRoute(interfaceName, ADD, route, DEFAULT); - } - - @Override - public void removeRoute(String interfaceName, RouteInfo route) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - modifyRoute(interfaceName, REMOVE, route, DEFAULT); + public void addRoute(int netId, RouteInfo route) { + modifyRoute(netId, ADD, route); } @Override - public void addSecondaryRoute(String interfaceName, RouteInfo route) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - modifyRoute(interfaceName, ADD, route, SECONDARY); + public void removeRoute(int netId, RouteInfo route) { + modifyRoute(netId, REMOVE, route); } - @Override - public void removeSecondaryRoute(String interfaceName, RouteInfo route) { + private void modifyRoute(int netId, String action, RouteInfo route) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - modifyRoute(interfaceName, REMOVE, route, SECONDARY); - } - private void modifyRoute(String interfaceName, String action, RouteInfo route, String type) { - final Command cmd = new Command("interface", "route", action, interfaceName, type); + final Command cmd = new Command("network", "route", action, netId); - // create triplet: dest-ip-addr prefixlength gateway-ip-addr + // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr final LinkAddress la = route.getDestination(); - cmd.appendArg(la.getAddress().getHostAddress()); - cmd.appendArg(la.getNetworkPrefixLength()); - - if (route.getGateway() == null) { - if (la.getAddress() instanceof Inet4Address) { - cmd.appendArg("0.0.0.0"); - } else { - cmd.appendArg("::0"); - } - } else { - cmd.appendArg(route.getGateway().getHostAddress()); - } + cmd.appendArg(route.getInterface()); + cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getNetworkPrefixLength()); + cmd.appendArg(route.getGateway().getHostAddress()); try { mConnector.execute(cmd); @@ -1624,20 +1604,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void setDefaultInterfaceForDns(String iface) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute("resolver", "setdefaultif", iface); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - } - - @Override - public void setDnsServersForInterface(String iface, String[] servers, String domains) { + public void setDnsServersForNetwork(int netId, String[] servers, String domains) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - final Command cmd = new Command("resolver", "setifdns", iface, + final Command cmd = new Command("resolver", "setnetdns", netId, (domains == null ? "" : domains)); for (String s : servers) { @@ -1655,11 +1625,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void setUidRangeRoute(String iface, int uid_start, int uid_end) { + public void setUidRangeRoute(String iface, int uid_start, int uid_end, boolean forward_dns) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { mConnector.execute("interface", "fwmark", - "uid", "add", iface, uid_start, uid_end); + "uid", "add", iface, uid_start, uid_end, forward_dns ? 1 : 0); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } @@ -1670,7 +1640,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { mConnector.execute("interface", "fwmark", - "uid", "remove", iface, uid_start, uid_end); + "uid", "remove", iface, uid_start, uid_end, 0); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } @@ -1767,51 +1737,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void setDnsInterfaceForUidRange(String iface, int uid_start, int uid_end) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute("resolver", "setifaceforuidrange", iface, uid_start, uid_end); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - } - - @Override - public void clearDnsInterfaceForUidRange(String iface, int uid_start, int uid_end) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute("resolver", "clearifaceforuidrange", iface, uid_start, uid_end); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - } - - @Override - public void clearDnsInterfaceMaps() { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute("resolver", "clearifacemapping"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - } - - - @Override - public void flushDefaultDnsCache() { + public void flushNetworkDnsCache(int netId) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("resolver", "flushdefaultif"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - } - - @Override - public void flushInterfaceDnsCache(String iface) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute("resolver", "flushif", iface); + mConnector.execute("resolver", "flushnet", netId); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } @@ -1890,28 +1819,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void setDnsInterfaceForPid(String iface, int pid) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute("resolver", "setifaceforpid", iface, pid); - } catch (NativeDaemonConnectorException e) { - throw new IllegalStateException( - "Error communicating with native deamon to set interface for pid" + iface, e); - } - } - - @Override - public void clearDnsInterfaceForPid(int pid) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute("resolver", "clearifaceforpid", pid); - } catch (NativeDaemonConnectorException e) { - throw new IllegalStateException( - "Error communicating with native deamon to clear interface for pid " + pid, e); - } - } - - @Override public void startClatd(String interfaceName) throws IllegalStateException { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); @@ -2030,16 +1937,18 @@ public class NetworkManagementService extends INetworkManagementService.Stub pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); } - public void createNetwork(int netId, String iface) { + @Override + public void createNetwork(int netId) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("network", "create", netId, iface); + mConnector.execute("network", "create", netId); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } } + @Override public void removeNetwork(int netId) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); @@ -2049,4 +1958,111 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw e.rethrowAsParcelableException(); } } + + @Override + public void addInterfaceToNetwork(String iface, int netId) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + try { + mConnector.execute("network", "addiface", netId, iface); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void removeInterfaceFromNetwork(String iface, int netId) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + try { + mConnector.execute("network", "removeiface", netId, iface); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) { + modifyLegacyRouteForNetId(netId, routeInfo, uid, ADD); + } + + @Override + public void removeLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) { + modifyLegacyRouteForNetId(netId, routeInfo, uid, REMOVE); + } + + private void modifyLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid, String action) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + final Command cmd = new Command("network", "legacy", uid, "route", action, netId); + + // create quadlet: dest-ip-addr prefixlength gateway-ip-addr iface + final LinkAddress la = routeInfo.getDestination(); + cmd.appendArg(la.getAddress().getHostAddress()); + cmd.appendArg(la.getNetworkPrefixLength()); + cmd.appendArg(routeInfo.getGateway().getHostAddress()); + cmd.appendArg(routeInfo.getInterface()); + + try { + mConnector.execute(cmd); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void setDefaultNetId(int netId) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + try { + mConnector.execute("network", "default", "set", netId); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void clearDefaultNetId() { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + try { + mConnector.execute("network", "default", "clear"); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void setPermission(boolean internal, boolean changeNetState, int[] uids) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + final Command cmd = new Command("network", "permission", "user", "set"); + if (internal) cmd.appendArg(CONNECTIVITY_INTERNAL); + if (changeNetState) cmd.appendArg(CHANGE_NETWORK_STATE); + for (int i=0; i<uids.length; i++) { + cmd.appendArg(uids[i]); + } + + try { + mConnector.execute(cmd); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void clearPermission(int[] uids) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + final Command cmd = new Command("network", "permission", "user", "clear"); + for (int i=0; i<uids.length; i++) { + cmd.appendArg(uids[i]); + } + + try { + mConnector.execute(cmd); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d6457c3..e7e2f4d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9087,7 +9087,7 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public boolean convertToTranslucent(IBinder token) { + public boolean convertToTranslucent(IBinder token, ActivityOptions options) { final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -9096,7 +9096,7 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } if (r.changeWindowTranslucency(false)) { - r.task.stack.convertToTranslucent(r); + r.task.stack.convertToTranslucent(r, options); mWindowManager.setAppFullscreen(token, false); mStackSupervisor.ensureActivitiesVisibleLocked(null, 0); return true; @@ -9109,6 +9109,24 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public ActivityOptions getActivityOptions(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (this) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + final ActivityOptions activityOptions = r.pendingOptions; + r.pendingOptions = null; + return activityOptions; + } + return null; + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public void setImmersive(IBinder token, boolean immersive) { synchronized(this) { final ActivityRecord r = ActivityRecord.isInStackLocked(token); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 8391f79..9582ac7 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -347,7 +347,7 @@ final class ActivityRecord { ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified, ActivityStackSupervisor supervisor, - ActivityContainer container) { + ActivityContainer container, Bundle options) { service = _service; appToken = new Token(this); info = aInfo; @@ -378,6 +378,9 @@ final class ActivityRecord { hasBeenLaunched = false; mStackSupervisor = supervisor; mInitialActivityContainer = container; + if (options != null) { + pendingOptions = new ActivityOptions(options); + } // This starts out true, since the initial state of an activity // is that we have everything, and we shouldn't never consider it @@ -711,6 +714,9 @@ final class ActivityRecord { + pendingOptions.getThumbnail().getHeight())); } break; + default: + Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType); + break; } pendingOptions = null; } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 2e439ae..c1e5e5b 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -205,6 +205,9 @@ final class ActivityStack { ActivityRecord mTranslucentActivityWaiting = null; ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<ActivityRecord>(); + // Options passed from the caller of the convertToTranslucent to the activity that will + // appear below it. + ActivityOptions mReturningActivityOptions = null; /** * Set when we know we are going to be calling updateConfiguration() @@ -1218,6 +1221,7 @@ final class ActivityStack { TAG, "Making visible and scheduling visibility: " + r); try { if (mTranslucentActivityWaiting != null) { + r.updateOptionsLocked(mReturningActivityOptions); mUndrawnActivitiesBelowTopTranslucent.add(r); } setVisibile(r, true); @@ -1295,9 +1299,10 @@ final class ActivityStack { } } - void convertToTranslucent(ActivityRecord r) { + void convertToTranslucent(ActivityRecord r, ActivityOptions options) { mTranslucentActivityWaiting = r; mUndrawnActivitiesBelowTopTranslucent.clear(); + mReturningActivityOptions = options; mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT); } @@ -1471,8 +1476,6 @@ final class ActivityStack { next.sleeping = false; mStackSupervisor.mWaitingVisibleActivities.remove(next); - next.updateOptionsLocked(options); - if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next); // If we are currently pausing an activity, then don't do anything @@ -1916,7 +1919,6 @@ final class ActivityStack { : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition); mNoAnimActivities.remove(r); } - r.updateOptionsLocked(options); mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId, @@ -1967,13 +1969,14 @@ final class ActivityStack { (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId, r.info.configChanges); ActivityOptions.abort(options); + options = null; } if (VALIDATE_TOKENS) { validateAppTokensLocked(); } if (doResume) { - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeTopActivitiesLocked(this, r, options); } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 6f62a03..5d744e6 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1022,14 +1022,12 @@ public final class ActivityStackSupervisor implements DisplayListener { } app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP); - Bundle options = (r.pendingOptions == null) ? null : r.pendingOptions.toBundle(); - r.clearOptionsLocked(); app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), r.compat, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, - mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop, - options); + mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop + ); if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) { // This may be a heavy-weight process! Note that the package @@ -1325,7 +1323,7 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, - requestCode, componentSpecified, this, container); + requestCode, componentSpecified, this, container, options); if (outActivity != null) { outActivity[0] = r; } diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index a15d678..3884ab0 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -25,11 +25,12 @@ import android.net.IConnectivityManager; import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; -import android.net.NetworkStateTracker; +import android.net.NetworkAgent; import android.net.NetworkUtils; import android.net.RouteInfo; import android.os.Handler; import android.os.Message; +import android.os.Messenger; import android.os.INetworkManagementService; import android.os.RemoteException; import android.util.Slog; @@ -45,15 +46,18 @@ public class Nat464Xlat extends BaseNetworkObserver { private Context mContext; private INetworkManagementService mNMService; private IConnectivityManager mConnService; - private NetworkStateTracker mTracker; - private Handler mHandler; - // Whether we started clatd and expect it to be running. private boolean mIsStarted; // Whether the clatd interface exists (i.e., clatd is running). private boolean mIsRunning; // The LinkProperties of the clat interface. private LinkProperties mLP; + // Current LinkProperties of the network. Includes mLP as a stacked link when clat is active. + private LinkProperties mBaseLP; + // ConnectivityService Handler for LinkProperties updates. + private Handler mHandler; + // Marker to connote which network we're augmenting. + private Messenger mNetworkMessenger; // This must match the interface name in clatd.conf. private static final String CLAT_INTERFACE_NAME = "clat4"; @@ -73,14 +77,13 @@ public class Nat464Xlat extends BaseNetworkObserver { } /** - * Determines whether an interface requires clat. - * @param netType the network type (one of the - * android.net.ConnectivityManager.TYPE_* constants) - * @param tracker the NetworkStateTracker corresponding to the network type. - * @return true if the interface requires clat, false otherwise. + * Determines whether a network requires clat. + * @param network the NetworkAgentInfo corresponding to the network. + * @return true if the network requires clat, false otherwise. */ - public boolean requiresClat(int netType, NetworkStateTracker tracker) { - LinkProperties lp = tracker.getLinkProperties(); + public boolean requiresClat(NetworkAgentInfo network) { + int netType = network.networkInfo.getType(); + LinkProperties lp = network.linkProperties; // Only support clat on mobile for now. Slog.d(TAG, "requiresClat: netType=" + netType + ", hasIPv4Address=" + lp.hasIPv4Address()); @@ -95,13 +98,18 @@ public class Nat464Xlat extends BaseNetworkObserver { * Starts the clat daemon. * @param lp The link properties of the interface to start clatd on. */ - public void startClat(NetworkStateTracker tracker) { + public void startClat(NetworkAgentInfo network) { + if (mNetworkMessenger != null && mNetworkMessenger != network.messenger) { + Slog.e(TAG, "startClat: too many networks requesting clat"); + return; + } + mNetworkMessenger = network.messenger; + LinkProperties lp = network.linkProperties; + mBaseLP = new LinkProperties(lp); if (mIsStarted) { Slog.e(TAG, "startClat: already started"); return; } - mTracker = tracker; - LinkProperties lp = mTracker.getLinkProperties(); String iface = lp.getInterfaceName(); Slog.i(TAG, "Starting clatd on " + iface + ", lp=" + lp); try { @@ -125,7 +133,8 @@ public class Nat464Xlat extends BaseNetworkObserver { } mIsStarted = false; mIsRunning = false; - mTracker = null; + mNetworkMessenger = null; + mBaseLP = null; mLP.clear(); } else { Slog.e(TAG, "stopClat: already stopped"); @@ -140,6 +149,14 @@ public class Nat464Xlat extends BaseNetworkObserver { return mIsRunning; } + private void updateConnectivityService() { + Message msg = mHandler.obtainMessage( + NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, mBaseLP); + msg.replyTo = mNetworkMessenger; + Slog.i(TAG, "sending message to ConnectivityService: " + msg); + msg.sendToTarget(); + } + @Override public void interfaceAdded(String iface) { if (iface.equals(CLAT_INTERFACE_NAME)) { @@ -165,19 +182,12 @@ public class Nat464Xlat extends BaseNetworkObserver { clatAddress.getAddress(), iface); mLP.addRoute(ipv4Default); mLP.addLinkAddress(clatAddress); - mTracker.addStackedLink(mLP); - Slog.i(TAG, "Adding stacked link. tracker LP: " + - mTracker.getLinkProperties()); + mBaseLP.addStackedLink(mLP); + Slog.i(TAG, "Adding stacked link. tracker LP: " + mBaseLP); + updateConnectivityService(); } catch(RemoteException e) { Slog.e(TAG, "Error getting link properties: " + e); } - - // Inform ConnectivityService that things have changed. - Message msg = mHandler.obtainMessage( - NetworkStateTracker.EVENT_CONFIGURATION_CHANGED, - mTracker.getNetworkInfo()); - Slog.i(TAG, "sending message to ConnectivityService: " + msg); - msg.sendToTarget(); } } @@ -192,8 +202,9 @@ public class Nat464Xlat extends BaseNetworkObserver { Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME + " removed, mIsRunning = " + mIsRunning + " -> false"); mIsRunning = false; - mTracker.removeStackedLink(mLP); + mBaseLP.removeStackedLink(mLP); mLP.clear(); + updateConnectivityService(); Slog.i(TAG, "mLP = " + mLP); } } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java new file mode 100644 index 0000000..8102591 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 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.connectivity; + +import android.content.Context; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.NetworkRequest; +import android.os.Handler; +import android.os.Messenger; +import android.util.SparseArray; + +import com.android.internal.util.AsyncChannel; +import com.android.server.connectivity.NetworkMonitor; + +import java.util.ArrayList; + +/** + * A bag class used by ConnectivityService for holding a collection of most recent + * information published by a particular NetworkAgent as well as the + * AsyncChannel/messenger for reaching that NetworkAgent and lists of NetworkRequests + * interested in using it. + */ +public class NetworkAgentInfo { + public NetworkInfo networkInfo; + public final Network network; + public LinkProperties linkProperties; + public NetworkCapabilities networkCapabilities; + public int currentScore; + public final NetworkMonitor networkMonitor; + + + // The list of NetworkRequests being satisfied by this Network. + public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>(); + public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>(); + + public final Messenger messenger; + public final AsyncChannel asyncChannel; + + public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, int netId, NetworkInfo info, + LinkProperties lp, NetworkCapabilities nc, int score, Context context, + Handler handler) { + this.messenger = messenger; + asyncChannel = ac; + network = new Network(netId); + networkInfo = info; + linkProperties = lp; + networkCapabilities = nc; + currentScore = score; + networkMonitor = new NetworkMonitor(context, handler, this); + } + + public String toString() { + return "NetworkAgentInfo{ ni{" + networkInfo + "} network{" + + network + "} lp{" + + linkProperties + "} nc{" + + networkCapabilities + "} Score{" + currentScore + "} }"; + } + + public String name() { + return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" + + networkInfo.getSubtypeName() + ")]"; + } +} diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java new file mode 100644 index 0000000..47789b1 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2014 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.connectivity; + +import android.content.Context; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.os.Handler; +import android.os.Message; +import android.os.SystemProperties; +import android.provider.Settings; + +import com.android.internal.util.Protocol; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; +import com.android.server.connectivity.NetworkAgentInfo; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URL; + +/** + * {@hide} + */ +public class NetworkMonitor extends StateMachine { + private static final boolean DBG = true; + private static final String TAG = "NetworkMonitor"; + private static final String DEFAULT_SERVER = "clients3.google.com"; + private static final int SOCKET_TIMEOUT_MS = 10000; + + private static final int BASE = Protocol.BASE_NETWORK_MONITOR; + + /** + * Inform NetworkMonitor that their network is connected. + * Initiates Network Validation. + */ + public static final int CMD_NETWORK_CONNECTED = BASE + 1; + + /** + * Inform ConnectivityService that the network is validated. + * obj = NetworkAgentInfo + */ + public static final int EVENT_NETWORK_VALIDATED = BASE + 2; + + /** + * Inform NetworkMonitor to linger a network. The Monitor should + * start a timer and/or start watching for zero live connections while + * moving towards LINGER_COMPLETE. After the Linger period expires + * (or other events mark the end of the linger state) the LINGER_COMPLETE + * event should be sent and the network will be shut down. If a + * CMD_NETWORK_CONNECTED happens before the LINGER completes + * it indicates further desire to keep the network alive and so + * the LINGER is aborted. + */ + public static final int CMD_NETWORK_LINGER = BASE + 3; + + /** + * Message to self indicating linger delay has expired. + * arg1 = Token to ignore old messages. + */ + private static final int CMD_LINGER_EXPIRED = BASE + 4; + + /** + * Inform ConnectivityService that the network LINGER period has + * expired. + * obj = NetworkAgentInfo + */ + public static final int EVENT_NETWORK_LINGER_COMPLETE = BASE + 5; + + /** + * Message to self indicating it's time to check for a captive portal again. + * TODO - Remove this once broadcast intents are used to communicate with + * apps to log into captive portals. + * arg1 = Token to ignore old messages. + */ + private static final int CMD_CAPTIVE_PORTAL_REEVALUATE = BASE + 6; + + /** + * Message to self indicating it's time to evaluate a network's connectivity. + * arg1 = Token to ignore old messages. + */ + private static final int CMD_REEVALUATE = BASE + 7; + + /** + * Message to self indicating network evaluation is complete. + * arg1 = Token to ignore old messages. + * arg2 = HTTP response code of network evaluation. + */ + private static final int EVENT_REEVALUATION_COMPLETE = BASE + 8; + + /** + * Inform NetworkMonitor that the network has disconnected. + */ + public static final int CMD_NETWORK_DISCONNECTED = BASE + 9; + + /** + * Force evaluation even if it has succeeded in the past. + */ + public static final int CMD_FORCE_REEVALUATION = BASE + 10; + + private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger"; + // Default to 30s linger time-out. + private static final int DEFAULT_LINGER_DELAY_MS = 30000; + private final int mLingerDelayMs; + private int mLingerToken = 0; + + private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 5000; + private int mCaptivePortalReevaluateToken = 0; + + // Negative values disable reevaluation. + private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay"; + // Default to 5s reevaluation delay. + private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000; + private final int mReevaluateDelayMs; + private int mReevaluateToken = 0; + + private final Context mContext; + private final Handler mConnectivityServiceHandler; + private final NetworkAgentInfo mNetworkAgentInfo; + + private String mServer; + private boolean mIsCaptivePortalCheckEnabled = false; + + private State mDefaultState = new DefaultState(); + private State mOfflineState = new OfflineState(); + private State mValidatedState = new ValidatedState(); + private State mEvaluatingState = new EvaluatingState(); + private State mCaptivePortalState = new CaptivePortalState(); + private State mLingeringState = new LingeringState(); + + public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo) { + // Add suffix indicating which NetworkMonitor we're talking about. + super(TAG + networkAgentInfo.name()); + + mContext = context; + mConnectivityServiceHandler = handler; + mNetworkAgentInfo = networkAgentInfo; + + addState(mDefaultState); + addState(mOfflineState, mDefaultState); + addState(mValidatedState, mDefaultState); + addState(mEvaluatingState, mDefaultState); + addState(mCaptivePortalState, mDefaultState); + addState(mLingeringState, mDefaultState); + setInitialState(mOfflineState); + + mServer = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.CAPTIVE_PORTAL_SERVER); + if (mServer == null) mServer = DEFAULT_SERVER; + + mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); + mReevaluateDelayMs = SystemProperties.getInt(REEVALUATE_DELAY_PROPERTY, + DEFAULT_REEVALUATE_DELAY_MS); + + // TODO: Enable this when we're ready. + // mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(), + // Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1; + + start(); + } + + private class DefaultState extends State { + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString()); + switch (message.what) { + case CMD_NETWORK_LINGER: + if (DBG) log("Lingering"); + transitionTo(mLingeringState); + break; + case CMD_NETWORK_CONNECTED: + if (DBG) log("Connected"); + transitionTo(mEvaluatingState); + break; + case CMD_NETWORK_DISCONNECTED: + if (DBG) log("Disconnected"); + transitionTo(mOfflineState); + break; + case CMD_FORCE_REEVALUATION: + if (DBG) log("Forcing reevaluation"); + transitionTo(mEvaluatingState); + break; + default: + break; + } + return HANDLED; + } + } + + private class OfflineState extends State { + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString()); + return NOT_HANDLED; + } + } + + private class ValidatedState extends State { + @Override + public void enter() { + if (DBG) log("Validated"); + mConnectivityServiceHandler.sendMessage( + obtainMessage(EVENT_NETWORK_VALIDATED, mNetworkAgentInfo)); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString()); + switch (message.what) { + case CMD_NETWORK_CONNECTED: + transitionTo(mValidatedState); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + private class EvaluatingState extends State { + private class EvaluateInternetConnectivity extends Thread { + private int mToken; + EvaluateInternetConnectivity(int token) { + mToken = token; + } + public void run() { + sendMessage(EVENT_REEVALUATION_COMPLETE, mToken, isCaptivePortal()); + } + } + + @Override + public void enter() { + sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString()); + switch (message.what) { + case CMD_REEVALUATE: + if (message.arg1 != mReevaluateToken) + break; + // If network provides no internet connectivity adjust evaluation. + if (mNetworkAgentInfo.networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + // TODO: Try to verify something works. Do all gateways respond to pings? + transitionTo(mValidatedState); + } + // Kick off a thread to perform internet connectivity evaluation. + Thread thread = new EvaluateInternetConnectivity(mReevaluateToken); + thread.run(); + break; + case EVENT_REEVALUATION_COMPLETE: + if (message.arg1 != mReevaluateToken) + break; + int httpResponseCode = message.arg2; + if (httpResponseCode == 204) { + transitionTo(mValidatedState); + } else if (httpResponseCode >= 200 && httpResponseCode <= 399) { + transitionTo(mCaptivePortalState); + } else { + if (mReevaluateDelayMs >= 0) { + Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); + sendMessageDelayed(msg, mReevaluateDelayMs); + } + } + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + // TODO: Until we add an intent from the app handling captive portal + // login we'll just re-evaluate after a delay. + private class CaptivePortalState extends State { + @Override + public void enter() { + Message message = obtainMessage(CMD_CAPTIVE_PORTAL_REEVALUATE, + ++mCaptivePortalReevaluateToken, 0); + sendMessageDelayed(message, CAPTIVE_PORTAL_REEVALUATE_DELAY_MS); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString()); + switch (message.what) { + case CMD_CAPTIVE_PORTAL_REEVALUATE: + if (message.arg1 != mCaptivePortalReevaluateToken) + break; + transitionTo(mEvaluatingState); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + private class LingeringState extends State { + @Override + public void enter() { + Message message = obtainMessage(CMD_LINGER_EXPIRED, ++mLingerToken, 0); + sendMessageDelayed(message, mLingerDelayMs); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString()); + switch (message.what) { + case CMD_NETWORK_CONNECTED: + // Go straight to active as we've already evaluated. + transitionTo(mValidatedState); + break; + case CMD_LINGER_EXPIRED: + if (message.arg1 != mLingerToken) + break; + mConnectivityServiceHandler.sendMessage( + obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo)); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + /** + * Do a URL fetch on a known server to see if we get the data we expect. + * Returns HTTP response code. + */ + private int isCaptivePortal() { + if (!mIsCaptivePortalCheckEnabled) return 204; + + String urlString = "http://" + mServer + "/generate_204"; + if (DBG) log("Checking " + urlString); + HttpURLConnection urlConnection = null; + Socket socket = null; + int httpResponseCode = 500; + try { + URL url = new URL(urlString); + if (false) { + // TODO: Need to add URLConnection.setNetwork() before we can enable. + urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setInstanceFollowRedirects(false); + urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); + urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); + urlConnection.setUseCaches(false); + urlConnection.getInputStream(); + httpResponseCode = urlConnection.getResponseCode(); + } else { + socket = new Socket(); + // TODO: setNetworkForSocket(socket, mNetworkAgentInfo.network.netId); + InetSocketAddress address = new InetSocketAddress(url.getHost(), 80); + // TODO: address = new InetSocketAddress( + // getByNameOnNetwork(mNetworkAgentInfo.network, url.getHost()), 80); + socket.connect(address); + BufferedReader reader = new BufferedReader( + new InputStreamReader(socket.getInputStream())); + OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream()); + writer.write("GET " + url.getFile() + " HTTP/1.1\r\n\n"); + writer.flush(); + String response = reader.readLine(); + if (response.startsWith("HTTP/1.1 ")) { + httpResponseCode = Integer.parseInt(response.substring(9, 12)); + } + } + if (DBG) log("isCaptivePortal: ret=" + httpResponseCode); + } catch (IOException e) { + if (DBG) log("Probably not a portal: exception " + e); + } finally { + if (urlConnection != null) { + urlConnection.disconnect(); + } + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + // Ignore + } + } + } + return httpResponseCode; + } +} diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index abe362a..92b5f52 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1325,7 +1325,7 @@ public class Tethering extends BaseNetworkObserver { } else { LinkProperties linkProperties = null; try { - linkProperties = mConnService.getLinkProperties(upType); + linkProperties = mConnService.getLinkPropertiesForType(upType); } catch (RemoteException e) { } if (linkProperties != null) { // Find the interface with the default IPv4 route. It may be the diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 5fa1584..4740cae 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -48,8 +48,7 @@ class AutomaticBrightnessController { private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false; // If true, enables the use of the screen auto-brightness adjustment setting. - private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = - PowerManager.useScreenAutoBrightnessAdjustmentFeature(); + private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true; // The maximum range of gamma adjustment possible using the screen // auto-brightness adjustment setting. @@ -202,7 +201,7 @@ class AutomaticBrightnessController { public void updatePowerState(DisplayManagerInternal.DisplayPowerRequest request) { if (setScreenAutoBrightnessAdjustment(request.screenAutoBrightnessAdjustment) - || setLightSensorEnabled(request.wantLightSensorEnabled())) { + | setLightSensorEnabled(request.wantLightSensorEnabled())) { updateAutoBrightness(false /*sendUpdate*/); } } diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index d074565..007032e 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -16,8 +16,13 @@ package com.android.server.notification; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.Uri; import android.os.Handler; import android.os.IBinder; @@ -30,6 +35,7 @@ import android.service.notification.ConditionProviderService; import android.service.notification.IConditionListener; import android.service.notification.IConditionProvider; import android.service.notification.ZenModeConfig; +import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -39,6 +45,7 @@ import com.android.internal.R; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; public class ConditionProviders extends ManagedServices { private static final Condition[] NO_CONDITIONS = new Condition[0]; @@ -47,6 +54,7 @@ public class ConditionProviders extends ManagedServices { private final ArrayMap<IBinder, IConditionListener> mListeners = new ArrayMap<IBinder, IConditionListener>(); private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>(); + private final CountdownConditionHelper mCountdownHelper = new CountdownConditionHelper(); public ConditionProviders(Context context, Handler handler, UserProfiles userProfiles, ZenModeHelper zenModeHelper) { @@ -267,6 +275,7 @@ public class ConditionProviders extends ManagedServices { } } } + mCountdownHelper.setZenModeCondition(conditionId); } private void subscribeLocked(ConditionRecord r) { @@ -434,6 +443,68 @@ public class ConditionProviders extends ManagedServices { mZenModeHelper.setConfig(config); } + private final class CountdownConditionHelper extends BroadcastReceiver { + private static final String ACTION = "CountdownConditionHelper"; + private static final int REQUEST_CODE = 100; + private static final String EXTRA_TIME = "time"; + + private long mCurrent; + + public CountdownConditionHelper() { + mContext.registerReceiver(this, new IntentFilter(ACTION)); + } + + public void setZenModeCondition(Uri conditionId) { + final long time = tryParseCondition(conditionId); + final AlarmManager alarms = (AlarmManager) + mContext.getSystemService(Context.ALARM_SERVICE); + final Intent intent = new Intent(ACTION).putExtra(EXTRA_TIME, time) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE, + intent, PendingIntent.FLAG_UPDATE_CURRENT); + alarms.cancel(pendingIntent); + mCurrent = time; + if (time > 0) { + final long now = System.currentTimeMillis(); + final CharSequence span = + DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS); + Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future (%s), now=%s", + ACTION, ts(time), time - now, span, ts(now))); + alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent); + } + } + + private String ts(long time) { + return new Date(time) + " (" + time + ")"; + } + + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION.equals(intent.getAction())) { + final long time = intent.getLongExtra(EXTRA_TIME, 0); + Slog.d(TAG, "Countdown condition fired. time=" + time + " mCurrent=" + mCurrent); + if (time > 0 && time == mCurrent) { + // countdown condition is still the manual condition, leave zen + mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF); + ConditionProviders.this.setZenModeCondition(null); + } + } + } + + private long tryParseCondition(Uri conditionId) { + // condition://android/countdown/1399917958951 + if (!Condition.isValidId(conditionId, "android")) return 0; + if (conditionId.getPathSegments().size() != 2 + || !"countdown".equals(conditionId.getPathSegments().get(0))) return 0; + try { + return Long.parseLong(conditionId.getPathSegments().get(1)); + } catch (RuntimeException e) { + Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e); + return 0; + } + } + } + private class ZenModeHelperCallback extends ZenModeHelper.Callback { @Override void onConfigChanged() { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index d34b09c..584145f 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -66,7 +66,7 @@ abstract public class ManagedServices { private static final String ENABLED_SERVICES_SEPARATOR = ":"; - private final Context mContext; + protected final Context mContext; protected final Object mMutex; private final UserProfiles mUserProfiles; private final SettingsObserver mSettingsObserver; diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java index c8b1ba0..49293d3 100644 --- a/services/core/java/com/android/server/notification/NotificationComparator.java +++ b/services/core/java/com/android/server/notification/NotificationComparator.java @@ -26,6 +26,9 @@ public class NotificationComparator @Override public int compare(NotificationManagerService.NotificationRecord lhs, NotificationManagerService.NotificationRecord rhs) { + if (lhs.isRecentlyIntrusive() != rhs.isRecentlyIntrusive()) { + return lhs.isRecentlyIntrusive() ? -1 : 1; + } final int leftScore = lhs.sbn.getScore(); final int rightScore = rhs.sbn.getScore(); if (leftScore != rightScore) { diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java new file mode 100644 index 0000000..125158f --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java @@ -0,0 +1,64 @@ +/* +* Copyright (C) 2014 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.notification; + +import android.app.Notification; +import android.content.Context; +import android.util.Slog; + +import com.android.internal.R; +import com.android.server.notification.NotificationManagerService.NotificationRecord; + +/** + * This {@link com.android.server.notification.NotificationSignalExtractor} noticies noisy + * notifications and marks them to get a temporary ranking bump. + */ +public class NotificationIntrusivenessExtractor implements NotificationSignalExtractor { + private static final String TAG = "NotificationNoiseExtractor"; + private static final boolean DBG = false; + + /** Length of time (in milliseconds) that an intrusive or noisy notification will stay at + the top of the ranking order, before it falls back to its natural position. */ + private static final long HANG_TIME_MS = 10000; + + public void initialize(Context ctx) { + if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); + } + + public RankingFuture process(NotificationRecord record) { + if (record == null || record.getNotification() == null) { + if (DBG) Slog.d(TAG, "skipping empty notification"); + return null; + } + + final Notification notification = record.getNotification(); + if ((notification.defaults & Notification.DEFAULT_VIBRATE) != 0 || + notification.vibrate != null || + (notification.defaults & Notification.DEFAULT_SOUND) != 0 || + notification.sound != null || + notification.fullScreenIntent != null) { + record.setRecentlyIntusive(true); + } + + return new RankingFuture(record, HANG_TIME_MS) { + @Override + public void work() { + mRecord.setRecentlyIntusive(false); + } + }; + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7a4f951..2f1d291 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -457,7 +457,11 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification sbn; SingleNotificationStats stats; IBinder statusBarKey; + + // These members are used by NotificationSignalExtractors + // to communicate with the ranking module. private float mContactAffinity; + private boolean mRecentlyIntrusive; NotificationRecord(StatusBarNotification sbn) { @@ -548,6 +552,14 @@ public class NotificationManagerService extends SystemService { public float getContactAffinity() { return mContactAffinity; } + + public boolean isRecentlyIntrusive() { + return mRecentlyIntrusive; + } + + public void setRecentlyIntusive(boolean recentlyIntrusive) { + mRecentlyIntrusive = recentlyIntrusive; + } } private static final class ToastRecord @@ -1401,7 +1413,12 @@ public class NotificationManagerService extends SystemService { @Override public void setZenModeCondition(Uri conditionId) { enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); - mConditionProviders.setZenModeCondition(conditionId); + final long identity = Binder.clearCallingIdentity(); + try { + mConditionProviders.setZenModeCondition(conditionId); + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override diff --git a/services/core/java/com/android/server/notification/RankingFuture.java b/services/core/java/com/android/server/notification/RankingFuture.java index 33aad8d..d711d02 100644 --- a/services/core/java/com/android/server/notification/RankingFuture.java +++ b/services/core/java/com/android/server/notification/RankingFuture.java @@ -64,8 +64,8 @@ public abstract class RankingFuture @Override public int compareTo(Delayed another) { - return Long.compare(getDelay(TimeUnit.MICROSECONDS), - another.getDelay(TimeUnit.MICROSECONDS)); + return Long.compare(getDelay(TimeUnit.MILLISECONDS), + another.getDelay(TimeUnit.MILLISECONDS)); } @Override diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index bd28e04..5e3325c 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -17,11 +17,9 @@ package com.android.server.pm; import android.app.AppGlobals; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ILauncherApps; import android.content.pm.IOnAppsChangedListener; @@ -157,10 +155,26 @@ public class LauncherAppsService extends ILauncherApps.Stub { } } + /** + * Checks if the user is enabled. + */ + private boolean isUserEnabled(UserHandle user) { + long ident = Binder.clearCallingIdentity(); + try { + UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier()); + return targetUserInfo != null && targetUserInfo.isEnabled(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + @Override public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user) throws RemoteException { ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user); + if (!isUserEnabled(user)) { + return new ArrayList<ResolveInfo>(); + } final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); @@ -179,6 +193,9 @@ public class LauncherAppsService extends ILauncherApps.Stub { public ResolveInfo resolveActivity(Intent intent, UserHandle user) throws RemoteException { ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user); + if (!isUserEnabled(user)) { + return null; + } long ident = Binder.clearCallingIdentity(); try { @@ -193,6 +210,10 @@ public class LauncherAppsService extends ILauncherApps.Stub { public boolean isPackageEnabled(String packageName, UserHandle user) throws RemoteException { ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user); + if (!isUserEnabled(user)) { + return false; + } + long ident = Binder.clearCallingIdentity(); try { IPackageManager pm = AppGlobals.getPackageManager(); @@ -207,6 +228,10 @@ public class LauncherAppsService extends ILauncherApps.Stub { public boolean isActivityEnabled(ComponentName component, UserHandle user) throws RemoteException { ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user); + if (!isUserEnabled(user)) { + return false; + } + long ident = Binder.clearCallingIdentity(); try { IPackageManager pm = AppGlobals.getPackageManager(); @@ -221,6 +246,9 @@ public class LauncherAppsService extends ILauncherApps.Stub { public void startActivityAsUser(ComponentName component, Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException { ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user); + if (!isUserEnabled(user)) { + throw new IllegalStateException("Cannot start activity for disabled profile " + user); + } Intent launchIntent = new Intent(Intent.ACTION_MAIN); launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); @@ -228,7 +256,6 @@ public class LauncherAppsService extends ILauncherApps.Stub { launchIntent.setSourceBounds(sourceBounds); launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - final int callingUserId = UserHandle.getCallingUserId(); long ident = Binder.clearCallingIdentity(); try { mContext.startActivityAsUser(launchIntent, opts, user); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index a5eccb3..03941c6 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -538,6 +538,9 @@ public final class PowerManagerService extends com.android.server.SystemService resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.SCREEN_BRIGHTNESS_MODE), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ), + false, mSettingsObserver, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.LOW_POWER_MODE), false, mSettingsObserver, UserHandle.USER_ALL); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 05f9947..8ad7fff 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -648,10 +648,10 @@ public final class TvInputManagerService extends SystemService { // Create a log entry and fill it later. ContentValues values = new ContentValues(); - values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS, + values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, currentTime); - values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, 0); - values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId); + values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0); + values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); sessionState.mLogUri = mContentResolver.insert( TvContract.WatchedPrograms.CONTENT_URI, values); @@ -944,31 +944,32 @@ public final class TvInputManagerService extends SystemService { private void onOpenEntry(Uri uri, long channelId, long watchStarttime) { String[] projection = { - TvContract.Programs.TITLE, - TvContract.Programs.START_TIME_UTC_MILLIS, - TvContract.Programs.END_TIME_UTC_MILLIS, - TvContract.Programs.DESCRIPTION + TvContract.Programs.COLUMN_TITLE, + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, + TvContract.Programs.COLUMN_DESCRIPTION }; - String selection = TvContract.Programs.CHANNEL_ID + "=? AND " - + TvContract.Programs.START_TIME_UTC_MILLIS + "<=? AND " - + TvContract.Programs.END_TIME_UTC_MILLIS + ">?"; + String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND " + + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND " + + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS + ">?"; String[] selectionArgs = { String.valueOf(channelId), String.valueOf(watchStarttime), String.valueOf(watchStarttime) }; - String sortOrder = TvContract.Programs.START_TIME_UTC_MILLIS + " ASC"; + String sortOrder = TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + " ASC"; Cursor cursor = null; try { cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection, selection, selectionArgs, sortOrder); if (cursor != null && cursor.moveToNext()) { ContentValues values = new ContentValues(); - values.put(TvContract.WatchedPrograms.TITLE, cursor.getString(0)); - values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, cursor.getLong(1)); + values.put(TvContract.WatchedPrograms.COLUMN_TITLE, cursor.getString(0)); + values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, + cursor.getLong(1)); long endTime = cursor.getLong(2); - values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime); - values.put(TvContract.WatchedPrograms.DESCRIPTION, cursor.getString(3)); + values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); + values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, cursor.getString(3)); mContentResolver.update(uri, values, null, null); // Schedule an update when the current program ends. @@ -988,12 +989,12 @@ public final class TvInputManagerService extends SystemService { private void onUpdateEntry(Uri uri, long channelId, long time) { String[] projection = { - TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS, - TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, - TvContract.WatchedPrograms.TITLE, - TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, - TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, - TvContract.WatchedPrograms.DESCRIPTION + TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, + TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, + TvContract.WatchedPrograms.COLUMN_TITLE, + TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, + TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, + TvContract.WatchedPrograms.COLUMN_DESCRIPTION }; Cursor cursor = null; try { @@ -1014,14 +1015,14 @@ public final class TvInputManagerService extends SystemService { // The current program has just ended. Create a (complete) log entry off the // current entry. ContentValues values = new ContentValues(); - values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS, + values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, watchStartTime); - values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, time); - values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId); - values.put(TvContract.WatchedPrograms.TITLE, title); - values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, startTime); - values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime); - values.put(TvContract.WatchedPrograms.DESCRIPTION, description); + values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, time); + values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); + values.put(TvContract.WatchedPrograms.COLUMN_TITLE, title); + values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime); + values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); + values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, description); mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); } } finally { @@ -1035,7 +1036,7 @@ public final class TvInputManagerService extends SystemService { private void onCloseEntry(Uri uri, long watchEndTime) { ContentValues values = new ContentValues(); - values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, watchEndTime); + values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, watchEndTime); mContentResolver.update(uri, values, null, null); } } diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java index f7bec6e..39f228f 100644 --- a/services/print/java/com/android/server/print/PrintManagerService.java +++ b/services/print/java/com/android/server/print/PrintManagerService.java @@ -28,6 +28,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; @@ -35,6 +36,7 @@ import android.os.Bundle; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.print.IPrintDocumentAdapter; import android.print.IPrintJobStateChangeListener; import android.print.IPrintManager; @@ -91,18 +93,23 @@ public final class PrintManagerService extends SystemService { private static final String EXTRA_PRINT_SERVICE_COMPONENT_NAME = "EXTRA_PRINT_SERVICE_COMPONENT_NAME"; + private static final int BACKGROUND_USER_ID = -10; + private final Object mLock = new Object(); private final Context mContext; + private final UserManager mUserManager; + private final SparseArray<UserState> mUserStates = new SparseArray<UserState>(); private int mCurrentUserId = UserHandle.USER_OWNER; PrintManagerImpl(Context context) { mContext = context; + mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); registerContentObservers(); - registerBoradcastReceivers(); + registerBroadcastReceivers(); } public void systemRunning() { @@ -125,11 +132,17 @@ public final class PrintManagerService extends SystemService { @Override public Bundle print(String printJobName, IPrintDocumentAdapter adapter, PrintAttributes attributes, String packageName, int appId, int userId) { - final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId); final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); - String resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName); + final int resolvedAppId; final UserState userState; + final String resolvedPackageName; synchronized (mLock) { + // Only the current group members can start new print jobs. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return null; + } + resolvedAppId = resolveCallingAppEnforcingPermissions(appId); + resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName); userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -143,10 +156,15 @@ public final class PrintManagerService extends SystemService { @Override public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) { - final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId); final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final int resolvedAppId; final UserState userState; synchronized (mLock) { + // Only the current group members can query for state of print jobs. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return null; + } + resolvedAppId = resolveCallingAppEnforcingPermissions(appId); userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -159,10 +177,15 @@ public final class PrintManagerService extends SystemService { @Override public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) { - final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId); final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final int resolvedAppId; final UserState userState; synchronized (mLock) { + // Only the current group members can query for state of a print job. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return null; + } + resolvedAppId = resolveCallingAppEnforcingPermissions(appId); userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -175,10 +198,15 @@ public final class PrintManagerService extends SystemService { @Override public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) { - final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId); final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final int resolvedAppId; final UserState userState; synchronized (mLock) { + // Only the current group members can cancel a print job. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } + resolvedAppId = resolveCallingAppEnforcingPermissions(appId); userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -191,10 +219,15 @@ public final class PrintManagerService extends SystemService { @Override public void restartPrintJob(PrintJobId printJobId, int appId, int userId) { - final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId); final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final int resolvedAppId; final UserState userState; synchronized (mLock) { + // Only the current group members can restart a print job. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } + resolvedAppId = resolveCallingAppEnforcingPermissions(appId); userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -210,6 +243,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can get enabled services. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return null; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -225,6 +262,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can get installed services. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return null; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -241,6 +282,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can create a discovery session. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -257,6 +302,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can destroy a discovery session. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -273,6 +322,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can start discovery. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -288,6 +341,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can stop discovery. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -303,6 +360,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can validate printers. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -318,6 +379,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can start printer tracking. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -333,6 +398,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can stop printer tracking. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -347,9 +416,14 @@ public final class PrintManagerService extends SystemService { public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener, int appId, int userId) throws RemoteException { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); - final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId); + final int resolvedAppId; final UserState userState; synchronized (mLock) { + // Only the current group members can add a print job listener. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } + resolvedAppId = resolveCallingAppEnforcingPermissions(appId); userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -366,6 +440,10 @@ public final class PrintManagerService extends SystemService { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { + // Only the current group members can remove a print job listener. + if (resolveCallingProfileParentLocked(resolvedUserId) != mCurrentUserId) { + return; + } userState = getOrCreateUserStateLocked(resolvedUserId); } final long identity = Binder.clearCallingIdentity(); @@ -421,11 +499,14 @@ public final class PrintManagerService extends SystemService { false, observer, UserHandle.USER_ALL); } - private void registerBoradcastReceivers() { + private void registerBroadcastReceivers() { PackageMonitor monitor = new PackageMonitor() { @Override public void onPackageModified(String packageName) { synchronized (mLock) { + // A background user/profile's print jobs are running but there is + // no UI shown. Hence, if the packages of such a user change we need + // to handle it as the change may affect ongoing print jobs. boolean servicesChanged = false; UserState userState = getOrCreateUserStateLocked(getChangingUserId()); Iterator<ComponentName> iterator = userState.getEnabledServices().iterator(); @@ -444,6 +525,9 @@ public final class PrintManagerService extends SystemService { @Override public void onPackageRemoved(String packageName, int uid) { synchronized (mLock) { + // A background user/profile's print jobs are running but there is + // no UI shown. Hence, if the packages of such a user change we need + // to handle it as the change may affect ongoing print jobs. boolean servicesRemoved = false; UserState userState = getOrCreateUserStateLocked(getChangingUserId()); Iterator<ComponentName> iterator = userState.getEnabledServices().iterator(); @@ -467,6 +551,9 @@ public final class PrintManagerService extends SystemService { public boolean onHandleForceStop(Intent intent, String[] stoppedPackages, int uid, boolean doit) { synchronized (mLock) { + // A background user/profile's print jobs are running but there is + // no UI shown. Hence, if the packages of such a user change we need + // to handle it as the change may affect ongoing print jobs. UserState userState = getOrCreateUserStateLocked(getChangingUserId()); boolean stoppedSomePackages = false; Iterator<ComponentName> iterator = userState.getEnabledServices() @@ -493,6 +580,9 @@ public final class PrintManagerService extends SystemService { @Override public void onPackageAdded(String packageName, int uid) { + // A background user/profile's print jobs are running but there is + // no UI shown. Hence, if the packages of such a user change we need + // to handle it as the change may affect ongoing print jobs. Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE); intent.setPackage(packageName); @@ -596,6 +686,23 @@ public final class PrintManagerService extends SystemService { } } + private int resolveCallingProfileParentLocked(int userId) { + if (userId != mCurrentUserId) { + final long identity = Binder.clearCallingIdentity(); + try { + UserInfo parent = mUserManager.getProfileParent(userId); + if (parent != null) { + return parent.getUserHandle().getIdentifier(); + } else { + return BACKGROUND_USER_ID; + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + return userId; + } + private int resolveCallingAppEnforcingPermissions(int appId) { final int callingUid = Binder.getCallingUid(); if (callingUid == 0 || callingUid == Process.SYSTEM_UID diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index f955f4f..88aaafc 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -163,9 +163,10 @@ public class ConnectivityServiceTest extends AndroidTestCase { nextConnBroadcast.get(); // verify that both routes were added and DNS was flushed - verify(mNetManager).addRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V4)); - verify(mNetManager).addRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V6)); - verify(mNetManager).flushInterfaceDnsCache(MOBILE_IFACE); + int mobileNetId = mMobile.tracker.getNetwork().netId; + verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4)); + verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6)); + verify(mNetManager).flushNetworkDnsCache(mobileNetId); } @@ -200,11 +201,14 @@ public class ConnectivityServiceTest extends AndroidTestCase { nextConnBroadcast.get(); // verify that wifi routes added, and teardown requested - verify(mNetManager).addRoute(eq(WIFI_IFACE), eq(WIFI_ROUTE_V4)); - verify(mNetManager).addRoute(eq(WIFI_IFACE), eq(WIFI_ROUTE_V6)); - verify(mNetManager).flushInterfaceDnsCache(WIFI_IFACE); + int wifiNetId = mWifi.tracker.getNetwork().netId; + verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V4)); + verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V6)); + verify(mNetManager).flushNetworkDnsCache(wifiNetId); verify(mMobile.tracker).teardown(); + int mobileNetId = mMobile.tracker.getNetwork().netId; + reset(mNetManager, mMobile.tracker); // tear down mobile network, as requested @@ -216,8 +220,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget(); nextConnBroadcast.get(); - verify(mNetManager).removeRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V4)); - verify(mNetManager).removeRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V6)); + verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4)); + verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6)); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 9787432..4af73cf 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -183,6 +183,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { SessionParams params = getParams(); BridgeContext context = getContext(); + RenderResources resources = getParams().getResources(); DisplayMetrics metrics = getContext().getMetrics(); diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java deleted file mode 100644 index 40e6649..0000000 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.BaseNetworkStateTracker; -import android.net.NetworkCapabilities; -import android.net.LinkQualityInfo; -import android.net.LinkProperties; -import android.net.NetworkInfo; -import android.net.NetworkInfo.DetailedState; -import android.net.SamplingDataTracker; -import android.net.WifiLinkQualityInfo; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.util.Slog; - -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Track the state of wifi for connectivity service. - * - * @hide - */ -public class WifiStateTracker extends BaseNetworkStateTracker { - - private static final String NETWORKTYPE = "WIFI"; - private static final String TAG = "WifiStateTracker"; - - private static final boolean LOGV = true; - - private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); - private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); - private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); - - private NetworkInfo.State mLastState = NetworkInfo.State.UNKNOWN; - - private WifiInfo mWifiInfo; - - /* For sending events to connectivity service handler */ - private Handler mCsHandler; - private BroadcastReceiver mWifiStateReceiver; - private WifiManager mWifiManager; - - private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker(); - - public WifiStateTracker(int netType, String networkName) { - mNetworkInfo = new NetworkInfo(netType, 0, networkName, ""); - mLinkProperties = new LinkProperties(); - mNetworkCapabilities = new NetworkCapabilities(); - - mNetworkInfo.setIsAvailable(false); - setTeardownRequested(false); - } - - - public void setTeardownRequested(boolean isRequested) { - mTeardownRequested.set(isRequested); - } - - public boolean isTeardownRequested() { - return mTeardownRequested.get(); - } - - /** - * Begin monitoring wifi connectivity - */ - public void startMonitoring(Context context, Handler target) { - mCsHandler = target; - mContext = context; - - mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - IntentFilter filter = new IntentFilter(); - filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); - - mWifiStateReceiver = new WifiStateReceiver(); - mContext.registerReceiver(mWifiStateReceiver, filter); - } - - /** - * Disable connectivity to a network - * TODO: do away with return value after making MobileDataStateTracker async - */ - public boolean teardown() { - mTeardownRequested.set(true); - mWifiManager.stopWifi(); - return true; - } - - /** - * Re-enable connectivity to a network after a {@link #teardown()}. - */ - public boolean reconnect() { - mTeardownRequested.set(false); - mWifiManager.startWifi(); - return true; - } - - @Override - public void captivePortalCheckCompleted(boolean isCaptivePortal) { - // not implemented - } - - /** - * Turn the wireless radio off for a network. - * @param turnOn {@code true} to turn the radio on, {@code false} - */ - public boolean setRadio(boolean turnOn) { - mWifiManager.setWifiEnabled(turnOn); - return true; - } - - /** - * Wi-Fi is considered available as long as we have a connection to the - * supplicant daemon and there is at least one enabled network. If a teardown - * was explicitly requested, then Wi-Fi can be restarted with a reconnect - * request, so it is considered available. If the driver has been stopped - * for any reason other than a teardown request, Wi-Fi is considered - * unavailable. - * @return {@code true} if Wi-Fi connections are possible - */ - public boolean isAvailable() { - return mNetworkInfo.isAvailable(); - } - - @Override - public void setUserDataEnable(boolean enabled) { - Slog.w(TAG, "ignoring setUserDataEnable(" + enabled + ")"); - } - - @Override - public void setPolicyDataEnable(boolean enabled) { - // ignored - } - - /** - * Check if private DNS route is set for the network - */ - public boolean isPrivateDnsRouteSet() { - return mPrivateDnsRouteSet.get(); - } - - /** - * Set a flag indicating private DNS route is set - */ - public void privateDnsRouteSet(boolean enabled) { - mPrivateDnsRouteSet.set(enabled); - } - - /** - * Fetch NetworkInfo for the network - */ - @Override - public NetworkInfo getNetworkInfo() { - return new NetworkInfo(mNetworkInfo); - } - - /** - * Fetch LinkProperties for the network - */ - @Override - public LinkProperties getLinkProperties() { - return new LinkProperties(mLinkProperties); - } - - /** - * Return link info - * @return an object of type WifiLinkQualityInfo - */ - @Override - public LinkQualityInfo getLinkQualityInfo() { - if (mNetworkInfo == null) { - // no data available yet; just return - return null; - } - - WifiLinkQualityInfo li = new WifiLinkQualityInfo(); - li.setNetworkType(mNetworkInfo.getType()); - - synchronized(mSamplingDataTracker.mSamplingDataLock) { - mSamplingDataTracker.setCommonLinkQualityInfoFields(li); - li.setTxGood(mSamplingDataTracker.getSampledTxPacketCount()); - li.setTxBad(mSamplingDataTracker.getSampledTxPacketErrorCount()); - } - - // li.setTheoreticalRxBandwidth(??); - // li.setTheoreticalTxBandwidth(??); - - if (mWifiInfo != null) { - li.setBssid(mWifiInfo.getBSSID()); - - int rssi = mWifiInfo.getRssi(); - li.setRssi(rssi); - - li.setNormalizedSignalStrength(mWifiManager.calculateSignalLevel(rssi, - LinkQualityInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE)); - } - - return li; - } - - /** - * Check if default route is set - */ - public boolean isDefaultRouteSet() { - return mDefaultRouteSet.get(); - } - - /** - * Set a flag indicating default route is set for the network - */ - public void defaultRouteSet(boolean enabled) { - mDefaultRouteSet.set(enabled); - } - - /** - * Return the system properties name associated with the tcp buffer sizes - * for this network. - */ - public String getTcpBufferSizesPropName() { - return "net.tcp.buffersize.wifi"; - } - - private class WifiStateReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - - if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( - WifiManager.EXTRA_NETWORK_INFO); - - mLinkProperties = intent.getParcelableExtra( - WifiManager.EXTRA_LINK_PROPERTIES); - if (mLinkProperties == null) { - mLinkProperties = new LinkProperties(); - } - mNetworkCapabilities = intent.getParcelableExtra( - WifiManager.EXTRA_NETWORK_CAPABILITIES); - if (mNetworkCapabilities == null) { - mNetworkCapabilities = new NetworkCapabilities(); - } - - mWifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); - // don't want to send redundant state messages - // but send portal check detailed state notice - NetworkInfo.State state = mNetworkInfo.getState(); - if (mLastState == state && - mNetworkInfo.getDetailedState() != DetailedState.CAPTIVE_PORTAL_CHECK) { - return; - } else { - mLastState = state; - /* lets not sample traffic data across state changes */ - mSamplingDataTracker.resetSamplingData(); - } - - Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, - new NetworkInfo(mNetworkInfo)); - msg.sendToTarget(); - } else if (intent.getAction().equals(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)) { - mLinkProperties = intent.getParcelableExtra(WifiManager.EXTRA_LINK_PROPERTIES); - Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); - msg.sendToTarget(); - } - } - } - - public void setDependencyMet(boolean met) { - // not supported on this network - } - - @Override - public void addStackedLink(LinkProperties link) { - mLinkProperties.addStackedLink(link); - } - - @Override - public void removeStackedLink(LinkProperties link) { - mLinkProperties.removeStackedLink(link); - } - - @Override - public void supplyMessenger(Messenger messenger) { - // not supported on this network - } - - @Override - public void startSampling(SamplingDataTracker.SamplingSnapshot s) { - mSamplingDataTracker.startSampling(s); - } - - @Override - public void stopSampling(SamplingDataTracker.SamplingSnapshot s) { - mSamplingDataTracker.stopSampling(s); - } -} |