summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt120
-rw-r--r--cmds/svc/src/com/android/commands/svc/DataCommand.java15
-rw-r--r--cmds/svc/src/com/android/commands/svc/WifiCommand.java15
-rw-r--r--core/java/android/app/Activity.java67
-rw-r--r--core/java/android/app/ActivityManagerNative.java40
-rw-r--r--core/java/android/app/ActivityThread.java27
-rw-r--r--core/java/android/app/AlertDialog.java20
-rw-r--r--core/java/android/app/ApplicationThreadNative.java7
-rw-r--r--core/java/android/app/DatePickerDialog.java1
-rw-r--r--core/java/android/app/EnterTransitionCoordinator.java4
-rw-r--r--core/java/android/app/IActivityManager.java4
-rw-r--r--core/java/android/app/IApplicationThread.java3
-rw-r--r--core/java/android/app/Instrumentation.java4
-rw-r--r--core/java/android/app/LauncherActivity.java12
-rw-r--r--core/java/android/app/TimePickerDialog.java1
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java45
-rw-r--r--core/java/android/hardware/camera2/ColorSpaceTransform.java3
-rw-r--r--core/java/android/hardware/camera2/LensShadingMap.java2
-rw-r--r--core/java/android/hardware/camera2/MeteringRectangle.java7
-rw-r--r--core/java/android/hardware/camera2/Rational.java17
-rw-r--r--core/java/android/hardware/camera2/ReprocessFormatsMap.java2
-rw-r--r--core/java/android/hardware/camera2/StreamConfiguration.java4
-rw-r--r--core/java/android/hardware/camera2/StreamConfigurationDuration.java4
-rw-r--r--core/java/android/hardware/camera2/StreamConfigurationMap.java2
-rw-r--r--core/java/android/hardware/camera2/TonemapCurve.java2
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java461
-rw-r--r--core/java/android/hardware/camera2/impl/MetadataMarshalClass.java67
-rw-r--r--core/java/android/hardware/camera2/impl/MetadataMarshalRect.java67
-rw-r--r--core/java/android/hardware/camera2/impl/MetadataMarshalSize.java60
-rw-r--r--core/java/android/hardware/camera2/impl/MetadataMarshalString.java79
-rw-r--r--core/java/android/hardware/camera2/marshal/MarshalHelpers.java243
-rw-r--r--core/java/android/hardware/camera2/marshal/MarshalQueryable.java63
-rw-r--r--core/java/android/hardware/camera2/marshal/MarshalRegistry.java133
-rw-r--r--core/java/android/hardware/camera2/marshal/Marshaler.java148
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java182
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java67
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java84
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java220
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java88
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java70
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java193
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java185
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java139
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java77
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java129
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java75
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java68
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java72
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java80
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java90
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java110
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/package.html3
-rw-r--r--core/java/android/hardware/camera2/marshal/package.html3
-rw-r--r--core/java/android/hardware/camera2/utils/HashCodeHelpers.java (renamed from core/java/android/hardware/camera2/impl/HashCodeHelpers.java)2
-rw-r--r--core/java/android/hardware/camera2/utils/TypeReference.java435
-rw-r--r--core/java/android/net/ConnectivityManager.java539
-rw-r--r--core/java/android/net/ConnectivityServiceProtocol.java70
-rw-r--r--core/java/android/net/IConnectivityManager.aidl41
-rw-r--r--core/java/android/net/LinkProperties.java29
-rw-r--r--core/java/android/net/Network.java28
-rw-r--r--core/java/android/net/NetworkAgent.java397
-rw-r--r--core/java/android/net/NetworkCapabilities.java2
-rw-r--r--core/java/android/net/NetworkInfo.java5
-rw-r--r--core/java/android/net/NetworkRequest.java62
-rw-r--r--core/java/android/os/Build.java22
-rw-r--r--core/java/android/os/INetworkManagementService.aidl86
-rw-r--r--core/java/android/os/PowerManager.java9
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/provider/TvContract.java161
-rw-r--r--core/java/android/transition/MoveImage.java8
-rw-r--r--core/java/android/tv/TvInputService.java2
-rw-r--r--core/java/android/tv/TvView.java10
-rw-r--r--core/java/android/util/Range.java2
-rw-r--r--core/java/android/view/KeyEvent.java8
-rw-r--r--core/java/android/view/MotionEvent.java8
-rw-r--r--core/java/android/view/ThreadedRenderer.java45
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java7
-rw-r--r--core/java/android/widget/AbsListView.java2
-rw-r--r--core/java/android/widget/EdgeEffect.java67
-rw-r--r--core/java/android/widget/Editor.java124
-rw-r--r--core/java/com/android/internal/app/AlertController.java27
-rw-r--r--core/java/com/android/internal/util/Protocol.java4
-rw-r--r--core/jni/android_hardware_camera2_CameraMetadata.cpp11
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp40
-rw-r--r--core/res/res/layout/alert_dialog_leanback.xml131
-rw-r--r--core/res/res/layout/alert_dialog_leanback_button_panel_right.xml142
-rw-r--r--core/res/res/layout/progress_dialog_leanback.xml48
-rw-r--r--core/res/res/values-television/themes.xml23
-rw-r--r--core/res/res/values-television/themes_device_defaults.xml (renamed from packages/SystemUI/res/layout/quick_settings_tile.xml)13
-rw-r--r--core/res/res/values/attrs.xml22
-rw-r--r--core/res/res/values/colors.xml3
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/dimens.xml4
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/styles_leanback.xml59
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/res/res/values/themes.xml28
-rw-r--r--core/res/res/values/themes_leanback.xml47
-rw-r--r--core/res/res/values/themes_quantum.xml3
-rw-r--r--libs/hwui/Animator.cpp7
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp38
-rw-r--r--libs/hwui/renderthread/CanvasContext.h3
-rw-r--r--media/tests/MediaFrameworkTest/Android.mk2
-rw-r--r--media/tests/MediaFrameworkTest/AndroidManifest.xml4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java1
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java253
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java451
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java223
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java9
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java42
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java6
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java2
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java27
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_auto_rotate.pngbin1408 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_rotation_locked.pngbin1414 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_auto_rotate.pngbin1033 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_rotation_locked.pngbin1015 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_auto_rotate.pngbin1901 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_rotation_locked.pngbin1886 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_auto_rotate.pngbin2567 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_rotation_locked.pngbin2085 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_airplane.xml31
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_bluetooth.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_bugreport.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_cast.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_close.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_hotspot.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_invert_colors.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_location.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_minus.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_plus.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_ringer_silent.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_ringer_vibrate.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_zen.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_01.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_02.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_03.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_04.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_05.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_06.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_07.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_08.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_09.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_10.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_11.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_12.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_13.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_14.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_24_15.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_locked_anim.xml35
-rw-r--r--packages/SystemUI/res/drawable/ic_rotate_unlocked_anim.xml35
-rw-r--r--packages/SystemUI/res/drawable/qs_panel_background.xml (renamed from packages/SystemUI/res/layout/quick_settings_tile_media.xml)19
-rw-r--r--packages/SystemUI/res/layout/flip_settings.xml25
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml (renamed from core/res/res/drawable/btn_color_quantum.xml)28
-rw-r--r--packages/SystemUI/res/layout/qs_zen_mode_detail.xml76
-rw-r--r--packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml57
-rw-r--r--packages/SystemUI/res/layout/quick_settings_tile_alarm.xml25
-rw-r--r--packages/SystemUI/res/layout/quick_settings_tile_basic.xml39
-rw-r--r--packages/SystemUI/res/layout/quick_settings_tile_battery.xml41
-rw-r--r--packages/SystemUI/res/layout/quick_settings_tile_ime.xml26
-rw-r--r--packages/SystemUI/res/layout/quick_settings_tile_monitoring.xml39
-rw-r--r--packages/SystemUI/res/layout/quick_settings_tile_rssi.xml71
-rw-r--r--packages/SystemUI/res/layout/quick_settings_tile_user.xml36
-rw-r--r--packages/SystemUI/res/layout/quick_settings_tile_wifi.xml57
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml2
-rw-r--r--packages/SystemUI/res/layout/status_bar_toggle_slider.xml3
-rw-r--r--packages/SystemUI/res/values-land/config.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml2
-rw-r--r--packages/SystemUI/res/values/colors.xml8
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/strings.xml8
-rw-r--r--packages/SystemUI/res/values/styles.xml12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSImageView.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java247
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTile.java317
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileView.java190
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java112
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java93
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java129
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java153
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java99
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java148
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java279
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java83
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java178
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java1119
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsBasicTile.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java295
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java1047
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsScrollView.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java149
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java424
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java230
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java137
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java110
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java200
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java214
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java1453
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java1491
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/TetheringController.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java132
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java6
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindowManager.java13
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java1603
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java242
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java22
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java8
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java8
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java63
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java80
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java405
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java2
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java5
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java71
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationComparator.java3
-rw-r--r--services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java64
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java19
-rw-r--r--services/core/java/com/android/server/notification/RankingFuture.java4
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java33
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java3
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java59
-rw-r--r--services/print/java/com/android/server/print/PrintManagerService.java125
-rw-r--r--services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java20
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java1
-rw-r--r--wifi/java/android/net/wifi/WifiStateTracker.java314
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&lt;Integer> intToken = new TypeReference&lt;Integer>() {{ }};
+ *
+ * // using named classes
+ * class IntTypeReference extends TypeReference&lt;Integer> {...}
+ * TypeReference&lt;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
+ * &lt;receiver&gt; 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
deleted file mode 100644
index 63acea0..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_auto_rotate.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_rotation_locked.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_rotation_locked.png
deleted file mode 100644
index f3dc08f..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_rotation_locked.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_auto_rotate.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_auto_rotate.png
deleted file mode 100644
index ac6c1cf..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_auto_rotate.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_rotation_locked.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_rotation_locked.png
deleted file mode 100644
index 3b7a284..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_rotation_locked.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_auto_rotate.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_auto_rotate.png
deleted file mode 100644
index c553bc2..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_auto_rotate.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_rotation_locked.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_rotation_locked.png
deleted file mode 100644
index b6daaf3..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_rotation_locked.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_auto_rotate.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_auto_rotate.png
deleted file mode 100644
index b6cfaec..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_auto_rotate.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_rotation_locked.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_rotation_locked.png
deleted file mode 100644
index 8e37884..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_rotation_locked.png
+++ /dev/null
Binary files differ
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);
- }
-}