OSDN Git Service

Merge "Change Linkify leniency to VALID" into lmp-mr1-dev
authorRaph Levien <raph@google.com>
Fri, 5 Dec 2014 02:14:51 +0000 (02:14 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Fri, 5 Dec 2014 02:14:52 +0000 (02:14 +0000)
347 files changed:
Android.mk
api/current.txt
cmds/appops/src/com/android/commands/appops/AppOpsCommand.java
core/java/android/animation/ValueAnimator.java
core/java/android/app/ActivityManagerNative.java
core/java/android/app/ActivityThread.java
core/java/android/app/ActivityTransitionCoordinator.java
core/java/android/app/AppOpsManager.java
core/java/android/app/ApplicationErrorReport.java
core/java/android/app/IActivityManager.java
core/java/android/app/ITaskStackListener.aidl [moved from core/java/android/service/carriermessaging/CarrierMessagingService.aidl with 85% similarity]
core/java/android/app/Instrumentation.java
core/java/android/app/TimePickerDialog.java
core/java/android/app/admin/DevicePolicyManager.java
core/java/android/app/backup/WallpaperBackupHelper.java
core/java/android/bluetooth/BluetoothGatt.java
core/java/android/bluetooth/BluetoothGattServer.java
core/java/android/bluetooth/BluetoothGattServerCallback.java
core/java/android/bluetooth/IBluetoothGattServerCallback.aidl
core/java/android/content/pm/PackageManager.java
core/java/android/content/pm/PackageParser.java
core/java/android/content/res/Resources.java
core/java/android/hardware/camera2/CameraCharacteristics.java
core/java/android/hardware/camera2/CameraMetadata.java
core/java/android/hardware/camera2/CaptureRequest.java
core/java/android/hardware/camera2/CaptureResult.java
core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
core/java/android/hardware/location/GeofenceHardwareImpl.java
core/java/android/inputmethodservice/KeyboardView.java
core/java/android/net/ConnectivityManager.java
core/java/android/net/IConnectivityManager.aidl
core/java/android/net/NetworkCapabilities.java
core/java/android/net/VpnService.java
core/java/android/os/BatteryStats.java
core/java/android/os/FileUtils.java
core/java/android/provider/Settings.java
core/java/android/service/carrier/CarrierMessagingService.java [new file with mode: 0644]
core/java/android/service/carrier/ICarrierMessagingCallback.aidl [moved from core/java/android/service/carriermessaging/ICarrierMessagingCallback.aidl with 73% similarity]
core/java/android/service/carrier/ICarrierMessagingService.aidl [moved from core/java/android/service/carriermessaging/ICarrierMessagingService.aidl with 72% similarity]
core/java/android/service/carrier/MessagePdu.aidl [moved from core/java/android/service/carriermessaging/MessagePdu.aidl with 90% similarity]
core/java/android/service/carrier/MessagePdu.java [moved from core/java/android/service/carriermessaging/MessagePdu.java with 98% similarity]
core/java/android/service/carriermessaging/CarrierMessagingService.java [deleted file]
core/java/android/service/carriermessaging/CarrierMessagingServiceManager.java [deleted file]
core/java/android/service/wallpaper/WallpaperService.java
core/java/android/text/StaticLayout.java
core/java/android/view/DisplayInfo.java
core/java/android/view/GLES20Canvas.java
core/java/android/view/HardwareRenderer.java
core/java/android/view/ThreadedRenderer.java
core/java/android/view/ViewGroup.java
core/java/android/view/ViewOverlay.java
core/java/android/view/ViewRootImpl.java
core/java/android/view/Window.java
core/java/android/widget/RadialTimePickerView.java
core/java/android/widget/TextView.java
core/java/com/android/internal/app/IAppOpsService.aidl
core/java/com/android/internal/app/ResolverActivity.java
core/java/com/android/internal/os/BatteryStatsImpl.java
core/java/com/android/internal/statusbar/IStatusBarService.aidl
core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
core/java/com/android/server/backup/SystemBackupAgent.java
core/jni/android/graphics/Graphics.cpp
core/jni/android/graphics/GraphicsJNI.h
core/jni/android_view_GLES20Canvas.cpp
core/jni/android_view_ThreadedRenderer.cpp
core/res/AndroidManifest.xml
core/res/res/drawable/btn_default_mtrl_shape.xml
core/res/res/layout/resolve_list_item.xml
core/res/res/layout/resolver_list.xml
core/res/res/layout/resolver_list_with_default.xml
core/res/res/values-de/strings.xml
core/res/res/values-gl-rES/strings.xml
core/res/res/values-mcc310-mnc120/config.xml
core/res/res/values-mcc311-mnc490/config.xml
core/res/res/values-mcc311-mnc870/config.xml
core/res/res/values-mcc312-mnc530/config.xml
core/res/res/values-mcc415-mnc01/config.xml [new file with mode: 0644]
core/res/res/values-mcc416-mnc77/config.xml [new file with mode: 0644]
core/res/res/values-mcc432-mnc20/config.xml [new file with mode: 0644]
core/res/res/values-si-rLK/strings.xml
core/res/res/values/config.xml
core/res/res/values/dimens_material.xml
core/res/res/values/symbols.xml
core/res/res/values/themes_device_defaults.xml
core/tests/coretests/src/android/os/FileUtilsTest.java
data/keyboards/Vendor_046d_Product_b501.kl [new file with mode: 0644]
data/keyboards/Vendor_1532_Product_0900.kl [new file with mode: 0644]
docs/html/about/dashboards/index.jd
docs/html/about/versions/kitkat.jd
docs/html/design/media/wear/ContextualExample.006.png
docs/html/design/media/wear/ContextualExample.006_2x.png
docs/html/design/wear/context.jd
docs/html/guide/topics/manifest/uses-feature-element.jd
docs/html/guide/topics/ui/settings.jd
docs/html/preview/index.html
docs/html/robots.txt
docs/html/samples/admin.jd [new file with mode: 0644]
docs/html/samples/new/index.jd
docs/html/tools/sdk/ndk/index.jd
docs/html/training/app-indexing/deep-linking.jd
docs/html/training/app-indexing/index.jd
docs/html/training/wearables/apps/creating.jd
docs/html/training/wearables/data-layer/index.jd
docs/html/training/wearables/ui/confirm.jd
docs/html/training/wearables/ui/exit.jd
docs/html/training/wearables/ui/layouts.jd
docs/html/training/wearables/ui/lists.jd
graphics/java/android/graphics/Bitmap.java
graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
graphics/java/android/graphics/drawable/AnimationDrawable.java
graphics/java/android/graphics/drawable/DrawableContainer.java
graphics/java/android/graphics/drawable/InsetDrawable.java
graphics/java/android/graphics/drawable/LevelListDrawable.java
graphics/java/android/graphics/drawable/Ripple.java
graphics/java/android/graphics/drawable/RippleBackground.java
graphics/java/android/graphics/drawable/RippleDrawable.java
include/androidfw/ResourceTypes.h
libs/androidfw/ResourceTypes.cpp
libs/hwui/OpenGLRenderer.cpp
libs/hwui/RenderNode.cpp
libs/hwui/renderthread/CanvasContext.cpp
libs/hwui/renderthread/CanvasContext.h
libs/hwui/renderthread/RenderProxy.cpp
libs/hwui/renderthread/RenderProxy.h
libs/hwui/renderthread/RenderThread.cpp
libs/hwui/renderthread/RenderThread.h
media/java/android/media/AudioAttributes.java
media/java/android/media/AudioManager.java
media/java/android/media/AudioService.java
media/java/android/media/ImageReader.java
media/java/android/media/MediaCodecInfo.java
media/java/android/media/audiopolicy/AudioMix.java
media/java/android/media/audiopolicy/AudioMixingRule.java
media/java/android/media/audiopolicy/AudioPolicy.java
media/java/android/media/audiopolicy/AudioPolicyConfig.java
media/java/android/media/projection/MediaProjection.java
media/java/android/media/tv/ITvInputManager.aidl
media/java/android/media/tv/TvInputManager.java
media/java/android/media/tv/TvInputService.java
packages/BackupRestoreConfirmation/res/values-es/strings.xml
packages/BackupRestoreConfirmation/res/values-hi/strings.xml
packages/BackupRestoreConfirmation/res/values-kk-rKZ/strings.xml
packages/BackupRestoreConfirmation/res/values-ky-rKG/strings.xml
packages/BackupRestoreConfirmation/res/values-ne-rNP/strings.xml
packages/BackupRestoreConfirmation/res/values-si-rLK/strings.xml
packages/BackupRestoreConfirmation/res/values-ur-rPK/strings.xml
packages/BackupRestoreConfirmation/res/values-uz-rUZ/strings.xml
packages/DocumentsUI/res/values-hi/strings.xml
packages/DocumentsUI/res/values-ky-rKG/strings.xml
packages/DocumentsUI/res/values-my-rMM/strings.xml
packages/DocumentsUI/res/values-pt/strings.xml
packages/DocumentsUI/res/values-zh-rTW/strings.xml
packages/ExternalStorageProvider/res/values-hi/strings.xml
packages/ExternalStorageProvider/res/values-my-rMM/strings.xml
packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
packages/ExternalStorageProvider/tests/Android.mk [new file with mode: 0644]
packages/ExternalStorageProvider/tests/AndroidManifest.xml [new file with mode: 0644]
packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java [new file with mode: 0644]
packages/InputDevices/res/values-hi/strings.xml
packages/Keyguard/res/values-ca/strings.xml
packages/Keyguard/res/values-zh-rHK/strings.xml
packages/Keyguard/res/values/strings.xml
packages/Keyguard/src/com/android/keyguard/CarrierText.java
packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java
packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
packages/PrintSpooler/res/values-bn-rBD/strings.xml
packages/PrintSpooler/res/values-es-rUS/arrays.xml [new file with mode: 0644]
packages/PrintSpooler/res/values-eu-rES/strings.xml
packages/PrintSpooler/res/values-gl-rES/strings.xml
packages/PrintSpooler/res/values-hi/strings.xml
packages/PrintSpooler/res/values-is-rIS/strings.xml
packages/PrintSpooler/res/values-kk-rKZ/strings.xml
packages/PrintSpooler/res/values-km-rKH/strings.xml
packages/PrintSpooler/res/values-kn-rIN/strings.xml
packages/PrintSpooler/res/values-ky-rKG/strings.xml
packages/PrintSpooler/res/values-mk-rMK/strings.xml
packages/PrintSpooler/res/values-ml-rIN/strings.xml
packages/PrintSpooler/res/values-mr-rIN/strings.xml
packages/PrintSpooler/res/values-my-rMM/strings.xml
packages/PrintSpooler/res/values-ne-rNP/strings.xml
packages/PrintSpooler/res/values-si-rLK/strings.xml
packages/PrintSpooler/res/values-sw600dp-land/constants.xml
packages/PrintSpooler/res/values-ta-rIN/strings.xml
packages/PrintSpooler/res/values-te-rIN/strings.xml
packages/PrintSpooler/res/values-ur-rPK/strings.xml
packages/PrintSpooler/res/values-uz-rUZ/strings.xml
packages/PrintSpooler/res/values/constants.xml
packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
packages/SettingsProvider/res/values-hi/strings.xml
packages/Shell/res/values-cs/strings.xml
packages/Shell/res/values-es/strings.xml
packages/Shell/res/values-fr-rCA/strings.xml
packages/Shell/res/values-ko/strings.xml
packages/Shell/res/values-lt/strings.xml
packages/Shell/res/values-pt/strings.xml
packages/Shell/res/values-ru/strings.xml
packages/Shell/res/values-zh-rHK/strings.xml
packages/SystemUI/res/drawable/pop_ball.xml [new file with mode: 0644]
packages/SystemUI/res/drawable/pop_belt.xml
packages/SystemUI/res/drawable/pop_pizza.xml
packages/SystemUI/res/drawable/pop_swirl.xml
packages/SystemUI/res/drawable/pop_vortex.xml
packages/SystemUI/res/drawable/qs_background_primary.xml
packages/SystemUI/res/drawable/stat_sys_no_sims.xml [new file with mode: 0644]
packages/SystemUI/res/layout/mobile_signal_group.xml [new file with mode: 0644]
packages/SystemUI/res/layout/signal_cluster_view.xml
packages/SystemUI/res/values-hy-rAM/strings.xml
packages/SystemUI/res/values-pl/strings.xml
packages/SystemUI/res/values-tr/strings.xml
packages/SystemUI/res/values-zh-rCN/strings.xml
packages/SystemUI/res/values/config.xml
packages/SystemUI/res/values/dimens.xml
packages/SystemUI/res/values/lland_config.xml
packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
packages/SystemUI/src/com/android/systemui/doze/DozeService.java
packages/SystemUI/src/com/android/systemui/egg/LLand.java
packages/SystemUI/src/com/android/systemui/egg/LLandActivity.java
packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java
packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java
packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
packages/VpnDialogs/res/values-eu-rES/strings.xml
packages/VpnDialogs/res/values-zh-rCN/strings.xml
policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
rs/jni/android_renderscript_RenderScript.cpp
services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
services/backup/java/com/android/server/backup/BackupManagerService.java
services/core/java/com/android/server/AlarmManagerService.java
services/core/java/com/android/server/AppOpsService.java
services/core/java/com/android/server/ConnectivityService.java
services/core/java/com/android/server/EventLogTags.logtags
services/core/java/com/android/server/MmsServiceBroker.java
services/core/java/com/android/server/MountServiceIdler.java
services/core/java/com/android/server/NativeDaemonConnector.java
services/core/java/com/android/server/NativeDaemonEvent.java
services/core/java/com/android/server/NetworkManagementService.java
services/core/java/com/android/server/accounts/AccountManagerService.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/ActivityRecord.java
services/core/java/com/android/server/am/ActivityStack.java
services/core/java/com/android/server/am/ActivityStackSupervisor.java
services/core/java/com/android/server/am/BroadcastQueue.java
services/core/java/com/android/server/am/TaskPersister.java
services/core/java/com/android/server/am/TaskRecord.java
services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
services/core/java/com/android/server/connectivity/NetworkMonitor.java
services/core/java/com/android/server/connectivity/Tethering.java
services/core/java/com/android/server/display/DisplayDevice.java
services/core/java/com/android/server/display/DisplayDeviceInfo.java
services/core/java/com/android/server/display/LocalDisplayAdapter.java
services/core/java/com/android/server/display/LogicalDisplay.java
services/core/java/com/android/server/display/OverlayDisplayAdapter.java
services/core/java/com/android/server/display/VirtualDisplayAdapter.java
services/core/java/com/android/server/display/WifiDisplayAdapter.java
services/core/java/com/android/server/hdmi/Constants.java
services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
services/core/java/com/android/server/hdmi/HdmiCecController.java
services/core/java/com/android/server/hdmi/HdmiConfig.java
services/core/java/com/android/server/hdmi/HdmiControlService.java
services/core/java/com/android/server/hdmi/NewDeviceAction.java
services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
services/core/java/com/android/server/hdmi/SystemAudioAction.java
services/core/java/com/android/server/media/MediaSessionService.java
services/core/java/com/android/server/media/MediaSessionStack.java
services/core/java/com/android/server/notification/NotificationDelegate.java
services/core/java/com/android/server/notification/NotificationManagerService.java
services/core/java/com/android/server/notification/RankingHelper.java
services/core/java/com/android/server/pm/PackageManagerService.java
services/core/java/com/android/server/pm/Settings.java
services/core/java/com/android/server/pm/UserManagerService.java
services/core/java/com/android/server/statusbar/StatusBarManagerService.java
services/core/java/com/android/server/tv/TvInputManagerService.java
services/core/java/com/android/server/wm/AccessibilityController.java
services/core/java/com/android/server/wm/AppTransition.java
services/core/java/com/android/server/wm/DisplaySettings.java
services/core/java/com/android/server/wm/WindowManagerService.java
services/core/java/com/android/server/wm/WindowStateAnimator.java
telecomm/java/android/telecom/AudioState.java
telecomm/java/android/telecom/Call.java
telecomm/java/android/telecom/Connection.java
telecomm/java/android/telecom/TelecomManager.java
telecomm/java/com/android/internal/telecom/ITelecomService.aidl
telephony/java/android/telephony/ServiceState.java
telephony/java/android/telephony/SubscriptionInfo.java
telephony/java/android/telephony/SubscriptionManager.java
telephony/java/android/telephony/TelephonyManager.java
telephony/java/com/android/ims/ImsReasonInfo.java
telephony/java/com/android/ims/internal/IImsService.aidl
telephony/java/com/android/internal/telephony/IMms.aidl
telephony/java/com/android/internal/telephony/ISms.aidl
telephony/java/com/android/internal/telephony/ISub.aidl
telephony/java/com/android/internal/telephony/ITelephony.aidl
telephony/java/com/android/internal/telephony/IccCardConstants.java
tests/ActivityTests/AndroidManifest.xml
tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
tests/ActivityTests/src/com/google/android/test/activity/IsolatedService.java [new file with mode: 0644]
tools/aapt/Resource.cpp
tools/aapt/ResourceTable.cpp
tools/aapt/ResourceTable.h
tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java
tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java [deleted file]
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/libcore/io/BridgeBufferIterator.java [new file with mode: 0644]
tools/layoutlib/bridge/src/libcore/io/MemoryMappedFile_Delegate.java [new file with mode: 0644]
tools/layoutlib/bridge/src/libcore/util/ZoneInfo_WallTime_Delegate.java [new file with mode: 0644]
tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java [new file with mode: 0644]

index ce72a85..ba45e6f 100644 (file)
@@ -69,6 +69,7 @@ LOCAL_SRC_FILES += \
        core/java/android/app/IActivityPendingResult.aidl \
        core/java/android/app/IAlarmManager.aidl \
        core/java/android/app/IAppTask.aidl \
+       core/java/android/app/ITaskStackListener.aidl \
        core/java/android/app/IBackupAgent.aidl \
        core/java/android/app/IInstrumentationWatcher.aidl \
        core/java/android/app/INotificationManager.aidl \
@@ -203,8 +204,8 @@ LOCAL_SRC_FILES += \
        core/java/android/os/IUpdateLock.aidl \
        core/java/android/os/IUserManager.aidl \
        core/java/android/os/IVibratorService.aidl \
-       core/java/android/service/carriermessaging/ICarrierMessagingCallback.aidl \
-       core/java/android/service/carriermessaging/ICarrierMessagingService.aidl \
+       core/java/android/service/carrier/ICarrierMessagingCallback.aidl \
+       core/java/android/service/carrier/ICarrierMessagingService.aidl \
        core/java/android/service/notification/INotificationListener.aidl \
        core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
        core/java/android/service/notification/IConditionListener.aidl \
@@ -523,8 +524,7 @@ aidl_files := \
        frameworks/base/core/java/android/view/textservice/SpellCheckerInfo.aidl \
        frameworks/base/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl \
        frameworks/base/core/java/android/view/textservice/SuggestionsInfo.aidl \
-       frameworks/base/core/java/android/service/carriermessaging/MessagePdu.aidl \
-       frameworks/base/core/java/android/service/carriermessaging/CarrierMessagingService.aidl \
+       frameworks/base/core/java/android/service/carrier/MessagePdu.aidl \
        frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
        frameworks/base/core/java/android/speech/tts/Voice.aidl \
        frameworks/base/core/java/android/app/usage/UsageEvents.aidl \
@@ -731,7 +731,8 @@ samples_dir := development/samples/browseable
 # Whitelist of valid groups, used for default TOC grouping. Each sample must
 # belong to one (and only one) group. Assign samples to groups by setting
 # a sample.group var to one of these groups in the sample's _index.jd.
-sample_groups := -samplegroup Background \
+sample_groups := -samplegroup Admin \
+                 -samplegroup Background \
                  -samplegroup Connectivity \
                  -samplegroup Content \
                  -samplegroup Input \
index 707ddab..5a17f6f 100644 (file)
@@ -6383,6 +6383,7 @@ package android.bluetooth {
     method public void onDescriptorReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattDescriptor);
     method public void onDescriptorWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattDescriptor, boolean, boolean, int, byte[]);
     method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean);
+    method public void onMtuChanged(android.bluetooth.BluetoothDevice, int);
     method public void onNotificationSent(android.bluetooth.BluetoothDevice, int);
     method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
   }
@@ -8781,11 +8782,9 @@ package android.content.pm {
     field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera";
     field public static final java.lang.String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
     field public static final java.lang.String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus";
-    field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_BURST_CAPTURE = "android.hardware.camera.capability.burst_capture";
     field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = "android.hardware.camera.capability.manual_post_processing";
     field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = "android.hardware.camera.capability.manual_sensor";
     field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_RAW = "android.hardware.camera.capability.raw";
-    field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_READ_SENSOR_SETTINGS = "android.hardware.camera.capability.read_sensor_settings";
     field public static final java.lang.String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
     field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
     field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
@@ -9213,9 +9212,9 @@ package android.content.res {
     method public int getDimensionPixelOffset(int) throws android.content.res.Resources.NotFoundException;
     method public int getDimensionPixelSize(int) throws android.content.res.Resources.NotFoundException;
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public android.graphics.drawable.Drawable getDrawable(int) throws android.content.res.Resources.NotFoundException;
+    method public deprecated android.graphics.drawable.Drawable getDrawable(int) throws android.content.res.Resources.NotFoundException;
     method public android.graphics.drawable.Drawable getDrawable(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
-    method public android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
+    method public deprecated android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
     method public android.graphics.drawable.Drawable getDrawableForDensity(int, int, android.content.res.Resources.Theme);
     method public float getFraction(int, int, int);
     method public int getIdentifier(java.lang.String, java.lang.String, java.lang.String);
@@ -26913,71 +26912,46 @@ package android.security {
 
 }
 
-package android.service.carriermessaging {
+package android.service.carrier {
 
   public abstract class CarrierMessagingService extends android.app.Service {
     ctor public CarrierMessagingService();
     method public android.os.IBinder onBind(android.content.Intent);
-    method public int onDownloadMms(android.net.Uri, java.lang.String);
-    method public boolean onFilterSms(android.service.carriermessaging.MessagePdu, java.lang.String, int);
-    method public android.service.carriermessaging.CarrierMessagingService.SendSmsResponse onSendDataSms(byte[], java.lang.String, java.lang.String, int);
-    method public android.service.carriermessaging.CarrierMessagingService.SendMmsResult onSendMms(android.net.Uri, java.lang.String);
-    method public java.util.List<android.service.carriermessaging.CarrierMessagingService.SendSmsResponse> onSendMultipartTextSms(java.util.List<java.lang.String>, java.lang.String, java.lang.String);
-    method public android.service.carriermessaging.CarrierMessagingService.SendSmsResponse onSendTextSms(java.lang.String, java.lang.String, java.lang.String);
+    method public void onDownloadMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Integer>);
+    method public void onFilterSms(android.service.carrier.MessagePdu, java.lang.String, int, int, android.service.carrier.CarrierMessagingService.ResultCallback<java.lang.Boolean>);
+    method public void onSendDataSms(byte[], int, java.lang.String, int, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
+    method public void onSendMms(android.net.Uri, int, android.net.Uri, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendMmsResult>);
+    method public void onSendMultipartTextSms(java.util.List<java.lang.String>, int, java.lang.String, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendMultipartSmsResult>);
+    method public void onSendTextSms(java.lang.String, int, java.lang.String, android.service.carrier.CarrierMessagingService.ResultCallback<android.service.carrier.CarrierMessagingService.SendSmsResult>);
     field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2
     field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0
     field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1
     field public static final int SEND_STATUS_ERROR = 2; // 0x2
     field public static final int SEND_STATUS_OK = 0; // 0x0
     field public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1
-    field public static final java.lang.String SERVICE_INTERFACE = "android.service.carriermessaging.CarrierMessagingService";
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.carrier.CarrierMessagingService";
+  }
+
+  public static abstract interface CarrierMessagingService.ResultCallback {
+    method public abstract void onReceiveResult(T) throws android.os.RemoteException;
   }
 
   public static final class CarrierMessagingService.SendMmsResult {
     ctor public CarrierMessagingService.SendMmsResult(int, byte[]);
-    method public int getResult();
     method public byte[] getSendConfPdu();
+    method public int getSendStatus();
   }
 
-  public static final class CarrierMessagingService.SendSmsResponse implements android.os.Parcelable {
-    ctor public CarrierMessagingService.SendSmsResponse(int, byte[], int);
-    method public int describeContents();
-    method public byte[] getAckPdu();
-    method public int getErrorCode();
-    method public int getMessageRef();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.carriermessaging.CarrierMessagingService.SendSmsResponse> CREATOR;
-  }
-
-  public abstract interface ICarrierMessagingCallback implements android.os.IInterface {
-    method public abstract void onDownloadMmsComplete(int) throws android.os.RemoteException;
-    method public abstract void onFilterComplete(boolean) throws android.os.RemoteException;
-    method public abstract void onSendMmsComplete(int, byte[]) throws android.os.RemoteException;
-    method public abstract void onSendMultipartSmsComplete(int, java.util.List<android.service.carriermessaging.CarrierMessagingService.SendSmsResponse>) throws android.os.RemoteException;
-    method public abstract void onSendSmsComplete(int, android.service.carriermessaging.CarrierMessagingService.SendSmsResponse) throws android.os.RemoteException;
-  }
-
-  public static abstract class ICarrierMessagingCallback.Stub extends android.os.Binder implements android.service.carriermessaging.ICarrierMessagingCallback {
-    ctor public ICarrierMessagingCallback.Stub();
-    method public android.os.IBinder asBinder();
-    method public static android.service.carriermessaging.ICarrierMessagingCallback asInterface(android.os.IBinder);
-    method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+  public static final class CarrierMessagingService.SendMultipartSmsResult {
+    ctor public CarrierMessagingService.SendMultipartSmsResult(int, int[]);
+    method public int[] getMessageRefs();
+    method public int getSendStatus();
   }
 
-  public abstract interface ICarrierMessagingService implements android.os.IInterface {
-    method public abstract void downloadMms(android.net.Uri, java.lang.String, android.service.carriermessaging.ICarrierMessagingCallback) throws android.os.RemoteException;
-    method public abstract void filterSms(android.service.carriermessaging.MessagePdu, java.lang.String, int, android.service.carriermessaging.ICarrierMessagingCallback) throws android.os.RemoteException;
-    method public abstract void sendDataSms(byte[], java.lang.String, java.lang.String, int, android.service.carriermessaging.ICarrierMessagingCallback) throws android.os.RemoteException;
-    method public abstract void sendMms(android.net.Uri, java.lang.String, android.service.carriermessaging.ICarrierMessagingCallback) throws android.os.RemoteException;
-    method public abstract void sendMultipartTextSms(java.util.List<java.lang.String>, java.lang.String, java.lang.String, android.service.carriermessaging.ICarrierMessagingCallback) throws android.os.RemoteException;
-    method public abstract void sendTextSms(java.lang.String, java.lang.String, java.lang.String, android.service.carriermessaging.ICarrierMessagingCallback) throws android.os.RemoteException;
-  }
-
-  public static abstract class ICarrierMessagingService.Stub extends android.os.Binder implements android.service.carriermessaging.ICarrierMessagingService {
-    ctor public ICarrierMessagingService.Stub();
-    method public android.os.IBinder asBinder();
-    method public static android.service.carriermessaging.ICarrierMessagingService asInterface(android.os.IBinder);
-    method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+  public static final class CarrierMessagingService.SendSmsResult {
+    ctor public CarrierMessagingService.SendSmsResult(int, int);
+    method public int getMessageRef();
+    method public int getSendStatus();
   }
 
   public final class MessagePdu implements android.os.Parcelable {
@@ -26985,7 +26959,7 @@ package android.service.carriermessaging {
     method public int describeContents();
     method public java.util.List<byte[]> getPdus();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.carriermessaging.MessagePdu> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.service.carrier.MessagePdu> CREATOR;
   }
 
 }
@@ -28721,7 +28695,7 @@ package android.telephony {
     method public void listen(android.telephony.PhoneStateListener, int);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public boolean setGlobalPreferredNetworkType();
-    method public void setLine1NumberForDisplay(java.lang.String, java.lang.String);
+    method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
index c414f58..3ec63b4 100644 (file)
@@ -24,10 +24,12 @@ import android.content.pm.IPackageManager;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 
+import android.util.TimeUtils;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.BaseCommand;
 
 import java.io.PrintStream;
+import java.util.List;
 
 /**
  * This class is a command line utility for manipulating AppOps permissions.
@@ -40,15 +42,19 @@ public class AppOpsCommand extends BaseCommand {
 
     @Override
     public void onShowUsage(PrintStream out) {
-        out.println("usage: adb shell appops set <PACKAGE> <OP> "
-                + "<allow|ignore|deny|default> [--user <USER_ID>]\n"
+        out.println("usage: appops set [--user <USER_ID>] <PACKAGE> <OP> <MODE>\n"
+                + "       appops get [--user <USER_ID>] <PACKAGE> [<OP>]\n"
+                + "       appops reset [--user <USER_ID>] [<PACKAGE>]\n"
                 + "  <PACKAGE> an Android package name.\n"
                 + "  <OP>      an AppOps operation.\n"
+                + "  <MODE>    one of allow, ignore, deny, or default\n"
                 + "  <USER_ID> the user id under which the package is installed. If --user is not\n"
                 + "            specified, the current user is assumed.\n");
     }
 
     private static final String COMMAND_SET = "set";
+    private static final String COMMAND_GET = "get";
+    private static final String COMMAND_RESET = "reset";
 
     @Override
     public void onRun() throws Exception {
@@ -58,8 +64,17 @@ public class AppOpsCommand extends BaseCommand {
                 runSet();
                 break;
 
+            case COMMAND_GET:
+                runGet();
+                break;
+
+            case COMMAND_RESET:
+                runReset();
+                break;
+
             default:
-                throw new IllegalArgumentException("Unknown command '" + command + "'.");
+                System.err.println("Error: Unknown command: '" + command + "'.");
+                break;
         }
     }
 
@@ -71,6 +86,23 @@ public class AppOpsCommand extends BaseCommand {
     private static final String MODE_IGNORE = "ignore";
     private static final String MODE_DEFAULT = "default";
 
+    private int strOpToOp(String op) {
+        try {
+            return AppOpsManager.strOpToOp(op);
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            return Integer.parseInt(op);
+        } catch (NumberFormatException e) {
+        }
+        try {
+            return AppOpsManager.strDebugOpToOp(op);
+        } catch (IllegalArgumentException e) {
+            System.err.println("Error: " + e.getMessage());
+            return -1;
+        }
+    }
+
     private void runSet() throws Exception {
         String packageName = null;
         String op = null;
@@ -87,20 +119,27 @@ public class AppOpsCommand extends BaseCommand {
                 } else if (mode == null) {
                     mode = argument;
                 } else {
-                    throw new IllegalArgumentException("Unsupported argument: " + argument);
+                    System.err.println("Error: Unsupported argument: " + argument);
+                    return;
                 }
             }
         }
 
         if (packageName == null) {
-            throw new IllegalArgumentException("Package name not specified.");
+            System.err.println("Error: Package name not specified.");
+            return;
         } else if (op == null) {
-            throw new IllegalArgumentException("Operation not specified.");
+            System.err.println("Error: Operation not specified.");
+            return;
         } else if (mode == null) {
-            throw new IllegalArgumentException("Mode not specified.");
+            System.err.println("Error: Mode not specified.");
+            return;
         }
 
-        final int opInt = AppOpsManager.strOpToOp(op);
+        final int opInt = strOpToOp(op);
+        if (opInt < 0) {
+            return;
+        }
         final int modeInt;
         switch (mode) {
             case MODE_ALLOW:
@@ -116,7 +155,8 @@ public class AppOpsCommand extends BaseCommand {
                 modeInt = AppOpsManager.MODE_DEFAULT;
                 break;
             default:
-                throw new IllegalArgumentException("Mode is invalid.");
+                System.err.println("Error: Mode " + mode + " is not valid,");
+                return;
         }
 
         // Parsing complete, let's execute the command.
@@ -130,8 +170,155 @@ public class AppOpsCommand extends BaseCommand {
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
         final int uid = pm.getPackageUid(packageName, userId);
         if (uid < 0) {
-            throw new Exception("No UID for " + packageName + " for user " + userId);
+            System.err.println("Error: No UID for " + packageName + " in user " + userId);
+            return;
         }
         appOpsService.setMode(opInt, uid, packageName, modeInt);
     }
+
+    private void runGet() throws Exception {
+        String packageName = null;
+        String op = null;
+        int userId = UserHandle.USER_CURRENT;
+        for (String argument; (argument = nextArg()) != null;) {
+            if (ARGUMENT_USER.equals(argument)) {
+                userId = Integer.parseInt(nextArgRequired());
+            } else {
+                if (packageName == null) {
+                    packageName = argument;
+                } else if (op == null) {
+                    op = argument;
+                } else {
+                    System.err.println("Error: Unsupported argument: " + argument);
+                    return;
+                }
+            }
+        }
+
+        if (packageName == null) {
+            System.err.println("Error: Package name not specified.");
+            return;
+        }
+
+        final int opInt = op != null ? strOpToOp(op) : 0;
+
+        // Parsing complete, let's execute the command.
+
+        if (userId == UserHandle.USER_CURRENT) {
+            userId = ActivityManager.getCurrentUser();
+        }
+
+        final IPackageManager pm = ActivityThread.getPackageManager();
+        final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
+                ServiceManager.getService(Context.APP_OPS_SERVICE));
+        final int uid = pm.getPackageUid(packageName, userId);
+        if (uid < 0) {
+            System.err.println("Error: No UID for " + packageName + " in user " + userId);
+            return;
+        }
+        List<AppOpsManager.PackageOps> ops = appOpsService.getOpsForPackage(uid, packageName,
+                op != null ? new int[] {opInt} : null);
+        if (ops == null || ops.size() <= 0) {
+            System.out.println("No operations.");
+            return;
+        }
+        final long now = System.currentTimeMillis();
+        for (int i=0; i<ops.size(); i++) {
+            List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();
+            for (int j=0; j<entries.size(); j++) {
+                AppOpsManager.OpEntry ent = entries.get(j);
+                System.out.print(AppOpsManager.opToName(ent.getOp()));
+                System.out.print(": ");
+                switch (ent.getMode()) {
+                    case AppOpsManager.MODE_ALLOWED:
+                        System.out.print("allow");
+                        break;
+                    case AppOpsManager.MODE_IGNORED:
+                        System.out.print("ignore");
+                        break;
+                    case AppOpsManager.MODE_ERRORED:
+                        System.out.print("deny");
+                        break;
+                    case AppOpsManager.MODE_DEFAULT:
+                        System.out.print("default");
+                        break;
+                    default:
+                        System.out.print("mode=");
+                        System.out.print(ent.getMode());
+                        break;
+                }
+                if (ent.getTime() != 0) {
+                    System.out.print("; time=");
+                    StringBuilder sb = new StringBuilder();
+                    TimeUtils.formatDuration(now - ent.getTime(), sb);
+                    System.out.print(sb);
+                    System.out.print(" ago");
+                }
+                if (ent.getRejectTime() != 0) {
+                    System.out.print("; rejectTime=");
+                    StringBuilder sb = new StringBuilder();
+                    TimeUtils.formatDuration(now - ent.getRejectTime(), sb);
+                    System.out.print(sb);
+                    System.out.print(" ago");
+                }
+                if (ent.getDuration() == -1) {
+                    System.out.print(" (running)");
+                } else if (ent.getDuration() != 0) {
+                    System.out.print("; duration=");
+                    StringBuilder sb = new StringBuilder();
+                    TimeUtils.formatDuration(ent.getDuration(), sb);
+                    System.out.print(sb);
+                }
+                System.out.println();
+            }
+        }
+    }
+
+    private void runReset() throws Exception {
+        String packageName = null;
+        int userId = UserHandle.USER_CURRENT;
+        for (String argument; (argument = nextArg()) != null;) {
+            if (ARGUMENT_USER.equals(argument)) {
+                String userStr = nextArgRequired();
+                if ("all".equals(userStr)) {
+                    userId = UserHandle.USER_ALL;
+                } else if ("current".equals(userStr)) {
+                    userId = UserHandle.USER_CURRENT;
+                } else if ("owner".equals(userStr)) {
+                    userId = UserHandle.USER_OWNER;
+                } else {
+                    userId = Integer.parseInt(nextArgRequired());
+                }
+            } else {
+                if (packageName == null) {
+                    packageName = argument;
+                } else {
+                    System.err.println("Error: Unsupported argument: " + argument);
+                    return;
+                }
+            }
+        }
+
+        // Parsing complete, let's execute the command.
+
+        if (userId == UserHandle.USER_CURRENT) {
+            userId = ActivityManager.getCurrentUser();
+        }
+
+        final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
+                ServiceManager.getService(Context.APP_OPS_SERVICE));
+        appOpsService.resetAllModes(userId, packageName);
+        System.out.print("Reset all modes for: ");
+        if (userId == UserHandle.USER_ALL) {
+            System.out.print("all users");
+        } else {
+            System.out.print("user "); System.out.print(userId);
+        }
+        System.out.print(", ");
+        if (packageName == null) {
+            System.out.println("all packages");
+        } else {
+            System.out.print("package "); System.out.println(packageName);
+        }
+    }
 }
index e18aa5c..5a44a74 100644 (file)
@@ -114,6 +114,15 @@ public class ValueAnimator extends Animator {
     private boolean mPlayingBackwards = false;
 
     /**
+     * Flag to indicate whether this animator is playing in reverse mode, specifically
+     * by being started or interrupted by a call to reverse(). This flag is different than
+     * mPlayingBackwards, which indicates merely whether the current iteration of the
+     * animator is playing in reverse. It is used in corner cases to determine proper end
+     * behavior.
+     */
+    private boolean mReversing;
+
+    /**
      * This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the
      * repeatCount (if repeatCount!=INFINITE), the animation ends
      */
@@ -545,21 +554,51 @@ public class ValueAnimator extends Animator {
      * Sets the position of the animation to the specified fraction. This fraction should
      * be between 0 and the total fraction of the animation, including any repetition. That is,
      * a fraction of 0 will position the animation at the beginning, a value of 1 at the end,
-     * and a value of 2 at the beginning of a reversing animator that repeats once. If
+     * and a value of 2 at the end of a reversing animator that repeats once. If
      * the animation has not yet been started, then it will not advance forward after it is
      * set to this fraction; it will simply set the fraction to this value and perform any
      * appropriate actions based on that fraction. If the animation is already running, then
      * setCurrentFraction() will set the current fraction to this value and continue
-     * playing from that point.
+     * playing from that point. {@link AnimatorListener} events are not called
+     * due to changing the fraction; those events are only processed while the animation
+     * is running.
      *
-     * @param fraction The fraction to which the animation is advanced or rewound.
+     * @param fraction The fraction to which the animation is advanced or rewound. Values
+     * outside the range of 0 to the maximum fraction for the animator will be clamped to
+     * the correct range.
      */
     public void setCurrentFraction(float fraction) {
         initAnimation();
+        if (fraction < 0) {
+            fraction = 0;
+        }
+        int iteration = (int) fraction;
+        if (fraction == 1) {
+            iteration -= 1;
+        } else if (fraction > 1) {
+            if (iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE) {
+                if (mRepeatMode == REVERSE) {
+                    mPlayingBackwards = (iteration % 2) != 0;
+                }
+                fraction = fraction % 1f;
+            } else {
+                fraction = 1;
+                iteration -= 1;
+            }
+        } else {
+            mPlayingBackwards = mReversing;
+        }
+        mCurrentIteration = iteration;
+        long seekTime = (long) (mDuration * fraction);
+        long currentTime = AnimationUtils.currentAnimationTimeMillis();
+        mStartTime = currentTime - seekTime;
         if (mPlayingState != RUNNING) {
             mSeekFraction = fraction;
             mPlayingState = SEEKED;
         }
+        if (mPlayingBackwards) {
+            fraction = 1f - fraction;
+        }
         animateValue(fraction);
     }
 
@@ -962,8 +1001,30 @@ public class ValueAnimator extends Animator {
         if (Looper.myLooper() == null) {
             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
         }
+        mReversing = playBackwards;
         mPlayingBackwards = playBackwards;
-        mCurrentIteration = 0;
+        if (playBackwards && mSeekFraction != -1) {
+            if (mSeekFraction == 0 && mCurrentIteration == 0) {
+                // special case: reversing from seek-to-0 should act as if not seeked at all
+                mSeekFraction = 0;
+            } else if (mRepeatCount == INFINITE) {
+                mSeekFraction = 1 - (mSeekFraction % 1);
+            } else {
+                mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
+            }
+            mCurrentIteration = (int) mSeekFraction;
+            mSeekFraction = mSeekFraction % 1;
+        }
+        if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
+                (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
+            // if we were seeked to some other iteration in a reversing animator,
+            // figure out the correct direction to start playing based on the iteration
+            if (playBackwards) {
+                mPlayingBackwards = (mCurrentIteration % 2) == 0;
+            } else {
+                mPlayingBackwards = (mCurrentIteration % 2) != 0;
+            }
+        }
         int prevPlayingState = mPlayingState;
         mPlayingState = STOPPED;
         mStarted = true;
@@ -1071,6 +1132,7 @@ public class ValueAnimator extends Animator {
             long currentPlayTime = currentTime - mStartTime;
             long timeLeft = mDuration - currentPlayTime;
             mStartTime = currentTime - timeLeft;
+            mReversing = !mReversing;
         } else if (mStarted) {
             end();
         } else {
@@ -1113,6 +1175,8 @@ public class ValueAnimator extends Animator {
         mStarted = false;
         mStartListenersCalled = false;
         mPlayingBackwards = false;
+        mReversing = false;
+        mCurrentIteration = 0;
         if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
             Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                     System.identityHashCode(this));
@@ -1201,8 +1265,16 @@ public class ValueAnimator extends Animator {
         case RUNNING:
         case SEEKED:
             float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
+            if (mDuration == 0 && mRepeatCount != INFINITE) {
+                // Skip to the end
+                mCurrentIteration = mRepeatCount;
+                if (!mReversing) {
+                    mPlayingBackwards = false;
+                }
+            }
             if (fraction >= 1f) {
-                if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
+                if (mCurrentIteration < mRepeatCount ||
+                        (mRepeatCount == INFINITE && mDuration != 0)) {
                     // Time to repeat
                     if (mListeners != null) {
                         int numListeners = mListeners.size();
@@ -1213,7 +1285,7 @@ public class ValueAnimator extends Animator {
                     if (mRepeatMode == REVERSE) {
                         mPlayingBackwards = !mPlayingBackwards;
                     }
-                    mCurrentIteration += (int)fraction;
+                    mCurrentIteration += (int) fraction;
                     fraction = fraction % 1f;
                     mStartTime += mDuration;
                 } else {
@@ -1313,6 +1385,7 @@ public class ValueAnimator extends Animator {
         }
         anim.mSeekFraction = -1;
         anim.mPlayingBackwards = false;
+        anim.mReversing = false;
         anim.mCurrentIteration = 0;
         anim.mInitialized = false;
         anim.mPlayingState = STOPPED;
index 6ec48e5..e8d08b8 100644 (file)
@@ -774,6 +774,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
             return true;
         }
 
+        case REGISTER_TASK_STACK_LISTENER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            registerTaskStackListener(ITaskStackListener.Stub.asInterface(token));
+            reply.writeNoException();
+            return true;
+        }
+
         case GET_TASK_FOR_ACTIVITY_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
@@ -2355,6 +2363,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
             reply.writeNoException();
             return true;
         }
+
+        case SYSTEM_BACKUP_RESTORED: {
+            data.enforceInterface(IActivityManager.descriptor);
+            systemBackupRestored();
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3266,6 +3281,18 @@ class ActivityManagerProxy implements IActivityManager
         data.recycle();
         reply.recycle();
     }
+    @Override
+    public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(listener.asBinder());
+        mRemote.transact(REGISTER_TASK_STACK_LISTENER_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
     public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -5438,5 +5465,16 @@ class ActivityManagerProxy implements IActivityManager
         reply.recycle();
     }
 
+    @Override
+    public void systemBackupRestored() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(SYSTEM_BACKUP_RESTORED, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
index b64e724..6b4db10 100644 (file)
@@ -2436,7 +2436,7 @@ public final class ActivityThread {
     private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) {
         final int N = intents.size();
         for (int i=0; i<N; i++) {
-            Intent intent = intents.get(i);
+            ReferrerIntent intent = intents.get(i);
             intent.setExtrasClassLoader(r.activity.getClassLoader());
             intent.prepareToEnterProcess();
             r.activity.mFragments.noteStateNotSaved();
index 9062892..d0d9d71 100644 (file)
@@ -641,17 +641,18 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
         Matrix tempMatrix = new Matrix();
         for (String name: names) {
             Bundle sharedElementBundle = state.getBundle(name);
+            View snapshot = null;
             if (sharedElementBundle != null) {
                 Parcelable parcelable = sharedElementBundle.getParcelable(KEY_SNAPSHOT);
-                View snapshot = null;
                 if (parcelable != null && mListener != null) {
                     snapshot = mListener.onCreateSnapshotView(context, parcelable);
                 }
                 if (snapshot != null) {
                     setSharedElementState(snapshot, name, state, tempMatrix, null, decorLoc);
                 }
-                snapshots.add(snapshot);
             }
+            // Even null snapshots are added so they remain in the same order as shared elements.
+            snapshots.add(snapshot);
         }
         return snapshots;
     }
index ba9c9d6..95870cf 100644 (file)
@@ -26,6 +26,7 @@ import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
 
@@ -734,6 +735,18 @@ public class AppOpsManager {
     }
 
     /**
+     * @hide
+     */
+    public static int strDebugOpToOp(String op) {
+        for (int i=0; i<sOpNames.length; i++) {
+            if (sOpNames[i].equals(op)) {
+                return i;
+            }
+        }
+        throw new IllegalArgumentException("Unknown operation string: " + op);
+    }
+
+    /**
      * Retrieve the permission associated with an operation, or null if there is not one.
      * @hide
      */
@@ -996,7 +1009,7 @@ public class AppOpsManager {
     /** @hide */
     public void resetAllModes() {
         try {
-            mService.resetAllModes();
+            mService.resetAllModes(UserHandle.myUserId(), null);
         } catch (RemoteException e) {
         }
     }
index 49ab7c1..6c2511e 100644 (file)
@@ -168,10 +168,20 @@ public class ApplicationErrorReport implements Parcelable {
         PackageManager pm = context.getPackageManager();
 
         // look for receiver in the installer package
-        String candidate = pm.getInstallerPackageName(packageName);
-        ComponentName result = getErrorReportReceiver(pm, packageName, candidate);
-        if (result != null) {
-            return result;
+        String candidate = null;
+        ComponentName result = null;
+
+        try {
+            candidate = pm.getInstallerPackageName(packageName);
+        } catch (IllegalArgumentException e) {
+            // the package could already removed
+        }
+
+        if (candidate != null) {
+            result = getErrorReportReceiver(pm, packageName, candidate);
+            if (result != null) {
+                return result;
+            }
         }
 
         // if the error app is on the system image, look for system apps
index 5362303..e505d69 100644 (file)
@@ -139,6 +139,7 @@ public interface IActivityManager extends IInterface {
     public StackInfo getStackInfo(int stackId) throws RemoteException;
     public boolean isInHomeStack(int taskId) throws RemoteException;
     public void setFocusedStack(int stackId) throws RemoteException;
+    public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException;
     public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
     public ContentProviderHolder getContentProvider(IApplicationThread caller,
             String name, int userId, boolean stable) throws RemoteException;
@@ -468,6 +469,8 @@ public interface IActivityManager extends IInterface {
     public void notifyLaunchTaskBehindComplete(IBinder token) throws RemoteException;
     public void notifyEnterAnimationComplete(IBinder token) throws RemoteException;
 
+    public void systemBackupRestored() throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -788,4 +791,6 @@ public interface IActivityManager extends IInterface {
     int LAUNCH_ASSIST_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+239;
     int START_IN_PLACE_ANIMATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+240;
     int CHECK_PERMISSION_WITH_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+241;
+    int REGISTER_TASK_STACK_LISTENER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+242;
+    int SYSTEM_BACKUP_RESTORED = IBinder.FIRST_CALL_TRANSACTION+243;
 }
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.service.carriermessaging;
+package android.app;
 
-parcelable CarrierMessagingService.SendSmsResponse;
\ No newline at end of file
+/** @hide */
+oneway interface ITaskStackListener {
+    void onTaskStackChanged();
+}
index d96153a..3c30404 100644 (file)
@@ -1208,14 +1208,17 @@ public class Instrumentation {
      * @param intent The new intent being received.
      */
     public void callActivityOnNewIntent(Activity activity, Intent intent) {
+        activity.onNewIntent(intent);
+    }
+
+    /**
+     * @hide
+     */
+    public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent) {
         final String oldReferrer = activity.mReferrer;
         try {
-            try {
-                activity.mReferrer = ((ReferrerIntent)intent).mReferrer;
-            } catch (ClassCastException e) {
-                activity.mReferrer = null;
-            }
-            activity.onNewIntent(intent);
+            activity.mReferrer = intent.mReferrer;
+            callActivityOnNewIntent(activity, new Intent(intent));
         } finally {
             activity.mReferrer = oldReferrer;
         }
index 6454367..3a2c21b 100644 (file)
@@ -134,6 +134,9 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener,
                             mTimePicker.getCurrentMinute());
                 }
                 break;
+            case BUTTON_NEGATIVE:
+                cancel();
+                break;
         }
     }
 
index 0e1c85c..4753099 100644 (file)
@@ -1481,8 +1481,8 @@ public class DevicePolicyManager {
      * Flag for {@link #wipeData(int)}: also erase the factory reset protection
      * data.
      *
-     * This flag may only be set by device owner admins; if it is set by other
-     * admins a {@link SecurityException} will be thrown.
+     * <p>This flag may only be set by device owner admins; if it is set by
+     * other admins a {@link SecurityException} will be thrown.
      */
     public static final int WIPE_RESET_PROTECTION_DATA = 0x0002;
 
index a55cc2b..7a80936 100644 (file)
@@ -102,9 +102,8 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu
         final Display d = wm.getDefaultDisplay();
         final Point size = new Point();
         d.getSize(size);
-        mDesiredMinWidth = size.x;
+        mDesiredMinWidth = Math.min(size.x, size.y);
         mDesiredMinHeight = (double) wpm.getDesiredMinimumHeight();
-
         if (mDesiredMinHeight <= 0) {
             mDesiredMinHeight = size.y;
         }
@@ -149,9 +148,13 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu
                         // We accept any wallpaper that is at least as wide as our preference
                         // (i.e. wide enough to fill the screen), and is within a comfortable
                         // factor of the target height, to avoid significant clipping/scaling/
-                        // letterboxing.
+                        // letterboxing.  At this point we know that mDesiredMinWidth is the
+                        // smallest dimension, regardless of current orientation, so we can
+                        // safely require that the candidate's width and height both exceed
+                        // that hard minimum.
                         final double heightRatio = mDesiredMinHeight / options.outHeight;
                         if (options.outWidth < mDesiredMinWidth
+                                || options.outHeight < mDesiredMinWidth
                                 || heightRatio >= MAX_HEIGHT_RATIO
                                 || heightRatio <= MIN_HEIGHT_RATIO) {
                             // Not wide enough for the screen, or too short/tall to be a good fit
index c203a8e..ea2dca0 100644 (file)
@@ -928,7 +928,7 @@ public final class BluetoothGatt implements BluetoothProfile {
                 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
 
         if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
-        if (mService == null || mClientIf == 0) return false;
+        if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
 
         BluetoothGattService service = characteristic.getService();
         if (service == null) return false;
@@ -1015,7 +1015,7 @@ public final class BluetoothGatt implements BluetoothProfile {
      */
     public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
         if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
-        if (mService == null || mClientIf == 0) return false;
+        if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
 
         BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
         if (characteristic == null) return false;
index e94a8ce..f451340 100644 (file)
@@ -284,6 +284,24 @@ public final class BluetoothGattServer implements BluetoothProfile {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
             }
+
+            /**
+             * The MTU for a connection has changed
+             * @hide
+             */
+            public void onMtuChanged(String address, int mtu) {
+                if (DBG) Log.d(TAG, "onMtuChanged() - "
+                    + "device=" + address + ", mtu=" + mtu);
+
+                BluetoothDevice device = mAdapter.getRemoteDevice(address);
+                if (device == null) return;
+
+                try {
+                    mCallback.onMtuChanged(device, mtu);
+                } catch (Exception ex) {
+                    Log.w(TAG, "Unhandled exception: " + ex);
+                }
+            }
         };
 
     /**
index 1dd06f2..2afcf9a 100644 (file)
@@ -145,4 +145,16 @@ public abstract class BluetoothGattServerCallback {
      */
     public void onNotificationSent(BluetoothDevice device, int status) {
     }
+
+    /**
+     * Callback indicating the MTU for a given device connection has changed.
+     *
+     * <p>This callback will be invoked if a remote client has requested to change
+     * the MTU for a given connection.
+     *
+     * @param device The remote device that requested the MTU change
+     * @param mtu The new MTU size
+     */
+    public void onMtuChanged(BluetoothDevice device, int mtu) {
+    }
 }
index 5d4d6c6..8b202b2 100644 (file)
@@ -59,4 +59,5 @@ oneway interface IBluetoothGattServerCallback {
                                      in byte[] value);
     void onExecuteWrite(in String address, in int transId, in boolean execWrite);
     void onNotificationSent(in String address, in int status);
+    void onMtuChanged(in String address, in int mtu);
 }
index 5c705e6..e9f7c50 100644 (file)
@@ -1078,26 +1078,6 @@ public abstract class PackageManager {
             "android.hardware.camera.capability.raw";
 
     /**
-     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
-     * of the cameras on the device supports the
-     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}
-     * capability level.
-     */
-    @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_CAMERA_CAPABILITY_BURST_CAPTURE =
-            "android.hardware.camera.capability.burst_capture";
-
-    /**
-     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
-     * of the cameras on the device supports the
-     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS READ_SENSOR_SETTINGS}
-     * capability level.
-     */
-    @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_CAMERA_CAPABILITY_READ_SENSOR_SETTINGS =
-            "android.hardware.camera.capability.read_sensor_settings";
-
-    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device is capable of communicating with
      * consumer IR devices.
index 82da7c5..d7d9e8b 100644 (file)
@@ -3528,6 +3528,10 @@ public class PackageParser {
             outError[0] = "<provider> does not include authorities attribute";
             return null;
         }
+        if (cpname.length() <= 0) {
+            outError[0] = "<provider> has empty authorities attribute";
+            return null;
+        }
         p.info.authority = cpname.intern();
 
         if (!parseProviderTags(res, parser, attrs, p, outError)) {
index 78d3e9c..73913b6 100644 (file)
@@ -744,7 +744,10 @@ public class Resources {
      * @throws NotFoundException Throws NotFoundException if the given ID does
      *         not exist.
      * @see #getDrawable(int, Theme)
+     * @deprecated Use {@link #getDrawable(int, Theme)} instead.
      */
+    @Deprecated
+    @Nullable
     public Drawable getDrawable(int id) throws NotFoundException {
         final Drawable d = getDrawable(id, null);
         if (d != null && d.canApplyTheme()) {
@@ -769,6 +772,7 @@ public class Resources {
      * @throws NotFoundException Throws NotFoundException if the given ID does
      *         not exist.
      */
+    @Nullable
     public Drawable getDrawable(int id, @Nullable Theme theme) throws NotFoundException {
         TypedValue value;
         synchronized (mAccessLock) {
@@ -813,7 +817,10 @@ public class Resources {
      * @throws NotFoundException Throws NotFoundException if the given ID does
      *             not exist.
      * @see #getDrawableForDensity(int, int, Theme)
+     * @deprecated Use {@link #getDrawableForDensity(int, int, Theme)} instead.
      */
+    @Deprecated
+    @Nullable
     public Drawable getDrawableForDensity(int id, int density) throws NotFoundException {
         return getDrawableForDensity(id, density, null);
     }
@@ -832,6 +839,7 @@ public class Resources {
      * @throws NotFoundException Throws NotFoundException if the given ID does
      *             not exist.
      */
+    @Nullable
     public Drawable getDrawableForDensity(int id, int density, @Nullable Theme theme) {
         TypedValue value;
         synchronized (mAccessLock) {
index 98096dc..f487f01 100644 (file)
@@ -337,8 +337,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
      * supported by a given camera device. This field lists the
      * valid anti-banding modes that the application may request
      * for this camera device with the
-     * {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} control. This list
-     * always includes AUTO.</p>
+     * {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} control.</p>
      * <p><b>Range of valid values:</b><br>
      * Any value listed in {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode}</p>
      * <p>This key is available on all devices.</p>
index 895ae04..999de8a 100644 (file)
@@ -493,9 +493,11 @@ public abstract class CameraMetadata<TKey> {
      * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}, the minimum frame
      * duration for that format and size is &lt;= 1/20 s, and
      * the {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges} entry
-     * lists at least one FPS range where the minimum FPS
-     * is &gt;= 1 / minimumFrameDuration for the maximum-size
+     * lists at least one FPS range where the minimum FPS is</p>
+     * <blockquote>
+     * <p>= 1 / minimumFrameDuration for the maximum-size
      * YUV_420_888 format.</p>
+     * </blockquote>
      * <p>In addition, the {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} field is
      * guaranted to have a value between 0 and 4, inclusive.</p>
      *
@@ -847,7 +849,8 @@ public abstract class CameraMetadata<TKey> {
     /**
      * <p>The camera device will automatically adapt its
      * antibanding routine to the current illumination
-     * conditions. This is the default.</p>
+     * condition. This is the default mode if AUTO is
+     * available on given camera device.</p>
      * @see CaptureRequest#CONTROL_AE_ANTIBANDING_MODE
      */
     public static final int CONTROL_AE_ANTIBANDING_MODE_AUTO = 3;
index 48af67c..e89a402 100644 (file)
@@ -642,8 +642,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
      * options for the antibanding mode. The
      * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_ANTIBANDING_MODES android.control.aeAvailableAntibandingModes} key contains
      * the available modes for a given camera device.</p>
-     * <p>The default mode is AUTO, which is supported by all
-     * camera devices.</p>
+     * <p>AUTO mode is the default if it is available on given
+     * camera device. When AUTO mode is not available, the
+     * default will be either 50HZ or 60HZ, and both 50HZ
+     * and 60HZ will be available.</p>
      * <p>If manual exposure control is enabled (by setting
      * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} to OFF),
      * then this setting has no effect, and the application must
index c5c843d..2c3b85f 100644 (file)
@@ -493,8 +493,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
      * options for the antibanding mode. The
      * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_ANTIBANDING_MODES android.control.aeAvailableAntibandingModes} key contains
      * the available modes for a given camera device.</p>
-     * <p>The default mode is AUTO, which is supported by all
-     * camera devices.</p>
+     * <p>AUTO mode is the default if it is available on given
+     * camera device. When AUTO mode is not available, the
+     * default will be either 50HZ or 60HZ, and both 50HZ
+     * and 60HZ will be available.</p>
      * <p>If manual exposure control is enabled (by setting
      * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} to OFF),
      * then this setting has no effect, and the application must
index a16e878..c708d20 100644 (file)
@@ -22,7 +22,7 @@ package android.hardware.hdmi;
  *
  * @hide
  */
-oneway interface IHdmiVendorCommandListener {
+interface IHdmiVendorCommandListener {
     void onReceived(int logicalAddress, int destAddress, in byte[] operands, boolean hasVendorId);
     void onControlStateChanged(boolean enabled, int reason);
 }
index 5c7a8da..6e5d064 100644 (file)
@@ -23,6 +23,7 @@ import android.location.IGpsGeofenceHardware;
 import android.location.Location;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IInterface;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -30,6 +31,7 @@ import android.util.Log;
 import android.util.SparseArray;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 
 /**
  * This class manages the geofences which are handled by hardware.
@@ -558,8 +560,34 @@ public final class GeofenceHardwareImpl {
                         try {
                             callback.onGeofenceRemove(geofenceId, msg.arg2);
                         } catch (RemoteException e) {}
+                        IBinder callbackBinder = callback.asBinder();
+                        boolean callbackInUse = false;
                         synchronized (mGeofences) {
                             mGeofences.remove(geofenceId);
+                            // Check if the underlying binder is still useful for other geofences,
+                            // if no, unlink the DeathRecipient to avoid memory leak.
+                            for (int i = 0; i < mGeofences.size(); i++) {
+                                 if (mGeofences.valueAt(i).asBinder() == callbackBinder) {
+                                     callbackInUse = true;
+                                     break;
+                                 }
+                            }
+                        }
+
+                        // Remove the reaper associated with this binder.
+                        if (!callbackInUse) {
+                            for (Iterator<Reaper> iterator = mReapers.iterator();
+                                    iterator.hasNext();) {
+                                Reaper reaper = iterator.next();
+                                if (reaper.mCallback != null &&
+                                        reaper.mCallback.asBinder() == callbackBinder) {
+                                    iterator.remove();
+                                    reaper.unlinkToDeath();
+                                    if (DEBUG) Log.d(TAG, String.format("Removed reaper %s " +
+                                          "because binder %s is no longer needed.",
+                                          reaper, callbackBinder));
+                                }
+                            }
                         }
                     }
                     releaseWakeLock();
@@ -803,8 +831,9 @@ public final class GeofenceHardwareImpl {
         @Override
         public int hashCode() {
             int result = 17;
-            result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0);
-            result = 31 * result + (mMonitorCallback != null ? mMonitorCallback.hashCode() : 0);
+            result = 31 * result + (mCallback != null ? mCallback.asBinder().hashCode() : 0);
+            result = 31 * result + (mMonitorCallback != null
+                    ? mMonitorCallback.asBinder().hashCode() : 0);
             result = 31 * result + mMonitoringType;
             return result;
         }
@@ -815,9 +844,38 @@ public final class GeofenceHardwareImpl {
             if (obj == this) return true;
 
             Reaper rhs = (Reaper) obj;
-            return rhs.mCallback == mCallback && rhs.mMonitorCallback == mMonitorCallback &&
+            return binderEquals(rhs.mCallback, mCallback) &&
+                    binderEquals(rhs.mMonitorCallback, mMonitorCallback) &&
                     rhs.mMonitoringType == mMonitoringType;
         }
+
+        /**
+         * Compares the underlying Binder of the given two IInterface objects and returns true if
+         * they equals. null values are accepted.
+         */
+        private boolean binderEquals(IInterface left, IInterface right) {
+          if (left == null) {
+            return right == null;
+          } else {
+            return right == null ? false : left.asBinder() == right.asBinder();
+          }
+        }
+
+        /**
+         * Unlinks this DeathRecipient.
+         */
+        private boolean unlinkToDeath() {
+          if (mMonitorCallback != null) {
+            return mMonitorCallback.asBinder().unlinkToDeath(this, 0);
+          } else if (mCallback != null) {
+            return mCallback.asBinder().unlinkToDeath(this, 0);
+          }
+          return true;
+        }
+
+        private boolean callbackEquals(IGeofenceHardwareCallback cb) {
+          return mCallback != null && mCallback.asBinder() == cb.asBinder();
+        }
     }
 
     int getAllowedResolutionLevel(int pid, int uid) {
index 3175345..b777e8c 100644 (file)
@@ -983,7 +983,7 @@ public class KeyboardView extends View implements View.OnClickListener {
             // This is very efficient since the properties are cached.
             final boolean speakPassword = Settings.Secure.getIntForUser(
                     mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0,
-                    UserHandle.USER_CURRENT) != 0;
+                    UserHandle.USER_CURRENT_OR_SELF) != 0;
             // Add text only if password announcement is enabled or if headset is
             // used to avoid leaking passwords.
             if (speakPassword || mAudioManager.isBluetoothA2dpOn()
index 19eca29..4fe418a 100644 (file)
@@ -71,7 +71,6 @@ import libcore.net.event.NetworkEventDispatcher;
  */
 public class ConnectivityManager {
     private static final String TAG = "ConnectivityManager";
-    private static final boolean LEGACY_DBG = true; // STOPSHIP
 
     /**
      * A change in network connectivity has occurred. A default connection has either
@@ -101,7 +100,7 @@ public class ConnectivityManager {
 
     /**
      * Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any
-     * applicable {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
+     * historic {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
      *
      * @hide
      */
@@ -429,18 +428,6 @@ public class ConnectivityManager {
     public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
 
     /**
-     * Default value for {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY} in
-     * milliseconds.  This was introduced because IPv6 routes seem to take a
-     * moment to settle - trying network activity before the routes are adjusted
-     * can lead to packets using the wrong interface or having the wrong IP address.
-     * This delay is a bit crude, but in the future hopefully we will have kernel
-     * notifications letting us know when it's safe to use the new network.
-     *
-     * @hide
-     */
-    public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
-
-    /**
      * @hide
      */
     public final static int REQUEST_ID_UNSET = 0;
@@ -722,6 +709,19 @@ public class ConnectivityManager {
     }
 
     /**
+     * Returns an array of of {@link NetworkCapabilities} objects, representing
+     * the Networks that applications run by the given user will use by default.
+     * @hide
+     */
+    public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
+        try {
+            return mService.getDefaultNetworkCapabilitiesForUser(userId);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns details about the Provisioning or currently active default data network. When
      * connected, this network is the default route for outgoing connections.
      * You should always check {@link NetworkInfo#isConnected()} before initiating
@@ -880,14 +880,6 @@ public class ConnectivityManager {
 
         NetworkRequest request = null;
         synchronized (sLegacyRequests) {
-            if (LEGACY_DBG) {
-                Log.d(TAG, "Looking for legacyRequest for netCap with hash: " + netCap + " (" +
-                        netCap.hashCode() + ")");
-                Log.d(TAG, "sLegacyRequests has:");
-                for (NetworkCapabilities nc : sLegacyRequests.keySet()) {
-                    Log.d(TAG, "  " + nc + " (" + nc.hashCode() + ")");
-                }
-            }
             LegacyRequest l = sLegacyRequests.get(netCap);
             if (l != null) {
                 Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest);
index 79f920e..8021210 100644 (file)
@@ -49,6 +49,7 @@ interface IConnectivityManager
     NetworkInfo[] getAllNetworkInfo();
     Network getNetworkForType(int networkType);
     Network[] getAllNetworks();
+    NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId);
 
     NetworkInfo getProvisioningOrActiveNetworkInfo();
 
index ce7ad65..a7f9c5b 100644 (file)
@@ -154,9 +154,16 @@ public final class NetworkCapabilities implements Parcelable {
      */
     public static final int NET_CAPABILITY_NOT_VPN        = 15;
 
+    /**
+     * Indicates that connectivity on this network was successfully validated. For example, for a
+     * network with NET_CAPABILITY_INTERNET, it means that Internet connectivity was successfully
+     * detected.
+     * @hide
+     */
+    public static final int NET_CAPABILITY_VALIDATED      = 16;
 
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VPN;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VALIDATED;
 
     /**
      * Adds the given capability to this {@code NetworkCapability} instance.
index 78b9c18..f6b6978 100644 (file)
@@ -291,16 +291,16 @@ public class VpnService extends Service {
     /**
      * Sets the underlying networks used by the VPN for its upstream connections.
      *
-     * Used by the system to know the actual networks that carry traffic for apps affected by this
-     * VPN in order to present this information to the user (e.g., via status bar icons).
+     * <p>Used by the system to know the actual networks that carry traffic for apps affected by
+     * this VPN in order to present this information to the user (e.g., via status bar icons).
      *
-     * This method only needs to be called if the VPN has explicitly bound its underlying
+     * <p>This method only needs to be called if the VPN has explicitly bound its underlying
      * communications channels &mdash; such as the socket(s) passed to {@link #protect(int)} &mdash;
      * to a {@code Network} using APIs such as {@link Network#bindSocket(Socket)} or
      * {@link Network#bindSocket(DatagramSocket)}. The VPN should call this method every time
      * the set of {@code Network}s it is using changes.
      *
-     * {@code networks} is one of the following:
+     * <p>{@code networks} is one of the following:
      * <ul>
      * <li><strong>a non-empty array</strong>: an array of one or more {@link Network}s, in
      * decreasing preference order. For example, if this VPN uses both wifi and mobile (cellular)
@@ -310,11 +310,11 @@ public class VpnService extends Service {
      * underlying network connection, and thus, app traffic will not be sent or received.</li>
      * <li><strong>null</strong>: (default) signifies that the VPN uses whatever is the system's
      * default network. I.e., it doesn't use the {@code bindSocket} or {@code bindDatagramSocket}
-     * APIs mentioned above to send traffic over specific channels.
+     * APIs mentioned above to send traffic over specific channels.</li>
      * </ul>
      *
-     * This call will succeed only if the VPN is currently established. For setting this value when
-     * the VPN has not yet been established, see {@link Builder#setUnderlyingNetworks}.
+     * <p>This call will succeed only if the VPN is currently established. For setting this value
+     * when the VPN has not yet been established, see {@link Builder#setUnderlyingNetworks}.
      *
      * @param networks An array of networks the VPN uses to tunnel traffic to/from its servers.
      *
index 4709443..11fc69e 100644 (file)
@@ -91,11 +91,6 @@ public abstract class BatteryStats implements Parcelable {
      public static final int WIFI_MULTICAST_ENABLED = 7;
 
     /**
-     * A constant indicating an audio turn on timer
-     */
-    public static final int AUDIO_TURNED_ON = 7;
-
-    /**
      * A constant indicating a video turn on timer
      */
     public static final int VIDEO_TURNED_ON = 8;
@@ -131,6 +126,11 @@ public abstract class BatteryStats implements Parcelable {
     public static final int JOB = 14;
 
     /**
+     * A constant indicating an audio turn on timer
+     */
+    public static final int AUDIO_TURNED_ON = 15;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
index fe47f5b..0a724a1 100644 (file)
@@ -17,9 +17,8 @@
 package android.os;
 
 import android.system.ErrnoException;
-import android.text.TextUtils;
 import android.system.Os;
-import android.system.OsConstants;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
@@ -403,20 +402,89 @@ public class FileUtils {
         return success;
     }
 
+    private static boolean isValidExtFilenameChar(char c) {
+        switch (c) {
+            case '\0':
+            case '/':
+                return false;
+            default:
+                return true;
+        }
+    }
+
     /**
-     * Assert that given filename is valid on ext4.
+     * Check if given filename is valid for an ext4 filesystem.
      */
     public static boolean isValidExtFilename(String name) {
+        return (name != null) && name.equals(buildValidExtFilename(name));
+    }
+
+    /**
+     * Mutate the given filename to make it valid for an ext4 filesystem,
+     * replacing any invalid characters with "_".
+     */
+    public static String buildValidExtFilename(String name) {
         if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
-            return false;
+            return "(invalid)";
         }
+        final StringBuilder res = new StringBuilder(name.length());
         for (int i = 0; i < name.length(); i++) {
             final char c = name.charAt(i);
-            if (c == '\0' || c == '/') {
+            if (isValidExtFilenameChar(c)) {
+                res.append(c);
+            } else {
+                res.append('_');
+            }
+        }
+        return res.toString();
+    }
+
+    private static boolean isValidFatFilenameChar(char c) {
+        if ((0x00 <= c && c <= 0x1f)) {
+            return false;
+        }
+        switch (c) {
+            case '"':
+            case '*':
+            case '/':
+            case ':':
+            case '<':
+            case '>':
+            case '?':
+            case '\\':
+            case '|':
+            case 0x7F:
                 return false;
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * Check if given filename is valid for a FAT filesystem.
+     */
+    public static boolean isValidFatFilename(String name) {
+        return (name != null) && name.equals(buildValidFatFilename(name));
+    }
+
+    /**
+     * Mutate the given filename to make it valid for a FAT filesystem,
+     * replacing any invalid characters with "_".
+     */
+    public static String buildValidFatFilename(String name) {
+        if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
+            return "(invalid)";
+        }
+        final StringBuilder res = new StringBuilder(name.length());
+        for (int i = 0; i < name.length(); i++) {
+            final char c = name.charAt(i);
+            if (isValidFatFilenameChar(c)) {
+                res.append(c);
+            } else {
+                res.append('_');
             }
         }
-        return true;
+        return res.toString();
     }
 
     public static String rewriteAfterRename(File beforeDir, File afterDir, String path) {
index 5e5fbc7..3bc74ae 100644 (file)
@@ -6118,7 +6118,7 @@ public final class Settings {
 
         /**
          * The number of milliseconds to delay before sending out
-         * {@link ConnectivityManager#CONNECTIVITY_ACTION} broadcasts.
+         * {@link ConnectivityManager#CONNECTIVITY_ACTION} broadcasts. Ignored.
          *
          * @hide
          */
@@ -6608,6 +6608,14 @@ public final class Settings {
         public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
 
         /**
+         * Global override to disable VoLTE (independent of user setting)
+         * <p>
+         * Type: int (1 for disable VoLTE, 0 to use user configuration)
+         * @hide
+         */
+        public static final String VOLTE_FEATURE_DISABLED = "volte_feature_disabled";
+
+        /**
          * Whether user can enable/disable LTE as a preferred network. A carrier might control
          * this via gservices, OMA-DM, carrier app, etc.
          * <p>
diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java
new file mode 100644 (file)
index 0000000..3d6ebca
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * 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.service.carrier;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A service that receives calls from the system when new SMS and MMS are
+ * sent or received.
+ * <p>To extend this class, you must declare the service in your manifest file with
+ * the {@link android.Manifest.permission#BIND_CARRIER_MESSAGING_SERVICE} permission
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
+ * <pre>
+ * &lt;service android:name=".MyMessagingService"
+ *          android:label="&#64;string/service_name"
+ *          android:permission="android.permission.BIND_CARRIER_MESSAGING_SERVICE">
+ *     &lt;intent-filter>
+ *         &lt;action android:name="android.service.carrier.CarrierMessagingService" />
+ *     &lt;/intent-filter>
+ * &lt;/service></pre>
+ */
+public abstract class CarrierMessagingService extends Service {
+    /**
+     * The {@link android.content.Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE
+            = "android.service.carrier.CarrierMessagingService";
+
+    /**
+     * Indicates that an SMS or MMS message was successfully sent.
+     */
+    public static final int SEND_STATUS_OK = 0;
+
+    /**
+     * SMS/MMS sending failed. We should retry via the carrier network.
+     */
+    public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
+
+    /**
+     * SMS/MMS sending failed. We should not retry via the carrier network.
+     */
+    public static final int SEND_STATUS_ERROR = 2;
+
+    /**
+     * Successfully downloaded an MMS message.
+     */
+    public static final int DOWNLOAD_STATUS_OK = 0;
+
+    /**
+     * MMS downloading failed. We should retry via the carrier network.
+     */
+    public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
+
+    /**
+     * MMS downloading failed. We should not retry via the carrier network.
+     */
+    public static final int DOWNLOAD_STATUS_ERROR = 2;
+
+    private final ICarrierMessagingWrapper mWrapper = new ICarrierMessagingWrapper();
+
+    /**
+     * Override this method to filter inbound SMS messages.
+     *
+     * @param pdu the PDUs of the message
+     * @param format the format of the PDUs, typically "3gpp" or "3gpp2"
+     * @param destPort the destination port of a binary SMS, this will be -1 for text SMS
+     * @param subId SMS subscription ID of the SIM
+     * @param callback result callback. Call with {@code true} to keep an inbound SMS message and
+     *        deliver to SMS apps, and {@code false} to drop the message.
+     */
+    public void onFilterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
+            int subId, @NonNull ResultCallback<Boolean> callback) {
+        // optional
+        try {
+            callback.onReceiveResult(true);
+        } catch (RemoteException ex) {
+        }
+    }
+
+    /**
+     * Override this method to intercept text SMSs sent from the device.
+     *
+     * @param text the text to send
+     * @param subId SMS subscription ID of the SIM
+     * @param destAddress phone number of the recipient of the message
+     * @param callback result callback. Call with a {@link SendSmsResult}.
+     */
+    public void onSendTextSms(
+            @NonNull String text, int subId, @NonNull String destAddress,
+            @NonNull ResultCallback<SendSmsResult> callback) {
+        // optional
+        try {
+            callback.onReceiveResult(new SendSmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 0));
+        } catch (RemoteException ex) {
+        }
+    }
+
+    /**
+     * Override this method to intercept binary SMSs sent from the device.
+     *
+     * @param data the binary content
+     * @param subId SMS subscription ID of the SIM
+     * @param destAddress phone number of the recipient of the message
+     * @param destPort the destination port
+     * @param callback result callback. Call with a {@link SendSmsResult}.
+     */
+    public void onSendDataSms(@NonNull byte[] data, int subId,
+            @NonNull String destAddress, int destPort,
+            @NonNull ResultCallback<SendSmsResult> callback) {
+        // optional
+        try {
+            callback.onReceiveResult(new SendSmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 0));
+        } catch (RemoteException ex) {
+        }
+    }
+
+    /**
+     * Override this method to intercept long SMSs sent from the device.
+     *
+     * @param parts a {@link List} of the message parts
+     * @param subId SMS subscription ID of the SIM
+     * @param destAddress phone number of the recipient of the message
+     * @param callback result callback. Call with a {@link SendMultipartSmsResult}.
+     */
+    public void onSendMultipartTextSms(@NonNull List<String> parts,
+            int subId, @NonNull String destAddress,
+            @NonNull ResultCallback<SendMultipartSmsResult> callback) {
+        // optional
+        try {
+            callback.onReceiveResult(
+                    new SendMultipartSmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null));
+        } catch (RemoteException ex) {
+        }
+    }
+
+    /**
+     * Override this method to intercept MMSs sent from the device.
+     *
+     * @param pduUri the content provider URI of the PDU to send
+     * @param subId SMS subscription ID of the SIM
+     * @param location the optional URI to send this MMS PDU. If this is {code null},
+     *        the PDU should be sent to the default MMSC URL.
+     * @param callback result callback. Call with a {@link SendMmsResult}.
+     */
+    public void onSendMms(@NonNull Uri pduUri, int subId,
+            @Nullable Uri location, @NonNull ResultCallback<SendMmsResult> callback) {
+        // optional
+        try {
+            callback.onReceiveResult(new SendMmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null));
+        } catch (RemoteException ex) {
+        }
+    }
+
+    /**
+     * Override this method to download MMSs received.
+     *
+     * @param contentUri the content provider URI of the PDU to be downloaded.
+     * @param subId SMS subscription ID of the SIM
+     * @param location the URI of the message to be downloaded.
+     * @param callback result callback. Call with a status code which is one of
+     *        {@link #DOWNLOAD_STATUS_OK},
+     *        {@link #DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK}, or {@link #DOWNLOAD_STATUS_ERROR}.
+     */
+    public void onDownloadMms(@NonNull Uri contentUri, int subId, @NonNull Uri location,
+            @NonNull ResultCallback<Integer> callback) {
+        // optional
+        try {
+            callback.onReceiveResult(DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK);
+        } catch (RemoteException ex) {
+        }
+    }
+
+    @Override
+    public @Nullable IBinder onBind(@NonNull Intent intent) {
+        if (!SERVICE_INTERFACE.equals(intent.getAction())) {
+            return null;
+        }
+        return mWrapper;
+    }
+
+    /**
+     * The result of sending an MMS.
+     */
+    public static final class SendMmsResult {
+        private int mSendStatus;
+        private byte[] mSendConfPdu;
+
+        /**
+         * Constructs a SendMmsResult with the MMS send result, and the SendConf PDU.
+         *
+         * @param sendStatus send status, one of {@link #SEND_STATUS_OK},
+         *        {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and
+         *        {@link #SEND_STATUS_ERROR}
+         * @param sendConfPdu a possibly {code null} SendConf PDU, which confirms that the message
+         *        was sent. sendConfPdu is ignored if the {@code result} is not
+         *        {@link #SEND_STATUS_OK}.
+         */
+        public SendMmsResult(int sendStatus, @Nullable byte[] sendConfPdu) {
+            mSendStatus = sendStatus;
+            mSendConfPdu = sendConfPdu;
+        }
+
+        /**
+         * Returns the send status of the just-sent MMS.
+         *
+         * @return the send status which is one of {@link #SEND_STATUS_OK},
+         *         {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}
+         */
+        public int getSendStatus() {
+            return mSendStatus;
+        }
+
+        /**
+         * Returns the SendConf PDU, which confirms that the message was sent.
+         *
+         * @return the SendConf PDU
+         */
+        public @Nullable byte[] getSendConfPdu() {
+            return mSendConfPdu;
+        }
+    }
+
+    /**
+     * The result of sending an SMS.
+     */
+    public static final class SendSmsResult {
+        private final int mSendStatus;
+        private final int mMessageRef;
+
+        /**
+         * Constructs a SendSmsResult with the send status and message reference for the
+         * just-sent SMS.
+         *
+         * @param sendStatus send status, one of {@link #SEND_STATUS_OK},
+         *        {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}.
+         * @param messageRef message reference of the just-sent SMS. This field is applicable only
+         *        if send status is {@link #SEND_STATUS_OK}.
+         */
+        public SendSmsResult(int sendStatus, int messageRef) {
+            mSendStatus = sendStatus;
+            mMessageRef = messageRef;
+        }
+
+        /**
+         * Returns the message reference of the just-sent SMS.
+         *
+         * @return the message reference
+         */
+        public int getMessageRef() {
+            return mMessageRef;
+        }
+
+        /**
+         * Returns the send status of the just-sent SMS.
+         *
+         * @return the send status
+         */
+        public int getSendStatus() {
+            return mSendStatus;
+        }
+    }
+
+    /**
+     * The result of sending a multipart SMS.
+     */
+    public static final class SendMultipartSmsResult {
+        private final int mSendStatus;
+        private final int[] mMessageRefs;
+
+        /**
+         * Constructs a SendMultipartSmsResult with the send status and message references for the
+         * just-sent multipart SMS.
+         *
+         * @param sendStatus send status, one of {@link #SEND_STATUS_OK},
+         *        {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}.
+         * @param messageRefs an array of message references, one for each part of the
+         *        multipart SMS. This field is applicable only if send status is
+         *        {@link #SEND_STATUS_OK}.
+         */
+        public SendMultipartSmsResult(int sendStatus, @Nullable int[] messageRefs) {
+            mSendStatus = sendStatus;
+            mMessageRefs = messageRefs;
+        }
+
+        /**
+         * Returns the message references of the just-sent multipart SMS.
+         *
+         * @return the message references, one for each part of the multipart SMS
+         */
+        public @Nullable int[] getMessageRefs() {
+            return mMessageRefs;
+        }
+
+        /**
+         * Returns the send status of the just-sent SMS.
+         *
+         * @return the send status
+         */
+        public int getSendStatus() {
+            return mSendStatus;
+        }
+    }
+
+    /**
+     * A callback interface used to provide results asynchronously.
+     */
+    public interface ResultCallback<T> {
+        /**
+         * Invoked when the result is available.
+         *
+         * @param result the result
+         */
+        public void onReceiveResult(@NonNull T result) throws RemoteException;
+    };
+
+    /**
+     * A wrapper around ICarrierMessagingService to enable the carrier messaging app to implement
+     * methods it cares about in the {@link ICarrierMessagingService} interface.
+     */
+    private class ICarrierMessagingWrapper extends ICarrierMessagingService.Stub {
+        @Override
+        public void filterSms(MessagePdu pdu, String format, int destPort,
+                              int subId, final ICarrierMessagingCallback callback) {
+            onFilterSms(pdu, format, destPort, subId, new ResultCallback<Boolean>() {
+                    @Override
+                    public void onReceiveResult(final Boolean result) throws RemoteException {
+                        callback.onFilterComplete(result);
+                    }
+                });
+        }
+
+        @Override
+        public void sendTextSms(String text, int subId, String destAddress,
+                                final ICarrierMessagingCallback callback) {
+            onSendTextSms(text, subId, destAddress, new ResultCallback<SendSmsResult>() {
+                    @Override
+                    public void onReceiveResult(final SendSmsResult result) throws RemoteException {
+                        callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef());
+                    }
+                });
+        }
+
+        @Override
+        public void sendDataSms(byte[] data, int subId, String destAddress, int destPort,
+                                final ICarrierMessagingCallback callback) {
+            onSendDataSms(data, subId, destAddress, destPort, new ResultCallback<SendSmsResult>() {
+                    @Override
+                    public void onReceiveResult(final SendSmsResult result) throws RemoteException {
+                        callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef());
+                    }
+                });
+        }
+
+        @Override
+        public void sendMultipartTextSms(List<String> parts, int subId, String destAddress,
+                                         final ICarrierMessagingCallback callback) {
+                onSendMultipartTextSms(parts, subId, destAddress,
+                        new ResultCallback<SendMultipartSmsResult>() {
+                                @Override
+                                public void onReceiveResult(final SendMultipartSmsResult result)
+                                        throws RemoteException {
+                                    callback.onSendMultipartSmsComplete(
+                                            result.getSendStatus(), result.getMessageRefs());
+                                }
+                            });
+        }
+
+        @Override
+        public void sendMms(Uri pduUri, int subId, Uri location,
+                final ICarrierMessagingCallback callback) {
+            onSendMms(pduUri, subId, location, new ResultCallback<SendMmsResult>() {
+                    @Override
+                    public void onReceiveResult(final SendMmsResult result) throws RemoteException {
+                        callback.onSendMmsComplete(result.getSendStatus(), result.getSendConfPdu());
+                    }
+                });
+        }
+
+        @Override
+        public void downloadMms(Uri pduUri, int subId, Uri location,
+                final ICarrierMessagingCallback callback) {
+            onDownloadMms(pduUri, subId, location, new ResultCallback<Integer>() {
+                    @Override
+                    public void onReceiveResult(Integer result) throws RemoteException {
+                        callback.onDownloadMmsComplete(result);
+                    }
+                });
+        }
+    }
+}
  * limitations under the License.
  */
 
-package android.service.carriermessaging;
-
-import android.service.carriermessaging.CarrierMessagingService;
+package android.service.carrier;
 
 /**
  * Callback interface definition for the Carrier Messaging Service client to get informed of the
  * result of various API invocations.
+ * @hide
  */
 oneway interface ICarrierMessagingCallback {
     void onFilterComplete(boolean keepMessage);
-    void onSendSmsComplete(
-        int result, in CarrierMessagingService.SendSmsResponse sendSmsResponse);
-    void onSendMultipartSmsComplete(
-        int result, in List<CarrierMessagingService.SendSmsResponse> sendSmsResponses);
+    void onSendSmsComplete(int result, int messageRef);
+    void onSendMultipartSmsComplete(int result, in int[] messageRefs);
     void onSendMmsComplete(int result, in byte[] sendConfPdu);
     void onDownloadMmsComplete(int result);
 }
  * limitations under the License.
  */
 
-package android.service.carriermessaging;
+package android.service.carrier;
 
 import android.net.Uri;
-import android.service.carriermessaging.ICarrierMessagingCallback;
-import android.service.carriermessaging.MessagePdu;
+import android.service.carrier.ICarrierMessagingCallback;
+import android.service.carrier.MessagePdu;
 
 /**
  * <p class="note"><strong>Note:</strong>
  * This service can only be implemented by a carrier privileged app.
+ * @hide
  */
 oneway interface ICarrierMessagingService {
     /**
@@ -32,10 +33,12 @@ oneway interface ICarrierMessagingService {
      * @param pdu the PDUs of the message
      * @param format the format of the PDUs, typically "3gpp" or "3gpp2"
      * @param destPort the destination port of a data SMS. It will be -1 for text SMS
+     * @param subId SMS subscription ID of the SIM
      * @param callback the callback to notify upon completion
      */
     void filterSms(
-        in MessagePdu pdu, String format, int destPort, in ICarrierMessagingCallback callback);
+        in MessagePdu pdu, String format, int destPort, int subId,
+        in ICarrierMessagingCallback callback);
 
     /**
      * Request sending a new text SMS from the device.
@@ -43,11 +46,11 @@ oneway interface ICarrierMessagingService {
      * status.
      *
      * @param text the text to send
-     * @param format the format of the response PDU, typically "3gpp" or "3gpp2"
+     * @param subId SMS subscription ID of the SIM
      * @param destAddress phone number of the recipient of the message
      * @param callback the callback to notify upon completion
      */
-    void sendTextSms(String text, String format, String destAddress,
+    void sendTextSms(String text, int subId, String destAddress,
             in ICarrierMessagingCallback callback);
 
     /**
@@ -56,12 +59,12 @@ oneway interface ICarrierMessagingService {
      * status.
      *
      * @param data the data to send
-     * @param format the format of the response PDU, typically "3gpp" or "3gpp2"
+     * @param subId SMS subscription ID of the SIM
      * @param destAddress phone number of the recipient of the message
      * @param destPort port number of the recipient of the message
      * @param callback the callback to notify upon completion
      */
-    void sendDataSms(in byte[] data, String format, String destAddress, int destPort,
+    void sendDataSms(in byte[] data, int subId, String destAddress, int destPort,
             in ICarrierMessagingCallback callback);
 
     /**
@@ -70,11 +73,11 @@ oneway interface ICarrierMessagingService {
      * with the send status.
      *
      * @param parts the parts of the multi-part text SMS to send
-     * @param format the format of the response PDU, typically "3gpp" or "3gpp2"
+     * @param subId SMS subscription ID of the SIM
      * @param destAddress phone number of the recipient of the message
      * @param callback the callback to notify upon completion
      */
-    void sendMultipartTextSms(in List<String> parts, String format, String destAddress,
+    void sendMultipartTextSms(in List<String> parts, int subId, String destAddress,
             in ICarrierMessagingCallback callback);
 
     /**
@@ -83,11 +86,13 @@ oneway interface ICarrierMessagingService {
      * status.
      *
      * @param pduUri the content provider URI of the PDU to send
-     * @param locationUrl the optional url to send this MMS PDU.
-     *         If this is not specified, PDU should be sent to the default MMSC url.
+     * @param subId SMS subscription ID of the SIM
+     * @param location the optional URI to send this MMS PDU. If this is {code null},
+     *        the PDU should be sent to the default MMSC URL.
      * @param callback the callback to notify upon completion
      */
-    void sendMms(in Uri pduUri, String locationUrl, in ICarrierMessagingCallback callback);
+    void sendMms(in Uri pduUri, int subId, in Uri location,
+            in ICarrierMessagingCallback callback);
 
     /**
      * Request downloading a new MMS.
@@ -95,9 +100,11 @@ oneway interface ICarrierMessagingService {
      * download status.
      *
      * @param pduUri the content provider URI of the PDU to be downloaded.
-     * @param locationUrl the URL of the message to be downloaded.
+     * @param subId SMS subscription ID of the SIM
+     * @param location the URI of the message to be downloaded.
      * @param callback the callback to notify upon completion
      */
-    void downloadMms(in Uri pduUri, String locationUrl, in ICarrierMessagingCallback callback);
+    void downloadMms(in Uri pduUri, int subId, in Uri location,
+            in ICarrierMessagingCallback callback);
 }
 
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.carriermessaging;
+package android.service.carrier;
 
-parcelable MessagePdu;
\ No newline at end of file
+parcelable MessagePdu;
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.service.carriermessaging;
+package android.service.carrier;
 
 import android.annotation.NonNull;
 import android.os.Parcel;
diff --git a/core/java/android/service/carriermessaging/CarrierMessagingService.java b/core/java/android/service/carriermessaging/CarrierMessagingService.java
deleted file mode 100644 (file)
index 7aea590..0000000
+++ /dev/null
@@ -1,393 +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 android.service.carriermessaging;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.app.Service;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-
-import java.util.List;
-
-/**
- * A service that receives calls from the system when new SMS and MMS are
- * sent or received.
- * <p>To extend this class, you must declare the service in your manifest file with
- * the {@link android.Manifest.permission#BIND_CARRIER_MESSAGING_SERVICE} permission
- * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
- * <pre>
- * &lt;service android:name=".MyMessagingService"
- *          android:label="&#64;string/service_name"
- *          android:permission="android.permission.BIND_CARRIER_MESSAGING_SERVICE">
- *     &lt;intent-filter>
- *         &lt;action android:name="android.service.carriermessaging.CarrierMessagingService" />
- *     &lt;/intent-filter>
- * &lt;/service></pre>
- */
-public abstract class CarrierMessagingService extends Service {
-    /**
-     * The {@link android.content.Intent} that must be declared as handled by the service.
-     */
-    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
-    public static final String SERVICE_INTERFACE
-            = "android.service.carriermessaging.CarrierMessagingService";
-
-    /**
-     * Indicates that an SMS or MMS message was successfully sent.
-     */
-    public static final int SEND_STATUS_OK = 0;
-
-    /**
-     * SMS/MMS sending failed. We should retry via the carrier network.
-     */
-    public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
-
-    /**
-     * SMS/MMS sending failed. We should not retry via the carrier network.
-     */
-    public static final int SEND_STATUS_ERROR = 2;
-
-    /**
-     * Successfully downloaded an MMS message.
-     */
-    public static final int DOWNLOAD_STATUS_OK = 0;
-
-    /**
-     * MMS downloading failed. We should retry via the carrier network.
-     */
-    public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
-
-    /**
-     * MMS downloading failed. We should not retry via the carrier network.
-     */
-    public static final int DOWNLOAD_STATUS_ERROR = 2;
-
-    private final ICarrierMessagingWrapper mWrapper = new ICarrierMessagingWrapper();
-
-    /**
-     * Implement this method to filter SMS messages.
-     *
-     * @param pdu the PDUs of the message
-     * @param format the format of the PDUs, typically "3gpp" or "3gpp2"
-     * @param destPort the destination port of a binary SMS, this will be -1 for text SMS
-     *
-     * @return True to keep an inbound SMS message and delivered to SMS apps. False to
-     * drop the message.
-     */
-    public boolean onFilterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort) {
-        // optional
-        return true;
-    }
-
-    /**
-     * Implement this method to intercept text SMSs sent from the devcie.
-     *
-     * @param text the text to send
-     * @param format the format of the response PDU, typically "3gpp" or "3gpp2"
-     * @param destAddress phone number of the recipient of the message
-     *
-     * @return a possibly {code null} {@link SendSmsResponse}. Upon returning {@code null}, the SMS
-     *         is sent using the carrier network.
-     */
-    public @Nullable SendSmsResponse onSendTextSms(
-            @NonNull String text, @NonNull String format, @NonNull String destAddress) {
-        // optional
-        return null;
-    }
-
-    /**
-     * Implement this method to intercept binary SMSs sent from the device.
-     *
-     * @param data the binary content
-     * @param format format the format of the response PDU, typically "3gpp" or "3gpp2"
-     * @param destAddress phone number of the recipient of the message
-     * @param destPort the destination port
-     *
-     * @return a possibly {code null} {@link SendSmsResponse}. Upon returning {@code null}, the SMS
-     *         is sent using the carrier network.
-     */
-    public @Nullable SendSmsResponse onSendDataSms(@NonNull byte[] data, @NonNull String format,
-            @NonNull String destAddress, int destPort) {
-        // optional
-        return null;
-    }
-
-    /**
-     * Implement this method to intercept long SMSs sent from the device.
-     *
-     * @param parts a {@link List} of the message parts
-     * @param format format the format of the response PDU, typically "3gpp" or "3gpp2"
-     * @param destAddress phone number of the recipient of the message
-     *
-     * @return a possibly {code null} {@link List} of {@link SendSmsResponse}, one for each message
-     *         part. Upon returning {@code null}, the SMS is sent using the carrier network.
-     */
-    public @Nullable List<SendSmsResponse> onSendMultipartTextSms(@NonNull List<String> parts,
-            @NonNull String format, @NonNull String destAddress) {
-        // optional
-        return null;
-    }
-
-    /**
-     * Implement this method to intercept MMSs sent from the device.
-     *
-     * @param pduUri the content provider URI of the PDU to send
-     * @param locationUrl the optional URL to send this MMS PDU. If this is not specified,
-     *                    the PDU should be sent to the default MMSC URL.
-     *
-     * @return a possibly {@code null} {@link SendMmsResult}. Upon returning {@code null}, the
-     *         MMS is sent using the carrier network.
-     */
-    public @Nullable SendMmsResult onSendMms(@NonNull Uri pduUri, @Nullable String locationUrl) {
-        // optional
-        return null;
-    }
-
-    /**
-     * Implement this method to download MMSs received.
-     *
-     * @param contentUri the content provider URI of the PDU to be downloaded.
-     * @param locationUrl the URL of the message to be downloaded.
-     *
-     * @return a {@link SendMmsResult}.
-     */
-    public int onDownloadMms(@NonNull Uri contentUri, @NonNull String locationUrl) {
-        // optional
-        return DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK;
-    }
-
-    @Override
-    public @Nullable IBinder onBind(@NonNull Intent intent) {
-        if (!SERVICE_INTERFACE.equals(intent.getAction())) {
-            return null;
-        }
-        return mWrapper;
-    }
-
-    /**
-     * The result of sending an MMS.
-     */
-    public static final class SendMmsResult {
-        private int mResult;
-        private byte[] mSendConfPdu;
-
-        /**
-         * Constructs a SendMmsResult with the MMS send result, and the SenConf PDU.
-         *
-         * @param result the result which is one of {@link #SEND_STATUS_OK},
-         *               {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and
-         *               {@link #SEND_STATUS_ERROR}
-         * @param sendConfPdu a possibly {code null} SendConf PDU, which confirms that the message
-         *        was sent. sendConfPdu is ignored if the {@code result} is not
-         *        {@link #SEND_STATUS_OK}
-         */
-        public SendMmsResult(int result, @Nullable byte[] sendConfPdu) {
-            mResult = result;
-            mSendConfPdu = sendConfPdu;
-        }
-
-        /**
-         * Returns the result of sending the MMS.
-         *
-         * @return the result which is one of {@link #SEND_STATUS_OK},
-         *         {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}
-         */
-        public int getResult() {
-            return mResult;
-        }
-
-        /**
-         * Returns the SendConf PDU, which confirms that the message was sent.
-         *
-         * @return the SendConf PDU
-         */
-        public @Nullable byte[] getSendConfPdu() {
-            return mSendConfPdu;
-        }
-    }
-
-    /**
-     * Object passed in callbacks upon successful completion of
-     * {@link ICarrierMessagingService#sendTextSms},
-     * {@link ICarrierMessagingService#sendDataSms}, and
-     * {@link ICarrierMessagingService#sendMultipartTextSms}.
-     * Contains message reference and ackPdu.
-     */
-    public static final class SendSmsResponse implements Parcelable {
-        private int mMessageRef;
-        private byte[] mAckPdu;
-        private int mErrorCode;
-
-        /**
-         * Constructs a SendSmsResponse for the message reference, the ack PDU, and error code for
-         * the just-sent SMS.
-         *
-         * @param messageRef message reference of the just-sent SMS
-         * @param ackPdu ackPdu for the just-sent SMS
-         * @param errorCode error code. See 3GPP 27.005, 3.2.5 for GSM/UMTS,
-         *     3GPP2 N.S0005 (IS-41C) Table 171 for CDMA, -1 if unknown or not applicable.
-         */
-        public SendSmsResponse(int messageRef, @NonNull byte[] ackPdu, int errorCode) {
-            mMessageRef = messageRef;
-            mAckPdu = ackPdu;
-            mErrorCode = errorCode;
-        }
-
-        /**
-         * Returns the message reference of the just-sent SMS.
-         *
-         * @return the message reference
-         */
-        public int getMessageRef() {
-            return mMessageRef;
-        }
-
-        /**
-         * Returns the ackPdu for the just-sent SMS.
-         *
-         * @return the ackPdu
-         */
-        public @NonNull byte[] getAckPdu() {
-            return mAckPdu;
-        }
-
-        /**
-         * Returns the error code upon encountering an error while sending the SMS, -1 if unknown or
-         * not applicable.
-         *
-         * @return errorCode the errorCode as defined in 3GPP 27.005, 3.2.5 for GSM/UMTS, and 3GPP2
-         * N.S0005 (IS-41C) Table 171 for CDMA, -1 if unknown or not applicable.
-         */
-        public int getErrorCode() {
-            return mErrorCode;
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mMessageRef);
-            dest.writeByteArray(mAckPdu);
-            dest.writeInt(mErrorCode);
-        }
-
-        public static final Parcelable.Creator<SendSmsResponse> CREATOR
-                = new Parcelable.Creator<SendSmsResponse>() {
-                    @Override
-                    public SendSmsResponse createFromParcel(Parcel source) {
-                        return new SendSmsResponse(source.readInt(),
-                                                   source.createByteArray(),
-                                                   source.readInt());
-                    }
-
-                    @Override
-                    public SendSmsResponse[] newArray(int size) {
-                        return new SendSmsResponse[size];
-                    }
-        };
-    }
-
-    /**
-     * A wrapper around ICarrierMessagingService to enable the carrier messaging APP to implement
-     * methods it cares about in the {@link ICarrierMessagingService} interface.
-     */
-    private class ICarrierMessagingWrapper extends ICarrierMessagingService.Stub {
-        @Override
-        public void filterSms(MessagePdu pdu, String format, int destPort,
-                              ICarrierMessagingCallback callback) {
-            try {
-                callback.onFilterComplete(onFilterSms(pdu, format, destPort));
-            } catch (RemoteException ex) {
-            }
-        }
-
-        @Override
-        public void sendTextSms(String text, String format, String destAddress,
-                                ICarrierMessagingCallback callback) {
-            try {
-                SendSmsResponse sendSmsResponse = onSendTextSms(text, format, destAddress);
-                if (sendSmsResponse == null) {
-                    callback.onSendSmsComplete(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null);
-                } else {
-                    callback.onSendSmsComplete(SEND_STATUS_OK, sendSmsResponse);
-                }
-            } catch (RemoteException ex) {
-            }
-        }
-
-        @Override
-        public void sendDataSms(byte[] data, String format, String destAddress, int destPort,
-                                ICarrierMessagingCallback callback) {
-            try {
-                SendSmsResponse sendSmsResponse = onSendDataSms(data, format, destAddress,
-                        destPort);
-                if (sendSmsResponse == null) {
-                    callback.onSendSmsComplete(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null);
-                } else {
-                    callback.onSendSmsComplete(SEND_STATUS_OK, sendSmsResponse);
-                }
-            } catch (RemoteException ex) {
-            }
-        }
-
-        @Override
-        public void sendMultipartTextSms(List<String> parts, String format, String destAddress,
-                                         ICarrierMessagingCallback callback) {
-            try {
-                List<SendSmsResponse> sendSmsResponses =
-                        onSendMultipartTextSms(parts, format, destAddress);
-                if (sendSmsResponses == null) {
-                    callback.onSendMultipartSmsComplete(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null);
-                } else {
-                    callback.onSendMultipartSmsComplete(SEND_STATUS_OK, sendSmsResponses);
-                }
-            } catch (RemoteException ex) {
-            }
-        }
-
-        @Override
-        public void sendMms(Uri pduUri, String locationUrl, ICarrierMessagingCallback callback) {
-            try {
-                SendMmsResult result = onSendMms(pduUri, locationUrl);
-                if (result == null) {
-                    callback.onSendMmsComplete(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null);
-                } else {
-                    callback.onSendMmsComplete(SEND_STATUS_OK, result.getSendConfPdu());
-                }
-            } catch (RemoteException ex) {
-            }
-        }
-
-        @Override
-        public void downloadMms(Uri contentUri, String locationUrl,
-                ICarrierMessagingCallback callback) {
-            try {
-                callback.onDownloadMmsComplete(onDownloadMms(contentUri, locationUrl));
-            } catch (RemoteException ex) {
-            }
-        }
-    }
-}
diff --git a/core/java/android/service/carriermessaging/CarrierMessagingServiceManager.java b/core/java/android/service/carriermessaging/CarrierMessagingServiceManager.java
deleted file mode 100644 (file)
index 56ee2c1..0000000
+++ /dev/null
@@ -1,97 +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 android.service.carriermessaging;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-
-import com.android.internal.util.Preconditions;
-
-/**
- * Provides basic structure for platform to connect to the carrier messaging service.
- * <p>
- * <code>
- * CarrierMessagingServiceManager carrierMessagingServiceManager =
- *     new CarrierMessagingServiceManagerImpl();
- * if (carrierMessagingServiceManager.bindToCarrierMessagingService(context, carrierPackageName)) {
- *   // wait for onServiceReady callback
- * } else {
- *   // Unable to bind: handle error.
- * }
- * </code>
- * <p> Upon completion {@link #disposeConnection} should be called to unbind the
- * CarrierMessagingService.
- * @hide
- */
-public abstract class CarrierMessagingServiceManager {
-    // Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
-    // prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
-    private volatile CarrierMessagingServiceConnection mCarrierMessagingServiceConnection;
-
-    /**
-     * Binds to the carrier messaging service under package {@code carrierPackageName}. This method
-     * should be called exactly once.
-     *
-     * @param context the context
-     * @param carrierPackageName the carrier package name
-     * @return true upon successfully binding to a carrier messaging service, false otherwise
-     */
-    public boolean bindToCarrierMessagingService(Context context, String carrierPackageName) {
-        Preconditions.checkState(mCarrierMessagingServiceConnection == null);
-
-        Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
-        intent.setPackage(carrierPackageName);
-        mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();
-        return context.bindService(intent, mCarrierMessagingServiceConnection,
-                Context.BIND_AUTO_CREATE);
-    }
-
-    /**
-     * Unbinds the carrier messaging service. This method should be called exactly once.
-     *
-     * @param context the context
-     */
-    public void disposeConnection(Context context) {
-        Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
-        context.unbindService(mCarrierMessagingServiceConnection);
-        mCarrierMessagingServiceConnection = null;
-    }
-
-    /**
-     * Implemented by subclasses to use the carrier messaging service once it is ready.
-     *
-     * @param carrierMessagingService the carirer messaing service interface
-     */
-    protected abstract void onServiceReady(ICarrierMessagingService carrierMessagingService);
-
-    /**
-     * A basic {@link ServiceConnection}.
-     */
-    private final class CarrierMessagingServiceConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            onServiceReady(ICarrierMessagingService.Stub.asInterface(service));
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-        }
-    }
-}
index 9496b53..67f632f 100644 (file)
@@ -691,8 +691,8 @@ public abstract class WallpaperService extends Service {
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
                     
-                    int w = mWinFrame.width();
-                    int h = mWinFrame.height();
+                    int w = mWinFrame.width() + mOverscanInsets.left + mOverscanInsets.right;
+                    int h = mWinFrame.height() + mOverscanInsets.top + mOverscanInsets.bottom;
 
                     if (!fixedSize) {
                         final Rect padding = mIWallpaperEngine.mDisplayPadding;
index 02297e3..74b7b69 100644 (file)
@@ -416,7 +416,11 @@ public class StaticLayout extends Layout {
                             currentTextWidth = widths[here - paraStart];
                         }
 
-                        v = out(source, here, endPos,
+                        int ellipseEnd = endPos;
+                        if (mMaximumVisibleLineCount == 1 && ellipsize == TextUtils.TruncateAt.MIDDLE) {
+                            ellipseEnd = paraEnd;
+                        }
+                        v = out(source, here, ellipseEnd,
                                 above, below, top, bottom,
                                 v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, hasTabOrEmoji,
                                 needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
@@ -704,7 +708,7 @@ public class StaticLayout extends Layout {
                 int left = 0, right = len;
 
                 float ravail = (avail - ellipsisWidth) / 2;
-                for (right = len; right >= 0; right--) {
+                for (right = len; right > 0; right--) {
                     float w = widths[right - 1 + lineStart - widthStart];
 
                     if (w + rsum > ravail) {
index 56a05fe..9feb681 100644 (file)
@@ -59,6 +59,11 @@ public final class DisplayInfo implements Parcelable {
     public String name;
 
     /**
+     * Unique identifier for the display. Shouldn't be displayed to the user.
+     */
+    public String uniqueId;
+
+    /**
      * The width of the portion of the display that is available to applications, in pixels.
      * Represents the size of the display minus any system decorations.
      */
@@ -257,7 +262,7 @@ public final class DisplayInfo implements Parcelable {
                 && flags == other.flags
                 && type == other.type
                 && Objects.equal(address, other.address)
-                && Objects.equal(name, other.name)
+                && Objects.equal(uniqueId, other.uniqueId)
                 && appWidth == other.appWidth
                 && appHeight == other.appHeight
                 && smallestNominalAppWidth == other.smallestNominalAppWidth
@@ -293,6 +298,7 @@ public final class DisplayInfo implements Parcelable {
         type = other.type;
         address = other.address;
         name = other.name;
+        uniqueId = other.uniqueId;
         appWidth = other.appWidth;
         appHeight = other.appHeight;
         smallestNominalAppWidth = other.smallestNominalAppWidth;
@@ -348,6 +354,7 @@ public final class DisplayInfo implements Parcelable {
         state = source.readInt();
         ownerUid = source.readInt();
         ownerPackageName = source.readString();
+        uniqueId = source.readString();
     }
 
     @Override
@@ -380,6 +387,7 @@ public final class DisplayInfo implements Parcelable {
         dest.writeInt(state);
         dest.writeInt(ownerUid);
         dest.writeString(ownerPackageName);
+        dest.writeString(uniqueId);
     }
 
     @Override
@@ -445,6 +453,8 @@ public final class DisplayInfo implements Parcelable {
         StringBuilder sb = new StringBuilder();
         sb.append("DisplayInfo{\"");
         sb.append(name);
+        sb.append("\", uniqueId \"");
+        sb.append(uniqueId);
         sb.append("\", app ");
         sb.append(appWidth);
         sb.append(" x ");
index 9061679..60a489b 100644 (file)
@@ -558,7 +558,7 @@ class GLES20Canvas extends HardwareCanvas {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
+        nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint);
     }
 
@@ -567,32 +567,31 @@ class GLES20Canvas extends HardwareCanvas {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
+        nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint);
     }
 
-    private static native void nDrawPatch(long renderer, long bitmap, byte[] buffer, long chunk,
+    private static native void nDrawPatch(long renderer, long bitmap, long chunk,
             float left, float top, float right, float bottom, long paint);
 
     @Override
     public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
         throwIfCannotDraw(bitmap);
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
     }
 
-    private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
-            float left, float top, long paint);
+    private static native void nDrawBitmap(long renderer, long bitmap, float left,
+            float top, long paint);
 
     @Override
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
         throwIfCannotDraw(bitmap);
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
-                matrix.native_instance, nativePaint);
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint);
     }
 
-    private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
+    private static native void nDrawBitmap(long renderer, long bitmap,
             long matrix, long paint);
 
     @Override
@@ -612,7 +611,7 @@ class GLES20Canvas extends HardwareCanvas {
             bottom = src.bottom;
         }
 
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint);
     }
 
@@ -633,11 +632,11 @@ class GLES20Canvas extends HardwareCanvas {
             bottom = src.bottom;
         }
 
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint);
     }
 
-    private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
+    private static native void nDrawBitmap(long renderer, long bitmap,
             float srcLeft, float srcTop, float srcRight, float srcBottom,
             float left, float top, float right, float bottom, long paint);
 
@@ -698,11 +697,11 @@ class GLES20Canvas extends HardwareCanvas {
         }
 
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
+        nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, meshWidth, meshHeight,
                 verts, vertOffset, colors, colorOffset, nativePaint);
     }
 
-    private static native void nDrawBitmapMesh(long renderer, long bitmap, byte[] buffer,
+    private static native void nDrawBitmapMesh(long renderer, long bitmap,
             int meshWidth, int meshHeight, float[] verts, int vertOffset,
             int[] colors, int colorOffset, long paint);
 
index 904e33f..c5c3f83 100644 (file)
@@ -235,7 +235,7 @@ public abstract class HardwareRenderer {
      * or not the surface used by the HardwareRenderer will be changing. It
      * Suspends any rendering into the surface, but will not do any destruction
      */
-    abstract void pauseSurface(Surface surface);
+    abstract boolean pauseSurface(Surface surface);
 
     /**
      * Destroys all hardware rendering resources associated with the specified
index 131c039..14b950f 100644 (file)
@@ -155,8 +155,8 @@ public class ThreadedRenderer extends HardwareRenderer {
     }
 
     @Override
-    void pauseSurface(Surface surface) {
-        nPauseSurface(mNativeProxy, surface);
+    boolean pauseSurface(Surface surface) {
+        return nPauseSurface(mNativeProxy, surface);
     }
 
     @Override
@@ -494,7 +494,7 @@ public class ThreadedRenderer extends HardwareRenderer {
 
     private static native boolean nInitialize(long nativeProxy, Surface window);
     private static native void nUpdateSurface(long nativeProxy, Surface window);
-    private static native void nPauseSurface(long nativeProxy, Surface window);
+    private static native boolean nPauseSurface(long nativeProxy, Surface window);
     private static native void nSetup(long nativeProxy, int width, int height,
             float lightX, float lightY, float lightZ, float lightRadius,
             int ambientShadowAlpha, int spotShadowAlpha);
index 1a5ff26..1551504 100644 (file)
@@ -5033,8 +5033,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
             child.getMatrix().mapRect(rect);
         }
 
-        int dx = child.mLeft - mScrollX;
-        int dy = child.mTop - mScrollY;
+        final int dx = child.mLeft - mScrollX;
+        final int dy = child.mTop - mScrollY;
 
         rect.offset(dx, dy);
 
@@ -5052,21 +5052,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
             offset.y += dy;
         }
 
+        final int width = mRight - mLeft;
+        final int height = mBottom - mTop;
+
         boolean rectIsVisible = true;
         if (mParent instanceof ViewGroup && ((ViewGroup)mParent).getClipChildren()) {
-            // clipChildren clips to the child's bounds
-            rectIsVisible = rect.intersect(0, 0, mRight - mLeft, mBottom - mTop);
+            // Clip to bounds.
+            rectIsVisible = rect.intersect(0, 0, width, height);
         }
 
         if (rectIsVisible && (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
-            // Clip to padding
+            // Clip to padding.
             rectIsVisible = rect.intersect(mPaddingLeft, mPaddingTop,
-                    mRight - mLeft - mPaddingLeft - mPaddingRight,
-                    mBottom - mTop - mPaddingTop - mPaddingBottom);
+                    width - mPaddingRight, height - mPaddingBottom);
         }
 
         if (rectIsVisible && mClipBounds != null) {
-            // Clip to clipBounds
+            // Clip to clipBounds.
             rectIsVisible = rect.intersect(mClipBounds.left, mClipBounds.top, mClipBounds.right,
                     mClipBounds.bottom);
         }
index 0cf9ddd..5e5ef29 100644 (file)
@@ -130,8 +130,11 @@ public class ViewOverlay {
             super(context);
             mHostView = hostView;
             mAttachInfo = mHostView.mAttachInfo;
+
             mRight = hostView.getWidth();
             mBottom = hostView.getHeight();
+            // pass right+bottom directly to RenderNode, since not going through setters
+            mRenderNode.setLeftTopRightBottom(0, 0, mRight, mBottom);
         }
 
         public void add(Drawable drawable) {
index b12c747..34c27d7 100644 (file)
@@ -1493,7 +1493,11 @@ public final class ViewRootImpl implements ViewParent,
                     // relayoutWindow may decide to destroy mSurface. As that decision
                     // happens in WindowManager service, we need to be defensive here
                     // and stop using the surface in case it gets destroyed.
-                    mAttachInfo.mHardwareRenderer.pauseSurface(mSurface);
+                    if (mAttachInfo.mHardwareRenderer.pauseSurface(mSurface)) {
+                        // Animations were running so we need to push a frame
+                        // to resume them
+                        mDirty.set(0, 0, mWidth, mHeight);
+                    }
                 }
                 final int surfaceGenerationId = mSurface.getGenerationId();
                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
index 2e5c1e0..6944c53 100644 (file)
@@ -364,7 +364,7 @@ public abstract class Window {
         /**
          * This hook is called whenever the window focus changes.  See
          * {@link View#onWindowFocusChanged(boolean)
-         * View.onWindowFocusChanged(boolean)} for more information.
+         * View.onWindowFocusChangedNotLocked(boolean)} for more information.
          *
          * @param hasFocus Whether the window now has focus.
          */
index 8b01dde..04b5616 100644 (file)
@@ -1200,69 +1200,87 @@ public class RadialTimePickerView extends View implements View.OnTouchListener {
         return degrees;
     }
 
+    boolean mChangedDuringTouch = false;
+
     @Override
     public boolean onTouch(View v, MotionEvent event) {
-        if(!mInputEnabled) {
+        if (!mInputEnabled) {
             return true;
         }
 
-        final float eventX = event.getX();
-        final float eventY = event.getY();
-
-        int degrees;
-        int snapDegrees;
-        boolean result = false;
-
-        switch(event.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-            case MotionEvent.ACTION_MOVE:
-                degrees = getDegreesFromXY(eventX, eventY);
-                if (degrees != -1) {
-                    snapDegrees = (mShowHours ?
-                            snapOnly30s(degrees, 0) : snapPrefer30s(degrees)) % 360;
-                    if (mShowHours) {
-                        mSelectionDegrees[HOURS] = snapDegrees;
-                        mSelectionDegrees[HOURS_INNER] = snapDegrees;
-                    } else {
-                        mSelectionDegrees[MINUTES] = snapDegrees;
-                    }
-                    performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
-                    if (mListener != null) {
-                        if (mShowHours) {
-                            mListener.onValueSelected(HOURS, getCurrentHour(), false);
-                        } else  {
-                            mListener.onValueSelected(MINUTES, getCurrentMinute(), false);
-                        }
-                    }
-                    result = true;
-                    invalidate();
+        final int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_MOVE
+                || action == MotionEvent.ACTION_UP
+                || action == MotionEvent.ACTION_DOWN) {
+            boolean forceSelection = false;
+            boolean autoAdvance = false;
+
+            if (action == MotionEvent.ACTION_DOWN) {
+                // This is a new event stream, reset whether the value changed.
+                mChangedDuringTouch = false;
+            } else if (action == MotionEvent.ACTION_UP) {
+                autoAdvance = true;
+
+                // If we saw a down/up pair without the value changing, assume
+                // this is a single-tap selection and force a change.
+                if (!mChangedDuringTouch) {
+                    forceSelection = true;
                 }
-                break;
+            }
 
-            case MotionEvent.ACTION_UP:
-                degrees = getDegreesFromXY(eventX, eventY);
-                if (degrees != -1) {
-                    snapDegrees = (mShowHours ?
-                            snapOnly30s(degrees, 0) : snapPrefer30s(degrees)) % 360;
-                    if (mShowHours) {
-                        mSelectionDegrees[HOURS] = snapDegrees;
-                        mSelectionDegrees[HOURS_INNER] = snapDegrees;
-                    } else {
-                        mSelectionDegrees[MINUTES] = snapDegrees;
-                    }
-                    if (mListener != null) {
-                        if (mShowHours) {
-                            mListener.onValueSelected(HOURS, getCurrentHour(), true);
-                        } else  {
-                            mListener.onValueSelected(MINUTES, getCurrentMinute(), true);
-                        }
-                    }
-                    invalidate();
-                    result = true;
-                }
-                break;
+            mChangedDuringTouch |= handleTouchInput(
+                    event.getX(), event.getY(), forceSelection, autoAdvance);
+        }
+
+        return true;
+    }
+
+    private boolean handleTouchInput(
+            float x, float y, boolean forceSelection, boolean autoAdvance) {
+        // Calling getDegreesFromXY has side effects, so cache
+        // whether we used to be on the inner circle.
+        final boolean wasOnInnerCircle = mIsOnInnerCircle;
+        final int degrees = getDegreesFromXY(x, y);
+        if (degrees == -1) {
+            return false;
         }
-        return result;
+
+        final int[] selectionDegrees = mSelectionDegrees;
+        int type = -1;
+        int newValue = -1;
+
+        if (mShowHours) {
+            final int snapDegrees = snapOnly30s(degrees, 0) % 360;
+            if (forceSelection
+                    || selectionDegrees[HOURS] != snapDegrees
+                    || selectionDegrees[HOURS_INNER] != snapDegrees
+                    || wasOnInnerCircle != mIsOnInnerCircle) {
+                selectionDegrees[HOURS] = snapDegrees;
+                selectionDegrees[HOURS_INNER] = snapDegrees;
+
+                type = HOURS;
+                newValue = getCurrentHour();
+            }
+        } else {
+            final int snapDegrees = snapPrefer30s(degrees) % 360;
+            if (forceSelection || selectionDegrees[MINUTES] != snapDegrees) {
+                selectionDegrees[MINUTES] = snapDegrees;
+
+                type = MINUTES;
+                newValue = getCurrentMinute();
+            }
+        }
+
+        if (newValue != -1) {
+            if (mListener != null) {
+                mListener.onValueSelected(type, newValue, autoAdvance);
+            }
+            performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+            invalidate();
+            return true;
+        }
+
+        return false;
     }
 
     @Override
index 139354a..34b3a72 100644 (file)
@@ -8392,7 +8392,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
      */
     private boolean shouldSpeakPasswordsForAccessibility() {
         return (Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0, UserHandle.USER_CURRENT) == 1);
+                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0,
+                UserHandle.USER_CURRENT_OR_SELF) == 1);
     }
 
     @Override
index a52dd48..99bf9f3 100644 (file)
@@ -36,7 +36,7 @@ interface IAppOpsService {
     List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
     List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
     void setMode(int code, int uid, String packageName, int mode);
-    void resetAllModes();
+    void resetAllModes(int reqUserId, String reqPackageName);
     int checkAudioOperation(int code, int usage, int uid, String packageName);
     void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages);
 
index 2db466a..634d03d 100644 (file)
@@ -95,12 +95,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
     private ListView mListView;
     private Button mAlwaysButton;
     private Button mOnceButton;
+    private View mProfileView;
     private int mIconDpi;
     private int mIconSize;
     private int mMaxColumns;
     private int mLastSelected = ListView.INVALID_POSITION;
     private boolean mResolvingHome = false;
     private int mProfileSwitchMessageId = -1;
+    private Intent mIntent;
 
     private UsageStatsManager mUsm;
     private Map<String, UsageStats> mStats;
@@ -110,6 +112,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override public void onSomePackagesChanged() {
             mAdapter.handlePackagesChanged();
+            if (mProfileView != null) {
+                bindProfileView();
+            }
         }
     };
 
@@ -217,7 +222,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
 
         final long sinceTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
         mStats = mUsm.queryAndAggregateUsageStats(sinceTime, System.currentTimeMillis());
-        Log.d(TAG, "sinceTime=" + sinceTime);
 
         mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
 
@@ -228,7 +232,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
         mIconDpi = am.getLauncherLargeIconDensity();
         mIconSize = am.getLauncherLargeIconSize();
 
-        mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
+        mIntent = new Intent(intent);
+        mAdapter = new ResolveListAdapter(this, initialIntents, rList,
                 mLaunchedFromUid, alwaysUseOption);
 
         final int layoutId;
@@ -324,6 +329,40 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
             setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
             mOnceButton.setEnabled(true);
         }
+
+        mProfileView = findViewById(R.id.profile_button);
+        if (mProfileView != null) {
+            mProfileView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    final DisplayResolveInfo dri = mAdapter.getOtherProfile();
+                    if (dri == null) {
+                        return;
+                    }
+
+                    final Intent intent = intentForDisplayResolveInfo(dri);
+                    onIntentSelected(dri.ri, intent, mAlwaysUseOption);
+                    finish();
+                }
+            });
+            bindProfileView();
+        }
+    }
+
+    void bindProfileView() {
+        final DisplayResolveInfo dri = mAdapter.getOtherProfile();
+        if (dri != null) {
+            mProfileView.setVisibility(View.VISIBLE);
+            final ImageView icon = (ImageView) mProfileView.findViewById(R.id.icon);
+            final TextView text = (TextView) mProfileView.findViewById(R.id.text1);
+            if (dri.displayIcon == null) {
+                new LoadIconTask().execute(dri);
+            }
+            icon.setImageDrawable(dri.displayIcon);
+            text.setText(dri.displayLabel);
+        } else {
+            mProfileView.setVisibility(View.GONE);
+        }
     }
 
     private void setProfileSwitchMessageId(int contentUserHint) {
@@ -416,6 +455,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
             mRegistered = true;
         }
         mAdapter.handlePackagesChanged();
+        if (mProfileView != null) {
+            bindProfileView();
+        }
     }
 
     @Override
@@ -702,6 +744,17 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
         startActivity(in);
     }
 
+    Intent intentForDisplayResolveInfo(DisplayResolveInfo dri) {
+        Intent intent = new Intent(dri.origIntent != null ? dri.origIntent :
+                getReplacementIntent(dri.ri.activityInfo, mIntent));
+        intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
+                |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+        ActivityInfo ai = dri.ri.activityInfo;
+        intent.setComponent(new ComponentName(
+                ai.applicationInfo.packageName, ai.name));
+        return intent;
+    }
+
     private final class DisplayResolveInfo {
         ResolveInfo ri;
         CharSequence displayLabel;
@@ -722,7 +775,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
         private final Intent[] mInitialIntents;
         private final List<ResolveInfo> mBaseResolveList;
         private ResolveInfo mLastChosen;
-        private final Intent mIntent;
+        private DisplayResolveInfo mOtherProfile;
         private final int mLaunchedFromUid;
         private final LayoutInflater mInflater;
 
@@ -732,10 +785,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
         private int mLastChosenPosition = -1;
         private boolean mFilterLastUsed;
 
-        public ResolveListAdapter(Context context, Intent intent,
-                Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
-                boolean filterLastUsed) {
-            mIntent = new Intent(intent);
+        public ResolveListAdapter(Context context, Intent[] initialIntents,
+                List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
             mInitialIntents = initialIntents;
             mBaseResolveList = rList;
             mLaunchedFromUid = launchedFromUid;
@@ -764,6 +815,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
             return null;
         }
 
+        public DisplayResolveInfo getOtherProfile() {
+            return mOtherProfile;
+        }
+
         public int getFilteredPosition() {
             if (mFilterLastUsed && mLastChosenPosition >= 0) {
                 return mLastChosenPosition;
@@ -870,7 +925,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
                             ri.nonLocalizedLabel = li.getNonLocalizedLabel();
                             ri.icon = li.getIconResource();
                         }
-                        mList.add(new DisplayResolveInfo(ri,
+                        addResolveInfo(new DisplayResolveInfo(ri,
                                 ri.loadLabel(getPackageManager()), null, ii));
                     }
                 }
@@ -915,7 +970,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
                     mLastChosenPosition = mList.size();
                 }
                 // No duplicate labels. Use label for entry at start
-                mList.add(new DisplayResolveInfo(ro, roLabel, null, null));
+                addResolveInfo(new DisplayResolveInfo(ro, roLabel, null, null));
             } else {
                 mShowExtended = true;
                 boolean usePkg = false;
@@ -951,32 +1006,34 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
                     }
                     if (usePkg) {
                         // Use application name for all entries from start to end-1
-                        mList.add(new DisplayResolveInfo(add, roLabel,
+                        addResolveInfo(new DisplayResolveInfo(add, roLabel,
                                 add.activityInfo.packageName, null));
                     } else {
                         // Use package name for all entries from start to end-1
-                        mList.add(new DisplayResolveInfo(add, roLabel,
+                        addResolveInfo(new DisplayResolveInfo(add, roLabel,
                                 add.activityInfo.applicationInfo.loadLabel(mPm), null));
                     }
                 }
             }
         }
 
+        private void addResolveInfo(DisplayResolveInfo dri) {
+            if (dri.ri.targetUserId != UserHandle.USER_CURRENT && mOtherProfile == null) {
+                // So far we only support a single other profile at a time.
+                // The first one we see gets special treatment.
+                mOtherProfile = dri;
+            } else {
+                mList.add(dri);
+            }
+        }
+
         public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
             return (filtered ? getItem(position) : mList.get(position)).ri;
         }
 
         public Intent intentForPosition(int position, boolean filtered) {
             DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position);
-
-            Intent intent = new Intent(dri.origIntent != null ? dri.origIntent :
-                    getReplacementIntent(dri.ri.activityInfo, mIntent));
-            intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
-                    |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
-            ActivityInfo ai = dri.ri.activityInfo;
-            intent.setComponent(new ComponentName(
-                    ai.applicationInfo.packageName, ai.name));
-            return intent;
+            return intentForDisplayResolveInfo(dri);
         }
 
         public int getCount() {
@@ -1067,6 +1124,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
 
         @Override
         protected void onPostExecute(DisplayResolveInfo info) {
+            if (mProfileView != null && mAdapter.getOtherProfile() == info) {
+                bindProfileView();
+            }
             mAdapter.notifyDataSetChanged();
         }
     }
index c00d209..0dfb11a 100644 (file)
@@ -94,7 +94,7 @@ public final class BatteryStatsImpl extends BatteryStats {
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 114 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 115 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
index 5e610ed..40c009f 100644 (file)
@@ -40,8 +40,10 @@ interface IStatusBarService
     // You need the STATUS_BAR_SERVICE permission
     void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
             out int[] switches, out List<IBinder> binders);
-    void onPanelRevealed();
+    void onPanelRevealed(boolean clearNotificationEffects);
     void onPanelHidden();
+    // Mark current notifications as "seen" and stop ringing, vibrating, blinking.
+    void clearNotificationEffects();
     void onNotificationClick(String key);
     void onNotificationActionClick(String key, int actionIndex);
     void onNotificationError(String pkg, String tag, int id,
index 054ca30..8d1f73a 100644 (file)
@@ -97,6 +97,7 @@ public class ToolbarWidgetWrapper implements DecorToolbar {
         mTitle = toolbar.getTitle();
         mSubtitle = toolbar.getSubtitle();
         mTitleSet = mTitle != null;
+        mNavIcon = mToolbar.getNavigationIcon();
         final TypedArray a = toolbar.getContext().obtainStyledAttributes(null,
                 R.styleable.ActionBar, R.attr.actionBarStyle, 0);
         mDefaultNavigationIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator);
@@ -120,7 +121,7 @@ public class ToolbarWidgetWrapper implements DecorToolbar {
             if (icon != null) {
                 setIcon(icon);
             }
-            if (mDefaultNavigationIcon != null) {
+            if (mNavIcon == null && mDefaultNavigationIcon != null) {
                 setNavigationIcon(mDefaultNavigationIcon);
             }
             setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0));
index ed7ce63..35a1a5a 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.server.backup;
 
 
+import android.app.ActivityManagerNative;
 import android.app.IWallpaperManager;
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
@@ -186,4 +187,13 @@ public class SystemBackupAgent extends BackupAgentHelper {
             }
         }
     }
+
+    @Override
+    public void onRestoreFinished() {
+        try {
+            ActivityManagerNative.getDefault().systemBackupRestored();
+        } catch (RemoteException e) {
+            // Not possible since this code is running in the system process.
+        }
+    }
 }
index 2eccfbd..a51af40 100644 (file)
@@ -492,19 +492,15 @@ AndroidPixelRef::AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* sto
         SkMallocPixelRef(info, storage, rowBytes, ctable, (storageObj == NULL)),
         fWrappedPixelRef(NULL) {
     SkASSERT(storage);
+    SkASSERT(storageObj);
     SkASSERT(env);
 
     if (env->GetJavaVM(&fVM) != JNI_OK) {
         SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
         sk_throw();
     }
-    fStorageObj = storageObj;
-    fHasGlobalRef = false;
-    fGlobalRefCnt = 0;
-
-    // If storageObj is NULL, the memory was NOT allocated on the Java heap
-    fOnJavaHeap = (storageObj != NULL);
 
+    fStorageObj = (jbyteArray) env->NewGlobalRef(storageObj);
 }
 
 AndroidPixelRef::AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info,
@@ -516,91 +512,18 @@ AndroidPixelRef::AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImage
     SkASSERT(fWrappedPixelRef);
     SkSafeRef(fWrappedPixelRef);
 
-    // don't need to initialize these, as all the relevant logic delegates to the wrapped ref
+    // don't need to initialize this, as all the relevant logic delegates to the wrapped ref
     fStorageObj = NULL;
-    fHasGlobalRef = false;
-    fGlobalRefCnt = 0;
-    fOnJavaHeap = false;
 }
 
 AndroidPixelRef::~AndroidPixelRef() {
     if (fWrappedPixelRef) {
         SkSafeUnref(fWrappedPixelRef);
-    } else if (fOnJavaHeap) {
+    } else {
+        SkASSERT(fStorageObj);
         JNIEnv* env = vm2env(fVM);
-
-        if (fStorageObj && fHasGlobalRef) {
-            env->DeleteGlobalRef(fStorageObj);
-        }
-        fStorageObj = NULL;
-    }
-}
-jbyteArray AndroidPixelRef::getStorageObj() {
-    if (fWrappedPixelRef) {
-        return fWrappedPixelRef->fStorageObj;
-    }
-    return fStorageObj;
-}
-
-void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) {
-    if (fWrappedPixelRef) {
-        // delegate java obj management to the wrapped ref
-        fWrappedPixelRef->setLocalJNIRef(arr);
-    } else if (!fHasGlobalRef) {
-        fStorageObj = arr;
-    }
-}
-
-void AndroidPixelRef::globalRef(void* localref) {
-    if (fWrappedPixelRef) {
-        // delegate java obj management to the wrapped ref
-        fWrappedPixelRef->globalRef(localref);
-
-        // Note: we only ref and unref the wrapped AndroidPixelRef so that
-        // bitmap->pixelRef()->globalRef() and globalUnref() can be used in a pair, even if
-        // the bitmap has its underlying AndroidPixelRef swapped out/wrapped
-        return;
-    }
-    if (fOnJavaHeap && sk_atomic_inc(&fGlobalRefCnt) == 0) {
-        JNIEnv *env = vm2env(fVM);
-
-        // If JNI ref was passed, it is always used
-        if (localref) fStorageObj = (jbyteArray) localref;
-
-        if (fStorageObj == NULL) {
-            SkDebugf("No valid local ref to create a JNI global ref\n");
-            sk_throw();
-        }
-        if (fHasGlobalRef) {
-            // This should never happen
-            SkDebugf("Already holding a JNI global ref");
-            sk_throw();
-        }
-
-        fStorageObj = (jbyteArray) env->NewGlobalRef(fStorageObj);
-        // TODO: Check for failure here
-        fHasGlobalRef = true;
-    }
-    ref();
-}
-
-void AndroidPixelRef::globalUnref() {
-    if (fWrappedPixelRef) {
-        // delegate java obj management to the wrapped ref
-        fWrappedPixelRef->globalUnref();
-        return;
-    }
-    if (fOnJavaHeap && sk_atomic_dec(&fGlobalRefCnt) == 1) {
-        JNIEnv *env = vm2env(fVM);
-        if (!fHasGlobalRef) {
-            SkDebugf("We don't have a global ref!");
-            sk_throw();
-        }
         env->DeleteGlobalRef(fStorageObj);
-        fStorageObj = NULL;
-        fHasGlobalRef = false;
     }
-    unref();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -657,25 +580,6 @@ bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
 
 ////////////////////////////////////////////////////////////////////////////////
 
-JavaHeapBitmapRef::JavaHeapBitmapRef(JNIEnv* env, SkBitmap* nativeBitmap, jbyteArray buffer) {
-    fEnv = env;
-    fNativeBitmap = nativeBitmap;
-    fBuffer = buffer;
-
-    // If the buffer is NULL, the backing memory wasn't allocated on the Java heap
-    if (fBuffer) {
-        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(fBuffer);
-    }
-}
-
-JavaHeapBitmapRef::~JavaHeapBitmapRef() {
-    if (fBuffer) {
-        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(NULL);
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 static jclass make_globalref(JNIEnv* env, const char classname[])
 {
     jclass c = env->FindClass(classname);
index dcc97e5..42973ba 100644 (file)
@@ -123,52 +123,11 @@ public:
 
     virtual ~AndroidPixelRef();
 
-    jbyteArray getStorageObj();
-
-    void setLocalJNIRef(jbyteArray arr);
-
-    /** Used to hold a ref to the pixels when the Java bitmap may be collected.
-     *  If specified, 'localref' is a valid JNI local reference to the byte array
-     *  containing the pixel data.
-     *
-     *  'localref' may only be NULL if setLocalJNIRef() was already called with
-     *  a JNI local ref that is still valid.
-     */
-    virtual void globalRef(void* localref=NULL);
-
-    /** Release a ref that was acquired using globalRef(). */
-    virtual void globalUnref();
-
 private:
     AndroidPixelRef* const fWrappedPixelRef; // if set, delegate memory management calls to this
 
     JavaVM* fVM;
-    bool fOnJavaHeap; // If true, the memory was allocated on the Java heap
-
     jbyteArray fStorageObj; // The Java byte[] object used as the bitmap backing store
-    bool fHasGlobalRef; // If true, fStorageObj holds a JNI global ref
-
-    mutable int32_t fGlobalRefCnt;
-};
-
-/** A helper class for accessing Java-heap-allocated bitmaps.
- *  This should be used when calling into a JNI method that retains a
- *  reference to the bitmap longer than the lifetime of the Java Bitmap.
- *
- *  After creating an instance of this class, a call to
- *  AndroidPixelRef::globalRef() will allocate a JNI global reference
- *  to the backing buffer object.
- */
-class JavaHeapBitmapRef {
-public:
-
-    JavaHeapBitmapRef(JNIEnv *env, SkBitmap* nativeBitmap, jbyteArray buffer);
-    ~JavaHeapBitmapRef();
-
-private:
-    JNIEnv* fEnv;
-    SkBitmap* fNativeBitmap;
-    jbyteArray fBuffer;
 };
 
 /** Allocator which allocates the backing buffer in the Java heap.
index b023ebd..9bbd4fc 100644 (file)
@@ -347,11 +347,8 @@ static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject clazz,
 // ----------------------------------------------------------------------------
 
 static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
-        jfloat left, jfloat top, jlong paintPtr) {
+        jlong rendererPtr, jlong bitmapPtr, jfloat left, jfloat top, jlong paintPtr) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
-    // This object allows the renderer to allocate a global JNI ref to the buffer object.
-    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
 
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     Paint* paint = reinterpret_cast<Paint*>(paintPtr);
@@ -364,12 +361,10 @@ static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject clazz,
 }
 
 static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
+        jlong rendererPtr, jlong bitmapPtr,
         float srcLeft, float srcTop, float srcRight, float srcBottom,
         float dstLeft, float dstTop, float dstRight, float dstBottom, jlong paintPtr) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
-    // This object allows the renderer to allocate a global JNI ref to the buffer object.
-    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
 
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     Paint* paint = reinterpret_cast<Paint*>(paintPtr);
@@ -378,11 +373,8 @@ static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject clazz,
 }
 
 static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
-        jlong matrixPtr, jlong paintPtr) {
+        jlong rendererPtr, jlong bitmapPtr, jlong matrixPtr, jlong paintPtr) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
-    // This object allows the renderer to allocate a global JNI ref to the buffer object.
-    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
 
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
@@ -427,12 +419,9 @@ static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
 }
 
 static void android_view_GLES20Canvas_drawBitmapMesh(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
-        jint meshWidth, jint meshHeight, jfloatArray vertices, jint offset, jintArray colors,
-        jint colorOffset, jlong paintPtr) {
+        jlong rendererPtr, jlong bitmapPtr, jint meshWidth, jint meshHeight,
+        jfloatArray vertices, jint offset, jintArray colors, jint colorOffset, jlong paintPtr) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
-    // This object allows the renderer to allocate a global JNI ref to the buffer object.
-    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
 
     jfloat* verticesArray = vertices ? env->GetFloatArrayElements(vertices, NULL) + offset : NULL;
     jint* colorsArray = colors ? env->GetIntArrayElements(colors, NULL) + colorOffset : NULL;
@@ -446,11 +435,9 @@ static void android_view_GLES20Canvas_drawBitmapMesh(JNIEnv* env, jobject clazz,
 }
 
 static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer, jlong patchPtr,
+        jlong rendererPtr, jlong bitmapPtr, jlong patchPtr,
         float left, float top, float right, float bottom, jlong paintPtr) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
-    // This object allows the renderer to allocate a global JNI ref to the buffer object.
-    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
 
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(patchPtr);
@@ -914,14 +901,14 @@ static JNINativeMethod gMethods[] = {
     { "nGetMatrix",         "(JJ)V",           (void*) android_view_GLES20Canvas_getMatrix },
     { "nConcatMatrix",      "(JJ)V",           (void*) android_view_GLES20Canvas_concatMatrix },
 
-    { "nDrawBitmap",        "(JJ[BFFJ)V",      (void*) android_view_GLES20Canvas_drawBitmap },
-    { "nDrawBitmap",        "(JJ[BFFFFFFFFJ)V",(void*) android_view_GLES20Canvas_drawBitmapRect },
-    { "nDrawBitmap",        "(JJ[BJJ)V",       (void*) android_view_GLES20Canvas_drawBitmapMatrix },
+    { "nDrawBitmap",        "(JJFFJ)V",      (void*) android_view_GLES20Canvas_drawBitmap },
+    { "nDrawBitmap",        "(JJFFFFFFFFJ)V",(void*) android_view_GLES20Canvas_drawBitmapRect },
+    { "nDrawBitmap",        "(JJJJ)V",       (void*) android_view_GLES20Canvas_drawBitmapMatrix },
     { "nDrawBitmap",        "(J[IIIFFIIZJ)V",  (void*) android_view_GLES20Canvas_drawBitmapData },
 
-    { "nDrawBitmapMesh",    "(JJ[BII[FI[IIJ)V",(void*) android_view_GLES20Canvas_drawBitmapMesh },
+    { "nDrawBitmapMesh",    "(JJII[FI[IIJ)V",(void*) android_view_GLES20Canvas_drawBitmapMesh },
 
-    { "nDrawPatch",         "(JJ[BJFFFFJ)V",   (void*) android_view_GLES20Canvas_drawPatch },
+    { "nDrawPatch",         "(JJJFFFFJ)V",   (void*) android_view_GLES20Canvas_drawPatch },
 
     { "nDrawColor",         "(JII)V",          (void*) android_view_GLES20Canvas_drawColor },
     { "nDrawRect",          "(JFFFFJ)V",       (void*) android_view_GLES20Canvas_drawRect },
index 6219956..b9d849c 100644 (file)
@@ -270,14 +270,14 @@ static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject cla
     proxy->updateSurface(window);
 }
 
-static void android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject clazz,
+static jboolean android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject jsurface) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     sp<ANativeWindow> window;
     if (jsurface) {
         window = android_view_Surface_getNativeWindow(env, jsurface);
     }
-    proxy->pauseSurface(window);
+    return proxy->pauseSurface(window);
 }
 
 static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
@@ -429,7 +429,7 @@ static JNINativeMethod gMethods[] = {
     { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties },
     { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
-    { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
+    { "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface },
     { "nSetup", "(JIIFFFFII)V", (void*) android_view_ThreadedRenderer_setup },
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
     { "nSyncAndDrawFrame", "(JJJF)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
index dec7f07..3dd9770 100644 (file)
         android:description="@string/permdesc_removeDrmCertificates"
         android:protectionLevel="signature|system" />
 
-    <!-- Must be required by a {@link android.service.carriermessaging.CarrierMessagingService}.
+    <!-- Must be required by a {@link android.service.carrier.CarrierMessagingService}.
          Any service that filters for this intent must be a carrier privileged app. -->
     <permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"
         android:label="@string/permlab_bindCarrierMessagingService"
index 6d0f7f8..8a31d5e 100644 (file)
        android:insetTop="@dimen/button_inset_vertical_material"
        android:insetRight="@dimen/button_inset_horizontal_material"
        android:insetBottom="@dimen/button_inset_vertical_material">
-    <shape android:shape="rectangle">
+    <shape android:shape="rectangle"
+           android:tint="?attr/colorButtonNormal">
         <corners android:radius="@dimen/control_corner_material" />
-        <solid android:color="?attr/colorButtonNormal" />
+        <solid android:color="@color/white" />
         <padding android:left="@dimen/button_padding_horizontal_material"
                  android:top="@dimen/button_padding_vertical_material"
                  android:right="@dimen/button_padding_horizontal_material"
index 2933a6a..5d52832 100644 (file)
@@ -33,7 +33,6 @@
                android:layout_height="24dp"
                android:layout_gravity="start|center_vertical"
                android:layout_marginStart="?attr/listPreferredItemPaddingStart"
-               android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
                android:layout_marginTop="12dp"
                android:layout_marginBottom="12dp"
                android:scaleType="fitCenter" />
index 9ae3aec..00c25e6 100644 (file)
     android:maxCollapsedHeightSmall="56dp"
     android:id="@id/contentPanel">
 
-    <TextView android:id="@+id/title"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:layout_alwaysShow="true"
-              android:minHeight="56dp"
-              android:textAppearance="?attr/textAppearanceMedium"
-              android:gravity="start|center_vertical"
-              android:paddingStart="?attr/dialogPreferredPadding"
-              android:paddingEnd="?attr/dialogPreferredPadding"
-              android:paddingTop="8dp"
-              android:paddingBottom="8dp"
-              android:background="@color/white"
-              android:elevation="8dp" />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alwaysShow="true"
+        android:elevation="8dp"
+        android:background="@color/white" >
+        <TextView android:id="@+id/title"
+                  android:layout_width="0dp"
+                  android:layout_height="wrap_content"
+                  android:layout_weight="1"
+                  android:minHeight="56dp"
+                  android:textAppearance="?attr/textAppearanceMedium"
+                  android:gravity="start|center_vertical"
+                  android:paddingStart="?attr/dialogPreferredPadding"
+                  android:paddingEnd="?attr/dialogPreferredPadding"
+                  android:paddingTop="8dp"
+                  android:paddingBottom="8dp" />
+        <LinearLayout android:id="@+id/profile_button"
+                      android:layout_width="wrap_content"
+                      android:layout_height="48dp"
+                      android:layout_marginTop="4dp"
+                      android:layout_marginEnd="4dp"
+                      android:paddingStart="8dp"
+                      android:paddingEnd="8dp"
+                      android:paddingTop="4dp"
+                      android:paddingBottom="4dp"
+                      android:focusable="true"
+                      android:visibility="gone"
+                      style="?attr/borderlessButtonStyle">
+            <ImageView android:id="@+id/icon"
+                       android:layout_width="24dp"
+                       android:layout_height="24dp"
+                       android:layout_gravity="start|center_vertical"
+                       android:layout_marginStart="4dp"
+                       android:layout_marginEnd="16dp"
+                       android:layout_marginTop="12dp"
+                       android:layout_marginBottom="12dp"
+                       android:scaleType="fitCenter" />
+            <TextView android:id="@id/text1"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:layout_gravity="start|center_vertical"
+                      android:layout_marginEnd="16dp"
+                      android:textAppearance="?attr/textAppearanceButton"
+                      android:textColor="?attr/textColorPrimary"
+                      android:minLines="1"
+                      android:maxLines="1"
+                      android:ellipsize="marquee" />
+        </LinearLayout>
+    </LinearLayout>
 
     <ListView
         android:layout_width="match_parent"
index 884f41e..31361e5 100644 (file)
@@ -36,8 +36,7 @@
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="64dp"
-            android:orientation="horizontal"
-            >
+            android:orientation="horizontal" >
 
             <ImageView android:id="@+id/icon"
                        android:layout_width="24dp"
@@ -46,8 +45,7 @@
                        android:layout_marginStart="16dp"
                        android:layout_marginEnd="16dp"
                        android:layout_marginTop="20dp"
-                       android:scaleType="fitCenter"
-                       />
+                       android:scaleType="fitCenter" />
             <TextView android:id="@+id/title"
                       android:layout_width="0dp"
                       android:layout_weight="1"
                       android:layout_marginStart="16dp"
                       android:textAppearance="?android:attr/textAppearanceMedium"
                       android:gravity="start|center_vertical"
-                      android:paddingEnd="16dp"
-                      />
+                      android:paddingEnd="16dp" />
+            <LinearLayout android:id="@+id/profile_button"
+                          android:layout_width="wrap_content"
+                          android:layout_height="48dp"
+                          android:layout_marginTop="4dp"
+                          android:layout_marginEnd="4dp"
+                          android:paddingStart="8dp"
+                          android:paddingEnd="8dp"
+                          android:paddingTop="4dp"
+                          android:paddingBottom="4dp"
+                          android:focusable="true"
+                          android:visibility="gone"
+                          style="?attr/borderlessButtonStyle">
+                <ImageView android:id="@+id/icon"
+                           android:layout_width="24dp"
+                           android:layout_height="24dp"
+                           android:layout_gravity="start|center_vertical"
+                           android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+                           android:layout_marginTop="12dp"
+                           android:layout_marginBottom="12dp"
+                           android:scaleType="fitCenter" />
+                <TextView android:id="@id/text1"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_gravity="start|center_vertical"
+                          android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+                          android:textAppearance="?attr/textAppearanceButton"
+                          android:textColor="?attr/textColorPrimary"
+                          android:minLines="1"
+                          android:maxLines="1"
+                          android:ellipsize="marquee" />
+            </LinearLayout>
         </LinearLayout>
 
         <LinearLayout
index 8f46e6e..1dab04c 100644 (file)
     <string name="permdesc_readProfile" product="default" msgid="5462475151849888848">"Ermöglicht der App, auf Ihrem Gerät gespeicherte personenbezogene Profildaten zu lesen, einschließlich Ihres Namens und Ihrer Kontaktdaten. Die App kann Sie somit identifizieren und Ihre Profildaten an andere senden."</string>
     <string name="permlab_writeProfile" msgid="907793628777397643">"Ihre Kontaktkarten ändern"</string>
     <string name="permdesc_writeProfile" product="default" msgid="5552084294598465899">"Ermöglicht der App, auf Ihrem Gerät gespeicherte personenbezogene Profildaten zu ändern, einschließlich Ihres Namens und Ihrer Kontaktdaten, sowie Daten hinzuzufügen. Die App kann Sie so identifizieren und Ihre Profildaten an andere senden."</string>
-    <string name="permlab_bodySensors" msgid="4871091374767171066">"Körpersensoren (wie Herzfrequenzmesser)"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ermöglicht der App, auf Daten von Sensoren zuzugreifen, die Ihre körperliche Verfassung überwachen, beispielsweise Ihre Herzfrequenz"</string>
+    <string name="permlab_bodySensors" msgid="4871091374767171066">"Körpersensoren (wie Pulsmonitore)"</string>
+    <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ermöglicht der App, auf Daten von Sensoren zuzugreifen, die Ihre körperliche Verfassung überwachen, beispielsweise Ihren Puls"</string>
     <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"In sozialem Stream lesen"</string>
     <string name="permdesc_readSocialStream" product="default" msgid="4255706027172050872">"Ermöglicht der App, auf Updates aus sozialen Netzwerken von Ihnen und Ihren Freunden zuzugreifen und diese zu synchronisieren. Seien Sie vorsichtig, wenn Sie Informationen teilen: Der App wird erlaubt, die Kommunikation zwischen Ihnen und Ihren Freunden in sozialen Netzwerken zu lesen, unabhängig von der Vertraulichkeit der kommunizierten Informationen. Hinweis: Diese Berechtigung kann möglicherweise nicht in allen sozialen Netzwerken erzwungen werden."</string>
     <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"In sozialem Stream schreiben"</string>
index 9010721..9386ead 100644 (file)
     <string name="capability_desc_canRequestTouchExploration" msgid="5800552516779249356">"Os elementos que toques pronunciaranse en voz alta e a pantalla poderá explorarse mediante xestos."</string>
     <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"Activar a accesibilidade web mellorada"</string>
     <string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"É posible que se instalen scripts para que o contido da aplicación resulte máis accesible."</string>
-    <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observa o texto que escribes"</string>
+    <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observar o texto que escribes"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclúe datos persoais como números e contrasinais de tarxetas de crédito."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"desactivar ou modificar a barra de estado"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite á aplicación desactivar a barra de estado ou engadir e eliminar as iconas do sistema."</string>
     <string name="permdesc_receiveMms" msgid="533019437263212260">"Permite á aplicación recibir e procesar mensaxes MMS. Isto significa que a aplicación pode supervisar ou eliminar mensaxes enviadas ao teu dispositivo sen mostrarchas."</string>
     <string name="permlab_receiveEmergencyBroadcast" msgid="1803477660846288089">"recibir difusións de emerxencia"</string>
     <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"Permite á aplicación recibir e procesar mensaxes de emisión de emerxencia. Este permiso só está dispoñible para as aplicacións do sistema."</string>
-    <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ler mensaxes de difusión de cela"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite á aplicación ler mensaxes de difusión de cela recibidas polo teu dispositivo. As alertas de difusión de cela entréganse nalgunhas situacións para avisar de situacións de emerxencia. É posible que aplicacións maliciosas afecten ao rendemento ou funcionamento do teu dispositivo cando se recibe unha difusión de cela de emerxencia."</string>
+    <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ler mensaxes de difusión móbil"</string>
+    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite á aplicación ler mensaxes de difusión móbil recibidas polo teu dispositivo. As alertas de difusión móbil entréganse nalgunhas situacións para avisar de situacións de emerxencia. É posible que aplicacións maliciosas afecten ao rendemento ou funcionamento do teu dispositivo cando se recibe unha difusión móbil de emerxencia."</string>
     <string name="permlab_sendSms" msgid="5600830612147671529">"enviar mensaxes SMS"</string>
     <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite á aplicación enviar mensaxes SMS. É posible que esta acción implique custos inesperados. É posible que as aplicacións maliciosas che custen diñeiro debido ao envío de mensaxes sen a túa confirmación."</string>
     <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"enviar eventos de resposta mediante mensaxe"</string>
index 774732d..4b61688 100644 (file)
     <!-- If this value is true, The mms content-disposition field is supported correctly.
          If false, Content-disposition fragments are ignored -->
     <bool name="config_mms_content_disposition_support">false</bool>
+
+    <!-- An array of CDMA roaming indicators which means international roaming -->
+    <integer-array translatable="false" name="config_cdma_international_roaming_indicators" >
+        <item>2</item>
+        <item>74</item>
+        <item>124</item>
+        <item>125</item>
+        <item>126</item>
+        <item>157</item>
+        <item>158</item>
+        <item>159</item>
+        <item>193</item>
+        <item>194</item>
+        <item>195</item>
+        <item>196</item>
+        <item>197</item>
+        <item>198</item>
+        <item>228</item>
+        <item>229</item>
+        <item>230</item>
+        <item>231</item>
+        <item>232</item>
+        <item>233</item>
+        <item>234</item>
+        <item>235</item>
+    </integer-array>
+
 </resources>
index defe78d..d481c97 100644 (file)
 
     <!-- Sprint need a 70 ms delay for 3way call -->
     <integer name="config_cdma_3waycall_flash_delay">70</integer>
+
+    <!-- An array of CDMA roaming indicators which means international roaming -->
+    <integer-array translatable="false" name="config_cdma_international_roaming_indicators" >
+        <item>2</item>
+        <item>74</item>
+        <item>124</item>
+        <item>125</item>
+        <item>126</item>
+        <item>157</item>
+        <item>158</item>
+        <item>159</item>
+        <item>193</item>
+        <item>194</item>
+        <item>195</item>
+        <item>196</item>
+        <item>197</item>
+        <item>198</item>
+        <item>228</item>
+        <item>229</item>
+        <item>230</item>
+        <item>231</item>
+        <item>232</item>
+        <item>233</item>
+        <item>234</item>
+        <item>235</item>
+    </integer-array>
+
 </resources>
index 24e55b1..98cb72e 100644 (file)
 
     <!-- Sprint need a 70 ms delay for 3way call -->
     <integer name="config_cdma_3waycall_flash_delay">70</integer>
+
+    <!-- An array of CDMA roaming indicators which means international roaming -->
+    <integer-array translatable="false" name="config_cdma_international_roaming_indicators" >
+        <item>2</item>
+        <item>74</item>
+        <item>124</item>
+        <item>125</item>
+        <item>126</item>
+        <item>157</item>
+        <item>158</item>
+        <item>159</item>
+        <item>193</item>
+        <item>194</item>
+        <item>195</item>
+        <item>196</item>
+        <item>197</item>
+        <item>198</item>
+        <item>228</item>
+        <item>229</item>
+        <item>230</item>
+        <item>231</item>
+        <item>232</item>
+        <item>233</item>
+        <item>234</item>
+        <item>235</item>
+    </integer-array>
+
 </resources>
index 24e55b1..98cb72e 100644 (file)
 
     <!-- Sprint need a 70 ms delay for 3way call -->
     <integer name="config_cdma_3waycall_flash_delay">70</integer>
+
+    <!-- An array of CDMA roaming indicators which means international roaming -->
+    <integer-array translatable="false" name="config_cdma_international_roaming_indicators" >
+        <item>2</item>
+        <item>74</item>
+        <item>124</item>
+        <item>125</item>
+        <item>126</item>
+        <item>157</item>
+        <item>158</item>
+        <item>159</item>
+        <item>193</item>
+        <item>194</item>
+        <item>195</item>
+        <item>196</item>
+        <item>197</item>
+        <item>198</item>
+        <item>228</item>
+        <item>229</item>
+        <item>230</item>
+        <item>231</item>
+        <item>232</item>
+        <item>233</item>
+        <item>234</item>
+        <item>235</item>
+    </integer-array>
+
 </resources>
diff --git a/core/res/res/values-mcc415-mnc01/config.xml b/core/res/res/values-mcc415-mnc01/config.xml
new file mode 100644 (file)
index 0000000..95d30a4
--- /dev/null
@@ -0,0 +1,21 @@
+<?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.
+*/
+-->
+<resources>
+    <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/res/res/values-mcc416-mnc77/config.xml b/core/res/res/values-mcc416-mnc77/config.xml
new file mode 100644 (file)
index 0000000..95d30a4
--- /dev/null
@@ -0,0 +1,21 @@
+<?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.
+*/
+-->
+<resources>
+    <bool name="config_use_sim_language_file">false</bool>
+</resources>
diff --git a/core/res/res/values-mcc432-mnc20/config.xml b/core/res/res/values-mcc432-mnc20/config.xml
new file mode 100644 (file)
index 0000000..95d30a4
--- /dev/null
@@ -0,0 +1,21 @@
+<?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.
+*/
+-->
+<resources>
+    <bool name="config_use_sim_language_file">false</bool>
+</resources>
index 437e14c..9883135 100644 (file)
     <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> තෝරාගෙන ඇත"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> මකා දමන ලදි"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"වැඩ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="lock_to_app_toast" msgid="7570091317001980053">"මෙම තීරයේ ඇමුණුම ඉවත් කිරීමට, Back සහ Overview එකම වේලාවේ ස්පර්ශ කර අල්ලා සිටින්න."</string>
-    <string name="lock_to_app_toast_accessible" msgid="8239120109365070664">"මෙම තීරයේ ඇමුණුම ඉවත් කිරීමට, Overview ස්පර්ශ කර අල්ලා සිටින්න."</string>
+    <string name="lock_to_app_toast" msgid="7570091317001980053">"මෙම තීරයේ ඇමුණුම ඉවත් කිරීමට, ආපසු සහ දළ විශ්ලේෂණය එකම වේලාවේ ස්පර්ශ කර අල්ලා සිටින්න."</string>
+    <string name="lock_to_app_toast_accessible" msgid="8239120109365070664">"මෙම තීරයේ ඇමුණුම ඉවත් කිරීමට, දළ විශ්ලේෂණය ස්පර්ශ කර අල්ලා සිටින්න."</string>
     <string name="lock_to_app_toast_locked" msgid="8739004135132606329">"තිරය අගුළු දමා ඇත. ඔබගේ සංවිධානය විසින් අගුළු ඇරීමට ඉඩ නොදෙයි."</string>
     <string name="lock_to_app_start" msgid="6643342070839862795">"තිරය අගුළු දමා ඇත"</string>
     <string name="lock_to_app_exit" msgid="8598219838213787430">"තිරයේ අගුළු ඇර ඇත"</string>
index cdbee61..926b53e 100755 (executable)
     <dimen name="config_mediaMetadataBitmapMaxSize">320dp</dimen>
 
     <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">false</string>
+
+    <!-- An array of CDMA roaming indicators which means international roaming -->
+    <integer-array translatable="false" name="config_cdma_international_roaming_indicators" />
+
+
+    <!-- set the system language as value of EF LI/EF PL -->
+    <bool name="config_use_sim_language_file">true</bool>
 </resources>
index f7ebbe4..8cc2a8a 100644 (file)
@@ -87,7 +87,7 @@
     <dimen name="floating_window_margin_bottom">32dp</dimen>
 
     <!-- Elevation when button is pressed -->
-    <dimen name="button_elevation_material">1dp</dimen>
+    <dimen name="button_elevation_material">4dp</dimen>
     <!-- Z translation to apply when button is pressed -->
     <dimen name="button_pressed_z_material">2dp</dimen>
     <!-- Default insets (outer padding) around buttons -->
index 5296966..90b8383 100755 (executable)
   <java-symbol type="id" name="scrollIndicatorDown" />
   <java-symbol type="array" name="config_sms_convert_destination_number_support" />
   <java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
+  <java-symbol type="id" name="profile_button" />
 
   <!-- From SignalStrength -->
   <java-symbol type="integer" name="config_LTE_RSRP_threshold_type" />
   <java-symbol type="string" name="system_error_wipe_data" />
   <java-symbol type="string" name="system_error_manufacturer" />
   <java-symbol type="dimen" name="fast_scroller_minimum_touch_target" />
+  <java-symbol type="array" name="config_cdma_international_roaming_indicators" />
+  <java-symbol type="string" name="kg_text_message_separator" />
 
+  <java-symbol type="bool" name="config_use_sim_language_file" />
 </resources>
index 36d7116..def8659 100644 (file)
@@ -551,6 +551,8 @@ easier.
         <item name="windowDrawsSystemBarBackgrounds">false</item>
         <item name="windowContentOverlay">@null</item>
         <item name="colorControlActivated">?attr/colorControlHighlight</item>
+        <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
+        <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
     </style>
 
 </resources>
index 93e68eb..5c9e813 100644 (file)
@@ -180,6 +180,51 @@ public class FileUtilsTest extends AndroidTestCase {
         assertDirContents("file1", "file2");
     }
 
+    public void testValidExtFilename() throws Exception {
+        assertTrue(FileUtils.isValidExtFilename("a"));
+        assertTrue(FileUtils.isValidExtFilename("foo.bar"));
+        assertTrue(FileUtils.isValidExtFilename("foo bar.baz"));
+        assertTrue(FileUtils.isValidExtFilename("foo.bar.baz"));
+        assertTrue(FileUtils.isValidExtFilename(".bar"));
+        assertTrue(FileUtils.isValidExtFilename("foo~!@#$%^&*()_[]{}+bar"));
+
+        assertFalse(FileUtils.isValidExtFilename(null));
+        assertFalse(FileUtils.isValidExtFilename("."));
+        assertFalse(FileUtils.isValidExtFilename("../foo"));
+        assertFalse(FileUtils.isValidExtFilename("/foo"));
+
+        assertEquals(".._foo", FileUtils.buildValidExtFilename("../foo"));
+        assertEquals("_foo", FileUtils.buildValidExtFilename("/foo"));
+        assertEquals("foo_bar", FileUtils.buildValidExtFilename("foo\0bar"));
+        assertEquals(".foo", FileUtils.buildValidExtFilename(".foo"));
+        assertEquals("foo.bar", FileUtils.buildValidExtFilename("foo.bar"));
+    }
+
+    public void testValidFatFilename() throws Exception {
+        assertTrue(FileUtils.isValidFatFilename("a"));
+        assertTrue(FileUtils.isValidFatFilename("foo bar.baz"));
+        assertTrue(FileUtils.isValidFatFilename("foo.bar.baz"));
+        assertTrue(FileUtils.isValidFatFilename(".bar"));
+        assertTrue(FileUtils.isValidFatFilename("foo.bar"));
+        assertTrue(FileUtils.isValidFatFilename("foo bar"));
+        assertTrue(FileUtils.isValidFatFilename("foo+bar"));
+        assertTrue(FileUtils.isValidFatFilename("foo,bar"));
+
+        assertFalse(FileUtils.isValidFatFilename("foo*bar"));
+        assertFalse(FileUtils.isValidFatFilename("foo?bar"));
+        assertFalse(FileUtils.isValidFatFilename("foo<bar"));
+        assertFalse(FileUtils.isValidFatFilename(null));
+        assertFalse(FileUtils.isValidFatFilename("."));
+        assertFalse(FileUtils.isValidFatFilename("../foo"));
+        assertFalse(FileUtils.isValidFatFilename("/foo"));
+
+        assertEquals(".._foo", FileUtils.buildValidFatFilename("../foo"));
+        assertEquals("_foo", FileUtils.buildValidFatFilename("/foo"));
+        assertEquals(".foo", FileUtils.buildValidFatFilename(".foo"));
+        assertEquals("foo.bar", FileUtils.buildValidFatFilename("foo.bar"));
+        assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz"));
+    }
+
     private void touch(String name, long age) throws Exception {
         final File file = new File(mDir, name);
         file.createNewFile();
diff --git a/data/keyboards/Vendor_046d_Product_b501.kl b/data/keyboards/Vendor_046d_Product_b501.kl
new file mode 100644 (file)
index 0000000..496ddc3
--- /dev/null
@@ -0,0 +1,65 @@
+# 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.
+
+# Logitech Bluetooth wireless gamepad (RedHawk)
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 312 BUTTON_L2
+key 313 BUTTON_R2
+
+key 314 BUTTON_SELECT
+key 315 BUTTON_START
+key 316 BUTTON_MODE
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+key 103 DPAD_UP
+key 105 DPAD_LEFT
+key 106 DPAD_RIGHT
+key 108 DPAD_DOWN
+key 353 DPAD_CENTER
+
+key 113 VOLUME_MUTE
+key 114 VOLUME_DOWN
+key 115 VOLUME_UP
+key 152 POWER
+key 163 MEDIA_NEXT
+key 164 MEDIA_PLAY_PAUSE
+key 165 MEDIA_PREVIOUS
+
+key 158 BACK
+key 172 HOME
+
+key 217 SEARCH
+key 580 APP_SWITCH
+key 582 ASSIST
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x05 RZ
+axis 0x09 RTRIGGER
+axis 0x0a LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+led 0x00 CONTROLLER_1
+led 0x01 CONTROLLER_2
+led 0x02 CONTROLLER_3
+led 0x03 CONTROLLER_4
diff --git a/data/keyboards/Vendor_1532_Product_0900.kl b/data/keyboards/Vendor_1532_Product_0900.kl
new file mode 100644 (file)
index 0000000..c2fc1b4
--- /dev/null
@@ -0,0 +1,42 @@
+# 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.
+
+# Razer Serval
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 316 BUTTON_MODE
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+key 158 BACK
+key 172 HOME
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x05 RZ
+axis 0x09 RTRIGGER
+axis 0x0a LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+led 0x00 CONTROLLER_1
+led 0x01 CONTROLLER_2
+led 0x02 CONTROLLER_3
+led 0x03 CONTROLLER_4
index 448dcda..063084d 100644 (file)
@@ -57,7 +57,7 @@ Platform Versions</a>.</p>
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on November 3, 2014.
+<p style="clear:both"><em>Data collected during a 7-day period ending on December 1, 2014.
 <br/>Any versions with less than 0.1% distribution are not shown.</em>
 </p>
 
@@ -88,7 +88,7 @@ Screens</a>.</p>
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on November 3, 2014.
+<p style="clear:both"><em>Data collected during a 7-day period ending on December 1, 2014.
 
 <br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
 
@@ -108,7 +108,8 @@ support for any lower version (for example, support for version 2.0 also implies
 
 
 <img alt="" style="float:right"
-src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0&chd=t%3A74.7%2C25.3&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=400x250" />
+src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0&chf=bg%2Cs%2C00000000&chd=t%3A72.2%2C27.8&chco=c4df9b%2C6fad0c&cht=p&chs=400x250" />
+
 
 <p>To declare which version of OpenGL ES your application requires, you should use the {@code
 android:glEsVersion} attribute of the <a
@@ -126,17 +127,17 @@ uses.</p>
 </tr>
 <tr>
 <td>2.0</td>
-<td>74.7%</td>
+<td>72.2%</td>
 </tr>
 <tr>
 <td>3.0</td>
-<td>25.3%</td>
+<td>27.8%</td>
 </tr>
 </table>
 
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on November 3, 2014</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on December 1, 2014</em></p>
 
 
 
@@ -154,42 +155,42 @@ uses.</p>
 var VERSION_DATA =
 [
   {
-    "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat&chd=t%3A0.6%2C9.8%2C8.5%2C50.9%2C30.2&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=500x250",
+    "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat&chf=bg%2Cs%2C00000000&chd=t%3A0.5%2C9.1%2C7.8%2C48.7%2C33.9&chco=c4df9b%2C6fad0c&cht=p&chs=500x250",
     "data": [
       {
         "api": 8,
         "name": "Froyo",
-        "perc": "0.6"
+        "perc": "0.5"
       },
       {
         "api": 10,
         "name": "Gingerbread",
-        "perc": "9.8"
+        "perc": "9.1"
       },
       {
         "api": 15,
         "name": "Ice Cream Sandwich",
-        "perc": "8.5"
+        "perc": "7.8"
       },
       {
         "api": 16,
         "name": "Jelly Bean",
-        "perc": "22.8"
+        "perc": "21.3"
       },
       {
         "api": 17,
         "name": "Jelly Bean",
-        "perc": "20.8"
+        "perc": "20.4"
       },
       {
         "api": 18,
         "name": "Jelly Bean",
-        "perc": "7.3"
+        "perc": "7.0"
       },
       {
         "api": 19,
         "name": "KitKat",
-        "perc": "30.2"
+        "perc": "33.9"
       }
     ]
   }
@@ -203,27 +204,28 @@ var SCREEN_DATA =
       "Large": {
         "hdpi": "0.6",
         "ldpi": "0.5",
-        "mdpi": "4.5",
-        "tvdpi": "1.9",
+        "mdpi": "4.6",
+        "tvdpi": "2.0",
         "xhdpi": "0.6"
       },
       "Normal": {
-        "hdpi": "36.6",
-        "mdpi": "9.9",
-        "xhdpi": "18.9",
-        "xxhdpi": "16.0"
+        "hdpi": "36.9",
+        "mdpi": "9.4",
+        "tvdpi": "0.2",
+        "xhdpi": "18.8",
+        "xxhdpi": "16.3"
       },
       "Small": {
-        "ldpi": "5.8"
+        "ldpi": "5.4"
       },
       "Xlarge": {
         "hdpi": "0.3",
-        "mdpi": "3.9",
-        "xhdpi": "0.5"
+        "mdpi": "3.8",
+        "xhdpi": "0.6"
       }
     },
-    "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chd=t%3A6.3%2C18.3%2C1.9%2C37.5%2C20.0%2C16.0&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=400x250",
-    "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chd=t%3A4.7%2C8.1%2C81.4%2C5.8&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=400x250"
+    "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chf=bg%2Cs%2C00000000&chd=t%3A5.9%2C17.8%2C2.2%2C37.8%2C20.0%2C16.3&chco=c4df9b%2C6fad0c&cht=p&chs=400x250",
+    "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chf=bg%2Cs%2C00000000&chd=t%3A4.7%2C8.3%2C81.6%2C5.4&chco=c4df9b%2C6fad0c&cht=p&chs=400x250"
   }
 ];
 
index 4237c98..dff3508 100644 (file)
@@ -148,7 +148,7 @@ window.onhashchange = function () {
 </p>
 
 <p>
-  New tools give also give you powerful insight into your app's memory use. The
+  New tools also give you powerful insight into your app's memory use. The
   <strong>procstats tool</strong> details memory use over time, with run times
   and memory footprint for foreground apps and background services. An
   on-device view is also available as a new developer option. The
index 7c3da57..e680afb 100644 (file)
Binary files a/docs/html/design/media/wear/ContextualExample.006.png and b/docs/html/design/media/wear/ContextualExample.006.png differ
index 319530d..ee4087e 100644 (file)
Binary files a/docs/html/design/media/wear/ContextualExample.006_2x.png and b/docs/html/design/media/wear/ContextualExample.006_2x.png differ
index 2e66532..688806f 100644 (file)
@@ -131,7 +131,7 @@ app.</p>
 
 <div class="slide">
 <h2>Zoo</h2>
-<p>Notifies visitors when the penguins are going to be fed!
+<p>Notifies visitors when the penguins are going to be fed.
 </p>
 <img src="{@docRoot}design/media/wear/ContextualExample.014.png" alt=""
      srcset="{@docRoot}design/media/wear/ContextualExample.014.png 1x,
index 775c424..59bf71c 100644 (file)
@@ -560,7 +560,7 @@ is sensitive to delays or lag in sound input or output.</td>
        <td></td>
     </tr>
     <tr>
-       <td rowspan="12">Camera</td>
+       <td rowspan="10">Camera</td>
        <td><code>android.hardware.camera</code></td>
        <td>The application uses the device's camera. If the device supports
            multiple cameras, the application uses the camera that facing
@@ -609,14 +609,6 @@ external camera device if one is connected. Use this in preference to
   <td><code>android.hardware.camera.capability.raw</code></td>
   <td>The application uses a a camera device with the <code>RAW</code> capability.</td>
 </tr>
-<tr>
-  <td><code>android.hardware.camera.capability.burst_capture</code></td>
-  <td>The application uses a a camera device with the <code>BURST_CAPTURE</code> capability.</td>
-</tr>
-<tr>
-  <td><code>android.hardware.camera.capability.read_sensor_settings</code></td>
-  <td>The application uses a a camera device with the <code>READ_SENSOR_SETTINGS</code> capability.</td>
-</tr>
 
 <tr>
   <td>Infrared</td>
index f454c4e..02f1255 100644 (file)
@@ -801,7 +801,7 @@ String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN,
 
 <h3 id="Listening">Listening for preference changes</h3>
 
-<p>There are several reasons you might want to be notified as soon as the use changes one of the
+<p>There are several reasons you might want to be notified as soon as the user changes one of the
 preferences. In order to receive a callback when a change happens to any one of the preferences,
 implement the {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener
 SharedPreference.OnSharedPreferenceChangeListener} interface and register the listener for the
index 4f7722c..7cd029f 100644 (file)
@@ -272,13 +272,11 @@ This is the Android SDK Preview License Agreement (the “License Agreement”).
   style="position:absolute;z-index:100;right:215px;top:375px">Android 5.0 API Overview</a>
 
       <div style="width:440px">
-<p>Android 5.0 (Lollipop) is almost here and users will begin receiving
-device updates in November. To help you prepare, the Android 5.0 SDK is now available
-with final APIs.</p>
+<p>Android 5.0 (Lollipop) is now out of preview and available to users.</p>
 
-<p>Since the L Developer Preview began, various APIs and behaviors have changed,
-so if you've been using the Preview SDK
-you should update now to test your apps and take advantage of new features.</p>
+<p>If you previously developed apps with the L Developer Preview, be aware that various APIs and
+behaviors have changed, so you should update your SDK now to test your apps and take advantage of
+new features in Android 5.0.</p>
 
 
         <p>To get the latest Android 5.0 SDK:</p>
index f522220..ab379bb 100644 (file)
@@ -15,4 +15,5 @@ Disallow: /shareables/
 Disallow: /guide/tutorials/
 Disallow: /guide/samples/
 Disallow: /community/
+Disallow: /preview/
 Sitemap: http://developer.android.com/sitemap.txt
diff --git a/docs/html/samples/admin.jd b/docs/html/samples/admin.jd
new file mode 100644 (file)
index 0000000..c6637d8
--- /dev/null
@@ -0,0 +1,11 @@
+page.title=Admin
+@jd:body
+
+
+<div id="samples" class="admin">
+</div>
+
+
+<script>
+  $(document).ready(showSamples);
+</script>
index ba75072..279b910 100644 (file)
@@ -2,19 +2,18 @@ page.title=What's New
 
 @jd:body
 
-<p>The following code samples were recently published for the L Developer Preview. You can
+<p>The following code samples were recently published. You can
 download them in the Android SDK Manager under the <b>SDK Samples</b> component
-for the L Developer Preview.</p>
+for API 21.</p>
 
 <p class="note">
-  <strong>Note:</strong> At this time, the downloadable projects are designed
+  <strong>Note:</strong> The downloadable projects are designed
    for use with Gradle and Android Studio.
 </p>
 
-
 <!-- NOTE TO EDITORS: add most recent samples first -->
 
-<h3 id="MediaBrowserService">Media Browser Service</h3>
+<h3 id="MediaBrowserService"><a href="/samples/MediaBrowserService/index.html">Media Browser Service</a></h3>
 
 <p>
 This sample is a simple audio media app that exposes its media
@@ -29,10 +28,8 @@ when not connected to a car.
   href="http://android.com/auto">Android Auto</a>.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-MediaBrowserService">Get it on GitHub</a></p>
-
 
-<h3 id="MessagingService">Messaging Service</h3>
+<h3 id="MessagingService"><a href="/samples/MessagingService/index.html">Messaging Service</a></h3>
 
 <p>
 This sample shows a simple service that sends notifications using
@@ -46,10 +43,8 @@ Each unread conversation from a user is sent as a distinct notification.
   href="http://android.com/auto">Android Auto</a>.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-MessagingService">Get it on GitHub</a></p>
-
 
-<h3 id="SpeedTracker">Speed Tracker (Wear)</h3>
+<h3 id="SpeedTracker"><a href="/samples/SpeedTracker/index.html">Speed Tracker (Wear)</a></h3>
 
 <p>
 This sample uses the FusedLocation APIs of Google Play Services on Android Wear 
@@ -62,10 +57,8 @@ is synced with the phone component of the app and user can see a track
 made of those coordinates on a map on the phone.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-SpeedTracker">Get it on GitHub</a></p>
 
-
-<h3 id="AppRestrictionSchema">AppRestrictionSchema</h3>
+<h3 id="AppRestrictionSchema"><a href="/samples/AppRestrictionSchema/index.html">AppRestrictionSchema</a></h3>
 
 <p>
 This sample shows how to use app restrictions. This application has one boolean
@@ -74,29 +67,23 @@ app (press the button to show "Hello" message) is enabled or disabled. Use
 AppRestrictionEnforcer sample to toggle the restriction.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-AppRestrictionSchema">Get it on GitHub</a></p>
-
 
-<h3 id="AppRestrictionEnforcer">AppRestrictionEnforcer</h3>
+<h3 id="AppRestrictionEnforcer"><a href="/samples/AppRestrictionEnforcer/index.html">AppRestrictionEnforcer</a></h3>
 
 <p>
 This sample demonstrates how to set restrictions to other apps as a profile owner.
 Use AppRestrictionSchema sample as a app with available restrictions.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-AppRestrictionEnforcer">Get it on GitHub</a></p>
-
 
-<h3 id="DocumentCentricRelinquishIdentity">DocumentCentricRelinquishIdentity</h3>
+<h3 id="DocumentCentricRelinquishIdentity"><a href="/samples/DocumentCentricRelinquishIdentity/index.html">DocumentCentricRelinquishIdentity</a></h3>
 
 <p>
 This sample shows how to relinquish identity to activities above it in the task stack.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-DocumentCentricRelinquishIdentity">Get it on GitHub</a></p>
 
-
-<h3 id="DocumentCentricApps">DocumentCentricApps</h3>
+<h3 id="DocumentCentricApps"><a href="/samples/DocumentCentricApps/index.html">DocumentCentricApps</a></h3>
 
 <p>
 This sample shows the basic usage of the new "Document Centric Apps" API.
@@ -105,10 +92,8 @@ state through reboots. If "Task per document" is checked a new task will be
 created for every new document in the overview menu.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-DocumentCentricApps">Get it on GitHub</a></p>
-
 
-<h3 id="HdrViewfinder">HdrViewfinder</h3>
+<h3 id="HdrViewfinder"><a href="/samples/HdrViewfinder/index.html">HdrViewfinder</a></h3>
 
 <p>
 This demo implements a real-time high-dynamic-range camera viewfinder, by alternating
@@ -116,10 +101,8 @@ the sensor's exposure time between two exposure values on even and odd frames, a
 compositing together the latest two frames whenever a new frame is captured.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-HdrViewfinder">Get it on GitHub</a></p>
-
 
-<h3 id="Interpolator">Interpolator</h3>
+<h3 id="Interpolator"><a href="/samples/Interpolator/index.html">Interpolator</a></h3>
 
 <p>
 This sample demonstrates the use of animation interpolators and path animations for
@@ -127,10 +110,8 @@ Material Design. It shows how an ObjectAnimator is used to animate two propertie
 view (scale X and Y) along a path.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-Interpolator">Get it on GitHub</a></p>
 
-
-<h3 id="DrawableTinting">DrawableTinting</h3>
+<h3 id="DrawableTinting"><a href="/samples/DrawableTinting/index.html">DrawableTinting</a></h3>
 
 <p>Sample that shows applying tinting and color filters to Drawables both programmatically
 and as Drawable resources in XML.</p>
@@ -141,54 +122,43 @@ states of a View (for example disabled/enabled, focused, pressed or selected).</
 with a reference to a color and a PorterDuff blend mode. The color and blend mode can be
 changed from the UI to see the effect of different options.</p>
 
-<p><a href="http://github.com/googlesamples/android-DrawableTinting">Get it on GitHub</a></p>
-
 
-<h3 id="LNotifications">LNotifications</h3>
+<h3 id="LNotifications"><a href="/samples/LNotifications/index.html">LNotifications</a></h3>
 
 <p>
 This sample demonstrates how new features for notifications introduced in Android 5.0
 are used such as Heads-Up notifications, visibility, people, category and priority
 metadata. </p>
-<p><a href="http://github.com/googlesamples/android-LNotifications">Get it on GitHub</a></p>
 
 
-<h3 id="CardView">CardView</h3>
+<h3 id="CardView"><a href="/samples/CardView/index.html">CardView</a></h3>
 
 <p>
 This sample demonstrates how to use the CardView UI widget introduced in Android 5.0, using the support library for backward compatibility.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-CardView">Get it on GitHub</a></p>
 
-
-<h3 id="RecyclerView">RecyclerView</h3>
+<h3 id="RecyclerView"><a href="/samples/RecyclerView/index.html">RecyclerView</a></h3>
 
 <p>
 Demonstration of using RecyclerView with a LayoutManager to create a vertical ListView.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-RecyclerView">Get it on GitHub</a></p>
-
 
-<h3 id="RevealEffectBasic">RevealEffectBasic</h3>
+<h3 id="RevealEffectBasic"><a href="/samples/RevealEffectBasic/index.html">RevealEffectBasic</a></h3>
 
 <p>
 A sample demonstrating how to perform a reveal effect for UI elements within the Material Design framework.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-RevealEffectBasic">Get it on GitHub</a></p>
-
 
-<h3 id="FloatingActionButtonBasic">FloatingActionButtonBasic</h3>
+<h3 id="FloatingActionButtonBasic"><a href="/samples/FloatingActionButtonBasic/index.html">FloatingActionButtonBasic</a></h3>
 
 <p>
 This sample shows the two sizes of Floating Action Buttons and how to interact with
 them.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-FloatingActionButtonBasic">Get it on GitHub</a></p>
-
 
 <!--
 <h3 id="">SampleName</h3>
@@ -205,36 +175,15 @@ them.
 </p>
 -->
 
-
-<h3 id="NavigationDrawerSample">NavigationDrawerSample</h3>
-<!--
-<div class="figure" style="width:220px">
-  <img src="" srcset="@2x.png 2x" alt="" height="375" />
-  <p class="img-caption">
-    <strong>Figure n.</strong> Single sentence summarizing the figure.
-  </p>
-</div>
--->
-
-<p>
-This sample illustrates a common usage of the Android support library's
-{@link android.support.v4.widget.DrawerLayout} widget.
-</p>
-
-<p><a href="http://github.com/googlesamples/android-NavigationDrawer">Get it on GitHub</a></p>
-
-
-<h3 id="JobSchedulerSample">JobSchedulerSample</h3>
+<h3 id="JobSchedulerSample"><a href="/samples/JobScheduler/index.html">JobScheduler</a></h3>
 
 <p>
 This sample app allows the user to schedule jobs through the UI, and shows
 visual cues when the jobs are executed.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-JobScheduler">Get it on GitHub</a></p>
 
-
-<h3 id="AndroidTVLeanbackSample">AndroidTVLeanbackSample</h3>
+<h3 id="AndroidTVLeanbackSample"><a href="https://github.com/googlesamples/androidtv-leanback">AndroidTVLeanbackSample</a></h3>
 <!--
 <div class="figure" style="width:220px">
   <img src="" srcset="@2x.png 2x" alt="" height="375" />
@@ -248,10 +197,7 @@ visual cues when the jobs are executed.
 This sample demonstrates use of the Android TV Leanback Support Library.
 </p>
 
-<p><a href="http://github.com/googlesamples/androidtv-Leanback">Get it on GitHub</a></p>
-
-
-<h3 id="Visual-Game-Controller">Visual-Game-Controller</h3>
+<h3 id="Visual-Game-Controller"><a href="https://github.com/googlesamples/androidtv-VisualGameController">Visual-Game-Controller</a></h3>
 <!--
 <div class="figure" style="width:220px">
   <img src="" srcset="@2x.png 2x" alt="" height="375" />
@@ -265,10 +211,8 @@ This sample demonstrates use of the Android TV Leanback Support Library.
 This sample displays events received from a game controller shown on the screen.
 </p>
 
-<p><a href="http://github.com/googlesamples/androidtv-VisualGameController">Get it on GitHub</a></p>
-
 
-<h3 id="GameControllerSample">GameControllerSample</h3>
+<h3 id="GameControllerSample"><a href="https://github.com/googlesamples/androidtv-GameController/">GameControllerSample</a></h3>
 <!--
 <div class="figure" style="width:220px">
   <img src="" srcset="@2x.png 2x" alt="" height="375" />
@@ -283,10 +227,8 @@ This sample implements a multi-player game, demonstrating game controller input
 handling.
 </p>
 
-<p><a href="http://github.com/googlesamples/androidtv-GameController">Get it on GitHub</a></p>
-
 
-<h3 id="ClippingBasic">ClippingBasic</h3>
+<h3 id="ClippingBasic"><a href="/samples/ClippingBasic/index.html">ClippingBasic</a></h3>
 <!--
 <div class="figure" style="width:220px">
   <img src="" srcset="@2x.png 2x" alt="" height="375" />
@@ -300,19 +242,17 @@ handling.
 This sample demonstrates clipping on a {@link android.view.View}.
 </p>
 
-<p><a href="http://github.com/googlesamples/android-ClippingBasic">Get it on GitHub</a></p>
-
 <div class="figure" style="width:220px">
   <img src="{@docRoot}samples/images/JobSchedulerSample.png"
       srcset="{@docRoot}samples/images/JobSchedulerSample@2x.png 2x"
       alt="" height="375" />
   <p class="img-caption">
-    <strong>Figure 3.</strong> The JobSchedulerSample sample app.
+    <strong>Figure 1.</strong> The JobSchedulerSample sample app.
   </p>
 </div>
 
 
-<h3 id="ElevationDrag">ElevationDrag</h3>
+<h3 id="ElevationDrag"><a href="/samples/ElevationDrag/index.html">ElevationDrag</a></h3>
 <!--
 <div class="figure" style="width:220px">
   <img src="" srcset="@2x.png 2x" alt="" height="375" />
@@ -326,10 +266,8 @@ This sample demonstrates clipping on a {@link android.view.View}.
 Elevation and z-translation are used to render the shadows. The views are
 clipped using different outlines.</p>
 
-<p><a href="http://github.com/googlesamples/android-ElevationDrag">Get it on GitHub</a></p>
-
 
-<h3 id="ElevationBasic">ElevationBasic</h3>
+<h3 id="ElevationBasic"><a href="/samples/ElevationBasic/index.html">ElevationBasic</a></h3>
 <!--
 <div class="figure" style="width:220px">
 <img src="" srcset="@2x.png 2x" alt="" height="375" />
@@ -348,10 +286,8 @@ This sample demonstrates two alternative ways to move a view in the z-axis:</p>
       <code>setTranslationZ()</code>.</li>
 </ul>
 
-<p><a href="http://github.com/googlesamples/android-ElevationBasic">Get it on GitHub</a></p>
 
-
-<h3 id="ActivitySceneTransitionBasic">ActivitySceneTransitionBasic</h3>
+<h3 id="ActivitySceneTransitionBasic"><a href="/samples/ActivitySceneTransitionBasic/index.html">ActivitySceneTransitionBasic</a></h3>
 <div class="figure" style="width:220px">
   <img src="{@docRoot}samples/images/ActivitySceneTransitionBasic.png"
       srcset="{@docRoot}samples/images/ActivitySceneTransitionBasic@2x.png 2x"
@@ -366,10 +302,8 @@ transitions when transitioning from one activity to another. Uses a combination
 of <code>moveImage</code> and <code>changeBounds</code> to nicely transition
 from a grid of images to an activity with a large image and detail text. </p>
 
-<p><a href="http://github.com/googlesamples/android-ActivitySceneTransition">Get it on GitHub</a></p>
-
 
-<h3 id="Camera2Video">Camera2Video</h3>
+<h3 id="Camera2Video"><a href="/samples/Camera2Video/index.html">Camera2Video</a></h3>
 <!--
 <div class="figure" style="width:220px">
 <img src="" srcset="@2x.png 2x" alt="" height="375" />
@@ -381,10 +315,8 @@ from a grid of images to an activity with a large image and detail text. </p>
 
 <p>This sample demonstrates how to record video using the Camera2 API.</p>
 
-<p><a href="http://github.com/googlesamples/android-Camera2Video">Get it on GitHub</a></p>
-
 
-<h3 id="Camera2Basic">Camera2Basic</h3>
+<h3 id="Camera2Basic"><a href="/samples/Camera2Basic/index.html">Camera2Basic</a></h3>
 
 <!--
 <div class="figure" style="width:220px">
@@ -398,16 +330,14 @@ from a grid of images to an activity with a large image and detail text. </p>
 <p>This sample demonstrates the basic use of the Camera2 API. The sample code
 demonstrates how you can display camera preview and take pictures.</p>
 
-<p><a href="http://github.com/googlesamples/android-Camera2Basic">Get it on GitHub</a></p>
 
-
-<h3 id="BasicManagedProfile">BasicManagedProfile</h3>
+<h3 id="BasicManagedProfile"><a href="/samples/BasicManagedProfile/index.html">BasicManagedProfile</a></h3>
 <div class="figure" style="width:220px">
   <img src="{@docRoot}samples/images/BasicManagedProfile.png"
      srcset="{@docRoot}samples/images/BasicManagedProfile@2x.png 2x"
      alt="" height="375" />
   <p class="img-caption">
-    <strong>Figure 1.</strong> The BasicManagedProfile sample app.
+    <strong>Figure 3.</strong> The BasicManagedProfile sample app.
   </p>
 </div>
 
@@ -422,5 +352,3 @@ demonstrates how you can display camera preview and take pictures.</p>
 <p class="note"><strong>Note:</strong> There can be only one managed profile on
   a device at a time.</p>
 
-<p><a href="http://github.com/googlesamples/android-BasicManagedProfile">Get it on GitHub</a></p>
-
index 06474dc..48dceb6 100644 (file)
@@ -3,28 +3,28 @@ page.template=sdk
 
 
 ndk.mac64_download=android-ndk-r10c-darwin-x86_64.bin
-ndk.mac64_bytes=436952863
-ndk.mac64_checksum=bc04ef44b920cf6cd2157b6f2c3531d6
+ndk.mac64_bytes=442691567
+ndk.mac64_checksum=cb101e1e62d56ea75b215f6bc6c27fae
 
 ndk.mac32_download=android-ndk-r10c-darwin-x86.bin
-ndk.mac32_bytes=435858709
-ndk.mac32_checksum=6b3e143f7e64d5cd337b727513e27913
+ndk.mac32_bytes=441545213
+ndk.mac32_checksum=0aeb3dc062dc457a4cd01e72eadb2379
 
 ndk.linux64_download=android-ndk-r10c-linux-x86_64.bin
-ndk.linux64_bytes=449013322
-ndk.linux64_checksum=792c61706cd9ec6713fa1b69b2f42996
+ndk.linux64_bytes=459151600
+ndk.linux64_checksum=263b83071e6bca15f67898548d8d236e
 
 ndk.linux32_download=android-ndk-r10c-linux-x86.bin
-ndk.linux32_bytes=438555265
-ndk.linux32_checksum=d1595d9ca5e15484e047f1ac326c4ceb
+ndk.linux32_bytes=449997190
+ndk.linux32_checksum=70ed6d8c34e7e620c145b791e8eeef89
 
 ndk.win64_download=android-ndk-r10c-windows-x86_64.exe
-ndk.win64_bytes=458925419
-ndk.win64_checksum=af8edf5d316e1bf1a5a72e04a9faec41
+ndk.win64_bytes=472613732
+ndk.win64_checksum=9a33f96da58a7e0b70e47d27b4a880b4
 
 ndk.win32_download=android-ndk-r10c-windows-x86.exe
-ndk.win32_bytes=433102815
-ndk.win32_checksum=805a04810719886674d3c7bff5eca53f
+ndk.win32_bytes=455427281
+ndk.win32_checksum=c0930abfae0c990c4d191cc4ebd46b68
 
 
 
@@ -388,6 +388,133 @@ $('#Downloads').after($('#download-table'));
  <p>
    <a href="#" onclick="return toggleContent(this)"> <img
      src="/assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+   >Android NDK, Revision 10d</a> <em>(December 2014)</em>
+ </p>
+ <div class="toggle-content-toggleme">
+    <dl>
+      <dt>Important changes:</dt>
+      <dd>
+      <ul>
+        <li>Made GCC 4.8 the default for all 32-bit ABIs.  Deprecated GCC 4.6, and
+            will remove it next release. To restore previous behavior, either add
+            <code>NDK_TOOLCHAIN_VERSION=4.6</code> to ndk-build, or
+            add <code>--toolchain=arm-linux-androideabi-4.6</code> when executing
+            <code>make-standalone-toolchain.sh</code> on the command line. GCC 4.9 remains the
+            default for 64-bit ABIs.</li>
+
+         <li>Stopped all x86[_64] toolchains from adding <code>-mstackrealign</code> by default. The
+             NDK toolchain assumes a 16-byte stack alignment. The tools and options used by default
+             enforce this rule. A user writing assembly code must make sure to preserve stack
+             alignment, and ensure that other compilers also comply with this rule.
+             (GCC bug <a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38496">38496</a>)</li>
+
+         <li>Added Address Sanitizer functionality to Clang 3.5 support to the ARM and x86 ABIs.
+             For more information on this change, see the
+             <a href="https://code.google.com/p/address-sanitizer/wiki/Android">Address
+             Sanitizer</a> project.</li>
+
+         <li>Introduced the requirement, starting from API level 21, to use <code>-fPIE -pie
+             </code> when building. In API levels 16 and higher, ndk-build uses <code>PIE</code>
+             when building. This change has a number of implications, which are discussed in
+             <a href="https://code.google.com/p/android-developer-preview/issues/detail?id=888">
+             Developer Preview Issue 888</a>.
+             These implications do not apply to shared libraries.</li>
+      </ul>
+      </dd>
+   <dl>
+
+
+     <dt>Important bug fixes:</dt>
+     <dd>
+     <ul>
+        <li>Made more fixes related to
+            <a href="https://gcc.gnu.org/ml/gcc-patches/2014-10/msg00906.html">
+            A53 Errata #835769</a> in the aarch64-linux-android-4.9 linker. As part of this, GCC
+            passes a new option, <code>--fix-cortex-a53-835769</code>, when
+            <code>-mfix-cortex-a53-835769</code> (enabled by default) is specified.
+            For more information, see this
+            <a href="https://sourceware.org/ml/binutils/2014-10/msg00198.html">binutils message</a>
+            and this
+            <a href="https://sourceware.org/ml/binutils/2014-11/msg00287.html">binutils message</a>.
+            </li>
+
+        <li>Documented a fix to a libc++ <code>sscanf/vsscanf</code> hang that occurred in API level
+            21. The fix itself had been implemented in r10c.
+            (Issue <a href="http://b.android.com/77988">77988</a>)</li>
+
+        <li>Fixed an AutoFDO (<code>-fauto-profile</code>) crash that occurred with GCC 4.9 when
+            <code>-Os</code> was specified. (Issue <a href="http://b.android.com/77571">77571</a>)</li>
+     </ul>
+     </dd>
+
+
+     <dt>Other bug fixes:</dt>
+     <dd>
+     <ul>
+        <li>Made the following header and library fixes:</li>
+           <ul>
+        <li>Added <code>posix_memalign</code> to API level 16. Also, added a prototype in
+            <code>stdlib.h</code> to API levels 16 to 19.
+            (Issue <a href="http://b.android.com/77861">77861</a>)</li>
+        <li>Fixed <code>stdatomic.h</code> so that it includes <code>&lt;atomic&gt;</code> only for
+            C++11.</li>
+        <li>Modified the following headers for standalone use: <code>sys/user.h</code>, and
+            <code>gl2ext.h</code>, <code>dlext.h</code>, <code>fts.h</code>, <code>sgidefs.h</code>
+            for API level 21.</li>
+        <li>Modified <code>sys/user.h</code> to rename <code>mxcsr_mask</code> as <code>mxcr_mask</code>,
+            and to change the data type for <code>u_ar0</code></li> from <code>unsigned long</code>
+            to </code>struct user_regs_struct*</code>.
+        <li>Changed <code>sysconf()</code> return value type from <code>int</code> to
+            <code>long</code>.</li>
+           </ul>
+
+        <li>Fixed ndk-build's handling of <code>thumb</code> for <code>LOCAL_ARM_MODE</code>: In
+            r10d, ndk-build adds <code>LOCAL_LDFLAGS+=-mthumb</code> by default, unless one of the
+            following conditions applies:</li>
+          <ul>
+            <li>You have set <code>LOCAL_ARM_MODE</code> equal to <code>arm</code>.</li>
+            <li>You are doing a debug build (with settings such as <code>APP_OPTIM=debug</code> and
+            <code>AndroidManifest.xml</code> containing <code>android:debuggable="true"</code>),
+            where ARM mode is the default in order to retain compatibility with earlier toolchains.
+            (Issue <a href="http://b.android.com/74040">74040</a>)</li>
+          </ul>
+
+        <li>Fixed <code>LOCAL_SRC_FILES</code> in ndk-build to use Windows absolute paths.
+            (Issue <a href="http://b.android.com/74333">74333</a>)</li>
+
+        <li>Removed bash-specific code from ndk-gdb. (Issue <a href="http://b.android.com/73338">73338</a>)</li>
+
+        <li>Removed bash-specific code from <code>make-standalone-toolchain.sh</code>.
+            (Issue <a href="http://b.android.com/74145">74145)</a></li>
+
+        <li>Revised documentation concerning a fix for <code>System.loadLibrary()</code> transitive
+            dependencies. (Issue <a href="http://b.android.com/41790">41790</a>)</li>
+
+        <li>Fixed a problem that was preventing 64-bit packages from extracting on Ubuntu 14.04 and
+            OS X 10.10 (Yosemite). (Issue <a href="http://b.android.com/78148">78148</a>)</li>
+
+        <li>Fixed an issue with <code>LOCAL_PCH</code> to improve Clang support. (Issue
+            <a href="http://b.android.com/77575">77575</a>)</li>
+
+        <li>Clarified "requires executable stack" warning from ld.gold. (Issue
+            <a href="http://b.android.com/79115">79115</a>)</li>
+     </ul>
+     </dd>
+
+   </dl>
+ </div>
+</div>
+
+
+
+
+
+
+
+<div class="toggle-content closed">
+ <p>
+   <a href="#" onclick="return toggleContent(this)"> <img
+     src="/assets/images/triangle-closed.png" class="toggle-content-img" alt=""
    >Android NDK, Revision 10c</a> <em>(October 2014)</em>
  </p>
  <div class="toggle-content-toggleme">
@@ -570,10 +697,6 @@ Symbol not found: _environ
  </div>
 </div>
 
-
-
-
-
 <div class="toggle-content closed">
  <p>
    <a href="#" onclick="return toggleContent(this)"> <img
index a52ae95..2679937 100644 (file)
@@ -61,13 +61,15 @@ in your manifest for deep linking. The URIs {@code “example://gizmos”} and
         &lt;action android:name="android.intent.action.VIEW" /&gt;
         &lt;category android:name="android.intent.category.DEFAULT" /&gt;
         &lt;category android:name="android.intent.category.BROWSABLE" /&gt;
-        &lt;!-- Accepts URIs that begin with "example://gizmos” --&gt;
-        &lt;data android:scheme="example"
-              android:host="gizmos" /&gt;
         &lt;!-- Accepts URIs that begin with "http://www.example.com/gizmos” --&gt;
         &lt;data android:scheme="http"
               android:host="www.example.com"
-              android:pathPrefix="gizmos" /&gt;
+              android:pathPrefix="/gizmos" /&gt;
+        &lt;!-- note that the leading "/" is required for pathPrefix--&gt;
+        &lt;!-- Accepts URIs that begin with "example://gizmos”
+        &lt;data android:scheme="example"
+              android:host="gizmos" /&gt;
+        --&gt;
     &lt;/intent-filter&gt;
 &lt;/activity&gt;
 </pre>
@@ -76,6 +78,11 @@ in your manifest for deep linking. The URIs {@code “example://gizmos”} and
 manifest, Android is able to route any {@link android.content.Intent}
 that has matching URIs to your app at runtime.</p>
 
+<p class="note">
+  <strong>Note:</strong> Intent filters may only contain a single {@code data} element
+  for a URI pattern. Create separate intent filters to capture additional URI patterns.
+</p>
+
 <p>To learn more about defining intent filters, see <a href="{@docRoot}training/basics/intents/filters.html">Allow Other Apps to Start Your Activity</a>.</p>
 
 <h2 id="handling-intents">Read Data from Incoming Intents</h2>
index 7e7241b..45afea8 100644 (file)
@@ -24,7 +24,7 @@ Filters</a></li>
 </ul>
 </div>
 </div>
-<a class="notice-developers-video wide" href="http://www.youtube.com/watch?v=Xh_W82JgOms">
+<a class="notice-developers-video wide" href="http://www.youtube.com/watch?v=aISUYHTkTOU">
 <div>
     <h3>Video</h3>
     <p>DevBytes: App Indexing</p>
index 018d9f7..683dd31 100644 (file)
@@ -6,6 +6,7 @@ page.title=Creating and Running a Wearable App
 <div id="tb">
 <h2>This lesson teaches you to</h2>
 <ol>
+  <li><a href="#UpdateSDK">Update Your SDK</a></li>
   <li><a href="#SetupEmulator">Set Up an Android Wear Emulator</a></li>
   <li><a href="#SetupDevice">Set Up an Android Wear Device</a></li>
   <li><a href="#CreateProject">Create a Project</a></li>
@@ -13,7 +14,7 @@ page.title=Creating and Running a Wearable App
 </ol>
 <h2>Dependencies and Prerequisites</h2>
   <ul>
-    <li>Android Studio 0.8 or later and Gradle 0.12 or later</li>
+    <li>Android Studio 0.8.12 or later and Gradle 0.12 or later</li>
   </ul>
 </div>
 </div>
@@ -34,6 +35,24 @@ sending the results to the wearable.
 both your wearable and handheld apps.
 </p>
 
+<h2 id="UpdateSDK">Update Your SDK</h2>
+
+<p>Before you begin building wearable apps, you must:</p>
+
+<ul>
+  <li><strong>Update your SDK tools to version 23.0.0 or higher</strong>
+    <br>
+    The updated SDK tools enable you to build and test wearable apps.
+  </li>
+  <li><strong>Update your SDK with Android 4.4W.2 (API 20) or higher</strong>
+    <br>
+    The updated platform version provides new APIs for wearable apps.
+  </li>
+</ul>
+
+<p>To update your SDK with these components, see
+<a href="{@docRoot}sdk/installing/adding-packages.html#GetTools"> Get the latest SDK tools</a>.</p>
+
 
 <h2 id="SetupEmulator">Set Up an Android Wear Emulator or Device</h2>
 <p>We recommend that you develop on real hardware so you can better
@@ -45,29 +64,24 @@ types of screen shapes, which is useful for testing.</p>
 <p>To set up an Android Wear virtual device:</p>
 <ol>
   <li>Click <b>Tools > Android > AVD Manager</b>.</li>
-  <li>Click <b>Create...</b>.</li>
-  <li>Fill in the following details for the AVD you want to specify and leave the rest
-  of the fields with their default values:
-    <ul>
-      <li><b>AVD Name</b> - A name for your AVD</li>
-      <li><b>Device</b> - Android Wear Round or Square device types</li>
-      <li><b>Target</b> - Android 4.4W - API Level 20</li>
-      <li><b>CPU/ABI</b> - Android Wear ARM (armeabi-v7a)</li>
-      <li><b>Keyboard</b> - Select <b>Hardware keyboard present</b></li>
-      <li><b>Skin</b> - AndroidWearRound or AndroidWearSquare depending on the selected device type</li>
-      <li><b>Snapshot</b> - Not selected</li>
-      <li><b>Use Host GPU</b> - Selected, to support custom activities for wearable notifications</li>
-    </ul>
-  </li>
-  <li>Click <b>OK</b>.</li>
+  <li>Click <b>Create Virtual Device...</b>.</li>
+  <ol>
+    <li>Click <b>Wear</b> in the Category list:</li>
+    <li>Select Android Wear Square or Android Wear Round.</li>
+    <li>Click <b>Next</b>.</li>
+    <li>Select a release name (for example, KitKat Wear).</li>
+    <li>Click <b>Next</b>.</li>
+    <li>(Optional) Change any preferences for your virtual device.</li>
+    <li>Click <b>Finish</b>.</li>
+  </ol>
 <li>Start the emulator:
 <ol>
   <li>Select the virtual device you just created.</li>
-  <li>Click <b>Start...</b>, then click <b>Launch</b>.</li>
+  <li>Click the <b>Play</b> button.</li>
   <li>Wait until the emulator initializes and shows the Android Wear home screen.</li>
 </ol>
 </li>
-<li>Pair Your handheld with the emulator:
+<li>Pair your handheld with the emulator:
 <ol>
   <li>On your handheld, install the Android Wear app from Google Play.</li>
   <li>Connect the handheld to your machine through USB.</li>
index 8d42ae3..85b2c33 100644 (file)
@@ -30,9 +30,12 @@ the data layer:</p>
   <dd>The <a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html"><code>MessageApi</code></a> class
   can send messages and is good for remote procedure calls (RPC), such as controlling a handheld's
   media player from the wearable or starting an intent on the wearable from the handheld.
-  The system always delivers the message when the handheld and wearable are connected and delivers
-  an error when the devices are disconnected. Messages are great for one-way requests or for a
-  request/response communication model.</dd>
+  Messages are also great for one-way requests or for a request/response communication model.
+  If the handheld and wearable are connected, the system queues the message for delivery and
+  returns a successful result code. If the devices are not connected, an error is returned. A
+  successful result code does not indicate that the message was delivered successfully as the
+  devices may disconnect after receiving the result code.
+</p></dd>
 
   <dt><b>Asset</b></dt>
   <dd><a href="{@docRoot}reference/com/google/android/gms/wearable/Asset.html"><code>Asset</code></a> objects are for
index 36330a6..07a352f 100644 (file)
@@ -116,15 +116,14 @@ mDelayedView.setTotalTimeMs(2000);
 mDelayedView.start();
 </pre>
 
-
-<h2 id="show-confirmation">Show Confirmation Animations</h2>
-
-<div style="float:right;margin-left:25px;width:200px">
+<div style="float:right;margin-left:25px;width:210px;margin-top:15px">
 <img src="{@docRoot}wear/images/08_uilib.png" width="200" height="200" alt=""/>
-<p class="img-caption" style="text-align:center"><strong>Figure 2:</strong>
+<p class="img-caption" style="text-align:center;margin-left:-5px"><strong>Figure 2:</strong>
 A confirmation animation.</p>
 </div>
 
+<h2 id="show-confirmation">Show Confirmation Animations</h2>
+
 <p>To show a confirmation animation when users complete an action in your app, create an intent
 that starts <code>ConfirmationActivity</code> from one of your activities. You can specify
 one of the these animations with the <code>EXTRA_ANIMATION_TYPE</code> intent extra:</p>
index 84e1e45..6b205a5 100644 (file)
@@ -67,7 +67,7 @@ For example:</p>
         android:id="@+id/dismiss_overlay"
         android:layout_height="match_parent"
         android:layout_width="match_parent"/>
-&lt;FrameLayout>
+&lt;/FrameLayout>
 </pre>
 
 <p>In your activity, obtain the <code>DismissOverlayView</code> element and set some introductory
@@ -100,8 +100,8 @@ public class WearActivity extends Activity {
 
     // Capture long presses
     &#64;Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        return mDetector.onTouchEvent(ev) || super.onTouchEvent(ev);
+    public boolean dispatchTouchEvent(MotionEvent e) {
+        return mDetector.onTouchEvent(e) || super.dispatchTouchEvent(e);
     }
 }
 </pre>
index 14b9403..130f1c4 100644 (file)
@@ -166,7 +166,7 @@ protected void onCreate(Bundle savedInstanceState) {
 
 <h2 id="same-layout">Use a Shape-Aware Layout</h2>
 
-<div style="float:right;margin-left:25px;width:250px">
+<div style="float:right;margin-left:25px;width:260px">
 <img src="{@docRoot}wear/images/02_uilib.png" width="250" height="250" alt=""/>
 <p class="img-caption"><strong>Figure 2.</strong> Window insets on a round screen.</p>
 </div>
index e8aaed4..1d6e8ed 100644 (file)
@@ -36,9 +36,9 @@ the <code>android-sdk/samples/android-20/wearable/Notifications</code> directory
 <li>Add a <code>WearableListView</code> element to your activity's layout definition.</li>
 <li>Create a custom layout implementation for your list items.</li>
 <li>Use this implementation to create a layout definition file for your list items.</li>
-<div style="float:right;margin-left:25px;width:215px;margin-top:-20px">
+<div style="float:right;margin-left:25px;width:220px;margin-top:-25px">
 <img src="{@docRoot}wear/images/06_uilib.png" width="200" height="200" alt=""/>
-<p class="img-caption" style="text-align:center"><strong>Figure 3:</strong>
+<p class="img-caption" style="text-align:center;margin-left:-10px"><strong>Figure 3:</strong>
 A list view on Android Wear.</p>
 </div>
 <li>Create an adapter to populate the list.</li>
index 3090ffd..3f79c2d 100644 (file)
@@ -48,13 +48,8 @@ public final class Bitmap implements Parcelable {
 
     /**
      * Backing buffer for the Bitmap.
-     * Made public for quick access from drawing methods -- do NOT modify
-     * from outside this class
-     *
-     * @hide
      */
-    @SuppressWarnings("UnusedDeclaration") // native code only
-    public byte[] mBuffer;
+    private byte[] mBuffer;
 
     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources
     private final BitmapFinalizer mFinalizer;
index 5fc40f2..4af5946 100644 (file)
@@ -651,6 +651,7 @@ public class AnimatedStateListDrawable extends StateListDrawable {
         }
     }
 
+    @Override
     protected void setConstantState(@NonNull DrawableContainerState state) {
         super.setConstantState(state);
 
index 28ada82..9eb0251 100644 (file)
@@ -23,6 +23,7 @@ import java.io.IOException;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.annotation.NonNull;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.Resources.Theme;
@@ -81,7 +82,7 @@ import android.util.AttributeSet;
  * @attr ref android.R.styleable#AnimationDrawableItem_drawable
  */
 public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable {
-    private final AnimationState mAnimationState;
+    private AnimationState mAnimationState;
 
     /** The current frame, may be -1 when not animating. */
     private int mCurFrame = -1;
@@ -408,9 +409,17 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
         }
     }
 
+    @Override
+    protected void setConstantState(@NonNull DrawableContainerState state) {
+        super.setConstantState(state);
+
+        if (state instanceof AnimationState) {
+            mAnimationState = (AnimationState) state;
+        }
+    }
+
     private AnimationDrawable(AnimationState state, Resources res) {
-        AnimationState as = new AnimationState(state, this, res);
-        mAnimationState = as;
+        final AnimationState as = new AnimationState(state, this, res);
         setConstantState(as);
         if (state != null) {
             setFrame(0, true, false);
index 6d43a0c..2748030 100644 (file)
@@ -54,19 +54,20 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
     private DrawableContainerState mDrawableContainerState;
     private Rect mHotspotBounds;
     private Drawable mCurrDrawable;
+    private Drawable mLastDrawable;
     private int mAlpha = 0xFF;
 
     /** Whether setAlpha() has been called at least once. */
     private boolean mHasAlpha;
 
     private int mCurIndex = -1;
+    private int mLastIndex = -1;
     private boolean mMutated;
 
     // Animations.
     private Runnable mAnimationRunnable;
     private long mEnterAnimationEnd;
     private long mExitAnimationEnd;
-    private Drawable mLastDrawable;
 
     // overrides from Drawable
 
@@ -255,6 +256,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
         if (mLastDrawable != null) {
             mLastDrawable.jumpToCurrentState();
             mLastDrawable = null;
+            mLastIndex = -1;
             changed = true;
         }
         if (mCurrDrawable != null) {
@@ -426,9 +428,11 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
             }
             if (mCurrDrawable != null) {
                 mLastDrawable = mCurrDrawable;
+                mLastIndex = mCurIndex;
                 mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration;
             } else {
                 mLastDrawable = null;
+                mLastIndex = -1;
                 mExitAnimationEnd = 0;
             }
         } else if (mCurrDrawable != null) {
@@ -522,6 +526,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
                 if (mExitAnimationEnd <= now) {
                     mLastDrawable.setVisible(false, false);
                     mLastDrawable = null;
+                    mLastIndex = -1;
                     mExitAnimationEnd = 0;
                 } else {
                     int animAlpha = (int)((mExitAnimationEnd-now)*255)
@@ -567,8 +572,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
     @Override
     public Drawable mutate() {
         if (!mMutated && super.mutate() == this) {
-            mDrawableContainerState = cloneConstantState();
-            mDrawableContainerState.mutate();
+            final DrawableContainerState clone = cloneConstantState();
+            clone.mutate();
+            setConstantState(clone);
             mMutated = true;
         }
         return this;
@@ -1102,5 +1108,13 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
 
     protected void setConstantState(DrawableContainerState state) {
         mDrawableContainerState = state;
+
+        // The locally cached drawables may have changed.
+        if (mCurIndex >= 0) {
+            mCurrDrawable = state.getChild(mCurIndex);
+        }
+        if (mLastIndex >= 0) {
+            mLastDrawable = state.getChild(mLastIndex);
+        }
     }
 }
index b88d9e6..acfd427 100644 (file)
@@ -30,6 +30,7 @@ import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.Insets;
 import android.graphics.Outline;
+import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -317,7 +318,13 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
 
     @Override
     public int getOpacity() {
-        return mState.mDrawable.getOpacity();
+        final InsetState state = mState;
+        final int opacity = state.mDrawable.getOpacity();
+        if (opacity == PixelFormat.OPAQUE && (state.mInsetLeft > 0 || state.mInsetTop > 0
+                || state.mInsetRight > 0 || state.mInsetBottom > 0)) {
+            return PixelFormat.TRANSLUCENT;
+        }
+        return opacity;
     }
 
     @Override
index dc41216..b01c643 100644 (file)
@@ -21,6 +21,7 @@ import java.io.IOException;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.annotation.NonNull;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.Resources.Theme;
@@ -57,7 +58,7 @@ import android.util.AttributeSet;
  * @attr ref android.R.styleable#LevelListDrawableItem_drawable
  */
 public class LevelListDrawable extends DrawableContainer {
-    private final LevelListState mLevelListState;
+    private LevelListState mLevelListState;
     private boolean mMutated;
 
     public LevelListDrawable() {
@@ -227,9 +228,17 @@ public class LevelListDrawable extends DrawableContainer {
         }
     }
 
+    @Override
+    protected void setConstantState(@NonNull DrawableContainerState state) {
+        super.setConstantState(state);
+
+        if (state instanceof LevelListState) {
+            mLevelListState = (LevelListState) state;
+        }
+    }
+
     private LevelListDrawable(LevelListState state, Resources res) {
-        LevelListState as = new LevelListState(state, this, res);
-        mLevelListState = as;
+        final LevelListState as = new LevelListState(state, this, res);
         setConstantState(as);
         onLevelChange(getLevel());
     }
index 6731366..a3a220c 100644 (file)
@@ -22,11 +22,8 @@ import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
-import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.Paint.Style;
 import android.graphics.Rect;
-import android.graphics.Xfermode;
 import android.util.MathUtils;
 import android.view.HardwareCanvas;
 import android.view.RenderNodeAnimator;
@@ -51,19 +48,12 @@ class Ripple {
     // Hardware animators.
     private final ArrayList<RenderNodeAnimator> mRunningAnimations =
             new ArrayList<RenderNodeAnimator>();
-    private final ArrayList<RenderNodeAnimator> mPendingAnimations =
-            new ArrayList<RenderNodeAnimator>();
 
     private final RippleDrawable mOwner;
 
     /** Bounds used for computing max radius. */
     private final Rect mBounds;
 
-    /** ARGB color for drawing this ripple. */
-    private int mColor;
-
-    private Xfermode mXfermode;
-
     /** Maximum ripple radius. */
     private float mOuterRadius;
 
@@ -112,6 +102,10 @@ class Ripple {
     /** Whether we were canceled externally and should avoid self-removal. */
     private boolean mCanceled;
 
+    private boolean mHasPendingHardwareExit;
+    private int mPendingRadiusDuration;
+    private int mPendingOpacityDuration;
+
     /**
      * Creates a new ripple.
      */
@@ -217,10 +211,6 @@ class Ripple {
      * Draws the ripple centered at (0,0) using the specified paint.
      */
     public boolean draw(Canvas c, Paint p) {
-        // Store the color and xfermode, we might need them later.
-        mColor = p.getColor();
-        mXfermode = p.getXfermode();
-
         final boolean canUseHardware = c.isHardwareAccelerated();
         if (mCanUseHardware != canUseHardware && mCanUseHardware) {
             // We've switched from hardware to non-hardware mode. Panic.
@@ -229,8 +219,8 @@ class Ripple {
         mCanUseHardware = canUseHardware;
 
         final boolean hasContent;
-        if (canUseHardware && mHardwareAnimating) {
-            hasContent = drawHardware((HardwareCanvas) c);
+        if (canUseHardware && (mHardwareAnimating || mHasPendingHardwareExit)) {
+            hasContent = drawHardware((HardwareCanvas) c, p);
         } else {
             hasContent = drawSoftware(c, p);
         }
@@ -238,24 +228,10 @@ class Ripple {
         return hasContent;
     }
 
-    private boolean drawHardware(HardwareCanvas c) {
-        // If we have any pending hardware animations, cancel any running
-        // animations and start those now.
-        final ArrayList<RenderNodeAnimator> pendingAnimations = mPendingAnimations;
-        final int N = pendingAnimations.size();
-        if (N > 0) {
+    private boolean drawHardware(HardwareCanvas c, Paint p) {
+        if (mHasPendingHardwareExit) {
             cancelHardwareAnimations(false);
-
-            // We canceled old animations, but we're about to run new ones.
-            mHardwareAnimating = true;
-
-            for (int i = 0; i < N; i++) {
-                pendingAnimations.get(i).setTarget(c);
-                pendingAnimations.get(i).start();
-            }
-
-            mRunningAnimations.addAll(pendingAnimations);
-            pendingAnimations.clear();
+            startPendingHardwareExit(c, p);
         }
 
         c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
@@ -347,8 +323,6 @@ class Ripple {
      * Starts the exit animation.
      */
     public void exit() {
-        cancel();
-
         final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
         final float remaining;
         if (mAnimRadius != null && mAnimRadius.isRunning()) {
@@ -357,19 +331,33 @@ class Ripple {
             remaining = mOuterRadius;
         }
 
+        cancel();
+
         final int radiusDuration = (int) (1000 * Math.sqrt(remaining / (WAVE_TOUCH_UP_ACCELERATION
                 + WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
         final int opacityDuration = (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
 
         if (mCanUseHardware) {
-            exitHardware(radiusDuration, opacityDuration);
+            createPendingHardwareExit(radiusDuration, opacityDuration);
         } else {
             exitSoftware(radiusDuration, opacityDuration);
         }
     }
 
-    private void exitHardware(int radiusDuration, int opacityDuration) {
-        mPendingAnimations.clear();
+    private void createPendingHardwareExit(int radiusDuration, int opacityDuration) {
+        mHasPendingHardwareExit = true;
+        mPendingRadiusDuration = radiusDuration;
+        mPendingOpacityDuration = opacityDuration;
+
+        // The animation will start on the next draw().
+        invalidateSelf();
+    }
+
+    private void startPendingHardwareExit(HardwareCanvas c, Paint p) {
+        mHasPendingHardwareExit = false;
+
+        final int radiusDuration = mPendingRadiusDuration;
+        final int opacityDuration = mPendingOpacityDuration;
 
         final float startX = MathUtils.lerp(
                 mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
@@ -377,12 +365,8 @@ class Ripple {
                 mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
 
         final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
-        final Paint paint = getTempPaint();
-        paint.setAntiAlias(true);
-        paint.setColor(mColor);
-        paint.setXfermode(mXfermode);
-        paint.setAlpha((int) (Color.alpha(mColor) * mOpacity + 0.5f));
-        paint.setStyle(Style.FILL);
+        final Paint paint = getTempPaint(p);
+        paint.setAlpha((int) (paint.getAlpha() * mOpacity + 0.5f));
         mPropPaint = CanvasProperty.createPaint(paint);
         mPropRadius = CanvasProperty.createFloat(startRadius);
         mPropX = CanvasProperty.createFloat(startX);
@@ -391,25 +375,33 @@ class Ripple {
         final RenderNodeAnimator radiusAnim = new RenderNodeAnimator(mPropRadius, mOuterRadius);
         radiusAnim.setDuration(radiusDuration);
         radiusAnim.setInterpolator(DECEL_INTERPOLATOR);
+        radiusAnim.setTarget(c);
+        radiusAnim.start();
 
         final RenderNodeAnimator xAnim = new RenderNodeAnimator(mPropX, mOuterX);
         xAnim.setDuration(radiusDuration);
         xAnim.setInterpolator(DECEL_INTERPOLATOR);
+        xAnim.setTarget(c);
+        xAnim.start();
 
         final RenderNodeAnimator yAnim = new RenderNodeAnimator(mPropY, mOuterY);
         yAnim.setDuration(radiusDuration);
         yAnim.setInterpolator(DECEL_INTERPOLATOR);
+        yAnim.setTarget(c);
+        yAnim.start();
 
         final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPropPaint,
                 RenderNodeAnimator.PAINT_ALPHA, 0);
         opacityAnim.setDuration(opacityDuration);
         opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
         opacityAnim.addListener(mAnimationListener);
+        opacityAnim.setTarget(c);
+        opacityAnim.start();
 
-        mPendingAnimations.add(radiusAnim);
-        mPendingAnimations.add(opacityAnim);
-        mPendingAnimations.add(xAnim);
-        mPendingAnimations.add(yAnim);
+        mRunningAnimations.add(radiusAnim);
+        mRunningAnimations.add(opacityAnim);
+        mRunningAnimations.add(xAnim);
+        mRunningAnimations.add(yAnim);
 
         mHardwareAnimating = true;
 
@@ -418,8 +410,6 @@ class Ripple {
         mTweenX = 1;
         mTweenY = 1;
         mTweenRadius = 1;
-
-        invalidateSelf();
     }
 
     /**
@@ -455,10 +445,11 @@ class Ripple {
         }
     }
 
-    private Paint getTempPaint() {
+    private Paint getTempPaint(Paint original) {
         if (mTempPaint == null) {
             mTempPaint = new Paint();
         }
+        mTempPaint.set(original);
         return mTempPaint;
     }
 
@@ -539,10 +530,7 @@ class Ripple {
         }
         runningAnimations.clear();
 
-        if (cancelPending && !mPendingAnimations.isEmpty()) {
-            mPendingAnimations.clear();
-        }
-
+        mHasPendingHardwareExit = false;
         mHardwareAnimating = false;
     }
 
index 69847b5..665d736 100644 (file)
@@ -24,9 +24,7 @@ import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.Paint.Style;
 import android.graphics.Rect;
-import android.graphics.Xfermode;
 import android.util.MathUtils;
 import android.view.HardwareCanvas;
 import android.view.RenderNodeAnimator;
@@ -53,8 +51,6 @@ class RippleBackground {
     // Hardware animators.
     private final ArrayList<RenderNodeAnimator> mRunningAnimations =
             new ArrayList<RenderNodeAnimator>();
-    private final ArrayList<RenderNodeAnimator> mPendingAnimations =
-            new ArrayList<RenderNodeAnimator>();
 
     private final RippleDrawable mOwner;
 
@@ -64,8 +60,6 @@ class RippleBackground {
     /** ARGB color for drawing this ripple. */
     private int mColor;
 
-    private Xfermode mXfermode;
-
     /** Maximum ripple radius. */
     private float mOuterRadius;
 
@@ -98,6 +92,11 @@ class RippleBackground {
     /** Whether we have an explicit maximum radius. */
     private boolean mHasMaxRadius;
 
+    private boolean mHasPendingHardwareExit;
+    private int mPendingOpacityDuration;
+    private int mPendingInflectionDuration;
+    private int mPendingInflectionOpacity;
+
     /**
      * Creates a new ripple.
      */
@@ -144,9 +143,7 @@ class RippleBackground {
      * Draws the ripple centered at (0,0) using the specified paint.
      */
     public boolean draw(Canvas c, Paint p) {
-        // Store the color and xfermode, we might need them later.
         mColor = p.getColor();
-        mXfermode = p.getXfermode();
 
         final boolean canUseHardware = c.isHardwareAccelerated();
         if (mCanUseHardware != canUseHardware && mCanUseHardware) {
@@ -156,8 +153,8 @@ class RippleBackground {
         mCanUseHardware = canUseHardware;
 
         final boolean hasContent;
-        if (canUseHardware && mHardwareAnimating) {
-            hasContent = drawHardware((HardwareCanvas) c);
+        if (canUseHardware && (mHardwareAnimating || mHasPendingHardwareExit)) {
+            hasContent = drawHardware((HardwareCanvas) c, p);
         } else {
             hasContent = drawSoftware(c, p);
         }
@@ -169,24 +166,10 @@ class RippleBackground {
         return (mCanUseHardware && mHardwareAnimating) || (mOuterOpacity > 0 && mOuterRadius > 0);
     }
 
-    private boolean drawHardware(HardwareCanvas c) {
-        // If we have any pending hardware animations, cancel any running
-        // animations and start those now.
-        final ArrayList<RenderNodeAnimator> pendingAnimations = mPendingAnimations;
-        final int N = pendingAnimations.size();
-        if (N > 0) {
+    private boolean drawHardware(HardwareCanvas c, Paint p) {
+        if (mHasPendingHardwareExit) {
             cancelHardwareAnimations(false);
-
-            // We canceled old animations, but we're about to run new ones.
-            mHardwareAnimating = true;
-
-            for (int i = 0; i < N; i++) {
-                pendingAnimations.get(i).setTarget(c);
-                pendingAnimations.get(i).start();
-            }
-
-            mRunningAnimations.addAll(pendingAnimations);
-            pendingAnimations.clear();
+            startPendingHardwareExit(c, p);
         }
 
         c.drawCircle(mPropOuterX, mPropOuterY, mPropOuterRadius, mPropOuterPaint);
@@ -263,21 +246,32 @@ class RippleBackground {
                 + inflectionDuration * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
 
         if (mCanUseHardware) {
-            exitHardware(opacityDuration, inflectionDuration, inflectionOpacity);
+            createPendingHardwareExit(opacityDuration, inflectionDuration, inflectionOpacity);
         } else {
             exitSoftware(opacityDuration, inflectionDuration, inflectionOpacity);
         }
     }
 
-    private void exitHardware(int opacityDuration, int inflectionDuration, int inflectionOpacity) {
-        mPendingAnimations.clear();
+    private void createPendingHardwareExit(
+            int opacityDuration, int inflectionDuration, int inflectionOpacity) {
+        mHasPendingHardwareExit = true;
+        mPendingOpacityDuration = opacityDuration;
+        mPendingInflectionDuration = inflectionDuration;
+        mPendingInflectionOpacity = inflectionOpacity;
+
+        // The animation will start on the next draw().
+        invalidateSelf();
+    }
+
+    private void startPendingHardwareExit(HardwareCanvas c, Paint p) {
+        mHasPendingHardwareExit = false;
+
+        final int opacityDuration = mPendingOpacityDuration;
+        final int inflectionDuration = mPendingInflectionDuration;
+        final int inflectionOpacity = mPendingInflectionOpacity;
 
-        final Paint outerPaint = getTempPaint();
-        outerPaint.setAntiAlias(true);
-        outerPaint.setXfermode(mXfermode);
-        outerPaint.setColor(mColor);
-        outerPaint.setAlpha((int) (Color.alpha(mColor) * mOuterOpacity + 0.5f));
-        outerPaint.setStyle(Style.FILL);
+        final Paint outerPaint = getTempPaint(p);
+        outerPaint.setAlpha((int) (outerPaint.getAlpha() * mOuterOpacity + 0.5f));
         mPropOuterPaint = CanvasProperty.createPaint(outerPaint);
         mPropOuterRadius = CanvasProperty.createFloat(mOuterRadius);
         mPropOuterX = CanvasProperty.createFloat(mOuterX);
@@ -301,8 +295,10 @@ class RippleBackground {
                 outerFadeOutAnim.setStartDelay(inflectionDuration);
                 outerFadeOutAnim.setStartValue(inflectionOpacity);
                 outerFadeOutAnim.addListener(mAnimationListener);
+                outerFadeOutAnim.setTarget(c);
+                outerFadeOutAnim.start();
 
-                mPendingAnimations.add(outerFadeOutAnim);
+                mRunningAnimations.add(outerFadeOutAnim);
             } else {
                 outerOpacityAnim.addListener(mAnimationListener);
             }
@@ -314,14 +310,15 @@ class RippleBackground {
             outerOpacityAnim.addListener(mAnimationListener);
         }
 
-        mPendingAnimations.add(outerOpacityAnim);
+        outerOpacityAnim.setTarget(c);
+        outerOpacityAnim.start();
+
+        mRunningAnimations.add(outerOpacityAnim);
 
         mHardwareAnimating = true;
 
         // Set up the software values to match the hardware end values.
         mOuterOpacity = 0;
-
-        invalidateSelf();
     }
 
     /**
@@ -340,10 +337,11 @@ class RippleBackground {
         }
     }
 
-    private Paint getTempPaint() {
+    private Paint getTempPaint(Paint original) {
         if (mTempPaint == null) {
             mTempPaint = new Paint();
         }
+        mTempPaint.set(original);
         return mTempPaint;
     }
 
@@ -422,10 +420,7 @@ class RippleBackground {
         }
         runningAnimations.clear();
 
-        if (cancelPending && !mPendingAnimations.isEmpty()) {
-            mPendingAnimations.clear();
-        }
-
+        mHasPendingHardwareExit = false;
         mHardwareAnimating = false;
     }
 
index 8cbc239..13e3d54 100644 (file)
@@ -27,15 +27,19 @@ import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.Matrix;
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
+import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 
@@ -56,7 +60,7 @@ import java.util.Arrays;
  * &ltripple android:color="#ffff0000">
  *   &ltitem android:id="@android:id/mask"
  *         android:drawable="@android:color/white" />
- * &ltripple /></code>
+ * &lt/ripple></code>
  * </pre>
  * <p>
  * If a mask layer is set, the ripple effect will be masked against that layer
@@ -65,15 +69,15 @@ import java.util.Arrays;
  * If no mask layer is set, the ripple effect is masked against the composite
  * of the child layers.
  * <pre>
- * <code>&lt!-- A blue ripple drawn atop a black rectangle. --/>
+ * <code>&lt!-- A green ripple drawn atop a black rectangle. --/>
  * &ltripple android:color="#ff00ff00">
  *   &ltitem android:drawable="@android:color/black" />
- * &ltripple />
+ * &lt/ripple>
  *
- * &lt!-- A red ripple drawn atop a drawable resource. --/>
- * &ltripple android:color="#ff00ff00">
+ * &lt!-- A blue ripple drawn atop a drawable resource. --/>
+ * &ltripple android:color="#ff0000ff">
  *   &ltitem android:drawable="@drawable/my_drawable" />
- * &ltripple /></code>
+ * &lt/ripple></code>
  * </pre>
  * <p>
  * If no child layers or mask is specified and the ripple is set as a View
@@ -81,16 +85,17 @@ import java.util.Arrays;
  * background within the View's hierarchy. In this case, the drawing region
  * may extend outside of the Drawable bounds.
  * <pre>
- * <code>&lt!-- An unbounded green ripple. --/>
- * &ltripple android:color="#ff0000ff" /></code>
+ * <code>&lt!-- An unbounded red ripple. --/>
+ * &ltripple android:color="#ffff0000" /></code>
  * </pre>
  *
  * @attr ref android.R.styleable#RippleDrawable_color
  */
 public class RippleDrawable extends LayerDrawable {
-    private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
-    private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
-    private static final PorterDuffXfermode SRC_OVER = new PorterDuffXfermode(Mode.SRC_OVER);
+    private static final int MASK_UNKNOWN = -1;
+    private static final int MASK_NONE = 0;
+    private static final int MASK_CONTENT = 1;
+    private static final int MASK_EXPLICIT = 2;
 
     /**
      * Constant for automatically determining the maximum ripple radius.
@@ -123,6 +128,13 @@ public class RippleDrawable extends LayerDrawable {
     /** The current background. May be actively animating or pending entry. */
     private RippleBackground mBackground;
 
+    private Bitmap mMaskBuffer;
+    private BitmapShader mMaskShader;
+    private Canvas mMaskCanvas;
+    private Matrix mMaskMatrix;
+    private PorterDuffColorFilter mMaskColorFilter;
+    private boolean mHasValidMask;
+
     /** Whether we expect to draw a background when visible. */
     private boolean mBackgroundActive;
 
@@ -147,9 +159,6 @@ public class RippleDrawable extends LayerDrawable {
     /** Paint used to control appearance of ripples. */
     private Paint mRipplePaint;
 
-    /** Paint used to control reveal layer masking. */
-    private Paint mMaskingPaint;
-
     /** Target density of the display into which ripples are drawn. */
     private float mDensity = 1.0f;
 
@@ -615,37 +624,116 @@ public class RippleDrawable extends LayerDrawable {
      */
     @Override
     public void draw(@NonNull Canvas canvas) {
-        final boolean hasMask = mMask != null;
-        final boolean hasRipples = mRipple != null || mExitingRipplesCount > 0
-                || (mBackground != null && mBackground.shouldDraw());
-
         // Clip to the dirty bounds, which will be the drawable bounds if we
         // have a mask or content and the ripple bounds if we're projecting.
         final Rect bounds = getDirtyBounds();
         final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
         canvas.clipRect(bounds);
 
-        // If we have content, draw it first. If we have ripples and no mask,
-        // we'll draw it into a SRC_OVER layer so that we can mask ripples
-        // against it using SRC_IN.
-        final boolean hasContentLayer = drawContent(canvas, bounds, hasRipples, hasMask);
-
-        // Next, try to draw the ripples. If we have a non-opaque mask, we'll
-        // draw the ripples into a SRC_OVER layer, draw the mask into a DST_IN
-        // layer, and blend.
-        if (hasRipples) {
-            final boolean hasNonOpaqueMask = hasMask && mMask.getOpacity() != PixelFormat.OPAQUE;
-            final boolean hasRippleLayer = drawBackgroundAndRipples(canvas, bounds,
-                    hasNonOpaqueMask, hasContentLayer);
-
-            // If drawing ripples created a layer, we have a non-opaque mask
-            // that needs to be blended on top of the ripples with DST_IN.
-            if (hasRippleLayer) {
-                drawMaskingLayer(canvas, bounds, DST_IN);
+        drawContent(canvas);
+        drawBackgroundAndRipples(canvas);
+
+        canvas.restoreToCount(saveCount);
+    }
+
+    @Override
+    public void invalidateSelf() {
+        super.invalidateSelf();
+
+        // Force the mask to update on the next draw().
+        mHasValidMask = false;
+    }
+
+    /**
+     * @return whether we need to use a mask
+     */
+    private void updateMaskShaderIfNeeded() {
+        if (mHasValidMask) {
+            return;
+        }
+
+        final int maskType = getMaskType();
+        if (maskType == MASK_UNKNOWN) {
+            return;
+        }
+
+        mHasValidMask = true;
+
+        if (maskType == MASK_NONE) {
+            if (mMaskBuffer != null) {
+                mMaskBuffer.recycle();
+                mMaskBuffer = null;
+                mMaskShader = null;
+                mMaskCanvas = null;
             }
+            mMaskMatrix = null;
+            mMaskColorFilter = null;
+            return;
         }
 
-        canvas.restoreToCount(saveCount);
+        // Ensure we have a correctly-sized buffer.
+        final Rect bounds = getBounds();
+        if (mMaskBuffer == null
+                || mMaskBuffer.getWidth() != bounds.width()
+                || mMaskBuffer.getHeight() != bounds.height()) {
+            if (mMaskBuffer != null) {
+                mMaskBuffer.recycle();
+            }
+
+            mMaskBuffer = Bitmap.createBitmap(
+                    bounds.width(), bounds.height(), Bitmap.Config.ALPHA_8);
+            mMaskShader = new BitmapShader(mMaskBuffer,
+                    Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+            mMaskCanvas = new Canvas(mMaskBuffer);
+        } else {
+            mMaskBuffer.eraseColor(Color.TRANSPARENT);
+        }
+
+        if (mMaskMatrix == null) {
+            mMaskMatrix = new Matrix();
+        } else {
+            mMaskMatrix.reset();
+        }
+
+        if (mMaskColorFilter == null) {
+            mMaskColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_IN);
+        }
+
+        // Draw the appropriate mask.
+        if (maskType == MASK_EXPLICIT) {
+            drawMask(mMaskCanvas);
+        } else if (maskType == MASK_CONTENT) {
+            drawContent(mMaskCanvas);
+        }
+    }
+
+    private int getMaskType() {
+        if (mRipple == null && mExitingRipplesCount <= 0
+                && (mBackground == null || !mBackground.shouldDraw())) {
+            // We might need a mask later.
+            return MASK_UNKNOWN;
+        }
+
+        if (mMask != null) {
+            if (mMask.getOpacity() == PixelFormat.OPAQUE) {
+                // Clipping handles opaque explicit masks.
+                return MASK_NONE;
+            } else {
+                return MASK_EXPLICIT;
+            }
+        }
+
+        // Check for non-opaque, non-mask content.
+        final ChildDrawable[] array = mLayerState.mChildren;
+        final int count = mLayerState.mNum;
+        for (int i = 0; i < count; i++) {
+            if (array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
+                return MASK_CONTENT;
+            }
+        }
+
+        // Clipping handles opaque content.
+        return MASK_NONE;
     }
 
     /**
@@ -678,65 +766,65 @@ public class RippleDrawable extends LayerDrawable {
         return -1;
     }
 
-    private boolean drawContent(Canvas canvas, Rect bounds, boolean hasRipples, boolean hasMask) {
+    private void drawContent(Canvas canvas) {
+        // Draw everything except the mask.
         final ChildDrawable[] array = mLayerState.mChildren;
         final int count = mLayerState.mNum;
-
-        boolean needsLayer = false;
-
-        if (hasRipples && !hasMask) {
-            // If we only have opaque content, we don't really need a layer
-            // because the ripples will be clipped to the drawable bounds.
-            for (int i = 0; i < count; i++) {
-                if (array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
-                    needsLayer = true;
-                    break;
-                }
-            }
-        }
-
-        if (needsLayer) {
-            canvas.saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
-                    getMaskingPaint(SRC_OVER));
-        }
-
-        // Draw everything except the mask.
         for (int i = 0; i < count; i++) {
             if (array[i].mId != R.id.mask) {
                 array[i].mDrawable.draw(canvas);
             }
         }
-
-        return needsLayer;
     }
 
-    private boolean drawBackgroundAndRipples(
-            Canvas canvas, Rect bounds, boolean hasNonOpaqueMask, boolean hasContentLayer) {
-        if (hasNonOpaqueMask) {
-            final Paint p = getMaskingPaint(SRC_OVER);
-            canvas.saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, p);
+    private void drawBackgroundAndRipples(Canvas canvas) {
+        final Ripple active = mRipple;
+        final RippleBackground background = mBackground;
+        final int count = mExitingRipplesCount;
+        if (active == null && count <= 0 && (background == null || !background.shouldDraw())) {
+            // Move along, nothing to draw here.
+            return;
         }
 
-        final PorterDuffXfermode mode = hasContentLayer ? SRC_ATOP : SRC_OVER;
         final float x = mHotspotBounds.exactCenterX();
         final float y = mHotspotBounds.exactCenterY();
         canvas.translate(x, y);
 
-        final Paint p = getRipplePaint();
-        p.setXfermode(mode);
+        updateMaskShaderIfNeeded();
+
+        // Position the shader to account for canvas translation.
+        if (mMaskShader != null) {
+            mMaskMatrix.setTranslate(-x, -y);
+            mMaskShader.setLocalMatrix(mMaskMatrix);
+        }
 
         // Grab the color for the current state and cut the alpha channel in
         // half so that the ripple and background together yield full alpha.
         final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
-        final int alpha = (Color.alpha(color) / 2) << 24;
-        p.setColor(color & 0xFFFFFF | alpha);
+        final int halfAlpha = (Color.alpha(color) / 2) << 24;
+        final Paint p = getRipplePaint();
+
+        if (mMaskColorFilter != null) {
+            // The ripple timing depends on the paint's alpha value, so we need
+            // to push just the alpha channel into the paint and let the filter
+            // handle the full-alpha color.
+            final int fullAlphaColor = color | (0xFF << 24);
+            mMaskColorFilter.setColor(fullAlphaColor);
+
+            p.setColor(halfAlpha);
+            p.setColorFilter(mMaskColorFilter);
+            p.setShader(mMaskShader);
+        } else {
+            final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha;
+            p.setColor(halfAlphaColor);
+            p.setColorFilter(null);
+            p.setShader(null);
+        }
 
-        final RippleBackground background = mBackground;
         if (background != null && background.shouldDraw()) {
             background.draw(canvas, p);
         }
 
-        final int count = mExitingRipplesCount;
         if (count > 0) {
             final Ripple[] ripples = mExitingRipples;
             for (int i = 0; i < count; i++) {
@@ -744,27 +832,15 @@ public class RippleDrawable extends LayerDrawable {
             }
         }
 
-        final Ripple active = mRipple;
         if (active != null) {
             active.draw(canvas, p);
         }
 
         canvas.translate(-x, -y);
-
-        // Returns true if a layer was created.
-        return hasNonOpaqueMask;
     }
 
-    private int drawMaskingLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
-        final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
-                bounds.right, bounds.bottom, getMaskingPaint(mode));
-
-        // Ensure that DST_IN blends using the entire layer.
-        canvas.drawColor(Color.TRANSPARENT);
-
+    private void drawMask(Canvas canvas) {
         mMask.draw(canvas);
-
-        return restoreToCount;
     }
 
     private Paint getRipplePaint() {
@@ -776,15 +852,6 @@ public class RippleDrawable extends LayerDrawable {
         return mRipplePaint;
     }
 
-    private Paint getMaskingPaint(PorterDuffXfermode xfermode) {
-        if (mMaskingPaint == null) {
-            mMaskingPaint = new Paint();
-        }
-        mMaskingPaint.setXfermode(xfermode);
-        mMaskingPaint.setAlpha(0xFF);
-        return mMaskingPaint;
-    }
-
     @Override
     public Rect getDirtyBounds() {
         if (isProjected()) {
@@ -832,6 +899,10 @@ public class RippleDrawable extends LayerDrawable {
         // LayerDrawable creates a new state using createConstantState, so
         // this should always be a safe cast.
         mState = (RippleState) mLayerState;
+
+        // The locally cached drawable may have changed.
+        mMask = findDrawableByLayerId(R.id.mask);
+
         return this;
     }
 
index 6b84494..5a28be5 100644 (file)
@@ -1828,6 +1828,9 @@ private:
         const ResTable_config* config,
         Entry* outEntry) const;
 
+    uint32_t findEntry(const PackageGroup* group, ssize_t typeIndex, const char16_t* name,
+            size_t nameLen, uint32_t* outTypeSpecFlags) const;
+
     status_t parsePackage(
         const ResTable_package* const pkg, const Header* const header);
 
index 6dfb4dc..d7b9765 100644 (file)
@@ -4173,6 +4173,9 @@ nope:
                  String8(name, nameLen).string(),
                  String8(package, packageLen).string()));
 
+    const String16 attr("attr");
+    const String16 attrPrivate("^attr-private");
+
     const size_t NG = mPackageGroups.size();
     for (size_t ig=0; ig<NG; ig++) {
         const PackageGroup* group = mPackageGroups[ig];
@@ -4185,64 +4188,72 @@ nope:
 
         const size_t packageCount = group->packages.size();
         for (size_t pi = 0; pi < packageCount; pi++) {
-            ssize_t ti = group->packages[pi]->typeStrings.indexOfString(type, typeLen);
-            if (ti < 0) {
-                continue;
-            }
+            const char16_t* targetType = type;
+            size_t targetTypeLen = typeLen;
 
-            ti += group->packages[pi]->typeIdOffset;
+            do {
+                ssize_t ti = group->packages[pi]->typeStrings.indexOfString(
+                        targetType, targetTypeLen);
+                if (ti < 0) {
+                    continue;
+                }
 
-            const TypeList& typeList = group->types[ti];
-            if (typeList.isEmpty()) {
-                TABLE_NOISY(printf("Expected type structure not found in package %s for index %d\n",
-                                   String8(group->name).string(), ti));
-                continue;
-            }
+                ti += group->packages[pi]->typeIdOffset;
 
-            const size_t typeCount = typeList.size();
-            for (size_t i = 0; i < typeCount; i++) {
-                const Type* t = typeList[i];
-                const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
-                if (ei < 0) {
-                    continue;
+                const uint32_t identifier = findEntry(group, ti, name, nameLen,
+                        outTypeSpecFlags);
+                if (identifier != 0) {
+                    if (fakePublic && outTypeSpecFlags) {
+                        *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
+                    }
+                    return identifier;
                 }
+            } while (strzcmp16(attr.string(), attr.size(), targetType, targetTypeLen) == 0
+                    && (targetType = attrPrivate.string())
+                    && (targetTypeLen = attrPrivate.size())
+            );
+        }
+        break;
+    }
+    return 0;
+}
 
-                const size_t configCount = t->configs.size();
-                for (size_t j = 0; j < configCount; j++) {
-                    const TypeVariant tv(t->configs[j]);
-                    for (TypeVariant::iterator iter = tv.beginEntries();
-                         iter != tv.endEntries();
-                         iter++) {
-                        const ResTable_entry* entry = *iter;
-                        if (entry == NULL) {
-                            continue;
-                        }
+uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const char16_t* name,
+        size_t nameLen, uint32_t* outTypeSpecFlags) const {
+    const TypeList& typeList = group->types[typeIndex];
+    const size_t typeCount = typeList.size();
+    for (size_t i = 0; i < typeCount; i++) {
+        const Type* t = typeList[i];
+        const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
+        if (ei < 0) {
+            continue;
+        }
 
-                        if (dtohl(entry->key.index) == (size_t) ei) {
-                            uint32_t resId = Res_MAKEID(group->id - 1, ti, iter.index());
-                            if (outTypeSpecFlags) {
-                                Entry result;
-                                if (getEntry(group, ti, iter.index(), NULL, &result) != NO_ERROR) {
-                                    ALOGW("Failed to find spec flags for %s:%s/%s (0x%08x)",
-                                            String8(group->name).string(),
-                                            String8(String16(type, typeLen)).string(),
-                                            String8(String16(name, nameLen)).string(),
-                                            resId);
-                                    return 0;
-                                }
-                                *outTypeSpecFlags = result.specFlags;
+        const size_t configCount = t->configs.size();
+        for (size_t j = 0; j < configCount; j++) {
+            const TypeVariant tv(t->configs[j]);
+            for (TypeVariant::iterator iter = tv.beginEntries();
+                 iter != tv.endEntries();
+                 iter++) {
+                const ResTable_entry* entry = *iter;
+                if (entry == NULL) {
+                    continue;
+                }
 
-                                if (fakePublic) {
-                                    *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
-                                }
-                            }
-                            return resId;
+                if (dtohl(entry->key.index) == (size_t) ei) {
+                    uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index());
+                    if (outTypeSpecFlags) {
+                        Entry result;
+                        if (getEntry(group, typeIndex, iter.index(), NULL, &result) != NO_ERROR) {
+                            ALOGW("Failed to find spec flags for 0x%08x", resId);
+                            return 0;
                         }
+                        *outTypeSpecFlags = result.specFlags;
                     }
+                    return resId;
                 }
             }
         }
-        break;
     }
     return 0;
 }
index 80b4c2a..075f2c5 100755 (executable)
@@ -1560,13 +1560,7 @@ void OpenGLRenderer::setupDraw(bool clearLayer) {
             setScissorFromClip();
         }
 
-        if (clearLayer) {
-            setStencilFromClip();
-        } else {
-            // While clearing layer, force disable stencil buffer, since
-            // it's invalid to stencil-clip *during* the layer clear
-            mCaches.stencil.disable();
-        }
+        setStencilFromClip();
     }
 
     mDescription.reset();
index bbba2dd..810e487 100644 (file)
@@ -680,13 +680,32 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T&
 
     float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha();
 
+
+    // holds temporary SkPath to store the result of intersections
+    SkPath* frameAllocatedPath = NULL;
     const SkPath* outlinePath = casterOutlinePath;
+
+    // intersect the outline with the reveal clip, if present
     if (revealClipPath) {
-        // if we can't simply use the caster's path directly, create a temporary one
-        SkPath* frameAllocatedPath = handler.allocPathForFrame();
+        frameAllocatedPath = handler.allocPathForFrame();
+
+        Op(*outlinePath, *revealClipPath, kIntersect_PathOp, frameAllocatedPath);
+        outlinePath = frameAllocatedPath;
+    }
+
+    // intersect the outline with the clipBounds, if present
+    if (properties().getClippingFlags() & CLIP_TO_CLIP_BOUNDS) {
+        if (!frameAllocatedPath) {
+            frameAllocatedPath = handler.allocPathForFrame();
+        }
+
+        Rect clipBounds;
+        properties().getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
+        SkPath clipBoundsPath;
+        clipBoundsPath.addRect(clipBounds.left, clipBounds.top,
+                clipBounds.right, clipBounds.bottom);
 
-        // intersect the outline with the convex reveal clip
-        Op(*casterOutlinePath, *revealClipPath, kIntersect_PathOp, frameAllocatedPath);
+        Op(*outlinePath, clipBoundsPath, kIntersect_PathOp, frameAllocatedPath);
         outlinePath = frameAllocatedPath;
     }
 
index 39528be..75bd067 100644 (file)
@@ -60,6 +60,7 @@ CanvasContext::~CanvasContext() {
 
 void CanvasContext::destroy() {
     stopDrawing();
+    setSurface(NULL);
     freePrefetechedLayers();
     destroyHardwareResources();
     mAnimationContext->destroy();
@@ -67,7 +68,6 @@ void CanvasContext::destroy() {
         delete mCanvas;
         mCanvas = 0;
     }
-    setSurface(NULL);
 }
 
 void CanvasContext::setSurface(ANativeWindow* window) {
@@ -123,8 +123,8 @@ void CanvasContext::updateSurface(ANativeWindow* window) {
     setSurface(window);
 }
 
-void CanvasContext::pauseSurface(ANativeWindow* window) {
-    stopDrawing();
+bool CanvasContext::pauseSurface(ANativeWindow* window) {
+    return mRenderThread.removeFrameCallback(this);
 }
 
 // TODO: don't pass viewport size, it's automatic via EGL
index 435244e..0cc2c7c 100644 (file)
@@ -67,7 +67,7 @@ public:
 
     bool initialize(ANativeWindow* window);
     void updateSurface(ANativeWindow* window);
-    void pauseSurface(ANativeWindow* window);
+    bool pauseSurface(ANativeWindow* window);
     bool hasSurface() { return mNativeWindow.get(); }
 
     void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
index 5d55ea6..6d063a4 100644 (file)
@@ -156,15 +156,14 @@ void RenderProxy::updateSurface(const sp<ANativeWindow>& window) {
 }
 
 CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) {
-    args->context->pauseSurface(args->window);
-    return NULL;
+    return (void*) args->context->pauseSurface(args->window);
 }
 
-void RenderProxy::pauseSurface(const sp<ANativeWindow>& window) {
+bool RenderProxy::pauseSurface(const sp<ANativeWindow>& window) {
     SETUP_TASK(pauseSurface);
     args->context = mContext;
     args->window = window.get();
-    postAndWait(task);
+    return (bool) postAndWait(task);
 }
 
 CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height,
index 4989b14..fd1fe05 100644 (file)
@@ -69,7 +69,7 @@ public:
 
     ANDROID_API bool initialize(const sp<ANativeWindow>& window);
     ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
-    ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
+    ANDROID_API bool pauseSurface(const sp<ANativeWindow>& window);
     ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     ANDROID_API void setOpaque(bool opaque);
index f887103..38cb4cd 100644 (file)
@@ -314,9 +314,11 @@ void RenderThread::postFrameCallback(IFrameCallback* callback) {
     mPendingRegistrationFrameCallbacks.insert(callback);
 }
 
-void RenderThread::removeFrameCallback(IFrameCallback* callback) {
-    mFrameCallbacks.erase(callback);
-    mPendingRegistrationFrameCallbacks.erase(callback);
+bool RenderThread::removeFrameCallback(IFrameCallback* callback) {
+    size_t erased;
+    erased = mFrameCallbacks.erase(callback);
+    erased |= mPendingRegistrationFrameCallbacks.erase(callback);
+    return erased;
 }
 
 void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {
index c461f3a..4126d02 100644 (file)
@@ -80,7 +80,7 @@ public:
 
     // Mimics android.view.Choreographer
     void postFrameCallback(IFrameCallback* callback);
-    void removeFrameCallback(IFrameCallback* callback);
+    bool removeFrameCallback(IFrameCallback* callback);
     // If the callback is currently registered, it will be pushed back until
     // the next vsync. If it is not currently registered this does nothing.
     void pushBackFrameCallback(IFrameCallback* callback);
index 20c4978..489f552 100644 (file)
@@ -241,11 +241,11 @@ public final class AudioAttributes implements Parcelable {
 
     /**
      * @hide
-     * CANDIDATE FOR PUBLIC API
      * Return the capture preset.
      * @return one of the values that can be set in {@link Builder#setCapturePreset(int)} or a
      *    negative value if none has been set.
      */
+    @SystemApi
     public int getCapturePreset() {
         return mSource;
     }
@@ -508,6 +508,7 @@ public final class AudioAttributes implements Parcelable {
          *     {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION}.
          * @return the same Builder instance.
          */
+        @SystemApi
         public Builder setCapturePreset(int preset) {
             switch (preset) {
                 case MediaRecorder.AudioSource.DEFAULT:
index 24817aa..543836b 100644 (file)
@@ -2459,6 +2459,7 @@ public class AudioManager {
      *     {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
      * @throws IllegalArgumentException
      */
+    @SystemApi
     public int requestAudioFocus(OnAudioFocusChangeListener l,
             @NonNull AudioAttributes requestAttributes,
             int durationHint,
@@ -2853,17 +2854,17 @@ public class AudioManager {
 
     /**
      * @hide
-     * CANDIDATE FOR PUBLIC API
      * Register the given {@link AudioPolicy}.
      * This call is synchronous and blocks until the registration process successfully completed
      * or failed to complete.
-     * @param policy the {@link AudioPolicy} to register.
+     * @param policy the non-null {@link AudioPolicy} to register.
      * @return {@link #ERROR} if there was an error communicating with the registration service
      *    or if the user doesn't have the required
      *    {@link android.Manifest.permission#MODIFY_AUDIO_ROUTING} permission,
      *    {@link #SUCCESS} otherwise.
      */
-    public int registerAudioPolicy(AudioPolicy policy) {
+    @SystemApi
+    public int registerAudioPolicy(@NonNull AudioPolicy policy) {
         if (policy == null) {
             throw new IllegalArgumentException("Illegal null AudioPolicy argument");
         }
@@ -2885,16 +2886,17 @@ public class AudioManager {
 
     /**
      * @hide
-     * CANDIDATE FOR PUBLIC API
-     * @param policy the {@link AudioPolicy} to unregister.
+     * @param policy the non-null {@link AudioPolicy} to unregister.
      */
-    public void unregisterAudioPolicyAsync(AudioPolicy policy) {
+    @SystemApi
+    public void unregisterAudioPolicyAsync(@NonNull AudioPolicy policy) {
         if (policy == null) {
             throw new IllegalArgumentException("Illegal null AudioPolicy argument");
         }
         IAudioService service = getService();
         try {
             service.unregisterAudioPolicyAsync(policy.token());
+            policy.setRegistration(null);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in unregisterAudioPolicyAsync()", e);
         }
index 4da2705..9a3ec42 100644 (file)
@@ -1124,20 +1124,11 @@ public class AudioService extends IAudioService.Stub {
 
             // Check if volume update should be send to Hdmi system audio.
             int newIndex = mStreamStates[streamType].getIndex(device);
+            if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
+                setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
+            }
             if (mHdmiManager != null) {
                 synchronized (mHdmiManager) {
-                    if (mHdmiTvClient != null &&
-                        streamTypeAlias == AudioSystem.STREAM_MUSIC &&
-                        (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0 &&
-                        oldIndex != newIndex) {
-                        int maxIndex = getStreamMaxVolume(streamType);
-                        synchronized (mHdmiTvClient) {
-                            if (mHdmiSystemAudioSupported) {
-                                mHdmiTvClient.setSystemAudioVolume(
-                                        (oldIndex + 5) / 10, (newIndex + 5) / 10, maxIndex);
-                            }
-                        }
-                    }
                     // mHdmiCecSink true => mHdmiPlaybackClient != null
                     if (mHdmiCecSink &&
                             streamTypeAlias == AudioSystem.STREAM_MUSIC &&
@@ -1156,6 +1147,23 @@ public class AudioService extends IAudioService.Stub {
         sendVolumeUpdate(streamType, oldIndex, index, flags);
     }
 
+    private void setSystemAudioVolume(int oldVolume, int newVolume, int maxVolume, int flags) {
+        if (mHdmiManager == null
+                || mHdmiTvClient == null
+                || oldVolume == newVolume
+                || (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) != 0) return;
+
+        // Sets the audio volume of AVR when we are in system audio mode. The new volume info
+        // is tranformed to HDMI-CEC commands and passed through CEC bus.
+        synchronized (mHdmiManager) {
+            if (!mHdmiSystemAudioSupported) return;
+            synchronized (mHdmiTvClient) {
+                mHdmiTvClient.setSystemAudioVolume(
+                        (oldVolume + 5) / 10, (newVolume + 5) / 10, maxVolume);
+            }
+        }
+    }
+
     /** @see AudioManager#adjustMasterVolume(int, int) */
     public void adjustMasterVolume(int steps, int flags, String callingPackage) {
         adjustMasterVolume(steps, flags, callingPackage, Binder.getCallingUid());
@@ -1267,21 +1275,8 @@ public class AudioService extends IAudioService.Stub {
                 }
             }
 
-            if (mHdmiManager != null) {
-                synchronized (mHdmiManager) {
-                    if (mHdmiTvClient != null &&
-                        streamTypeAlias == AudioSystem.STREAM_MUSIC &&
-                        (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0 &&
-                        oldIndex != index) {
-                        int maxIndex = getStreamMaxVolume(streamType);
-                        synchronized (mHdmiTvClient) {
-                            if (mHdmiSystemAudioSupported) {
-                                mHdmiTvClient.setSystemAudioVolume(
-                                        (oldIndex + 5) / 10, (index + 5) / 10, maxIndex);
-                            }
-                        }
-                    }
-                }
+            if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
+                setSystemAudioVolume(oldIndex, index, getStreamMaxVolume(streamType), flags);
             }
 
             flags &= ~AudioManager.FLAG_FIXED_VOLUME;
@@ -1422,15 +1417,8 @@ public class AudioService extends IAudioService.Stub {
             streamType = AudioSystem.STREAM_NOTIFICATION;
         }
 
-        // If Hdmi-CEC system audio mode is on, show volume bar
-        // only when TV receives volume notification from Audio Receiver.
-        if (mHdmiTvClient != null && streamType == AudioSystem.STREAM_MUSIC) {
-            synchronized (mHdmiTvClient) {
-                if (mHdmiSystemAudioSupported &&
-                        ((flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0)) {
-                    flags &= ~AudioManager.FLAG_SHOW_UI;
-                }
-            }
+        if (streamType == AudioSystem.STREAM_MUSIC) {
+            flags = updateFlagsForSystemAudio(flags);
         }
         mVolumeController.postVolumeChanged(streamType, flags);
 
@@ -1445,9 +1433,23 @@ public class AudioService extends IAudioService.Stub {
         }
     }
 
+    // If Hdmi-CEC system audio mode is on, we show volume bar only when TV
+    // receives volume notification from Audio Receiver.
+    private int updateFlagsForSystemAudio(int flags) {
+        if (mHdmiTvClient != null) {
+            synchronized (mHdmiTvClient) {
+                if (mHdmiSystemAudioSupported &&
+                        ((flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0)) {
+                    flags &= ~AudioManager.FLAG_SHOW_UI;
+                }
+            }
+        }
+        return flags;
+    }
+
     // UI update and Broadcast Intent
     private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
-        mVolumeController.postMasterVolumeChanged(flags);
+        mVolumeController.postMasterVolumeChanged(updateFlagsForSystemAudio(flags));
 
         Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
         intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
@@ -1457,7 +1459,7 @@ public class AudioService extends IAudioService.Stub {
 
     // UI update and Broadcast Intent
     private void sendMasterMuteUpdate(boolean muted, int flags) {
-        mVolumeController.postMasterMuteChanged(flags);
+        mVolumeController.postMasterMuteChanged(updateFlagsForSystemAudio(flags));
         broadcastMasterMuteStatus(muted);
     }
 
@@ -1517,18 +1519,26 @@ public class AudioService extends IAudioService.Stub {
         }
 
         if (isStreamAffectedByMute(streamType)) {
-            if (mHdmiManager != null) {
-                synchronized (mHdmiManager) {
-                    if (streamType == AudioSystem.STREAM_MUSIC && mHdmiTvClient != null) {
-                        synchronized (mHdmiTvClient) {
-                            if (mHdmiSystemAudioSupported) {
-                                mHdmiTvClient.setSystemAudioMute(state);
-                            }
-                        }
+            if (streamType == AudioSystem.STREAM_MUSIC) {
+                setSystemAudioMute(state);
+            }
+            mStreamStates[streamType].mute(cb, state);
+        }
+    }
+
+    private void setSystemAudioMute(boolean state) {
+        if (mHdmiManager == null || mHdmiTvClient == null) return;
+        synchronized (mHdmiManager) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mHdmiTvClient) {
+                    if (mHdmiSystemAudioSupported) {
+                        mHdmiTvClient.setSystemAudioMute(state);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
-            mStreamStates[streamType].mute(cb, state);
         }
     }
 
@@ -1649,6 +1659,7 @@ public class AudioService extends IAudioService.Stub {
             return;
         }
         if (state != AudioSystem.getMasterMute()) {
+            setSystemAudioMute(state);
             AudioSystem.setMasterMute(state);
             // Post a persist master volume msg
             sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
@@ -1729,6 +1740,7 @@ public class AudioService extends IAudioService.Stub {
                 // Post a persist master volume msg
                 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
                         Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
+                setSystemAudioVolume(oldVolume, newVolume, getMasterMaxVolume(), flags);
             }
             // Send the volume update regardless whether there was a change.
             sendMasterVolumeUpdate(flags, oldVolume, newVolume);
@@ -5526,6 +5538,8 @@ public class AudioService extends IAudioService.Stub {
         pw.print("  mMusicActiveMs="); pw.println(mMusicActiveMs);
         pw.print("  mMcc="); pw.println(mMcc);
         pw.print("  mHasVibrator="); pw.println(mHasVibrator);
+
+        dumpAudioPolicies(pw);
     }
 
     private static String safeMediaVolumeStateToString(Integer state) {
@@ -5785,6 +5799,10 @@ public class AudioService extends IAudioService.Stub {
         }
         synchronized (mAudioPolicies) {
             try {
+                if (mAudioPolicies.containsKey(cb)) {
+                    Slog.e(TAG, "Cannot re-register policy");
+                    return null;
+                }
                 AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, cb);
                 cb.linkToDeath(app, 0/*flags*/);
                 regId = app.connectMixes();
@@ -5805,6 +5823,7 @@ public class AudioService extends IAudioService.Stub {
             if (app == null) {
                 Slog.w(TAG, "Trying to unregister unknown audio policy for pid "
                         + Binder.getCallingPid() + " / uid " + Binder.getCallingUid());
+                return;
             } else {
                 cb.unlinkToDeath(app, 0/*flags*/);
             }
@@ -5813,12 +5832,21 @@ public class AudioService extends IAudioService.Stub {
         // TODO implement clearing mix attribute matching info in native audio policy
     }
 
+    private void dumpAudioPolicies(PrintWriter pw) {
+        pw.println("\nAudio policies:");
+        synchronized (mAudioPolicies) {
+            for(AudioPolicyProxy policy : mAudioPolicies.values()) {
+                pw.println(policy.toLogFriendlyString());
+            }
+        }
+    }
+
     //======================
     // Audio policy proxy
     //======================
     /**
-     * This internal class inherits from AudioPolicyConfig which contains all the mixes and
-     * their configurations.
+     * This internal class inherits from AudioPolicyConfig, each instance contains all the
+     * mixes of an AudioPolicy and their configurations.
      */
     public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient {
         private static final String TAG = "AudioPolicyProxy";
@@ -5826,7 +5854,7 @@ public class AudioService extends IAudioService.Stub {
         IBinder mToken;
         AudioPolicyProxy(AudioPolicyConfig config, IBinder token) {
             super(config);
-            setRegistration(new String(config.toString() + ":ap:" + mAudioPolicyCounter++));
+            setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++));
             mToken = token;
         }
 
@@ -5840,7 +5868,7 @@ public class AudioService extends IAudioService.Stub {
 
         String connectMixes() {
             updateMixes(AudioSystem.DEVICE_STATE_AVAILABLE);
-            return mRegistrationId;
+            return getRegistration();
         }
 
         void disconnectMixes() {
@@ -5851,8 +5879,9 @@ public class AudioService extends IAudioService.Stub {
             for (AudioMix mix : mMixes) {
                 // TODO implement sending the mix attribute matching info to native audio policy
                 if (DEBUG_AP) {
-                    Log.v(TAG, "AudioPolicyProxy connect mix state=" + connectionState
-                            + " addr=" + mix.getRegistration()); }
+                    Log.v(TAG, "AudioPolicyProxy mix new connection state=" + connectionState
+                            + " addr=" + mix.getRegistration());
+                }
                 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_REMOTE_SUBMIX,
                         connectionState,
                         mix.getRegistration());
index b541454..400c082 100644 (file)
@@ -26,6 +26,7 @@ import android.view.Surface;
 import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.nio.NioUtils;
 
 /**
  * <p>The ImageReader class allows direct application access to image data
@@ -688,6 +689,15 @@ public class ImageReader implements AutoCloseable {
             }
 
             private void clearBuffer() {
+                // Need null check first, as the getBuffer() may not be called before an image
+                // is closed.
+                if (mBuffer == null) {
+                    return;
+                }
+
+                if (mBuffer.isDirect()) {
+                    NioUtils.freeDirectBuffer(mBuffer);
+                }
                 mBuffer = null;
             }
 
index 1029fcc..4513643 100644 (file)
@@ -648,7 +648,7 @@ public final class MediaCodecInfo {
                 maxChannels = 48;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS)) {
                 bitRates = Range.create(32000, 500000);
-                sampleRates = new int[] { 8000, 12000, 16000, 24000, 48000, 192000 };
+                sampleRateRange = Range.create(8000, 192000);
                 maxChannels = 255;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS)) {
                 bitRates = Range.create(6000, 510000);
index bb52682..1806662 100644 (file)
 package android.media.audiopolicy;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.media.AudioFormat;
 import android.media.AudioSystem;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * @hide
  */
+@SystemApi
 public class AudioMix {
 
     private AudioMixingRule mRule;
     private AudioFormat mFormat;
     private int mRouteFlags;
     private String mRegistrationId;
+    private int mMixType = MIX_TYPE_INVALID;
 
     /**
      * All parameters are guaranteed valid through the Builder.
@@ -41,20 +45,39 @@ public class AudioMix {
         mFormat = format;
         mRouteFlags = routeFlags;
         mRegistrationId = null;
+        mMixType = rule.getTargetMixType();
     }
 
     /**
      * An audio mix behavior where the output of the mix is sent to the original destination of
      * the audio signal, i.e. an output device for an output mix, or a recording for an input mix.
      */
+    @SystemApi
     public static final int ROUTE_FLAG_RENDER    = 0x1;
     /**
      * An audio mix behavior where the output of the mix is rerouted back to the framework and
-     * is accessible for injection or capture through the {@link Audiotrack} and {@link AudioRecord}
+     * is accessible for injection or capture through the {@link AudioTrack} and {@link AudioRecord}
      * APIs.
      */
+    @SystemApi
     public static final int ROUTE_FLAG_LOOP_BACK = 0x1 << 1;
 
+    /**
+     * @hide
+     * Invalid mix type, default value.
+     */
+    public static final int MIX_TYPE_INVALID = -1;
+    /**
+     * @hide
+     * Mix type indicating playback streams are mixed.
+     */
+    public static final int MIX_TYPE_PLAYERS = 0;
+    /**
+     * @hide
+     * Mix type indicating recording streams are mixed.
+     */
+    public static final int MIX_TYPE_RECORDERS = 1;
+
     int getRouteFlags() {
         return mRouteFlags;
     }
@@ -67,6 +90,11 @@ public class AudioMix {
         return mRule;
     }
 
+    /** @hide */
+    public int getMixType() {
+        return mMixType;
+    }
+
     void setRegistration(String regId) {
         mRegistrationId = regId;
     }
@@ -77,6 +105,12 @@ public class AudioMix {
     }
 
     /** @hide */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRouteFlags, mRule, mMixType, mFormat);
+    }
+
+    /** @hide */
     @IntDef(flag = true,
             value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } )
     @Retention(RetentionPolicy.SOURCE)
@@ -86,6 +120,7 @@ public class AudioMix {
      * Builder class for {@link AudioMix} objects
      *
      */
+    @SystemApi
     public static class Builder {
         private AudioMixingRule mRule = null;
         private AudioFormat mFormat = null;
@@ -102,6 +137,7 @@ public class AudioMix {
          * @param rule a non-null {@link AudioMixingRule} instance.
          * @throws IllegalArgumentException
          */
+        @SystemApi
         public Builder(AudioMixingRule rule)
                 throws IllegalArgumentException {
             if (rule == null) {
@@ -132,6 +168,7 @@ public class AudioMix {
          * @return the same Builder instance.
          * @throws IllegalArgumentException
          */
+        @SystemApi
         public Builder setFormat(AudioFormat format)
                 throws IllegalArgumentException {
             if (format == null) {
@@ -148,6 +185,7 @@ public class AudioMix {
          * @return the same Builder instance.
          * @throws IllegalArgumentException
          */
+        @SystemApi
         public Builder setRouteFlags(@RouteFlags int routeFlags)
                 throws IllegalArgumentException {
             if (routeFlags == 0) {
@@ -166,6 +204,7 @@ public class AudioMix {
          * @return a new {@link AudioMix} object
          * @throws IllegalArgumentException if no {@link AudioMixingRule} has been set.
          */
+        @SystemApi
         public AudioMix build() throws IllegalArgumentException {
             if (mRule == null) {
                 throw new IllegalArgumentException("Illegal null AudioMixingRule");
index 2e06a80..02b03d2 100644 (file)
 
 package android.media.audiopolicy;
 
+import android.annotation.SystemApi;
 import android.media.AudioAttributes;
+import android.os.Parcel;
 
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.Objects;
 
 
 /**
@@ -35,44 +38,114 @@ import java.util.Iterator;
  *         .build();
  * </pre>
  */
+@SystemApi
 public class AudioMixingRule {
 
-    private AudioMixingRule(ArrayList<AttributeMatchCriterion> criteria) {
+    private AudioMixingRule(int mixType, ArrayList<AttributeMatchCriterion> criteria) {
         mCriteria = criteria;
+        mTargetMixType = mixType;
     }
 
     /**
-     * A rule requiring the usage information of the {@link AudioAttributes} to match
+     * A rule requiring the usage information of the {@link AudioAttributes} to match.
      */
+    @SystemApi
     public static final int RULE_MATCH_ATTRIBUTE_USAGE = 0x1;
     /**
-     * A rule requiring the usage information of the {@link AudioAttributes} to differ
+     * A rule requiring the capture preset information of the {@link AudioAttributes} to match.
      */
-    public static final int RULE_EXCLUDE_ATTRIBUTE_USAGE = 0x1 << 1;
+    @SystemApi
+    public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 0x1 << 1;
+
+    private final static int RULE_EXCLUSION_MASK = 0x8000;
+    /**
+     * @hide
+     * A rule requiring the usage information of the {@link AudioAttributes} to differ.
+     */
+    public static final int RULE_EXCLUDE_ATTRIBUTE_USAGE =
+            RULE_EXCLUSION_MASK | RULE_MATCH_ATTRIBUTE_USAGE;
+    /**
+     * @hide
+     * A rule requiring the capture preset information of the {@link AudioAttributes} to differ.
+     */
+    public static final int RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET =
+            RULE_EXCLUSION_MASK | RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET;
 
     static final class AttributeMatchCriterion {
         AudioAttributes mAttr;
         int mRule;
 
+        /** input parameters must be valid */
         AttributeMatchCriterion(AudioAttributes attributes, int rule) {
             mAttr = attributes;
             mRule = rule;
         }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mAttr, mRule);
+        }
+
+        void writeToParcel(Parcel dest) {
+            dest.writeInt(mRule);
+            if ((mRule == RULE_MATCH_ATTRIBUTE_USAGE) || (mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE)) {
+                dest.writeInt(mAttr.getUsage());
+            } else {
+                // capture preset rule
+                dest.writeInt(mAttr.getCapturePreset());
+            }
+        }
     }
 
-    private ArrayList<AttributeMatchCriterion> mCriteria;
+    private final int mTargetMixType;
+    int getTargetMixType() { return mTargetMixType; }
+    private final ArrayList<AttributeMatchCriterion> mCriteria;
     ArrayList<AttributeMatchCriterion> getCriteria() { return mCriteria; }
 
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTargetMixType, mCriteria);
+    }
+
+    private static boolean isValidSystemApiRule(int rule) {
+        switch(rule) {
+            case RULE_MATCH_ATTRIBUTE_USAGE:
+            case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private static boolean isValidIntRule(int rule) {
+        switch(rule) {
+            case RULE_MATCH_ATTRIBUTE_USAGE:
+            case RULE_EXCLUDE_ATTRIBUTE_USAGE:
+            case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+            case RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private static boolean isPlayerRule(int rule) {
+        return ((rule == RULE_MATCH_ATTRIBUTE_USAGE)
+                || (rule == RULE_EXCLUDE_ATTRIBUTE_USAGE));
+    }
+
     /**
      * Builder class for {@link AudioMixingRule} objects
-     *
      */
+    @SystemApi
     public static class Builder {
         private ArrayList<AttributeMatchCriterion> mCriteria;
+        private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
 
         /**
          * Constructs a new Builder with no rules.
          */
+        @SystemApi
         public Builder() {
             mCriteria = new ArrayList<AttributeMatchCriterion>();
         }
@@ -81,18 +154,80 @@ public class AudioMixingRule {
          * Add a rule for the selection of which streams are mixed together.
          * @param attrToMatch a non-null AudioAttributes instance for which a contradictory
          *     rule hasn't been set yet.
-         * @param rule one of {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_USAGE},
-         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE}.
+         * @param rule {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE} or
+         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}.
          * @return the same Builder instance.
          * @throws IllegalArgumentException
          */
+        @SystemApi
         public Builder addRule(AudioAttributes attrToMatch, int rule)
                 throws IllegalArgumentException {
+            if (!isValidSystemApiRule(rule)) {
+                throw new IllegalArgumentException("Illegal rule value " + rule);
+            }
+            return addRuleInt(attrToMatch, rule);
+        }
+
+        /**
+         * Add a rule by exclusion for the selection of which streams are mixed together.
+         * <br>For instance the following code
+         * <br><pre>
+         * AudioAttributes mediaAttr = new AudioAttributes.Builder()
+         *         .setUsage(AudioAttributes.USAGE_MEDIA)
+         *         .build();
+         * AudioMixingRule noMediaRule = new AudioMixingRule.Builder()
+         *         .excludeRule(mediaAttr, AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE)
+         *         .build();
+         * </pre>
+         * <br>will create a rule which maps to any usage value, except USAGE_MEDIA.
+         * @param attrToMatch a non-null AudioAttributes instance for which a contradictory
+         *     rule hasn't been set yet.
+         * @param rule {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE} or
+         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}.
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        @SystemApi
+        public Builder excludeRule(AudioAttributes attrToMatch, int rule)
+                throws IllegalArgumentException {
+            if (!isValidSystemApiRule(rule)) {
+                throw new IllegalArgumentException("Illegal rule value " + rule);
+            }
+            return addRuleInt(attrToMatch, rule | RULE_EXCLUSION_MASK);
+        }
+
+        /**
+         * Add or exclude a rule for the selection of which streams are mixed together.
+         * @param attrToMatch a non-null AudioAttributes instance for which a contradictory
+         *     rule hasn't been set yet.
+         * @param rule one of {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_USAGE},
+         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
+         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
+         *     {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET}.
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        Builder addRuleInt(AudioAttributes attrToMatch, int rule)
+                throws IllegalArgumentException {
             if (attrToMatch == null) {
                 throw new IllegalArgumentException("Illegal null AudioAttributes argument");
             }
-            if ((rule != RULE_MATCH_ATTRIBUTE_USAGE) && (rule != RULE_EXCLUDE_ATTRIBUTE_USAGE)) {
+            if (!isValidIntRule(rule)) {
                 throw new IllegalArgumentException("Illegal rule value " + rule);
+            } else {
+                // as rules are added to the Builder, we verify they are consistent with the type
+                // of mix being built. When adding the first rule, the mix type is MIX_TYPE_INVALID.
+                if (mTargetMixType == AudioMix.MIX_TYPE_INVALID) {
+                    if (isPlayerRule(rule)) {
+                        mTargetMixType = AudioMix.MIX_TYPE_PLAYERS;
+                    } else {
+                        mTargetMixType = AudioMix.MIX_TYPE_RECORDERS;
+                    }
+                } else if (((mTargetMixType == AudioMix.MIX_TYPE_PLAYERS) && !isPlayerRule(rule))
+                        || ((mTargetMixType == AudioMix.MIX_TYPE_RECORDERS) && isPlayerRule(rule)))
+                {
+                    throw new IllegalArgumentException("Incompatible rule for mix");
+                }
             }
             synchronized (mCriteria) {
                 Iterator<AttributeMatchCriterion> crIterator = mCriteria.iterator();
@@ -111,6 +246,19 @@ public class AudioMixingRule {
                                         + attrToMatch);
                             }
                         }
+                    } else if ((rule == RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET)
+                            || (rule == RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET)) {
+                        // "capture preset"-base rule
+                        if (criterion.mAttr.getCapturePreset() == attrToMatch.getCapturePreset()) {
+                            if (criterion.mRule == rule) {
+                             // rule already exists, we're done
+                                return this;
+                            } else {
+                                // criterion already exists with a another rule, it is incompatible
+                                throw new IllegalArgumentException("Contradictory rule exists for "
+                                        + attrToMatch);
+                            }
+                        }
                     }
                 }
                 // rule didn't exist, add it
@@ -119,13 +267,32 @@ public class AudioMixingRule {
             return this;
         }
 
+        Builder addRuleFromParcel(Parcel in) throws IllegalArgumentException {
+            int rule = in.readInt();
+            AudioAttributes attr;
+            if ((rule == RULE_MATCH_ATTRIBUTE_USAGE) || (rule == RULE_EXCLUDE_ATTRIBUTE_USAGE)) {
+                int usage = in.readInt();
+                attr = new AudioAttributes.Builder()
+                        .setUsage(usage).build();
+            } else if ((rule == RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET)
+                    || (rule == RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET)) {
+                int preset = in.readInt();
+                attr = new AudioAttributes.Builder()
+                        .setInternalCapturePreset(preset).build();
+            } else {
+                in.readInt(); // assume there was in int value to read as for now they come in pair
+                throw new IllegalArgumentException("Illegal rule value " + rule + " in parcel");
+            }
+            return addRuleInt(attr, rule);
+        }
+
         /**
          * Combines all of the matching and exclusion rules that have been set and return a new
          * {@link AudioMixingRule} object.
          * @return a new {@link AudioMixingRule} object
          */
         public AudioMixingRule build() {
-            return new AudioMixingRule(mCriteria);
+            return new AudioMixingRule(mTargetMixType, mCriteria);
         }
     }
 }
index e9dc3af..44d2430 100644 (file)
 package android.media.audiopolicy;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioRecord;
-import android.media.AudioSystem;
 import android.media.AudioTrack;
 import android.media.MediaRecorder;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.util.Log;
 import android.util.Slog;
 
@@ -39,21 +43,20 @@ import java.util.ArrayList;
  * @hide
  * AudioPolicy provides access to the management of audio routing and audio focus.
  */
+@SystemApi
 public class AudioPolicy {
 
     private static final String TAG = "AudioPolicy";
 
     /**
-     * The status of an audio policy that cannot be used because it is invalid.
-     */
-    public static final int POLICY_STATUS_INVALID = 0;
-    /**
      * The status of an audio policy that is valid but cannot be used because it is not registered.
      */
+    @SystemApi
     public static final int POLICY_STATUS_UNREGISTERED = 1;
     /**
      * The status of an audio policy that is valid, successfully registered and thus active.
      */
+    @SystemApi
     public static final int POLICY_STATUS_REGISTERED = 2;
 
     private int mStatus;
@@ -72,22 +75,29 @@ public class AudioPolicy {
     /**
      * The parameter is guaranteed non-null through the Builder
      */
-    private AudioPolicy(AudioPolicyConfig config, Context context) {
+    private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper) {
         mConfig = config;
-        if (mConfig.mMixes.isEmpty()) {
-            mStatus = POLICY_STATUS_INVALID;
+        mStatus = POLICY_STATUS_UNREGISTERED;
+        mContext = context;
+        if (looper == null) {
+            looper = Looper.getMainLooper();
+        }
+        if (looper != null) {
+            mEventHandler = new EventHandler(this, looper);
         } else {
-            mStatus = POLICY_STATUS_UNREGISTERED;
+            mEventHandler = null;
+            Log.e(TAG, "No event handler due to looper without a thread");
         }
-        mContext = context;
     }
 
     /**
      * Builder class for {@link AudioPolicy} objects
      */
+    @SystemApi
     public static class Builder {
         private ArrayList<AudioMix> mMixes;
         private Context mContext;
+        private Looper mLooper;
 
         /**
          * Constructs a new Builder with no audio mixes.
@@ -104,7 +114,7 @@ public class AudioPolicy {
          * @return the same Builder instance.
          * @throws IllegalArgumentException
          */
-        public Builder addMix(AudioMix mix) throws IllegalArgumentException {
+        public Builder addMix(@NonNull AudioMix mix) throws IllegalArgumentException {
             if (mix == null) {
                 throw new IllegalArgumentException("Illegal null AudioMix argument");
             }
@@ -112,18 +122,41 @@ public class AudioPolicy {
             return this;
         }
 
+        /**
+         * Sets the {@link Looper} on which to run the event loop.
+         * @param looper a non-null specific Looper.
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        public Builder setLooper(@NonNull Looper looper) throws IllegalArgumentException {
+            if (looper == null) {
+                throw new IllegalArgumentException("Illegal null Looper argument");
+            }
+            mLooper = looper;
+            return this;
+        }
+
         public AudioPolicy build() {
-            return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext);
+            return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper);
         }
     }
 
-    /** @hide */
     public void setRegistration(String regId) {
         mRegistrationId = regId;
         mConfig.setRegistration(regId);
+        if (regId != null) {
+            mStatus = POLICY_STATUS_REGISTERED;
+        } else {
+            mStatus = POLICY_STATUS_UNREGISTERED;
+        }
+        sendMsg(mEventHandler, MSG_POLICY_STATUS_CHANGE);
     }
 
     private boolean policyReadyToUse() {
+        if (mStatus != POLICY_STATUS_REGISTERED) {
+            Log.e(TAG, "Cannot use unregistered AudioPolicy");
+            return false;
+        }
         if (mContext == null) {
             Log.e(TAG, "Cannot use AudioPolicy without context");
             return false;
@@ -155,11 +188,17 @@ public class AudioPolicy {
         {
             throw new IllegalArgumentException("Invalid AudioMix: not defined for loop back");
         }
-        // TODO also check mix is defined for playback or recording, and matches forTrack argument
+        if (forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_RECORDERS)) {
+            throw new IllegalArgumentException(
+                    "Invalid AudioMix: not defined for being a recording source");
+        }
+        if (!forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_PLAYERS)) {
+            throw new IllegalArgumentException(
+                    "Invalid AudioMix: not defined for capturing playback");
+        }
     }
 
     /**
-     * @hide
      * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}.
      * Audio buffers recorded through the created instance will contain the mix of the audio
      * streams that fed the given mixer.
@@ -170,6 +209,7 @@ public class AudioPolicy {
      *     with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
      * @throws IllegalArgumentException
      */
+    @SystemApi
     public AudioRecord createAudioRecordSink(AudioMix mix) throws IllegalArgumentException {
         if (!policyReadyToUse()) {
             Log.e(TAG, "Cannot create AudioRecord sink for AudioMix");
@@ -186,7 +226,7 @@ public class AudioPolicy {
         AudioRecord ar = new AudioRecord(
                 new AudioAttributes.Builder()
                         .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX)
-                        .addTag(mix.getRegistration())
+                        .addTag(addressForTag(mix))
                         .build(),
                 mixFormat,
                 AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(),
@@ -198,17 +238,17 @@ public class AudioPolicy {
     }
 
     /**
-     * @hide
      * Create an {@link AudioTrack} instance that is associated with the given {@link AudioMix}.
      * Audio buffers played through the created instance will be sent to the given mix
      * to be recorded through the recording APIs.
      * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with
      *     {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy.
-     * @returna new {@link AudioTrack} instance whose data format is the one defined in the
+     * @return a new {@link AudioTrack} instance whose data format is the one defined in the
      *     {@link AudioMix}, or null if this policy was not successfully registered
      *     with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
      * @throws IllegalArgumentException
      */
+    @SystemApi
     public AudioTrack createAudioTrackSource(AudioMix mix) throws IllegalArgumentException {
         if (!policyReadyToUse()) {
             Log.e(TAG, "Cannot create AudioTrack source for AudioMix");
@@ -219,7 +259,7 @@ public class AudioPolicy {
         AudioTrack at = new AudioTrack(
                 new AudioAttributes.Builder()
                         .setUsage(AudioAttributes.USAGE_VIRTUAL_SOURCE)
-                        .addTag(mix.getRegistration())
+                        .addTag(addressForTag(mix))
                         .build(),
                 mix.getFormat(),
                 AudioTrack.getMinBufferSize(mix.getFormat().getSampleRate(),
@@ -230,20 +270,63 @@ public class AudioPolicy {
         return at;
     }
 
+    @SystemApi
     public int getStatus() {
         return mStatus;
     }
 
+    @SystemApi
     public static abstract class AudioPolicyStatusListener {
-        void onStatusChange() {}
-        void onMixStateUpdate(AudioMix mix) {}
+        public void onStatusChange() {}
+        public void onMixStateUpdate(AudioMix mix) {}
     }
 
-    void setStatusListener(AudioPolicyStatusListener l) {
+    @SystemApi
+    synchronized public void setAudioPolicyStatusListener(AudioPolicyStatusListener l) {
         mStatusListener = l;
     }
 
-    /** @hide */
+    synchronized private void onPolicyStatusChange() {
+        if (mStatusListener == null) {
+            return;
+        }
+        mStatusListener.onStatusChange();
+    }
+
+    //==================================================
+    // Event handling
+    private final EventHandler mEventHandler;
+    private final static int MSG_POLICY_STATUS_CHANGE = 0;
+
+    private class EventHandler extends Handler {
+        public EventHandler(AudioPolicy ap, Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_POLICY_STATUS_CHANGE:
+                    onPolicyStatusChange();
+                    break;
+                default:
+                    Log.e(TAG, "Unknown event " + msg.what);
+            }
+        }
+    }
+
+    //==========================================================
+    // Utils
+    private static String addressForTag(AudioMix mix) {
+        return "addr=" + mix.getRegistration();
+    }
+
+    private static void sendMsg(Handler handler, int msg) {
+        if (handler != null) {
+            handler.sendEmptyMessage(msg);
+        }
+    }
+
     public String toLogFriendlyString() {
         String textDump = new String("android.media.audiopolicy.AudioPolicy:\n");
         textDump += "config=" + mConfig.toLogFriendlyString();
@@ -252,7 +335,6 @@ public class AudioPolicy {
 
     /** @hide */
     @IntDef({
-        POLICY_STATUS_INVALID,
         POLICY_STATUS_REGISTERED,
         POLICY_STATUS_UNREGISTERED
     })
index a9a4175..e2a20da 100644 (file)
@@ -27,6 +27,7 @@ import android.os.Parcelable;
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Objects;
 
 /**
  * @hide
@@ -38,7 +39,7 @@ public class AudioPolicyConfig implements Parcelable {
 
     protected ArrayList<AudioMix> mMixes;
 
-    protected String mRegistrationId = null;
+    private String mRegistrationId = null;
 
     protected AudioPolicyConfig(AudioPolicyConfig conf) {
         mMixes = conf.mMixes;
@@ -62,6 +63,11 @@ public class AudioPolicyConfig implements Parcelable {
     }
 
     @Override
+    public int hashCode() {
+        return Objects.hash(mMixes);
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
@@ -80,8 +86,7 @@ public class AudioPolicyConfig implements Parcelable {
             final ArrayList<AttributeMatchCriterion> criteria = mix.getRule().getCriteria();
             dest.writeInt(criteria.size());
             for (AttributeMatchCriterion criterion : criteria) {
-                dest.writeInt(criterion.mRule);
-                dest.writeInt(criterion.mAttr.getUsage());
+                criterion.writeToParcel(dest);
             }
         }
     }
@@ -106,17 +111,7 @@ public class AudioPolicyConfig implements Parcelable {
             AudioMixingRule.Builder ruleBuilder = new AudioMixingRule.Builder();
             for (int j = 0 ; j < nbRules ; j++) {
                 // read the matching rules
-                int matchRule = in.readInt();
-                if ((matchRule == AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_USAGE)
-                    || (matchRule == AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE)) {
-                    int usage = in.readInt();
-                    final AudioAttributes attr = new AudioAttributes.Builder()
-                            .setUsage(usage).build();
-                    ruleBuilder.addRule(attr, matchRule);
-                } else {
-                    Log.w(TAG, "Encountered unsupported rule, skipping");
-                    in.readInt();
-                }
+                ruleBuilder.addRuleFromParcel(in);
             }
             mixBuilder.setMixingRule(ruleBuilder.build());
             mMixes.add(mixBuilder.build());
@@ -140,7 +135,7 @@ public class AudioPolicyConfig implements Parcelable {
 
     public String toLogFriendlyString () {
         String textDump = new String("android.media.audiopolicy.AudioPolicyConfig:\n");
-        textDump += mMixes.size() + " AudioMix:\n";
+        textDump += mMixes.size() + " AudioMix: "+ mRegistrationId + "\n";
         for(AudioMix mix : mMixes) {
             // write mix route flags
             textDump += "* route flags=0x" + Integer.toHexString(mix.getRouteFlags()) + "\n";
@@ -161,6 +156,14 @@ public class AudioPolicyConfig implements Parcelable {
                         textDump += "  match usage ";
                         textDump += criterion.mAttr.usageToString();
                         break;
+                    case AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET:
+                        textDump += "  exclude capture preset ";
+                        textDump += criterion.mAttr.getCapturePreset();
+                        break;
+                    case AudioMixingRule.RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+                        textDump += "  match capture preset ";
+                        textDump += criterion.mAttr.getCapturePreset();
+                        break;
                     default:
                         textDump += "invalid rule!";
                 }
@@ -170,12 +173,32 @@ public class AudioPolicyConfig implements Parcelable {
         return textDump;
     }
 
-    public void setRegistration(String regId) {
-        mRegistrationId = regId;
+    protected void setRegistration(String regId) {
+        final boolean currentRegNull = (mRegistrationId == null) || mRegistrationId.isEmpty();
+        final boolean newRegNull = (regId == null) || regId.isEmpty();
+        if (!currentRegNull && !newRegNull && !mRegistrationId.equals(regId)) {
+            Log.e(TAG, "Invalid registration transition from " + mRegistrationId + " to " + regId);
+            return;
+        }
+        mRegistrationId = regId == null ? "" : regId;
         int mixIndex = 0;
         for (AudioMix mix : mMixes) {
-            mix.setRegistration(mRegistrationId + "mix:" + mixIndex++);
+            if (!mRegistrationId.isEmpty()) {
+                mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
+                        + mixIndex++);
+            } else {
+                mix.setRegistration("");
+            }
         }
     }
 
+    private static String mixTypeId(int type) {
+        if (type == AudioMix.MIX_TYPE_PLAYERS) return "p";
+        else if (type == AudioMix.MIX_TYPE_RECORDERS) return "r";
+        else return "i";
+    }
+
+    protected String getRegistration() {
+        return mRegistrationId;
+    }
 }
index 2ca9b9a..a6bde1d 100644 (file)
@@ -76,6 +76,9 @@ public final class MediaProjection {
         if (callback == null) {
             throw new IllegalArgumentException("callback should not be null");
         }
+        if (handler == null) {
+            handler = new Handler();
+        }
         mCallbacks.put(callback, new CallbackRecord(callback, handler));
     }
 
@@ -182,16 +185,15 @@ public final class MediaProjection {
     private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub {
         @Override
         public void onStop() {
-            final int N = mCallbacks.size();
-            for (int i = 0; i < N; i++) {
-                mCallbacks.get(i).onStop();
+            for (CallbackRecord cbr : mCallbacks.values()) {
+                cbr.onStop();
             }
         }
     }
 
     private final static class CallbackRecord {
-        private Callback mCallback;
-        private Handler mHandler;
+        private final Callback mCallback;
+        private final Handler mHandler;
 
         public CallbackRecord(Callback callback, Handler handler) {
             mCallback = callback;
index 6ca794e..21549c9 100644 (file)
@@ -38,6 +38,7 @@ import android.view.Surface;
 interface ITvInputManager {
     List<TvInputInfo> getTvInputList(int userId);
     TvInputInfo getTvInputInfo(in String inputId, int userId);
+    int getTvInputState(in String inputId, int userId);
 
     List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId);
 
index de9d54f..f55299e 100644 (file)
@@ -72,6 +72,17 @@ public final class TvInputManager {
     public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = VIDEO_UNAVAILABLE_REASON_END;
 
     /**
+     * The TV input is in unknown state.
+     * <p>
+     * State for denoting unknown TV input state. The typical use case is when a requested TV
+     * input is removed from the device or it is not registered. Used in
+     * {@code ITvInputManager.getTvInputState()}.
+     * </p>
+     * @hide
+     */
+    public static final int INPUT_STATE_UNKNOWN = -1;
+
+    /**
      * The TV input is connected.
      * <p>
      * State for {@link #getInputState} and {@link
@@ -127,10 +138,10 @@ public final class TvInputManager {
      * <receiver android:name=".TvInputReceiver">
      *     <intent-filter>
      *         <action android:name=
-     *                 "android.media.tv.TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS" />
+     *                 "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS" />
      *     </intent-filter>
      *     <meta-data
-     *             android:name="android.media.tv.TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS"
+     *             android:name="android.media.tv.metadata.CONTENT_RATING_SYSTEMS"
      *             android:resource="@xml/tv_content_rating_systems" />
      * </receiver>}</pre></p>
      * In the above example, the <code>@xml/tv_content_rating_systems</code> resource refers to an
@@ -751,9 +762,19 @@ public final class TvInputManager {
         try {
             if (mService != null) {
                 mService.registerCallback(mManagerCallback, mUserId);
+                List<TvInputInfo> infos = mService.getTvInputList(mUserId);
+                synchronized (mLock) {
+                    for (TvInputInfo info : infos) {
+                        String inputId = info.getId();
+                        int state = mService.getTvInputState(inputId, mUserId);
+                        if (state != INPUT_STATE_UNKNOWN) {
+                            mStateMap.put(inputId, state);
+                        }
+                    }
+                }
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "mService.registerCallback failed: " + e);
+            Log.e(TAG, "TvInputManager initialization failed: " + e);
         }
     }
 
index 93cca2f..b19a1fb 100644 (file)
@@ -1210,6 +1210,7 @@ public abstract class TvInputService extends Service {
                 }
                 mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, args)
                         .sendToTarget();
+                session.tune(TvContract.buildChannelUriForPassthroughInput(getHardwareInputId()));
             }
 
             @Override
index 01f7cf7..ac0836d 100644 (file)
     <string name="allow_restore_button_label" msgid="3081286752277127827">"Restaurar mis datos"</string>
     <string name="deny_restore_button_label" msgid="1724367334453104378">"No restaurar"</string>
     <string name="current_password_text" msgid="8268189555578298067">"Introduce a continuación la contraseña actual de copia de seguridad:"</string>
-    <string name="device_encryption_restore_text" msgid="1570864916855208992">"Introduce a continuación la contraseña de encriptación del dispositivo."</string>
-    <string name="device_encryption_backup_text" msgid="5866590762672844664">"Introduce a continuación la contraseña de encriptación del dispositivo. Esta contraseña se usará también para encriptar el archivo de copia de seguridad."</string>
+    <string name="device_encryption_restore_text" msgid="1570864916855208992">"Introduce a continuación la contraseña de cifrado del dispositivo."</string>
+    <string name="device_encryption_backup_text" msgid="5866590762672844664">"Introduce a continuación la contraseña de cifrado del dispositivo. Esta contraseña se usará también para cifrar el archivo de copia de seguridad."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"Introduce la contraseña que quieras usar para cifrar los datos de la copia de seguridad completa. Si dejas este campo en blanco, se usará tu contraseña de copia de seguridad actual:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"Si quieres cifrar los datos de la copia de seguridad completa, introduce la contraseña a continuación:"</string>
-    <string name="backup_enc_password_required" msgid="7889652203371654149">"Tu dispositivo está encriptado, por lo que debes encriptar tu copia de seguridad. Introduce una contraseña a continuación:"</string>
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"Tu dispositivo está cifrado, por lo que debes cifrar tu copia de seguridad. Introduce una contraseña a continuación:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"Si los datos de restauración están cifrados, introduce la contraseña a continuación:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Iniciando copia de seguridad..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Copia de seguridad finalizada"</string>
index 71a319f..2578e8f 100644 (file)
     <string name="backup_confirm_text" msgid="1878021282758896593">"कनेक्‍ट कि‍ए गए डेस्‍कटॉप कंप्‍यूटर से सभी डेटा के संपूर्ण सुरक्षा का अनुरोध कि‍या गया है. क्‍या आप इसकी अनुमति‍ देना चाहते हैं?\n\nयदि‍ आपने स्‍वयं बैकअप का अनुरोध नहीं कि‍या है, तो प्रक्रि‍या जारी रखने की अनुमति‍ न दें."</string>
     <string name="allow_backup_button_label" msgid="4217228747769644068">"मेरे डेटा का बैकअप लें"</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"बैकअप न लें"</string>
-    <string name="restore_confirm_text" msgid="7499866728030461776">"à¤\95नà¥\87à¤\95à¥\8dâ\80\8dà¤\9f à¤\95िâ\80\8dà¤\8f à¤\97à¤\8f à¤¡à¥\87सà¥\8dâ\80\8dà¤\95à¤\9fà¥\89प à¤\95à¤\82पà¥\8dâ\80\8dयà¥\82à¤\9fर à¤¸à¥\87 à¤¸à¤­à¥\80 à¤¡à¥\87à¤\9fा à¤\95à¥\80 à¤ªà¥\82रà¥\8dण à¤ªà¥\81नरà¥\8dसà¥\8dथापना à¤\95ा à¤\85नà¥\81रà¥\8bध à¤\95िâ\80\8dया à¤\97या à¤¹à¥\88. à¤\95à¥\8dâ\80\8dया à¤\86प à¤\87सà¤\95à¥\80 à¤\85नà¥\81मतिâ\80\8d à¤¦à¥\87ना à¤\9aाहतà¥\87 à¤¹à¥\88à¤\82?\n\nयदिâ\80\8d à¤\86पनà¥\87 à¤¸à¥\8dâ\80\8dवयà¤\82 à¤ªà¥\81नरà¥\8dपà¥\8dरापà¥\8dतिâ\80\8d à¤\95ा à¤\85नà¥\81रà¥\8bध à¤¨à¤¹à¥\80à¤\82 à¤\95िâ\80\8dया à¤¹à¥\88, à¤¤à¥\8b à¤ªà¥\8dरà¤\95à¥\8dरिâ\80\8dया à¤\9cारà¥\80 à¤°à¤\96नà¥\87 à¤\95à¥\80 à¤\85नà¥\81मतिâ\80\8d à¤¨ à¤¦à¥\87à¤\82. à¤\87ससà¥\87 à¤µà¤°à¥\8dतमान à¤®à¥\87à¤\82 à¤\86पà¤\95à¥\87 à¤\89पà¤\95रण पर मौजूद डेटा बदल जाएगा!"</string>
+    <string name="restore_confirm_text" msgid="7499866728030461776">"à¤\95नà¥\87à¤\95à¥\8dâ\80\8dà¤\9f à¤\95िâ\80\8dà¤\8f à¤\97à¤\8f à¤¡à¥\87सà¥\8dâ\80\8dà¤\95à¤\9fà¥\89प à¤\95à¤\82पà¥\8dâ\80\8dयà¥\82à¤\9fर à¤¸à¥\87 à¤¸à¤­à¥\80 à¤¡à¥\87à¤\9fा à¤\95à¥\80 à¤ªà¥\82रà¥\8dण à¤ªà¥\81नरà¥\8dसà¥\8dथापना à¤\95ा à¤\85नà¥\81रà¥\8bध à¤\95िâ\80\8dया à¤\97या à¤¹à¥\88. à¤\95à¥\8dâ\80\8dया à¤\86प à¤\87सà¤\95à¥\80 à¤\85नà¥\81मतिâ\80\8d à¤¦à¥\87ना à¤\9aाहतà¥\87 à¤¹à¥\88à¤\82?\n\nयदिâ\80\8d à¤\86पनà¥\87 à¤¸à¥\8dâ\80\8dवयà¤\82 à¤ªà¥\81नरà¥\8dपà¥\8dरापà¥\8dतिâ\80\8d à¤\95ा à¤\85नà¥\81रà¥\8bध à¤¨à¤¹à¥\80à¤\82 à¤\95िâ\80\8dया à¤¹à¥\88, à¤¤à¥\8b à¤ªà¥\8dरà¤\95à¥\8dरिâ\80\8dया à¤\9cारà¥\80 à¤°à¤\96नà¥\87 à¤\95à¥\80 à¤\85नà¥\81मतिâ\80\8d à¤¨ à¤¦à¥\87à¤\82. à¤\87ससà¥\87 à¤µà¤°à¥\8dतमान à¤®à¥\87à¤\82 à¤\86पà¤\95à¥\87 à¤¡à¤¿à¤µà¤¾à¤\87स पर मौजूद डेटा बदल जाएगा!"</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"मेरा डेटा पुनर्स्थापित करें"</string>
     <string name="deny_restore_button_label" msgid="1724367334453104378">"पुनर्स्‍थापित न करें"</string>
     <string name="current_password_text" msgid="8268189555578298067">"कृपया नीचे अपना वर्तमान सुरक्षित करने का पासवर्ड डालें:"</string>
-    <string name="device_encryption_restore_text" msgid="1570864916855208992">"à¤\95à¥\83पया à¤¨à¥\80à¤\9aà¥\87 à¤\85पना à¤\89पà¤\95रण एन्‍क्रिप्शन पासवर्ड डालें."</string>
-    <string name="device_encryption_backup_text" msgid="5866590762672844664">"à¤\95à¥\83पया à¤\85पना à¤\89पà¤\95रण à¤¸à¥\81रà¤\95à¥\8dषित à¤¤à¤°à¥\80à¤\95ा à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤¨à¥\80à¤\9aà¥\87 à¤¦à¤°à¥\8dà¤\9c à¤\95रà¥\87à¤\82. à¤¬à¥\88à¤\95à¤\85प à¤¸à¤\82à¤\97à¥\8dरहण को एन्‍क्रिप्‍ट करने के लिए भी इसका उपयोग किया जाएगा."</string>
+    <string name="device_encryption_restore_text" msgid="1570864916855208992">"à¤\95à¥\83पया à¤¨à¥\80à¤\9aà¥\87 à¤\85पना à¤¡à¤¿à¤µà¤¾à¤\87स एन्‍क्रिप्शन पासवर्ड डालें."</string>
+    <string name="device_encryption_backup_text" msgid="5866590762672844664">"à¤\95à¥\83पया à¤\85पना à¤¡à¤¿à¤µà¤¾à¤\87स à¤¸à¥\81रà¤\95à¥\8dषित à¤¤à¤°à¥\80à¤\95ा à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤¨à¥\80à¤\9aà¥\87 à¤¦à¤°à¥\8dà¤\9c à¤\95रà¥\87à¤\82. à¤¬à¥\88à¤\95à¤\85प à¤®à¥\87मà¥\8bरà¥\80 को एन्‍क्रिप्‍ट करने के लिए भी इसका उपयोग किया जाएगा."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"कृपया संपूर्ण सुरक्षित डेटा को एन्‍क्रि‍प्‍ट करने में उपयोग के लि‍ए पासवर्ड डालें. यदि‍ यह खाली छोड़ दि‍या जाता है, तो आपके वर्तमान बैकअप पासवर्ड का उपयोग कि‍या जाएगा:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"यदि‍ आप संपूर्ण सुरक्षित डेटा को एन्‍क्रि‍प्‍ट करना चाहते हैं, तो नीचे पासवर्ड डालें:"</string>
-    <string name="backup_enc_password_required" msgid="7889652203371654149">"à¤\9aà¥\82à¤\82à¤\95ि à¤\86पà¤\95ा à¤\89पà¤\95रण एन्क्रिप्ट किया हुआ है, इसलिए आपको अपने बैकअप को एन्क्रिप्ट करना आवश्यक है. कृपया नीचे पासवर्ड डालें:"</string>
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"à¤\9aà¥\82à¤\82à¤\95ि à¤\86पà¤\95ा à¤¡à¤¿à¤µà¤¾à¤\87स एन्क्रिप्ट किया हुआ है, इसलिए आपको अपने बैकअप को एन्क्रिप्ट करना आवश्यक है. कृपया नीचे पासवर्ड डालें:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"यदि‍ पुनर्स्थापित डेटा को एन्‍क्रि‍प्‍ट कि‍या गया है, तो कृपया नीचे पासवर्ड डालें:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"सुरक्षित करना शुरु हो रहा है..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"सुरक्षित करना पूर्ण"</string>
index 6a36b9e..38d7172 100644 (file)
@@ -29,8 +29,7 @@
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"Құрылғыңыздың кодтық кілтсөзін енгізіңіз. Ол сақтық көшірме мұрағатын кодтау үшін де қолданылады."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"Деректердің сақтық көшірмесін толығымен шифрлау үшін кілтсөзді енгізіңіз. Егер бұл бос қалдырылса, сіздің қазіргі сақтық көшірме кілтсөзіңіз қолданылады:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"Деректердің сақтық көшірмесін толығымен шифрлауды қаласаңыз, кілтсөзді енгізіңіз:"</string>
-    <!-- no translation found for backup_enc_password_required (7889652203371654149) -->
-    <skip />
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"Құрылғыңыз шифрланғандықтан, сақтық көшірмені шифрлау қажет. Төменде құпия сөзді енгізіңіз:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"Қалпына келтіру деректері кодталса, кілтсөзді енгізіңіз:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Сақтық көшірме басталуда..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Қалпына келтіру аяқталды"</string>
index 3c7b6e7..6333b18 100644 (file)
@@ -29,8 +29,7 @@
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"Түзмөгүңүздүн шифрлөө сырсөзүн төмөндө киргизиңиз. Ал бэкап архивин шифрлегенге дагы колдонулат."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"Эгер сиз толук бэкапты шифрлегиңиз келсе, төмөндө сырсөз киргизиңиз. Эгер ал бош калтырылса, анда учурдагы сырсөз колдонулат:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"Эгер сиз толук бэкапты шифрлегиңиз келсе, төмөндө сырсөз киргизиңиз:"</string>
-    <!-- no translation found for backup_enc_password_required (7889652203371654149) -->
-    <skip />
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"Түзмөгүңүз шифрленген болгондуктан, камдооңузду шифрлешиңиз керек. Төмөнгө сырсөз киргизиңиз:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"Эгер калыбына келтирүү берилиштери шифрленген болсо, төмөндө сырсөздү киргизиңиз:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Бэкап башталды..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Бэкап аяктады"</string>
index 993311d..473802e 100644 (file)
@@ -29,8 +29,7 @@
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"कृपया तल तपाईंको उपकरण एन्क्रिप्सन पासवर्ड प्रविष्टि गर्नुहोस्: यो ब्याकप सँग्रह एन्क्रिप्ट गर्न पनि प्रयोग हुने छ।"</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"ब्याकप डेटालाई encrypt गर्न पासवर्ड प्रविष्टि गर्नुहोस्, यदि यो खालि छोडिएको खण्डमा तपाईको पुरानै पासवर्ड प्रयोग हुने छ।"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"यदि तपाईं पूर्ण ब्याकअप डेटा इन्क्रिप्ट गर्न चाहनु हुन्छ भने तल पासवर्ड प्रविष्टि गर्नुहोस्।"</string>
-    <!-- no translation found for backup_enc_password_required (7889652203371654149) -->
-    <skip />
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"तपाईँको उपकरण गुप्तिकरण गरिए देखि, तपाईंले आफ्नो जगेडा गुप्तिकरण गर्न आवश्यक छ। कृपया तल पासवर्ड प्रविष्ट गर्नुहोस्:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"यदि पुनःबहाली डेटा इन्क्रिप्ट छ भने कृपया तल पासवर्ड प्रविष्टि गर्नुहोस्:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"जगेडा राख्न सुरु हुँदै..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"ब्याकअप सकियो"</string>
index 8d1ede4..aaeaf04 100644 (file)
@@ -29,8 +29,7 @@
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"කරුණාකර ඔබගේ උපාංගයේ සංකේතන මුරපදය පහත ඇතුලත් කරන්න. සංරක්ෂිත උපස්ථ සංකේතනය කිරීමට මෙය භාවිත කළ හැක."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"කරුණාකර සියලු උපස්ථ දත්ත සංකේතනය කිරීම සඳහා භාවිතයට මුරපදයක් ඇතුළත් කරන්න. මෙය හිස්ව තැබුවොත්, ඔබගේ වර්තමාන උපස්ථ මුරපදය භාවිත වෙයි:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"සියලු උපස්ථ දත්ත සංකේතනය කිරීමට ඔබ අදහස් කරන්නේ නම්, මුරපදය පහලින් ඇතුලත් කරන්න:"</string>
-    <!-- no translation found for backup_enc_password_required (7889652203371654149) -->
-    <skip />
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"ඔබගේ උපාංගය සංකේතනය කර තිබෙන නිසා, ඔබගේ උපස්ථය සංකේතනය ඔබට අවශ්‍ය වී තිබේ. කරුණාකර මුරපදය පහළින් එකතු කරන්න:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"යළි පිහිටුවන දත්ත සංකේතනය කරන ලද ඒවානම්, කරුණාකර මුරපදය පහලින් ඇතුල් කරන්න:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"උපස්ථ කිරීම ආරම්භ කරමින්..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"උපස්ථය අවසන්"</string>
index 416b693..6f1c9b5 100644 (file)
@@ -29,8 +29,7 @@
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"براہ کرم ذیل میں اپنے آلہ کی مرموز کاری کا پاس ورڈ درج کریں۔ یہ بیک اپ آرکائیو کی مرموز کاری کرنے کیلئے بھی استعمال کیا جائے گا۔"</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"مکمل بیک اپ ڈیٹا کی مرموز کاری کرنے کیلئے استعمال کیلئے براہ کرم ایک پاس ورڈ درج کریں۔ اگر یہ خالی رہتا ہے تو آپ کا موجودہ بیک اپ پاس ورڈ استعمال کیا جائے گا:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"اگر آپ مکمل بیک اپ ڈیٹا کی مرموز کاری کرنا چاہتے ہیں تو ذیل میں ایک پاس ورڈ درج کریں:"</string>
-    <!-- no translation found for backup_enc_password_required (7889652203371654149) -->
-    <skip />
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"چونکہ آپ کا آلہ مرموز کردہ ہے، آپ کو اپنے بیک اپ کی مرموز کاری کرنے کی ضرورت ہے۔ براہ کرم ذیل میں ایک پاس ورڈ درج کریں:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"اگر بحال ہونے والا ڈیٹا مرموز کردہ ہے تو براہ کرم ذیل میں پاس ورڈ درج کریں:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"بیک اپ شروع ہو رہا ہے…"</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"بیک اپ مکمل ہو گیا"</string>
index 032f884..1b5741f 100644 (file)
@@ -29,8 +29,7 @@
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"Qurilmangizning shifr parolini kiriting. U zahira arxivni shifrlash uchun ham ishlatiladi."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"To‘liq zahira fayllarini shifrlash uchun parol kiriting. Agar bo‘sh qoldirsangiz, joriy zahiralash parolingizdan foydalaniladi:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"To‘liq zahira ma’lumotlarini shifrlashni xohlasangiz, quyidagi parolni kiriting:"</string>
-    <!-- no translation found for backup_enc_password_required (7889652203371654149) -->
-    <skip />
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"Qurilmangiz shifrlangani bois ma’lumotlaringizning zaxira nusxasini ham shifrlash zarur. Shifrlash uchun parolni kiriting:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"Agar tiklash ma’lumoti shifrlangan bo‘lsa, pastga parolni kiriting:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Zahiralash jarayoni boshlandi..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Zahiralash jarayoni bajarildi"</string>
index c4dc652..88f26ed 100644 (file)
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="2783841764617238354">"दस्तावेज़"</string>
     <string name="title_open" msgid="4353228937663917801">"यहां से खोलें"</string>
-    <string name="title_save" msgid="2433679664882857999">"यहाà¤\82 à¤¸à¤¹à¥\87à¤\9cें"</string>
+    <string name="title_save" msgid="2433679664882857999">"यहाà¤\82 à¤\9cà¥\8bड़ें"</string>
     <string name="menu_create_dir" msgid="5947289605844398389">"फ़ोल्डर बनाएं"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string>
     <string name="menu_list" msgid="7279285939892417279">"सूची दृश्य"</string>
     <string name="menu_search" msgid="3816712084502856974">"खोजें"</string>
     <string name="menu_settings" msgid="6008033148948428823">"सेटिंग"</string>
     <string name="menu_open" msgid="432922957274920903">"खोलें"</string>
-    <string name="menu_save" msgid="2394743337684426338">"सहà¥\87à¤\9cें"</string>
+    <string name="menu_save" msgid="2394743337684426338">"à¤\9cà¥\8bड़ें"</string>
     <string name="menu_share" msgid="3075149983979628146">"साझा करें"</string>
     <string name="menu_delete" msgid="8138799623850614177">"हटाएं"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" चुनें"</string>
-    <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"à¤\86à¤\82तरिà¤\95 à¤¸à¤\82à¤\97à¥\8dरहण दिखाएं"</string>
+    <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"à¤\86à¤\82तरिà¤\95 à¤®à¥\87मà¥\8bरà¥\80 दिखाएं"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD कार्ड दिखाएं"</string>
-    <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"à¤\86à¤\82तरिà¤\95 à¤¸à¤\82à¤\97à¥\8dरहण छिपाएं"</string>
+    <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"à¤\86à¤\82तरिà¤\95 à¤®à¥\87मà¥\8bरà¥\80 छिपाएं"</string>
     <string name="menu_advanced_hide" product="default" msgid="4845869969015718848">"SD कार्ड छिपाएं"</string>
     <string name="menu_file_size_show" msgid="3240323619260823076">"फ़ाइल आकार दिखाएं"</string>
     <string name="menu_file_size_hide" msgid="8881975928502581042">"फ़ाइल आकार छिपाएं"</string>
     <string name="create_error" msgid="3735649141335444215">"फ़ोल्डर बनाने में विफल"</string>
     <string name="query_error" msgid="1222448261663503501">"दस्तावेजों के लिए क्वेरी करने में विफल रहा"</string>
     <string name="root_recent" msgid="4470053704320518133">"हाल ही के"</string>
-    <string name="root_available_bytes" msgid="8568452858617033281">"<xliff:g id="SIZE">%1$s</xliff:g> à¤°à¤¿à¤\95à¥\8dत"</string>
-    <string name="root_type_service" msgid="2178854894416775409">"सà¤\82à¤\97à¥\8dरहण सेवाएं"</string>
+    <string name="root_available_bytes" msgid="8568452858617033281">"<xliff:g id="SIZE">%1$s</xliff:g> à¤\96ालà¥\80"</string>
+    <string name="root_type_service" msgid="2178854894416775409">"मà¥\87मà¥\8bरà¥\80 सेवाएं"</string>
     <string name="root_type_shortcut" msgid="3318760609471618093">"शॉर्टकट"</string>
-    <string name="root_type_device" msgid="7121342474653483538">"à¤\89पà¤\95रण"</string>
+    <string name="root_type_device" msgid="7121342474653483538">"डिवाà¤\87स"</string>
     <string name="root_type_apps" msgid="8838065367985945189">"अधिक ऐप्स"</string>
     <string name="empty" msgid="7858882803708117596">"कोई आइटम नहीं"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"फ़ाइल नहीं खोली जा सकती"</string>
index 5513746..a4f718b 100644 (file)
     <string name="menu_share" msgid="3075149983979628146">"Бөлүшүү"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Өчүрүү"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" тандоо"</string>
-    <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ð\98Ñ\87ки Ñ\8dÑ\81Ñ\82Ñ\83Ñ\82Ñ\83мдÑ\83 көрсөтүү"</string>
+    <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ð\98Ñ\87ки Ñ\81акÑ\82агÑ\8bÑ\87Ñ\82Ñ\8b көрсөтүү"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD картаны көрсөтүү"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ички эстутумду жашыруу"</string>
-    <string name="menu_advanced_hide" product="default" msgid="4845869969015718848">"SD ÐºÐ°Ñ\82Ñ\80аны жашыруу"</string>
+    <string name="menu_advanced_hide" product="default" msgid="4845869969015718848">"SD ÐºÐ°Ñ\80Ñ\82аны жашыруу"</string>
     <string name="menu_file_size_show" msgid="3240323619260823076">"Файл өлчөмүн көрсөтүү"</string>
     <string name="menu_file_size_hide" msgid="8881975928502581042">"Файл өлчөмүн жашыруу"</string>
     <string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> тандалды"</string>
index 98b3a90..50f8363 100644 (file)
@@ -26,7 +26,7 @@
     <string name="menu_search" msgid="3816712084502856974">"ရှာဖွေရန်"</string>
     <string name="menu_settings" msgid="6008033148948428823">"ဆက်တင်များ"</string>
     <string name="menu_open" msgid="432922957274920903">"ဖွင့်ရန်"</string>
-    <string name="menu_save" msgid="2394743337684426338">"á\80\9eá\80­á\80\99á\80ºá\80¸á\80\86á\80\8aá\80ºá\80¸á\80\9bá\80\94á\80º"</string>
+    <string name="menu_save" msgid="2394743337684426338">"á\80\9eá\80­á\80\99á\80ºá\80¸á\80\95á\80«"</string>
     <string name="menu_share" msgid="3075149983979628146">"မျှဝေခြင်း"</string>
     <string name="menu_delete" msgid="8138799623850614177">"ဖျက်ပစ်ရန်"</string>
     <string name="menu_select" msgid="8711270657353563424">"ရွေးရန်\"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
index 184fb53..2aaa4d2 100644 (file)
@@ -50,7 +50,7 @@
     <string name="root_type_service" msgid="2178854894416775409">"Serviços de armazenamento"</string>
     <string name="root_type_shortcut" msgid="3318760609471618093">"Atalhos"</string>
     <string name="root_type_device" msgid="7121342474653483538">"Dispositivos"</string>
-    <string name="root_type_apps" msgid="8838065367985945189">"Mais aplicativos"</string>
+    <string name="root_type_apps" msgid="8838065367985945189">"Mais apps"</string>
     <string name="empty" msgid="7858882803708117596">"Nenhum item"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"Não é possível abrir o arquivo"</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"Não foi possível excluir alguns documentos"</string>
index a37dbe8..464a13e 100644 (file)
@@ -45,7 +45,7 @@
     <string name="save_error" msgid="6167009778003223664">"無法儲存文件"</string>
     <string name="create_error" msgid="3735649141335444215">"無法建立資料夾"</string>
     <string name="query_error" msgid="1222448261663503501">"無法查詢文件"</string>
-    <string name="root_recent" msgid="4470053704320518133">"最近存取過"</string>
+    <string name="root_recent" msgid="4470053704320518133">"最近"</string>
     <string name="root_available_bytes" msgid="8568452858617033281">"可用空間:<xliff:g id="SIZE">%1$s</xliff:g>"</string>
     <string name="root_type_service" msgid="2178854894416775409">"儲存空間服務"</string>
     <string name="root_type_shortcut" msgid="3318760609471618093">"捷徑"</string>
index 1227bd4..8538081 100644 (file)
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="7123375275748530234">"बाहरà¥\80 à¤¸à¤\82à¤\97à¥\8dरहण"</string>
-    <string name="root_internal_storage" msgid="827844243068584127">"मà¥\8bबाà¤\87ल à¤¸à¤\82à¤\97à¥\8dरहण"</string>
+    <string name="app_label" msgid="7123375275748530234">"बाहरà¥\80 à¤®à¥\87मà¥\8bरà¥\80"</string>
+    <string name="root_internal_storage" msgid="827844243068584127">"मà¥\8bबाà¤\87ल à¤®à¥\87मà¥\8bरà¥\80"</string>
     <string name="root_documents" msgid="4051252304075469250">"दस्तावेज़"</string>
 </resources>
index 643fb92..dc9d684 100644 (file)
@@ -17,6 +17,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7123375275748530234">"ပြင်ပသိုလှောင်ရာပစ္စည်း"</string>
-    <string name="root_internal_storage" msgid="827844243068584127">"á\80\85á\80\80á\80ºá\80¡á\80\90á\80½á\80\84á\80ºá\80¸á\80\9eá\80­á\80\99á\80ºá\80¸á\80\86á\80\8aá\80ºá\80¸á\80\9bá\80\94á\80ºá\80\94á\80±á\80\9bá\80¬"</string>
+    <string name="root_internal_storage" msgid="827844243068584127">"á\80\85á\80\80á\80ºá\80\90á\80½á\80\84á\80ºá\80¸ á\80\9eá\80­á\80¯á\80\9cá\80¾á\80±á\80¬á\80\84á\80ºá\80\91á\80¬á\80¸á\80\99á\80¾á\80¯"</string>
     <string name="root_documents" msgid="4051252304075469250">"စာရွက်စာတန်းများ"</string>
 </resources>
index 066acac..073d9c7 100644 (file)
@@ -43,6 +43,7 @@ import android.util.Log;
 import android.webkit.MimeTypeMap;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
@@ -53,6 +54,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
+import java.util.Objects;
 
 public class ExternalStorageProvider extends DocumentsProvider {
     private static final String TAG = "ExternalStorage";
@@ -313,27 +315,19 @@ public class ExternalStorageProvider extends DocumentsProvider {
     @Override
     public String createDocument(String docId, String mimeType, String displayName)
             throws FileNotFoundException {
+        displayName = FileUtils.buildValidFatFilename(displayName);
+
         final File parent = getFileForDocId(docId);
         if (!parent.isDirectory()) {
             throw new IllegalArgumentException("Parent document isn't a directory");
         }
 
-        File file;
+        final File file = buildUniqueFile(parent, mimeType, displayName);
         if (Document.MIME_TYPE_DIR.equals(mimeType)) {
-            file = new File(parent, displayName);
             if (!file.mkdir()) {
                 throw new IllegalStateException("Failed to mkdir " + file);
             }
         } else {
-            displayName = removeExtension(mimeType, displayName);
-            file = new File(parent, addExtension(mimeType, displayName));
-
-            // If conflicting file, try adding counter suffix
-            int n = 0;
-            while (file.exists() && n++ < 32) {
-                file = new File(parent, addExtension(mimeType, displayName + " (" + n + ")"));
-            }
-
             try {
                 if (!file.createNewFile()) {
                     throw new IllegalStateException("Failed to touch " + file);
@@ -342,11 +336,78 @@ public class ExternalStorageProvider extends DocumentsProvider {
                 throw new IllegalStateException("Failed to touch " + file + ": " + e);
             }
         }
+
         return getDocIdForFile(file);
     }
 
+    private static File buildFile(File parent, String name, String ext) {
+        if (TextUtils.isEmpty(ext)) {
+            return new File(parent, name);
+        } else {
+            return new File(parent, name + "." + ext);
+        }
+    }
+
+    @VisibleForTesting
+    public static File buildUniqueFile(File parent, String mimeType, String displayName)
+            throws FileNotFoundException {
+        String name;
+        String ext;
+
+        if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+            name = displayName;
+            ext = null;
+        } else {
+            String mimeTypeFromExt;
+
+            // Extract requested extension from display name
+            final int lastDot = displayName.lastIndexOf('.');
+            if (lastDot >= 0) {
+                name = displayName.substring(0, lastDot);
+                ext = displayName.substring(lastDot + 1);
+                mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+                        ext.toLowerCase());
+            } else {
+                name = displayName;
+                ext = null;
+                mimeTypeFromExt = null;
+            }
+
+            if (mimeTypeFromExt == null) {
+                mimeTypeFromExt = "application/octet-stream";
+            }
+
+            final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
+                    mimeType);
+            if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
+                // Extension maps back to requested MIME type; allow it
+            } else {
+                // No match; insist that create file matches requested MIME
+                name = displayName;
+                ext = extFromMimeType;
+            }
+        }
+
+        File file = buildFile(parent, name, ext);
+
+        // If conflicting file, try adding counter suffix
+        int n = 0;
+        while (file.exists()) {
+            if (n++ >= 32) {
+                throw new FileNotFoundException("Failed to create unique file");
+            }
+            file = buildFile(parent, name + " (" + n + ")", ext);
+        }
+
+        return file;
+    }
+
     @Override
     public String renameDocument(String docId, String displayName) throws FileNotFoundException {
+        // Since this provider treats renames as generating a completely new
+        // docId, we're okay with letting the MIME type change.
+        displayName = FileUtils.buildValidFatFilename(displayName);
+
         final File before = getFileForDocId(docId);
         final File after = new File(before.getParentFile(), displayName);
         if (after.exists()) {
@@ -482,34 +543,6 @@ public class ExternalStorageProvider extends DocumentsProvider {
         return "application/octet-stream";
     }
 
-    /**
-     * Remove file extension from name, but only if exact MIME type mapping
-     * exists. This means we can reapply the extension later.
-     */
-    private static String removeExtension(String mimeType, String name) {
-        final int lastDot = name.lastIndexOf('.');
-        if (lastDot >= 0) {
-            final String extension = name.substring(lastDot + 1).toLowerCase();
-            final String nameMime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
-            if (mimeType.equals(nameMime)) {
-                return name.substring(0, lastDot);
-            }
-        }
-        return name;
-    }
-
-    /**
-     * Add file extension to name, but only if exact MIME type mapping exists.
-     */
-    private static String addExtension(String mimeType, String name) {
-        final String extension = MimeTypeMap.getSingleton()
-                .getExtensionFromMimeType(mimeType);
-        if (extension != null) {
-            return name + "." + extension;
-        }
-        return name;
-    }
-
     private void startObserving(File file, Uri notifyUri) {
         synchronized (mObservers) {
             DirectoryObserver observer = mObservers.get(file);
diff --git a/packages/ExternalStorageProvider/tests/Android.mk b/packages/ExternalStorageProvider/tests/Android.mk
new file mode 100644 (file)
index 0000000..830731a
--- /dev/null
@@ -0,0 +1,16 @@
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := ExternalStorageProviderTests
+LOCAL_INSTRUMENTATION_FOR := ExternalStorageProvider
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/packages/ExternalStorageProvider/tests/AndroidManifest.xml b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..ffcd499
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.externalstorage.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.externalstorage"
+        android:label="Tests for ExternalStorageProvider" />
+
+</manifest>
diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
new file mode 100644 (file)
index 0000000..f980b60
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.externalstorage;
+
+import static com.android.externalstorage.ExternalStorageProvider.buildUniqueFile;
+
+import android.os.FileUtils;
+import android.provider.DocumentsContract.Document;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.io.File;
+
+@MediumTest
+public class ExternalStorageProviderTest extends AndroidTestCase {
+
+    private File mTarget;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTarget = getContext().getFilesDir();
+        FileUtils.deleteContents(mTarget);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        FileUtils.deleteContents(mTarget);
+    }
+
+    public void testBuildUniqueFile_normal() throws Exception {
+        assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test"));
+        assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+        assertNameEquals("test.jpeg", buildUniqueFile(mTarget, "image/jpeg", "test.jpeg"));
+        assertNameEquals("TEst.JPeg", buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg"));
+        assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg"));
+        assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png"));
+
+        assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test"));
+        assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test.flac"));
+        assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test"));
+        assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
+    }
+
+    public void testBuildUniqueFile_unknown() throws Exception {
+        assertNameEquals("test", buildUniqueFile(mTarget, "application/octet-stream", "test"));
+        assertNameEquals("test.jpg", buildUniqueFile(mTarget, "application/octet-stream", "test.jpg"));
+        assertNameEquals(".test", buildUniqueFile(mTarget, "application/octet-stream", ".test"));
+
+        assertNameEquals("test", buildUniqueFile(mTarget, "lolz/lolz", "test"));
+        assertNameEquals("test.lolz", buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
+    }
+
+    public void testBuildUniqueFile_dir() throws Exception {
+        assertNameEquals("test", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+        new File(mTarget, "test").mkdir();
+        assertNameEquals("test (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+
+        assertNameEquals("test.jpg", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+        new File(mTarget, "test.jpg").mkdir();
+        assertNameEquals("test.jpg (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+    }
+
+    public void testBuildUniqueFile_increment() throws Exception {
+        assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+        new File(mTarget, "test.jpg").createNewFile();
+        assertNameEquals("test (1).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+        new File(mTarget, "test (1).jpg").createNewFile();
+        assertNameEquals("test (2).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+    }
+
+    private static void assertNameEquals(String expected, File actual) {
+        assertEquals(expected, actual.getName());
+    }
+}
index 77cb8fe..fcd7f84 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="8016145283189546017">"à¤\87नपà¥\81à¤\9f à¤\89पà¤\95रण"</string>
+    <string name="app_label" msgid="8016145283189546017">"à¤\87नपà¥\81à¤\9f à¤¡à¤¿à¤µà¤¾à¤\87स"</string>
     <string name="keyboard_layouts_label" msgid="6688773268302087545">"Android कीबोर्ड"</string>
     <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"अंग्रेज़ी (यूके)"</string>
     <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"अंग्रेज़ी (यूएस)"</string>
index 3ca1368..82023d2 100644 (file)
     <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has escrit malament el PIN <xliff:g id="NUMBER_0">%d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
     <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has dibuixat el patró de desbloqueig de manera incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, la tauleta es restablirà i se n\'esborraran totes les dades."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, el telèfon es restablirà i se n\'esborraran totes les dades."</string>
-    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. La tauleta es restablirà i se n\'esborraran totes les dades."</string>
-    <string name="kg_failed_attempts_now_wiping" product="default" msgid="7154028908459817066">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. El telèfon es restablirà i se n\'esborraran totes les dades."</string>
-    <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="6159955099372112688">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, l\'usuari se suprimirà, juntament amb totes les seves dades."</string>
-    <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="6945823186629369880">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, l\'usuari se suprimirà, juntament amb totes les seves dades."</string>
-    <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="3963486905355778734">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. L\'usuari se suprimirà, juntament amb totes les seves dades."</string>
-    <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="7729009752252111673">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. L\'usuari se suprimirà, juntament amb totes les seves dades."</string>
-    <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="4621778507387853694">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, el perfil professional se suprimirà, juntament amb totes les dades que contingui."</string>
-    <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, el perfil professional se suprimirà, juntament amb totes les dades que contingui."</string>
-    <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. El perfil professional se suprimirà, juntament amb totes les dades que contingui."</string>
-    <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. El perfil professional se suprimirà, juntament amb totes les dades que contingui."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"Has provat de desbloquejar la tauleta incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, la tauleta es restablirà i se n\'esborraran totes les dades."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"Has provat de desbloquejar el telèfon incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, el telèfon es restablirà i se n\'esborraran totes les dades."</string>
+    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"Has provat de desbloquejar la tauleta incorrectament <xliff:g id="NUMBER">%d</xliff:g> vegades. La tauleta es restablirà i se n\'esborraran totes les dades."</string>
+    <string name="kg_failed_attempts_now_wiping" product="default" msgid="7154028908459817066">"Has provat de desbloquejar el telèfon incorrectament <xliff:g id="NUMBER">%d</xliff:g> vegades. El telèfon es restablirà i se n\'esborraran totes les dades."</string>
+    <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="6159955099372112688">"Has provat de desbloquejar la tauleta incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, l\'usuari se suprimirà, juntament amb totes les seves dades."</string>
+    <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="6945823186629369880">"Has provat de desbloquejar el telèfon incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, l\'usuari se suprimirà, juntament amb totes les seves dades."</string>
+    <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="3963486905355778734">"Has provat de desbloquejar la tauleta incorrectament <xliff:g id="NUMBER">%d</xliff:g> vegades. L\'usuari se suprimirà, juntament amb totes les seves dades."</string>
+    <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="7729009752252111673">"Has provat de desbloquejar el telèfon incorrectament <xliff:g id="NUMBER">%d</xliff:g> vegades. L\'usuari se suprimirà, juntament amb totes les seves dades."</string>
+    <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="4621778507387853694">"Has provat de desbloquejar la tauleta incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, el perfil professional se suprimirà, juntament amb totes les dades que contingui."</string>
+    <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"Has provat de desbloquejar el telèfon incorrectament <xliff:g id="NUMBER_0">%d</xliff:g> vegades. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents més. Si no ho fas bé, el perfil professional se suprimirà, juntament amb totes les dades que contingui."</string>
+    <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"Has provat de desbloquejar la tauleta incorrectament <xliff:g id="NUMBER">%d</xliff:g> vegades. El perfil professional se suprimirà, juntament amb totes les dades que contingui."</string>
+    <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"Has provat de desbloquejar el telèfon incorrectament <xliff:g id="NUMBER">%d</xliff:g> vegades. El perfil professional se suprimirà, juntament amb totes les dades que contingui."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Després de <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, se\'t demanarà que desbloquegis la tauleta amb un compte de correu electrònic.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Després de <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, se\'t demanarà que desbloquegis el telèfon amb un compte de correu electrònic.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
index 190a19e..9051a7b 100644 (file)
     <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您已輸入錯誤的 PIN 碼 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
     <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您已輸入錯誤的密碼 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤解鎖平板電腦。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,平板電腦將被重設,平板電腦的所有資料將因此被刪除。"</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤解鎖手機。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,手機將被重設,手機的所有資料將因此被刪除。"</string>
-    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"您已 <xliff:g id="NUMBER">%d</xliff:g> 次錯誤解鎖手機。平板電腦將被重設,平板電腦的所有資料將因此被刪除。"</string>
-    <string name="kg_failed_attempts_now_wiping" product="default" msgid="7154028908459817066">"您已 <xliff:g id="NUMBER">%d</xliff:g> 次錯誤解鎖手機。手機將被重設,手機的所有資料將因此被刪除。"</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤解鎖平板電腦。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,平板電腦將被重設,平板電腦的所有資料將因此被刪除。"</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤解鎖手機。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,手機將被重設,手機的所有資料將因此被刪除。"</string>
+    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"您已 <xliff:g id="NUMBER">%d</xliff:g> 次錯誤解鎖手機。平板電腦將被重設,平板電腦的所有資料將因此被刪除。"</string>
+    <string name="kg_failed_attempts_now_wiping" product="default" msgid="7154028908459817066">"您已 <xliff:g id="NUMBER">%d</xliff:g> 次錯誤解鎖手機。手機將被重設,手機的所有資料將因此被刪除。"</string>
     <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="6159955099372112688">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤解鎖平板電腦。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,該使用者將被移除,所有使用者資料將因此被刪除。"</string>
     <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="6945823186629369880">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次錯誤解鎖手機。如再解鎖失敗 <xliff:g id="NUMBER_1">%d</xliff:g> 次,該使用者將被移除,所有使用者資料將因此被刪除。"</string>
     <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="3963486905355778734">"您已 <xliff:g id="NUMBER">%d</xliff:g> 次錯誤解鎖平板電腦。該使用者將被移除,所有使用者資料將因此被刪除。"</string>
index b97b5a3..368d789 100644 (file)
        you will be asked to unlock your phone using an email account.\n\n
        Try again in <xliff:g id="number">%d</xliff:g> seconds.
     </string>
-    <!-- Sequence of characters used to separate message strings in keyguard. Typically just em-dash
-         with spaces on either side. [CHAR LIMIT=3] -->
-    <string name="kg_text_message_separator" product="default">" \u2014 "</string>
     <!-- The delete-widget drop target button text -->
     <string name="kg_reordering_delete_drop_target_text">Remove</string>
 
index 55bfe49..7f4ce59 100644 (file)
 
 package com.android.keyguard;
 
+import java.util.List;
+import java.util.Locale;
+
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.text.method.SingleLineTransformationMethod;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
+import android.text.method.SingleLineTransformationMethod;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.widget.TextView;
 
@@ -28,29 +34,19 @@ import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.widget.LockPatternUtils;
 
-import java.util.Locale;
-
 public class CarrierText extends TextView {
+    private static final boolean DEBUG = KeyguardConstants.DEBUG;
+    private static final String TAG = "CarrierText";
+
     private static CharSequence mSeparator;
 
     private LockPatternUtils mLockPatternUtils;
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
-        private CharSequence mPlmn;
-        private CharSequence mSpn;
-        private State mSimState;
-
-        @Override
-        public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
-            mPlmn = plmn;
-            mSpn = spn;
-            updateCarrierText(mSimState, mPlmn, mSpn);
-        }
-
         @Override
-        public void onSimStateChanged(int subId, int slotId, State simState) {
-            mSimState = simState;
-            updateCarrierText(mSimState, mPlmn, mSpn);
+        public void onRefreshCarrierInfo() {
+            updateCarrierText();
         }
 
         public void onScreenTurnedOff(int why) {
@@ -93,14 +89,50 @@ public class CarrierText extends TextView {
         setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps));
     }
 
-    protected void updateCarrierText(State simState, CharSequence plmn, CharSequence spn) {
-        setText(getCarrierTextForSimState(simState, plmn, spn));
+    protected void updateCarrierText() {
+        boolean allSimsMissing = true;
+        CharSequence displayText = null;
+
+        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+        final int N = subs.size();
+        if (DEBUG) Log.d(TAG, "updateCarrierText(): " + N);
+        for (int i = 0; i < N; i++) {
+            State simState = mKeyguardUpdateMonitor.getSimState(subs.get(i).getSubscriptionId());
+            CharSequence carrierName = subs.get(i).getCarrierName();
+            CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
+            if (DEBUG) Log.d(TAG, "Handling " + simState + " " + carrierName);
+            if (carrierTextForSimState != null) {
+                allSimsMissing = false;
+                displayText = concatenate(displayText, carrierTextForSimState);
+            }
+        }
+        if (allSimsMissing) {
+            if (N != 0) {
+                // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
+                // This depends on mPlmn containing the text "Emergency calls only" when the radio
+                // has some connectivity. Otherwise, it should be null or empty and just show
+                // "No SIM card"
+                // Grab the first subscripton, because they all should contain the emergency text,
+                // described above.
+                displayText =  makeCarrierStringOnEmergencyCapable(
+                        getContext().getText(R.string.keyguard_missing_sim_message_short),
+                        subs.get(0).getCarrierName());
+            } else {
+                // We don't have a SubscriptionInfo to get the emergency calls only from.
+                // Lets just make it ourselves.
+                displayText =  makeCarrierStringOnEmergencyCapable(
+                        getContext().getText(R.string.keyguard_missing_sim_message_short),
+                        getContext().getText(com.android.internal.R.string.emergency_calls_only));
+            }
+        }
+        setText(displayText);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mSeparator = getResources().getString(R.string.kg_text_message_separator);
+        mSeparator = getResources().getString(
+                com.android.internal.R.string.kg_text_message_separator);
         final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
         setSelected(screenOn); // Allow marquee to work.
     }
@@ -108,13 +140,14 @@ public class CarrierText extends TextView {
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mCallback);
+        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+        mKeyguardUpdateMonitor.registerCallback(mCallback);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mCallback);
+        mKeyguardUpdateMonitor.removeCallback(mCallback);
     }
 
     /**
@@ -122,36 +155,31 @@ public class CarrierText extends TextView {
      * and SPN as well as device capabilities, such as being emergency call capable.
      *
      * @param simState
-     * @param plmn
+     * @param text
      * @param spn
-     * @return
+     * @return Carrier text if not in missing state, null otherwise.
      */
     private CharSequence getCarrierTextForSimState(IccCardConstants.State simState,
-            CharSequence plmn, CharSequence spn) {
+            CharSequence text) {
         CharSequence carrierText = null;
         StatusMode status = getStatusForIccState(simState);
         switch (status) {
             case Normal:
-                carrierText = concatenate(plmn, spn);
+                carrierText = text;
                 break;
 
             case SimNotReady:
-                carrierText = null; // nothing to display yet.
+                // Null is reserved for denoting missing, in this case we have nothing to display.
+                carrierText = ""; // nothing to display yet.
                 break;
 
             case NetworkLocked:
                 carrierText = makeCarrierStringOnEmergencyCapable(
-                        mContext.getText(R.string.keyguard_network_locked_message), plmn);
+                        mContext.getText(R.string.keyguard_network_locked_message), text);
                 break;
 
             case SimMissing:
-                // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
-                // This depends on mPlmn containing the text "Emergency calls only" when the radio
-                // has some connectivity. Otherwise, it should be null or empty and just show
-                // "No SIM card"
-                carrierText =  makeCarrierStringOnEmergencyCapable(
-                        getContext().getText(R.string.keyguard_missing_sim_message_short),
-                        plmn);
+                carrierText = null;
                 break;
 
             case SimPermDisabled:
@@ -160,21 +188,19 @@ public class CarrierText extends TextView {
                 break;
 
             case SimMissingLocked:
-                carrierText =  makeCarrierStringOnEmergencyCapable(
-                        getContext().getText(R.string.keyguard_missing_sim_message_short),
-                        plmn);
+                carrierText = null;
                 break;
 
             case SimLocked:
                 carrierText = makeCarrierStringOnEmergencyCapable(
                         getContext().getText(R.string.keyguard_sim_locked_message),
-                        plmn);
+                        text);
                 break;
 
             case SimPukLocked:
                 carrierText = makeCarrierStringOnEmergencyCapable(
                         getContext().getText(R.string.keyguard_sim_puk_locked_message),
-                        plmn);
+                        text);
                 break;
         }
 
index 10baf23..05a5f66 100644 (file)
@@ -25,6 +25,6 @@ public class KeyguardConstants {
      * Turns on debugging information for the whole Keyguard. This is very verbose and should only
      * be used temporarily for debugging.
      */
-    public static final boolean DEBUG = true;
+    public static final boolean DEBUG = false;
     public static final boolean DEBUG_SIM_STATES = true;
 }
index 9bc2a4d..236cbf6 100644 (file)
@@ -153,7 +153,8 @@ class KeyguardMessageArea extends TextView {
         mUpdateMonitor.registerCallback(mInfoCallback);
         mHandler = new Handler(Looper.myLooper());
 
-        mSeparator = getResources().getString(R.string.kg_text_message_separator);
+        mSeparator = getResources().getString(
+                com.android.internal.R.string.kg_text_message_separator);
 
         update();
     }
index 7f1314d..8458ae0 100644 (file)
@@ -97,7 +97,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
     // Callback messages
     private static final int MSG_TIME_UPDATE = 301;
     private static final int MSG_BATTERY_UPDATE = 302;
-    private static final int MSG_CARRIER_INFO_UPDATE = 303;
     private static final int MSG_SIM_STATE_CHANGE = 304;
     private static final int MSG_RINGER_MODE_CHANGED = 305;
     private static final int MSG_PHONE_STATE_CHANGED = 306;
@@ -126,8 +125,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
     private final Context mContext;
     HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>();
 
-    private CharSequence mTelephonyPlmn;
-    private CharSequence mTelephonySpn;
     private int mRingMode;
     private int mPhoneState;
     private boolean mKeyguardIsVisible;
@@ -168,9 +165,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
                 case MSG_BATTERY_UPDATE:
                     handleBatteryUpdate((BatteryStatus) msg.obj);
                     break;
-                case MSG_CARRIER_INFO_UPDATE:
-                    handleCarrierInfoUpdate();
-                    break;
                 case MSG_SIM_STATE_CHANGE:
                     handleSimStateChange(msg.arg1, msg.arg2, (State) msg.obj);
                     break;
@@ -290,6 +284,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
                 KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
                 if (cb != null) {
                     cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                    cb.onRefreshCarrierInfo();
                 }
             }
         }
@@ -435,10 +430,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
                     || Intent.ACTION_TIME_CHANGED.equals(action)
                     || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
                 mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
-            } else if (TelephonyIntents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
-                mTelephonyPlmn = getTelephonyPlmnFrom(intent);
-                mTelephonySpn = getTelephonySpnFrom(intent);
-                mHandler.sendEmptyMessage(MSG_CARRIER_INFO_UPDATE);
             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
                 final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
                 final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
@@ -682,7 +673,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
 
         // Take a guess at initial SIM state, battery status and PLMN until we get an update
         mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0);
-        mTelephonyPlmn = getDefaultPlmn();
 
         // Watch for interesting updates
         final IntentFilter filter = new IntentFilter();
@@ -692,7 +682,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
-        filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
         filter.addAction(Intent.ACTION_USER_REMOVED);
         context.registerReceiver(mBroadcastReceiver, filter);
@@ -940,21 +929,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
     }
 
     /**
-     * Handle {@link #MSG_CARRIER_INFO_UPDATE}
-     */
-    private void handleCarrierInfoUpdate() {
-        if (DEBUG) Log.d(TAG, "handleCarrierInfoUpdate: plmn = " + mTelephonyPlmn
-            + ", spn = " + mTelephonySpn);
-
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-            if (cb != null) {
-                cb.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
-            }
-        }
-    }
-
-    /**
      * Handle {@link #MSG_SIM_STATE_CHANGE}
      */
     private void handleSimStateChange(int subId, int slotId, State state) {
@@ -1087,18 +1061,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
     }
 
     /**
-     * @param intent The intent with action {@link TelephonyIntents#SPN_STRINGS_UPDATED_ACTION}
-     * @return The string to use for the plmn, or null if it should not be shown.
-     */
-    private CharSequence getTelephonyPlmnFrom(Intent intent) {
-        if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
-            final String plmn = intent.getStringExtra(TelephonyIntents.EXTRA_PLMN);
-            return (plmn != null) ? plmn : getDefaultPlmn();
-        }
-        return null;
-    }
-
-    /**
      * @return The default plmn (no service)
      */
     private CharSequence getDefaultPlmn() {
@@ -1106,20 +1068,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
     }
 
     /**
-     * @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION}
-     * @return The string to use for the plmn, or null if it should not be shown.
-     */
-    private CharSequence getTelephonySpnFrom(Intent intent) {
-        if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
-            final String spn = intent.getStringExtra(TelephonyIntents.EXTRA_SPN);
-            if (spn != null) {
-                return spn;
-            }
-        }
-        return null;
-    }
-
-    /**
      * Remove the given observer's callback.
      *
      * @param callback The callback to remove
@@ -1159,7 +1107,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
         callback.onTimeChanged();
         callback.onRingerModeChanged(mRingMode);
         callback.onPhoneStateChanged(mPhoneState);
-        callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
+        callback.onRefreshCarrierInfo();
         callback.onClockVisibilityChanged();
         for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
             final SimData state = data.getValue();
@@ -1219,14 +1167,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
         }
     }
 
-    public CharSequence getTelephonyPlmn() {
-        return mTelephonyPlmn;
-    }
-
-    public CharSequence getTelephonySpn() {
-        return mTelephonySpn;
-    }
-
     /**
      * @return Whether the device is provisioned (whether they have gone through
      *   the setup wizard)
@@ -1289,7 +1229,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
         return false;
     }
 
-    private State getSimState(int subId) {
+    public State getSimState(int subId) {
         if (mSimDatas.containsKey(subId)) {
             return mSimDatas.get(subId).simState;
         } else {
@@ -1303,16 +1243,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
         // that don't return the complete set of values and have different types. In Keyguard we
         // need IccCardConstants, but TelephonyManager would only give us
         // TelephonyManager.SIM_STATE*, so we retrieve it manually.
-        final int phoneId = SubscriptionManager.getPhoneId(subId);
-        final String stateString = TelephonyManager.getTelephonyProperty(phoneId,
-                TelephonyProperties.PROPERTY_SIM_STATE, "");
+        final TelephonyManager tele = TelephonyManager.from(mContext);
+        int simState =  tele.getSimState(slotId);
         State state;
         try {
-            state = State.valueOf(stateString);
+            state = State.intToState(simState);
         } catch(IllegalArgumentException ex) {
-            Log.w(TAG, "Unknown sim state: " + stateString);
+            Log.w(TAG, "Unknown sim state: " + simState);
             state = State.UNKNOWN;
-        }
+       }
         mSimDatas.put(subId, new SimData(state, slotId, subId));
     }
 
index de72ddd..c2f355a 100644 (file)
@@ -49,12 +49,8 @@ public class KeyguardUpdateMonitorCallback {
 
     /**
      * Called when the carrier PLMN or SPN changes.
-     *
-     * @param plmn The operator name of the registered network.  May be null if it shouldn't
-     *   be displayed.
-     * @param spn The service provider name.  May be null if it shouldn't be displayed.
      */
-    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { }
+    public void onRefreshCarrierInfo() { }
 
     /**
      * Called when the ringer mode changes.
index 35a7285..5c5fabf 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"সমস্ত মুদ্রক…"</string>
     <string name="print_dialog" msgid="32628687461331979">"মুদ্রণ ডায়লগ"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g>টির মধ্যে <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> নম্বর পৃষ্ঠা"</string>
+    <string name="summary_template" msgid="8899734908625669193">"সারাংশ, <xliff:g id="COPIES">%1$s</xliff:g>টি অনুলিপি, কাগজের আকার <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"প্রসারিত করার হ্যান্ডেল"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"সঙ্কুচিত করার হ্যান্ডেল"</string>
+    <string name="print_button" msgid="645164566271246268">"মুদ্রণ করুন"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF হিসাবে সংরক্ষণ করুন"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"মুদ্রণ বিকল্প প্রসারিত হয়েছে"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"মুদ্রণ বিকল্প সংকুচিত হয়েছে"</string>
     <string name="search" msgid="5421724265322228497">"অনুসন্ধান করুন"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"সমস্ত মুদ্রক"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"পরিষেবা যোগ করুন"</string>
diff --git a/packages/PrintSpooler/res/values-es-rUS/arrays.xml b/packages/PrintSpooler/res/values-es-rUS/arrays.xml
new file mode 100644 (file)
index 0000000..c1b149c
--- /dev/null
@@ -0,0 +1,33 @@
+<?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>
+
+    <string-array name="pdf_printer_media_sizes" translatable="false">
+        <item>NA_LETTER</item>
+        <item>NA_GOVT_LETTER</item>
+        <item>NA_LEGAL</item>
+        <item>NA_JUNIOR_LEGAL</item>
+        <item>NA_LEDGER</item>
+        <item>NA_TABLOID</item>
+        <item>NA_INDEX_3X5</item>
+        <item>NA_INDEX_4X6</item>
+        <item>NA_INDEX_5X8</item>
+        <item>NA_MONARCH</item>
+        <item>NA_QUARTO</item>
+        <item>NA_FOOLSCAP</item>
+    </string-array>
+
+</resources>
index 69a04ad..4f0f8fc 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"Inprimagailu guztiak…"</string>
     <string name="print_dialog" msgid="32628687461331979">"Inprimatzeko elkarrizketa-koadroa"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g> orria"</string>
+    <string name="summary_template" msgid="8899734908625669193">"Laburpena, <xliff:g id="COPIES">%1$s</xliff:g> kopia, <xliff:g id="PAPER_SIZE">%2$s</xliff:g> paper-tamaina"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"Zabaldu heldulekua"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"Tolestu heldulekua"</string>
+    <string name="print_button" msgid="645164566271246268">"Inprimatu"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"Gorde PDF gisa"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"Inprimatzeko aukerak zabalduta daude"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"Inprimatzeko aukerak tolestuta daude"</string>
     <string name="search" msgid="5421724265322228497">"Bilatu"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Inprimagailu guztiak"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"Gehitu zerbitzua"</string>
index 9b180cc..6e542ea 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"Todas as impresoras..."</string>
     <string name="print_dialog" msgid="32628687461331979">"Diálogo de impresión"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"Páxina <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> de <xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"Resumo, copias <xliff:g id="COPIES">%1$s</xliff:g>, tamaño do papel <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"Amplía a agarradoira"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"Contrae a agarradoira"</string>
+    <string name="print_button" msgid="645164566271246268">"Imprimir"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"Gardar en PDF"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"Opcións de impresión ampliadas"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"Opcións de impresión contraídas"</string>
     <string name="search" msgid="5421724265322228497">"Buscar"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Todas as impresoras"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"Engadir servizo"</string>
index 81d3bf9..a3f7fef 100644 (file)
@@ -83,7 +83,7 @@
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"फ़ाइल पर नहीं लिखा जा सका"</string>
     <string name="print_error_default_message" msgid="8602678405502922346">"क्षमा करें, उससे बात नहीं बनी. पुन: प्रयास करें."</string>
-    <string name="print_error_retry" msgid="1426421728784259538">"पà¥\81नà¤\83 प्रयास करें"</string>
+    <string name="print_error_retry" msgid="1426421728784259538">"फिर à¤¸à¥\87 प्रयास करें"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"यह प्रिंटर इस समय उपलब्ध नहीं है."</string>
     <string name="print_preparing_preview" msgid="3939930735671364712">"पूर्वावलोकन तैयार हो रहा है..."</string>
 </resources>
index d18b8b5..41a047d 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"Allir prentarar…"</string>
     <string name="print_dialog" msgid="32628687461331979">"Prentgluggi"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"Síða <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> af <xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"Yfirlit, <xliff:g id="COPIES">%1$s</xliff:g> eintök, pappírsstærð <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"Stækkunarhandfang"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"Minnkunarhandfang"</string>
+    <string name="print_button" msgid="645164566271246268">"Prenta"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"Vista sem PDF"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"Prentvalkostir stækkaðir"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"Prentvalkostir minnkaðir"</string>
     <string name="search" msgid="5421724265322228497">"Leita"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Allir prentarar"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"Bæta við þjónustu"</string>
index 8c6a34f..b02714b 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"Барлық принтерлер…"</string>
     <string name="print_dialog" msgid="32628687461331979">"Басу терезесі"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g> ішінен <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> бет"</string>
+    <string name="summary_template" msgid="8899734908625669193">"Жиынтық мәліметтер, көшірмелер <xliff:g id="COPIES">%1$s</xliff:g>, қағаз өлшемі <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"Кеңейту таңбалауышы"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"Тасалау таңбалауышы"</string>
+    <string name="print_button" msgid="645164566271246268">"Басып шығару"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF пішімінде сақтау"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"Басып шығару опциялары кеңейтілді"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"Басып шығару опциялары тасаланды"</string>
     <string name="search" msgid="5421724265322228497">"Іздеу"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Барлық принтерлер"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"Қызмет қосу"</string>
index 9279fe4..63d710a 100644 (file)
@@ -70,7 +70,7 @@
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
     <string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
-    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព"</string>
+    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"មិន​ស្គាល់"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិន​អាច​ប្រើ​បាន"</string>
   <string-array name="color_mode_labels">
index 7f848e4..3950866 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"ಎಲ್ಲಾ ಮುದ್ರಕಗಳು…"</string>
     <string name="print_dialog" msgid="32628687461331979">"ಮುದ್ರಣ ಸಂವಾದ"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"ಪುಟ <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> / <xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"ಸಾರಾಂಶ, ನಕಲುಗಳು <xliff:g id="COPIES">%1$s</xliff:g>, ಪೇಪರ್ ಗಾತ್ರ <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"ಹ್ಯಾಂಡಲ್ ವಿಸ್ತರಿಸಿ"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"ಹ್ಯಾಂಡಲ್ ಮುಚ್ಚಿರಿ"</string>
+    <string name="print_button" msgid="645164566271246268">"ಮುದ್ರಿಸು"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF ಗೆ ಉಳಿಸು"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"ಪ್ರಿಂಟ್ ಆಯ್ಕೆಗಳನ್ನು ವಿಸ್ತರಿಸಲಾಗಿದೆ"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"ಪ್ರಿಂಟ್ ಆಯ್ಕೆಗಳನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string>
     <string name="search" msgid="5421724265322228497">"ಹುಡುಕು"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"ಎಲ್ಲಾ ಮುದ್ರಕಗಳು"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"ಸೇವೆಯನ್ನು ಸೇರಿಸು"</string>
index f60f994..602f660 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"Бардык принтерлер…"</string>
     <string name="print_dialog" msgid="32628687461331979">"Басуу баарлашуусу"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g> ичинен <xliff:g id="CURRENT_PAGE">%1$d</xliff:g>-бет"</string>
+    <string name="summary_template" msgid="8899734908625669193">"Корутунду, <xliff:g id="COPIES">%1$s</xliff:g> көчүрмө, барак өлчөмү <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"Тутканы жаюу"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"Тутканы жыйноо"</string>
+    <string name="print_button" msgid="645164566271246268">"Басуу"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF\'ке сактоо"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"Басып чыгаруу параметрлери жайылды"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"Басып чыгаруу параметрлери жыйналды"</string>
     <string name="search" msgid="5421724265322228497">"Издөө"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Бардык принтерлер"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"Кызматты кошуу"</string>
index eee7c6b..91b5763 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"Сите печатачи..."</string>
     <string name="print_dialog" msgid="32628687461331979">"Дијалог рамка Печати"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"Страница <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> од <xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"Краток преглед, копии <xliff:g id="COPIES">%1$s</xliff:g>, големина на хартија <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"Рачка за прикажување"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"Рачка за сокривање"</string>
+    <string name="print_button" msgid="645164566271246268">"Печати"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"Зачувај во PDF"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"Опциите на печатачот се прикажани"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"Опциите на печатачот се сокриени"</string>
     <string name="search" msgid="5421724265322228497">"Пребарај"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Сите печатачи"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"Додај услуга"</string>
index 9093af1..a06ca7d 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"എല്ലാ പ്രിന്ററുകളും..."</string>
     <string name="print_dialog" msgid="32628687461331979">"പ്രിന്റ് സംഭാഷണം"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> / <xliff:g id="PAGE_COUNT">%2$d</xliff:g> പേജ്"</string>
+    <string name="summary_template" msgid="8899734908625669193">"സംഗ്രഹം, പകർപ്പുകൾ <xliff:g id="COPIES">%1$s</xliff:g>, പേപ്പർ വലുപ്പം <xliff:g id="PAPER_SIZE">%2$s</xliff:g> എന്നിവ"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"വിപുലീകരണം കൈകാര്യം ചെയ്യുക"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"ചുരുക്കുന്നത് കൈകാര്യം ചെയ്യുക"</string>
+    <string name="print_button" msgid="645164566271246268">"പ്രിന്റുചെയ്യുക"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF-ൽ സംരക്ഷിക്കുക"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"പ്രിന്റ് ചെയ്യാനുള്ള ഓപ്‌ഷനുകൾ വിപുലീകരിച്ചു"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"പ്രിന്റ് ചെയ്യാനുള്ള ഓപ്‌ഷനുകൾ ചുരുക്കി"</string>
     <string name="search" msgid="5421724265322228497">"തിരയൽ"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"എല്ലാ പ്രിന്ററുകളും"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"സേവനം ചേർക്കുക"</string>
index 6afe957..1fade66 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"सर्व प्रिंटर..."</string>
     <string name="print_dialog" msgid="32628687461331979">"मुद्रण संवाद"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g> पैकी <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> पृष्‍ठ"</string>
+    <string name="summary_template" msgid="8899734908625669193">"सारांश, प्रती <xliff:g id="COPIES">%1$s</xliff:g>, कागद आकार <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"विस्तृत करण्याचे हँडल"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"संक्षिप्त करण्याचे हँडल"</string>
+    <string name="print_button" msgid="645164566271246268">"मुद्रण करा"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF वर जतन करा"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"मुद्रण पर्याय विस्तृत झाले"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"मुद्रण पर्याय संक्षिप्त झाले"</string>
     <string name="search" msgid="5421724265322228497">"शोध"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"सर्व प्रिंटर"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"सेवा जोडा"</string>
index 04da765..d6eb380 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"စာထုတ်စက် အားလုံး"</string>
     <string name="print_dialog" msgid="32628687461331979">"စာထုတ်ရန် အချက်ပြခြင်း"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g>ထဲက စာမျက်နှာ <xliff:g id="CURRENT_PAGE">%1$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"အကျဉ်းချုပ်၊ ကော်ပီများ<xliff:g id="COPIES">%1$s</xliff:g>၊ စက္ကူ ဆိုက် <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"ချဲ့ရန် လက်"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"ခေါက်ရန် လက်"</string>
+    <string name="print_button" msgid="645164566271246268">"စာထုတ်ရန်"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF သို့ သိမ်းဆည်းခဲ့"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"ပရင့်ထုတ် ရွေးစရာများကို ချဲ့ထား"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"ပရင့်ထုတ် ရွေးစရာများကို ခေါက်ထား"</string>
     <string name="search" msgid="5421724265322228497">"ရှာဖွေခြင်း"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"စာထုတ်စက် အားလုံး"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"ဆားဗစ် အသစ်ထည့်ရန်"</string>
index 110f698..eb97530 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"सबै प्रिन्टरहरू..."</string>
     <string name="print_dialog" msgid="32628687461331979">"सम्वाद छाप्नुहोस्"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g> को <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> पृष्ठ"</string>
+    <string name="summary_template" msgid="8899734908625669193">"सारांश, प्रतिहरू <xliff:g id="COPIES">%1$s</xliff:g> , कागज आकार <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"ह्यान्डल विस्तार गर्नुहोस्"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"ह्यान्डल कोल्याप्स गर्नुहोस्"</string>
+    <string name="print_button" msgid="645164566271246268">"प्रिन्ट गर्नुहोस्"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF सुरक्षित गर्नुहोस्"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"विस्तार गरेका विकल्पहरू प्रिन्ट गर्नुहोस्"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"कोल्याप्स गरेका विकल्पहरू प्रिन्ट गर्नुहोस्"</string>
     <string name="search" msgid="5421724265322228497">"खोज्नुहोस्"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"सबै प्रिन्टरहरू"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"सेवा थप्नुहोस्"</string>
index f6cc5e2..386ce8d 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"සියලු මුද්‍රණ යන්ත්‍ර…"</string>
     <string name="print_dialog" msgid="32628687461331979">"මුද්‍රණ සංවාද කොටුව"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"පිටු <xliff:g id="PAGE_COUNT">%2$d</xliff:g> න් <xliff:g id="CURRENT_PAGE">%1$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"සාරාංශය, පිටපත් <xliff:g id="COPIES">%1$s</xliff:g>, පිටුවේ ප්‍රමණය <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"විහිදන මිට"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"හකුළන මිට"</string>
+    <string name="print_button" msgid="645164566271246268">"මුද්‍රණය කරන්න"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF වෙත සුරකින්න"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"විහිදන ලද විකල්ප මුද්‍රණය කරන්න"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"හකුළන ලද විකල්ප මුද්‍රණය කරන්න"</string>
     <string name="search" msgid="5421724265322228497">"සෙවීම"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"සියලු මුද්‍රණ යන්ත්‍ර"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"සේවාව එක් කිරීම"</string>
index f7f8a18..ea8da8e 100644 (file)
@@ -20,6 +20,4 @@
 
     <integer name="preview_page_per_row_count">4</integer>
 
-    <dimen name="preview_destination_spinner_width">256dip</dimen>
-
 </resources>
index 39eead8..0421bd6 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"எல்லா அச்சுப்பொறிகளும்…"</string>
     <string name="print_dialog" msgid="32628687461331979">"அச்சிடல் செய்தி"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"பக்கம்: <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> / <xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"சுருக்கம், நகல்கள் <xliff:g id="COPIES">%1$s</xliff:g>, தாள் அளவு <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"ஹேண்டிலை விரிவாக்கு"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"ஹேண்டிலைச் சுருக்கு"</string>
+    <string name="print_button" msgid="645164566271246268">"அச்சிடு"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF ஆகச் சேமி"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"அச்சு விருப்பங்கள் விரிவாக்கப்பட்டன"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"அச்சு விருப்பங்கள் சுருக்கப்பட்டன"</string>
     <string name="search" msgid="5421724265322228497">"தேடு"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"எல்லா அச்சுப்பொறிகளும்"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"சேவையைச் சேர்"</string>
index 3ad4d1d..edb6e60 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"అన్ని ప్రింటర్‌లు…"</string>
     <string name="print_dialog" msgid="32628687461331979">"ముద్రణ డైలాగ్"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g>లో <xliff:g id="CURRENT_PAGE">%1$d</xliff:g>వ పేజీ"</string>
+    <string name="summary_template" msgid="8899734908625669193">"సారాంశం, కాపీలు <xliff:g id="COPIES">%1$s</xliff:g>, కాగితం పరిమాణం <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"విస్తరణ హ్యాండిల్"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"కుదింపు హ్యాండిల్"</string>
+    <string name="print_button" msgid="645164566271246268">"ముద్రించు"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF వలె సేవ్ చేయి"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"ముద్రణ ఎంపికలు విస్తరించబడ్డాయి"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"ముద్రణ ఎంపికలు కుదించబడ్డాయి"</string>
     <string name="search" msgid="5421724265322228497">"శోధించు"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"అన్ని ప్రింటర్‌లు"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"సేవను జోడించు"</string>
index 1ff57a7..722d027 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"سبھی پرنٹرز…"</string>
     <string name="print_dialog" msgid="32628687461331979">"پرنٹ ڈائلاگ"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"صفحہ <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> از <xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"خلاصہ، کاپیاں <xliff:g id="COPIES">%1$s</xliff:g>، کاغذ کا سائز <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"پھیلانے والا ہینڈل"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"سکیڑنے والا ہینڈل"</string>
+    <string name="print_button" msgid="645164566271246268">"پرنٹ کریں"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"‏PDF میں محفوظ کریں"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"پرنٹ کے اختیارات پھیلا دیے گئے"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"پرنٹ کے اختیارات سکیڑ دیے گئے"</string>
     <string name="search" msgid="5421724265322228497">"تلاش کریں"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"سبھی پرنٹرز"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"سروس شامل کریں"</string>
index 8ab3410..f62728f 100644 (file)
     <string name="all_printers" msgid="5018829726861876202">"Barcha printerlar…"</string>
     <string name="print_dialog" msgid="32628687461331979">"Chop etish oynasi"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"Sahifa: <xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"Xulosa: nusxalar soni – <xliff:g id="COPIES">%1$s</xliff:g>, qog‘oz o‘lchami – <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"Dastakni yoyish"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"Dastakni yig‘ish"</string>
+    <string name="print_button" msgid="645164566271246268">"Chop etish"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDF sifatida saqlash"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"Chop qilish tanlamalari yoyildi"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"Chop qilish tanlamalari yig‘ildi"</string>
     <string name="search" msgid="5421724265322228497">"Izlash"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Barcha printerlar"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"Xizmat qo‘shish"</string>
index 357d3e1..f6c52d3 100644 (file)
@@ -45,6 +45,6 @@
     <dimen name="preview_page_footer_height">32dip</dimen>
     <dimen name="preview_page_min_width">128dip</dimen>
 
-    <dimen name="preview_destination_spinner_width">192dip</dimen>
+    <dimen name="preview_destination_spinner_width">256dip</dimen>
 
 </resources>
index 90875c0..ea6281d 100644 (file)
@@ -486,7 +486,9 @@ public final class PageContentRepository {
                 protected Void doInBackground(Void... params) {
                     synchronized (mLock) {
                         try {
-                            mRenderer.closeDocument();
+                            if (mRenderer != null) {
+                                mRenderer.closeDocument();
+                            }
                         } catch (RemoteException re) {
                             /* ignore */
                         }
index 352b545..f3a5c95 100644 (file)
@@ -487,6 +487,10 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
                 requestCreatePdfFileOrFinish();
             } break;
 
+            case STATE_PRINT_CANCELED: {
+                updateOptionsUi();
+            } break;
+
             default: {
                 updatePrintPreviewController(document.changed);
 
index 0b0bd8a..da8193f 100644 (file)
@@ -19,5 +19,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4567566098528588863">"सà¥\87à¤\9fिà¤\82à¤\97 à¤¸à¤\82à¤\97à¥\8dरहण"</string>
+    <string name="app_label" msgid="4567566098528588863">"सà¥\87à¤\9fिà¤\82à¤\97 à¤®à¥\87मà¥\8bरà¥\80"</string>
 </resources>
index 10ef1a6..d321159 100644 (file)
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="3701846017049540910">"Prostředí"</string>
+    <string name="app_label" msgid="3701846017049540910">"Shell"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Bylo vytvořeno chybové hlášení"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Chcete-li hlášení chyby sdílet, přejeďte doleva."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Chybové hlášení můžete sdílet klepnutím."</string>
index c9a9bfa..19bfc25 100644 (file)
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
-    <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de error capturado"</string>
+    <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de error registrado"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Desliza el dedo hacia la izquierda para compartir el informe de error"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca para compartir tu informe de error"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida información personal y privada. Comparte los informes de errores únicamente con aplicaciones y usuarios en los que confíes."</string>
index 844c209..d82ec95 100644 (file)
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Rapport de bogue enregistré"</string>
-    <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Faites glisser le doigt vers la gauche pour partager votre rapport d\'erreur."</string>
+    <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Faites glisser le doigt vers la gauche pour partager votre rapport de bogue."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Appuyer ici pour partager votre rapport de bogue"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bogue contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bogue qu\'avec les applications et les personnes que vous estimez fiables."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
index fe5d855..3e0dd0b 100644 (file)
@@ -20,6 +20,6 @@
     <string name="bugreport_finished_title" msgid="2293711546892863898">"버그 신고서 캡처됨"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"왼쪽으로 스와이프하여 버그 신고서를 공유하세요."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"버그 신고서를 공유하려면 터치하세요."</string>
-    <string name="bugreport_confirm" msgid="5130698467795669780">"ë²\84ê·¸ ì\8b ê³ ì\84\9cë\8a\94 ì\8b\9cì\8a¤í\85\9cì\9d\98 ë\8b¤ì\96\91í\95\9c ë¡\9cê·¸ í\8c\8cì\9d¼ ë\8d°ì\9d´í\84°(ì\98\88: ê°\9cì\9d¸ ë°\8f ë¹\84ê³µê°\9c ì \95ë³´)를 í\8f¬í\95¨í\95©ë\8b\88ë\8b¤. ì\8b ë¢°í\95  ì\88\98 ì\9e\88ë\8a\94 ì\95±ê³¼ ì\82¬ì\9a©ì\9e\90ì\97\90ê²\8cë§\8c ë²\84ê·¸ ì\8b ê³ ì\84\9c를 ê³µì\9c í\95©ë\8b\88ë\8b¤."</string>
+    <string name="bugreport_confirm" msgid="5130698467795669780">"ë²\84ê·¸ ì\8b ê³ ì\84\9cë\8a\94 ì\8b\9cì\8a¤í\85\9cì\9d\98 ë\8b¤ì\96\91í\95\9c ë¡\9cê·¸ í\8c\8cì\9d¼ ë\8d°ì\9d´í\84°(ì\98\88: ê°\9cì\9d¸ ë°\8f ë¹\84ê³µê°\9c ì \95ë³´)를 í\8f¬í\95¨í\95©ë\8b\88ë\8b¤. ì\8b ë¢°í\95  ì\88\98 ì\9e\88ë\8a\94 ì\95±ê³¼ ì\82¬ì\9a©ì\9e\90ì\97\90ê²\8cë§\8c ë²\84ê·¸ ì\8b ê³ ì\84\9c를 ê³µì\9c í\95\98ì\84¸ì\9a\94."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"다음에 이 메시지 표시"</string>
 </resources>
index 914ef46..a78b1db 100644 (file)
@@ -17,9 +17,9 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Apvalkalas"</string>
-    <string name="bugreport_finished_title" msgid="2293711546892863898">"Trikčių ataskaita užfiksuota"</string>
-    <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Perbraukite kairėn, kad bendrintumėte trikties ataskaitą"</string>
-    <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Palieskite, kad bendrintumėte trikčių ataskaitą"</string>
-    <string name="bugreport_confirm" msgid="5130698467795669780">"Trikčių ataskaitose pateikiami duomenys iš įvairių sistemos žurnalo failų, įskaitant asmeninę ir privačią informaciją. Trikčių ataskaitas bendrinkite tik su patikimomis programomis ir žmonėmis."</string>
+    <string name="bugreport_finished_title" msgid="2293711546892863898">"Riktų ataskaita užfiksuota"</string>
+    <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Perbraukite kairėn, kad bendrintumėte rikto ataskaitą"</string>
+    <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Palieskite, kad bendrintumėte riktų ataskaitą"</string>
+    <string name="bugreport_confirm" msgid="5130698467795669780">"Riktų ataskaitose pateikiami duomenys iš įvairių sistemos žurnalo failų, įskaitant asmeninę ir privačią informaciją. Riktų ataskaitas bendrinkite tik su patikimomis programomis ir žmonėmis."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rodyti šį pranešimą kitą kartą"</string>
 </resources>
index 3d4c51f..c166bf3 100644 (file)
@@ -20,6 +20,6 @@
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Relatório de bugs capturado"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslize para a esquerda para compartilhar seu relatório de bugs"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para compartilhar seu relatório de bugs"</string>
-    <string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de bugs contêm dados de diversos arquivos de registro do sistema, inclusive informações pessoais e particulares. Compartilhe relatórios de bugs somente com aplicativos e pessoas nos quais você confia."</string>
+    <string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de bugs contêm dados de diversos arquivos de registro do sistema, inclusive informações pessoais e particulares. Compartilhe relatórios de bugs somente com apps e pessoas nos quais você confia."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
 </resources>
index 7c80736..77a8cd0 100644 (file)
@@ -17,8 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Оболочка"</string>
-    <string name="bugreport_finished_title" msgid="2293711546892863898">"Ð\9eÑ\82Ñ\87еÑ\82 Ð¾Ð± Ð¾Ñ\88ибкаÑ\85 сохранен"</string>
-    <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Ð\9fÑ\80оведиÑ\82е Ð¿Ð°Ð»Ñ\8cÑ\86ем Ð²Ð»ÐµÐ²Ð¾, Ñ\87Ñ\82обÑ\8b Ð¾Ñ\82пÑ\80авиÑ\82Ñ\8c Ð¾Ñ\82Ñ\87еÑ\82 Ð¾Ð± Ð¾Ñ\88ибке"</string>
+    <string name="bugreport_finished_title" msgid="2293711546892863898">"Ð\9eÑ\82Ñ\87еÑ\82 Ð¾Ð± Ð¾Ñ\88ибке сохранен"</string>
+    <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Ð\9fÑ\80оведиÑ\82е Ð²Ð»ÐµÐ²Ð¾, Ñ\87Ñ\82обÑ\8b Ð¾Ñ\82пÑ\80авиÑ\82Ñ\8c Ð¾Ñ\82Ñ\87еÑ\82"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Нажмите, чтобы отправить отчет об ошибках"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Отчеты об ошибках содержат данные различных системных журналов и могут включать личную информацию. Рекомендуем открывать к ним доступ только лицам и приложениям, заслуживающим доверие."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показать это сообщение в следующий раз"</string>
index 0d56d76..f2e034b 100644 (file)
@@ -20,6 +20,6 @@
     <string name="bugreport_finished_title" msgid="2293711546892863898">"已擷取錯誤報告"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑動即可分享錯誤報告"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string>
-    <string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告中有來自系統各個記錄檔案的資料,包括個人和私人資料。請只與您信任的應用程式和用戶分享錯誤報告。"</string>
+    <string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告中有來自系統各個記錄檔案的資料,包括個人和私人資料。請只與您信任的應用程式和使用者分享錯誤報告。"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再顯示這則訊息"</string>
 </resources>
diff --git a/packages/SystemUI/res/drawable/pop_ball.xml b/packages/SystemUI/res/drawable/pop_ball.xml
new file mode 100644 (file)
index 0000000..ee1220f
--- /dev/null
@@ -0,0 +1,29 @@
+<!--
+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"
+        android:width="100.0dp"
+        android:height="100.0dp"
+        android:viewportWidth="100.0"
+        android:viewportHeight="100.0">
+    <path
+        android:pathData="M0,50 a50,50 0 1 1 100,0
+                                a50,50 0 1 1 -100,0"
+        android:fillColor="#FFFF1744"/>
+    <path
+        android:pathData="M16,36 A24,24 0 1 1 64,36
+                          M64,36 A24,24 0 1 1 16,36"
+        android:fillColor="#20FFFFFF"/>
+</vector>
index 5c0c3d7..e0ea575 100644 (file)
@@ -20,14 +20,14 @@ Copyright (C) 2014 The Android Open Source Project
         android:viewportHeight="100.0">
     <path
         android:pathData="M50.000000,50.000000m-47.599998,0.000000a47.599998,47.599998 0.000000,1.000000 1.000000,95.199997 0.000000a47.599998,47.599998 0.000000,1.000000 1.000000,-95.199997 0.000000"
-        android:fillColor="#D81B60"/>
+        android:fillColor="#9C27B0"/>
     <path
         android:pathData="M50.000000,2.429000c-26.337999,0.000000 -47.688999,21.351000 -47.688999,47.688999c0.000000,13.168000 5.337000,25.091000 13.968000,33.722000l67.444000,-67.443001C75.092003,7.766000 63.168999,2.429000 50.000000,2.429000z"
-        android:fillColor="#F06292"/>
+        android:fillColor="#BA68C8"/>
     <path
         android:pathData="M0.000000,41.573002l100.000000,0.000000l0.000000,17.090000l-100.000000,0.000000z"
-        android:fillColor="#D81B60"/>
+        android:fillColor="#9C27B0"/>
     <path
         android:pathData="M0.000000,58.662998l0.000000,-17.089996 100.000000,0.000000z"
-        android:fillColor="#F06292"/>
+        android:fillColor="#BA68C8"/>
 </vector>
index b6d2bb3..1806b5a 100644 (file)
@@ -20,26 +20,26 @@ Copyright (C) 2014 The Android Open Source Project
         android:viewportHeight="100.0">
     <path
         android:pathData="M14.645000,14.645000C5.597000,23.693001 0.000000,36.193001 0.000000,50.000000l50.000000,0.000000L14.645000,14.645000z"
-        android:fillColor="#7BAAF7"/>
+        android:fillColor="#2979FF"/>
     <path
         android:pathData="M100.000000,50.000000c0.000000,-13.807000 -5.597000,-26.306999 -14.645000,-35.355000L50.000000,50.000000L100.000000,50.000000z"
-        android:fillColor="#D81B60"/>
+        android:fillColor="#FF1744"/>
     <path
         android:pathData="M85.355003,14.645000C76.306999,5.597000 63.806999,0.000000 50.000000,0.000000l0.000000,50.000000L85.355003,14.645000z"
-        android:fillColor="#F06292"/>
+        android:fillColor="#0F9D58"/>
     <path
         android:pathData="M50.000000,0.000000C36.193001,0.000000 23.693001,5.597000 14.645000,14.645000L50.000000,50.000000L50.000000,0.000000z"
-        android:fillColor="#FFF176"/>
+        android:fillColor="#FFBC00"/>
     <path
         android:pathData="M50.000000,50.000000l35.355000,35.355000C94.403000,76.307999 100.000000,63.807999 100.000000,50.000000L50.000000,50.000000z"
-        android:fillColor="#7BAAF7"/>
+        android:fillColor="#2979FF"/>
     <path
         android:pathData="M50.000000,100.000000c13.807000,0.000000 26.306999,-5.596000 35.355000,-14.645000L50.000000,50.000000L50.000000,100.000000z"
-        android:fillColor="#FFF176"/>
+        android:fillColor="#FFBC00"/>
     <path
         android:pathData="M14.645000,85.355003C23.693001,94.403999 36.193001,100.000000 50.000000,100.000000L50.000000,50.000000L14.645000,85.355003z"
-        android:fillColor="#F06292"/>
+        android:fillColor="#0F9D58"/>
     <path
         android:pathData="M0.000000,50.000000c0.000000,13.808000 5.597000,26.308001 14.645000,35.355000L50.000000,50.000000L0.000000,50.000000z"
-        android:fillColor="#D81B60"/>
+        android:fillColor="#FF1744"/>
 </vector>
index acd6d3d..f87569b 100644 (file)
@@ -20,7 +20,7 @@ Copyright (C) 2014 The Android Open Source Project
         android:viewportHeight="100.0">
     <path
         android:pathData="M50.000000,50.000000C86.898003,27.834999 79.244003,11.688000 76.177002,7.399000c-7.240000,-4.459000 -15.703000,-7.112000 -24.770000,-7.363000C56.247002,2.253000 70.815002,12.456000 50.000000,50.000000z"
-        android:fillColor="#82B1FF"/>
+        android:fillColor="#FFFFFA"/>
     <path
         android:pathData="M50.000000,50.000000c20.815001,-37.543999 6.247000,-47.747002 1.407000,-49.964001C50.938000,0.022000 50.472000,0.000000 50.000000,0.000000c-8.627000,0.000000 -16.743999,2.186000 -23.827000,6.032000C31.392000,5.514000 49.251999,6.903000 50.000000,50.000000z"
         android:fillColor="#76FF03"/>
@@ -32,7 +32,7 @@ Copyright (C) 2014 The Android Open Source Project
         android:fillColor="#303F9F"/>
     <path
         android:pathData="M50.000000,50.000000C27.834000,13.103000 11.687000,20.757000 7.398000,23.823999C2.940000,31.063000 0.287000,39.527000 0.035000,48.592999C2.253000,43.752998 12.456000,29.184999 50.000000,50.000000z"
-        android:fillColor="#FAFAFA"/>
+        android:fillColor="#FFFFFA"/>
     <path
         android:pathData="M50.000000,50.000000C49.251999,6.903000 31.392000,5.514000 26.173000,6.032000c-7.709000,4.187000 -14.188000,10.344000 -18.774000,17.792000C11.687000,20.757000 27.834000,13.103000 50.000000,50.000000z"
         android:fillColor="#303F9F"/>
@@ -44,13 +44,13 @@ Copyright (C) 2014 The Android Open Source Project
         android:fillColor="#303F9F"/>
     <path
         android:pathData="M50.000000,50.000000c22.166000,36.897999 38.313000,29.243999 42.602001,26.177999c4.458000,-7.240000 7.111000,-15.703000 7.363000,-24.770000C97.747002,56.248001 87.543999,70.816002 50.000000,50.000000z"
-        android:fillColor="#FAFAFA"/>
+        android:fillColor="#FFFFFA"/>
     <path
         android:pathData="M50.000000,50.000000c-20.815001,37.544998 -6.247000,47.748001 -1.407000,49.965000C49.062000,99.978996 49.528000,100.000000 50.000000,100.000000c8.627000,0.000000 16.743999,-2.185000 23.827000,-6.031000C68.608002,94.486000 50.748001,93.098000 50.000000,50.000000z"
         android:fillColor="#76FF03"/>
     <path
         android:pathData="M50.000000,50.000000C13.103000,72.166000 20.757000,88.313004 23.823000,92.601997c7.240000,4.459000 15.703000,7.112000 24.770000,7.363000C43.752998,97.748001 29.184999,87.544998 50.000000,50.000000z"
-        android:fillColor="#82B1FF"/>
+        android:fillColor="#FFFFFA"/>
     <path
         android:pathData="M50.000000,50.000000C6.902000,50.749001 5.514000,68.609001 6.032000,73.828003c4.186000,7.708000 10.344000,14.188000 17.791000,18.773001C20.757000,88.313004 13.103000,72.166000 50.000000,50.000000z"
         android:fillColor="#303F9F"/>
index faefcb1..2380e68 100644 (file)
@@ -20,7 +20,7 @@ Copyright (C) 2014 The Android Open Source Project
         android:viewportHeight="100.0">
     <path
         android:pathData="M50.000000,50.000000m-50.000000,0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,100.000000 0.000000a50.000000,50.000000 0.000000,1.000000 1.000000,-100.000000 0.000000"
-        android:fillColor="#FFF176"/>
+        android:fillColor="#F8F8FF"/>
     <path
         android:pathData="M58.658001,89.648003c-19.330000,0.000000 -35.000000,-15.670000 -35.000000,-35.000000c0.000000,-13.531000 10.969000,-24.500000 24.500000,-24.500000c9.472000,0.000000 17.150000,7.679000 17.150000,17.150000c0.000000,6.631000 -5.375000,12.006000 -12.006000,12.006000c-3.798000,0.000000 -7.004000,-2.522000 -8.045000,-5.982000c1.021000,1.136000 2.497000,1.854000 4.145000,1.854000c2.644000,0.000000 4.853000,-1.841000 5.428000,-4.310000c0.175000,-0.558000 0.271000,-1.150000 0.271000,-1.766000c0.000000,-4.642000 -3.763000,-8.404000 -8.403000,-8.404000c-6.631000,0.000000 -12.006000,5.375000 -12.006000,12.006000c0.000000,9.472000 7.679000,17.149000 17.150000,17.149000c13.531000,0.000000 24.500000,-10.969000 24.500000,-24.500000c0.000000,-19.330000 -15.670000,-35.000000 -35.000000,-35.000000c-12.963000,0.000000 -24.773001,4.935000 -33.657001,13.025000C2.824000,31.087000 0.000000,40.212002 0.000000,50.000000c0.000000,27.615000 22.386000,50.000000 50.000000,50.000000c17.825001,0.000000 33.462002,-9.335000 42.313999,-23.375999C83.431000,84.714996 71.621002,89.648003 58.658001,89.648003z"
         android:fillColor="#7BAAF7"/>
index e33ff1e..686df2c 100644 (file)
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:top="@dimen/notification_material_rounded_rect_radius_negative" >
-    <solid android:color="@color/system_primary_color" />
-    <corners android:radius="@dimen/notification_material_rounded_rect_radius"/>
-</shape>
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetTop="@dimen/notification_material_rounded_rect_radius_negative">
+    <shape>
+        <solid android:color="@color/system_primary_color"/>
+        <corners android:radius="@dimen/notification_material_rounded_rect_radius"/>
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/stat_sys_no_sims.xml b/packages/SystemUI/res/drawable/stat_sys_no_sims.xml
new file mode 100644 (file)
index 0000000..8bad226
--- /dev/null
@@ -0,0 +1,25 @@
+<!--
+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"
+        android:width="17dp"
+        android:height="17dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:fillColor="#4DFFFFFF"
+        android:pathData="M19.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-7.0,0.0L7.7,5.3L19.0,16.7L19.0,5.0zM3.7,3.9L2.4,5.2L5.0,7.8L5.0,19.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c0.4,0.0 0.7,-0.1 1.0,-0.3l1.9,1.9l1.3,-1.3L3.7,3.9z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
new file mode 100644 (file)
index 0000000..9769718
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/mobile_combo"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    >
+    <ImageView
+        android:id="@+id/mobile_signal"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        />
+    <ImageView
+        android:id="@+id/mobile_type"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        />
+</FrameLayout>
index f45aa85..8fbd8f7 100644 (file)
         android:layout_height="4dp"
         android:visibility="gone"
         />
-    <FrameLayout
+    <LinearLayout
+        android:id="@+id/mobile_signal_group"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         >
-        <FrameLayout
-            android:id="@+id/mobile_combo"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            >
-            <ImageView
-                android:id="@+id/mobile_signal"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                />
-            <ImageView
-                android:id="@+id/mobile_type"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                />
-        </FrameLayout>
-    </FrameLayout>
+    </LinearLayout>
+    <ImageView
+        android:id="@+id/no_sims"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:src="@drawable/stat_sys_no_sims"
+        />
     <View
         android:id="@+id/wifi_airplane_spacer"
         android:layout_width="4dp"
index 0c22321..ea00568 100644 (file)
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Հավելյալ կարգավորումներ"</string>
     <string name="quick_settings_done" msgid="3402999958839153376">"Պատրաստ է"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"Կապակցված է"</string>
-    <string name="quick_settings_connected_via_wfa" msgid="1587051627194895715">"Կապակցված է Wi‑Fi օգնականի միջոցով"</string>
+    <string name="quick_settings_connected_via_wfa" msgid="1587051627194895715">"Կապակցված է Wi‑Fi Օգնականի միջոցով"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"Միանում է..."</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Միացում"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Թեժ կետ"</string>
index 4c5d890..a68d076 100644 (file)
     <string name="description_direction_left" msgid="7207478719805562165">"Przesuń w lewo: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
     <string name="zen_no_interruptions_with_warning" msgid="4396898053735625287">"Żadnych powiadomień. Nawet alarmów."</string>
     <string name="zen_no_interruptions" msgid="7970973750143632592">"Bez przerw"</string>
-    <string name="zen_important_interruptions" msgid="3477041776609757628">"Tylko przerwy priorytetowe"</string>
+    <string name="zen_important_interruptions" msgid="3477041776609757628">"Tylko dźwięki priorytetowe"</string>
     <string name="zen_alarm_information_time" msgid="5235772206174372272">"Następny alarm o <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
     <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Następny alarm: <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
     <string name="zen_alarm_warning" msgid="6873910860111498041">"Nie usłyszysz alarmu o <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
     <string name="phone_hint" msgid="3101468054914424646">"Przesuń w prawo, by przełączyć się na telefon"</string>
     <string name="camera_hint" msgid="5241441720959174226">"Przesuń w lewo, by włączyć aparat"</string>
     <string name="interruption_level_none" msgid="3831278883136066646">"Żadne"</string>
-    <string name="interruption_level_priority" msgid="6517366750688942030">"Priorytet"</string>
+    <string name="interruption_level_priority" msgid="6517366750688942030">"Priorytetowe"</string>
     <string name="interruption_level_all" msgid="1330581184930945764">"Wszystkie"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ładuje się (pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Przełącz użytkownika"</string>
index 48b4cb9..03ddc46 100644 (file)
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Diğer ayarlar"</string>
     <string name="quick_settings_done" msgid="3402999958839153376">"Bitti"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"Bağlı"</string>
-    <string name="quick_settings_connected_via_wfa" msgid="1587051627194895715">"Kablosuz yardımcısıyla bağlandı"</string>
+    <string name="quick_settings_connected_via_wfa" msgid="1587051627194895715">"Kablosuz bağlantı yardımcısıyla bağlandı"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"Bağlanılıyor..."</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
index aae1d69..c3811f8 100644 (file)
     <string name="quick_settings_more_settings" msgid="326112621462813682">"更多设置"</string>
     <string name="quick_settings_done" msgid="3402999958839153376">"完成"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"已连接"</string>
-    <string name="quick_settings_connected_via_wfa" msgid="1587051627194895715">"已通过 WLAN 助手连接"</string>
+    <string name="quick_settings_connected_via_wfa" msgid="1587051627194895715">"已连接(通过 WLAN 助手)"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"正在连接…"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"网络共享"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"热点"</string>
index 9a95b37..98f03b5 100644 (file)
     <!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations -->
     <integer name="doze_pickup_vibration_threshold">2000</integer>
 
+    <!-- Doze: can we assume the pickup sensor includes a proximity check? -->
+    <bool name="doze_pickup_performs_proximity_check">true</bool>
+
     <!-- Doze: pulse parameter - how long does it take to fade in? -->
     <integer name="doze_pulse_duration_in">900</integer>
 
-    <!-- Doze: pulse parameter - delay for fading so the screen can wake up before -->
+    <!-- Doze: pulse parameter - how long does it take to fade in after a pickup? -->
+    <integer name="doze_pulse_duration_in_pickup">300</integer>
+
+    <!-- Doze: pulse parameter - delay to wait for the screen to wake up -->
     <integer name="doze_pulse_delay_in">200</integer>
 
+    <!-- Doze: pulse parameter - delay to wait for the screen to wake up after a pickup -->
+    <integer name="doze_pulse_delay_in_pickup">200</integer>
+
     <!-- Doze: pulse parameter - once faded in, how long does it stay visible? -->
     <integer name="doze_pulse_duration_visible">3000</integer>
 
index 68a7622..44330e8 100644 (file)
          type icon is wide. -->
     <dimen name="wide_type_icon_start_padding">2dp</dimen>
 
+    <!-- Extra padding between multiple phone signal icons. -->
+    <dimen name="secondary_telephony_padding">2dp</dimen>
+
     <!-- Extra padding between the mobile data type icon and the strength indicator when the data
          type icon is wide for the tile in quick settings. -->
     <dimen name="wide_type_icon_start_padding_qs">3dp</dimen>
index 5d74082..a9e732e 100644 (file)
@@ -26,7 +26,7 @@
     <dimen name="obstacle_width">90dp</dimen>
     <dimen name="obstacle_stem_width">12dp</dimen>
     <dimen name="obstacle_gap">170dp</dimen>
-    <dimen name="obstacle_height_min">40dp</dimen>
+    <dimen name="obstacle_height_min">48dp</dimen>
     <dimen name="building_width_min">20dp</dimen>
     <dimen name="building_width_max">250dp</dimen>
     <dimen name="building_height_min">20dp</dimen>
index 6337956..191cba5 100644 (file)
@@ -9,6 +9,7 @@ option java_package com.android.systemui;
 36001 sysui_heads_up_status (key|3),(visible|1)
 36002 sysui_fullscreen_notification (key|3)
 36003 sysui_heads_up_escalation (key|3)
+36004 sysui_status_bar_state (state|1)
 
 # ---------------------------
 # PhoneStatusBarView.java
index 6ea5c70..89a2c74 100644 (file)
@@ -28,6 +28,7 @@ public interface DozeHost {
     void pulseWhileDozing(@NonNull PulseCallback callback, int reason);
     void stopDozing();
     boolean isPowerSaveActive();
+    boolean isNotificationLightOn();
 
     public interface Callback {
         void onNewNotifications();
@@ -40,4 +41,4 @@ public interface DozeHost {
         void onPulseStarted();
         void onPulseFinished();
     }
-}
\ No newline at end of file
+}
index 7eb9c6e..1f3a830 100644 (file)
@@ -153,7 +153,7 @@ public class DozeLog {
         sProxStats[pulseReason][near ? 0 : 1].append();
     }
 
-    private static String pulseReasonToString(int pulseReason) {
+    public static String pulseReasonToString(int pulseReason) {
         switch (pulseReason) {
             case PULSE_REASON_INTENT: return "intent";
             case PULSE_REASON_NOTIFICATION: return "notification";
index 1e29476..8d27450 100644 (file)
@@ -56,6 +56,17 @@ public class DozeService extends DreamService {
     private static final String NOTIFICATION_PULSE_ACTION = ACTION_BASE + ".notification_pulse";
     private static final String EXTRA_INSTANCE = "instance";
 
+    /**
+     * Earliest time we pulse due to a notification light after the service started.
+     *
+     * <p>Incoming notification light events during the blackout period are
+     * delayed to the earliest time defined by this constant.</p>
+     *
+     * <p>This delay avoids a pulse immediately after screen off, at which
+     * point the notification light is re-enabled again by NoMan.</p>
+     */
+    private static final int EARLIEST_LIGHT_PULSE_AFTER_START_MS = 10 * 1000;
+
     private final String mTag = String.format(TAG + ".%08x", hashCode());
     private final Context mContext = this;
     private final DozeParameters mDozeParameters = new DozeParameters(mContext);
@@ -77,6 +88,7 @@ public class DozeService extends DreamService {
     private boolean mPowerSaveActive;
     private boolean mCarMode;
     private long mNotificationPulseTime;
+    private long mEarliestPulseDueToLight;
     private int mScheduleResetsRemaining;
 
     public DozeService() {
@@ -161,8 +173,9 @@ public class DozeService extends DreamService {
         }
 
         mDreaming = true;
-        listenForPulseSignals(true);
         rescheduleNotificationPulse(false /*predicate*/);  // cancel any pending pulse alarms
+        mEarliestPulseDueToLight = System.currentTimeMillis() + EARLIEST_LIGHT_PULSE_AFTER_START_MS;
+        listenForPulseSignals(true);
 
         // Ask the host to get things ready to start dozing.
         // Once ready, we call startDozing() at which point the CPU may suspend
@@ -209,15 +222,25 @@ public class DozeService extends DreamService {
                 continuePulsing(reason);
                 return;
             }
-            // perform a proximity check before pulsing
             final long start = SystemClock.uptimeMillis();
+            final boolean nonBlocking = reason == DozeLog.PULSE_REASON_SENSOR_PICKUP
+                    && mDozeParameters.getPickupPerformsProxCheck();
+            if (nonBlocking) {
+                // proximity check is only done to capture statistics, continue pulsing
+                continuePulsing(reason);
+            }
+            // perform a proximity check
             new ProximityCheck() {
                 @Override
                 public void onProximityResult(int result) {
-                    // avoid pulsing in pockets
                     final boolean isNear = result == RESULT_NEAR;
                     final long end = SystemClock.uptimeMillis();
                     DozeLog.traceProximityResult(isNear, end - start, reason);
+                    if (nonBlocking) {
+                        // we already continued
+                        return;
+                    }
+                    // avoid pulsing in pockets
                     if (isNear) {
                         mPulsing = false;
                         mWakeLock.release();
@@ -298,6 +321,12 @@ public class DozeService extends DreamService {
         if (listen) {
             resetNotificationResets();
             mHost.addCallback(mHostCallback);
+
+            // Continue to pulse for existing LEDs.
+            mNotificationLightOn = mHost.isNotificationLightOn();
+            if (mNotificationLightOn) {
+                updateNotificationPulseDueToLight();
+            }
         } else {
             mHost.removeCallback(mHostCallback);
         }
@@ -308,21 +337,27 @@ public class DozeService extends DreamService {
         mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets();
     }
 
-    private void updateNotificationPulse() {
-        if (DEBUG) Log.d(mTag, "updateNotificationPulse");
+    private void updateNotificationPulseDueToLight() {
+        long timeMs = System.currentTimeMillis();
+        timeMs = Math.max(timeMs, mEarliestPulseDueToLight);
+        updateNotificationPulse(timeMs);
+    }
+
+    private void updateNotificationPulse(long notificationTimeMs) {
+        if (DEBUG) Log.d(mTag, "updateNotificationPulse notificationTimeMs=" + notificationTimeMs);
         if (!mDozeParameters.getPulseOnNotifications()) return;
         if (mScheduleResetsRemaining <= 0) {
             if (DEBUG) Log.d(mTag, "No more schedule resets remaining");
             return;
         }
-        final long now = System.currentTimeMillis();
-        if ((now - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) {
+        final long pulseDuration = mDozeParameters.getPulseDuration(false /*pickup*/);
+        if ((notificationTimeMs - mNotificationPulseTime) < pulseDuration) {
             if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule");
             return;
         }
         mScheduleResetsRemaining--;
         if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
-        mNotificationPulseTime = now;
+        mNotificationPulseTime = notificationTimeMs;
         rescheduleNotificationPulse(true /*predicate*/);
     }
 
@@ -404,14 +439,14 @@ public class DozeService extends DreamService {
     private final DozeHost.Callback mHostCallback = new DozeHost.Callback() {
         @Override
         public void onNewNotifications() {
-            if (DEBUG) Log.d(mTag, "onNewNotifications");
+            if (DEBUG) Log.d(mTag, "onNewNotifications (noop)");
             // noop for now
         }
 
         @Override
         public void onBuzzBeepBlinked() {
             if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked");
-            updateNotificationPulse();
+            updateNotificationPulse(System.currentTimeMillis());
         }
 
         @Override
@@ -420,7 +455,7 @@ public class DozeService extends DreamService {
             if (mNotificationLightOn == on) return;
             mNotificationLightOn = on;
             if (mNotificationLightOn) {
-                updateNotificationPulse();
+                updateNotificationPulseDueToLight();
             }
         }
 
index cdfe6e5..5de09a3 100644 (file)
@@ -29,11 +29,15 @@ import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.os.Vibrator;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.util.Slog;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 import android.view.animation.DecelerateInterpolator;
@@ -51,9 +55,9 @@ public class LLand extends FrameLayout {
     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final boolean DEBUG_DRAW = false; // DEBUG
 
-    public static final void L(String s, Object ... objects) {
+    public static void L(String s, Object ... objects) {
         if (DEBUG) {
-            Log.d(TAG, String.format(s, objects));
+            Slog.d(TAG, objects.length == 0 ? s : String.format(s, objects));
         }
     }
 
@@ -61,17 +65,18 @@ public class LLand extends FrameLayout {
     public static final boolean HAVE_STARS = true;
 
     public static final float DEBUG_SPEED_MULTIPLIER = 1f; // 0.1f;
-    public static final boolean DEBUG_IDDQD = false;
+    public static final boolean DEBUG_IDDQD = Log.isLoggable(TAG + ".iddqd", Log.DEBUG);
 
     final static int[] POPS = {
-            // resid                // spinny!
-            R.drawable.pop_belt,    0,
-            R.drawable.pop_droid,   0,
-            R.drawable.pop_pizza,   1,
-            R.drawable.pop_stripes, 0,
-            R.drawable.pop_swirl,   1,
-            R.drawable.pop_vortex,  1,
-            R.drawable.pop_vortex2, 1,
+            // resid                // spinny!  // alpha
+            R.drawable.pop_belt,    0,          255,
+            R.drawable.pop_droid,   0,          255,
+            R.drawable.pop_pizza,   1,          255,
+            R.drawable.pop_stripes, 0,          255,
+            R.drawable.pop_swirl,   1,          255,
+            R.drawable.pop_vortex,  1,          255,
+            R.drawable.pop_vortex2, 1,          255,
+            R.drawable.pop_ball,    0,          190,
     };
 
     private static class Params {
@@ -117,10 +122,20 @@ public class LLand extends FrameLayout {
             PLAYER_Z = res.getDimensionPixelSize(R.dimen.player_z);
             PLAYER_Z_BOOST = res.getDimensionPixelSize(R.dimen.player_z_boost);
             HUD_Z = res.getDimensionPixelSize(R.dimen.hud_z);
+
+            // Sanity checking
+            if (OBSTACLE_MIN <= OBSTACLE_WIDTH / 2) {
+                Slog.e(TAG, "error: obstacles might be too short, adjusting");
+                OBSTACLE_MIN = OBSTACLE_WIDTH / 2 + 1;
+            }
         }
     }
 
     private TimeAnimator mAnim;
+    private Vibrator mVibrator;
+    private AudioManager mAudioManager;
+    private final AudioAttributes mAudioAttrs = new AudioAttributes.Builder()
+            .setUsage(AudioAttributes.USAGE_GAME).build();
 
     private TextView mScoreField;
     private View mSplash;
@@ -158,6 +173,8 @@ public class LLand extends FrameLayout {
 
     public LLand(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         setFocusable(true);
         PARAMS = new Params(getResources());
         mTimeOfDay = irand(0, SKIES.length);
@@ -198,7 +215,15 @@ public class LLand extends FrameLayout {
 
     final float hsv[] = {0, 0, 0};
 
-    private void reset() {
+    private void thump() {
+        if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
+            // No interruptions. Not even game haptics.
+            return;
+        }
+        mVibrator.vibrate(80, mAudioAttrs);
+    }
+
+    public void reset() {
         L("reset");
         final Drawable sky = new GradientDrawable(
                 GradientDrawable.Orientation.BOTTOM_TOP,
@@ -313,14 +338,16 @@ public class LLand extends FrameLayout {
 
     private void setScore(int score) {
         mScore = score;
-        if (mScoreField != null) mScoreField.setText(String.valueOf(score));
+        if (mScoreField != null) {
+            mScoreField.setText(DEBUG_IDDQD ? "??" : String.valueOf(score));
+        }
     }
 
     private void addScore(int incr) {
         setScore(mScore + incr);
     }
 
-    private void start(boolean startPlaying) {
+    public void start(boolean startPlaying) {
         L("start(startPlaying=%s)", startPlaying?"true":"false");
         if (startPlaying) {
             mPlaying = true;
@@ -352,7 +379,7 @@ public class LLand extends FrameLayout {
         }
     }
 
-    private void stop() {
+    public void stop() {
         if (mAnimating) {
             mAnim.cancel();
             mAnim = null;
@@ -417,8 +444,10 @@ public class LLand extends FrameLayout {
         if (mPlaying && mDroid.below(mHeight)) {
             if (DEBUG_IDDQD) {
                 poke();
+                unpoke();
             } else {
                 L("player hit the floor");
+                thump();
                 stop();
             }
         }
@@ -429,6 +458,7 @@ public class LLand extends FrameLayout {
             final Obstacle ob = mObstaclesInPlay.get(j);
             if (mPlaying && ob.intersects(mDroid) && !DEBUG_IDDQD) {
                 L("player hit an obstacle");
+                thump();
                 stop();
             } else if (ob.cleared(mDroid)) {
                 if (ob instanceof Stem) passedBarrier = true;
@@ -459,8 +489,9 @@ public class LLand extends FrameLayout {
         // 3. Time for more obstacles!
         if (mPlaying && (t - mLastPipeTime) > PARAMS.OBSTACLE_PERIOD) {
             mLastPipeTime = t;
-            final int obstacley = (int) (Math.random()
-                    * (mHeight - 2*PARAMS.OBSTACLE_MIN - PARAMS.OBSTACLE_GAP)) + PARAMS.OBSTACLE_MIN;
+            final int obstacley =
+                    (int)(frand() * (mHeight - 2*PARAMS.OBSTACLE_MIN - PARAMS.OBSTACLE_GAP)) +
+                    PARAMS.OBSTACLE_MIN;
 
             final int inset = (PARAMS.OBSTACLE_WIDTH - PARAMS.OBSTACLE_STEM_WIDTH) / 2;
             final int yinset = PARAMS.OBSTACLE_WIDTH/2;
@@ -539,7 +570,7 @@ public class LLand extends FrameLayout {
     
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (DEBUG) L("touch: %s", ev);
+        L("touch: %s", ev);
         switch (ev.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 poke();
@@ -553,7 +584,7 @@ public class LLand extends FrameLayout {
 
     @Override
     public boolean onTrackballEvent(MotionEvent ev) {
-        if (DEBUG) L("trackball: %s", ev);
+        L("trackball: %s", ev);
         switch (ev.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 poke();
@@ -567,7 +598,7 @@ public class LLand extends FrameLayout {
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent ev) {
-        if (DEBUG) L("keyDown: %d", keyCode);
+        L("keyDown: %d", keyCode);
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_CENTER:
             case KeyEvent.KEYCODE_DPAD_UP:
@@ -582,7 +613,7 @@ public class LLand extends FrameLayout {
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent ev) {
-        if (DEBUG) L("keyDown: %d", keyCode);
+        L("keyDown: %d", keyCode);
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_CENTER:
             case KeyEvent.KEYCODE_DPAD_UP:
@@ -597,7 +628,7 @@ public class LLand extends FrameLayout {
 
     @Override
     public boolean onGenericMotionEvent (MotionEvent ev) {
-        if (DEBUG) L("generic: %s", ev);
+        L("generic: %s", ev);
         return false;
     }
 
@@ -684,6 +715,10 @@ public class LLand extends FrameLayout {
 
         private boolean mBoosting;
 
+        private final int[] sColors = new int[] {
+                0xFF78C557,
+        };
+
         private final float[] sHull = new float[] {
                 0.3f,  0f,    // left antenna
                 0.7f,  0f,    // right antenna
@@ -692,7 +727,7 @@ public class LLand extends FrameLayout {
                 0.6f,  1f,    // right foot
                 0.4f,  1f,    // left foot BLUE!
                 0.08f, 0.75f, // sinistram
-                0.08f, 0.33f,  // cold shoulder
+                0.08f, 0.33f, // cold shoulder
         };
         public final float[] corners = new float[sHull.length];
 
@@ -701,7 +736,7 @@ public class LLand extends FrameLayout {
 
             setBackgroundResource(R.drawable.android);
             getBackground().setTintMode(PorterDuff.Mode.SRC_ATOP);
-            getBackground().setTint(0xFF00FF00);
+            getBackground().setTint(sColors[0]);
             setOutlineProvider(new ViewOutlineProvider() {
                 @Override
                 public void getOutline(View view, Outline outline) {
@@ -822,8 +857,9 @@ public class LLand extends FrameLayout {
         int cx, cy, r;
         public Pop(Context context, float h) {
             super(context, h);
-            int idx = 2*irand(0, POPS.length/2);
+            int idx = 3*irand(0, POPS.length/3);
             setBackgroundResource(POPS[idx]);
+            setAlpha((float)(POPS[idx+2])/255);
             setScaleX(frand() < 0.5f ? -1 : 1);
             mRotate = POPS[idx+1] == 0 ? 0 : (frand() < 0.5f ? -1 : 1);
             setOutlineProvider(new ViewOutlineProvider() {
index 88fd952..b9f8106 100644 (file)
@@ -24,13 +24,21 @@ import android.widget.TextView;
 import com.android.systemui.R;
 
 public class LLandActivity extends Activity {
+    LLand mLand;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.lland);
-        LLand world = (LLand) findViewById(R.id.world);
-        world.setScoreField((TextView) findViewById(R.id.score));
-        world.setSplash(findViewById(R.id.welcome));
-        Log.v(LLand.TAG, "focus: " + world.requestFocus());
+        mLand = (LLand) findViewById(R.id.world);
+        mLand.setScoreField((TextView) findViewById(R.id.score));
+        mLand.setSplash(findViewById(R.id.welcome));
+        //Log.v(LLand.TAG, "focus: " + mLand.requestFocus());
+    }
+
+    @Override
+    public void onPause() {
+        mLand.stop();
+        super.onPause();
     }
 }
index 80ddd4a..f2ebcf6 100644 (file)
@@ -147,16 +147,15 @@ public class CellularTile extends QSTile<QSTile.SignalState> {
     }
 
     private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() {
-        private boolean mWifiEnabled;
-        private boolean mWifiConnected;
-        private boolean mAirplaneModeEnabled;
+        private final CallbackInfo mInfo = new CallbackInfo();
 
         @Override
         public void onWifiSignalChanged(boolean enabled, boolean connected, int wifiSignalIconId,
                 boolean activityIn, boolean activityOut,
                 String wifiSignalContentDescriptionId, String description) {
-            mWifiEnabled = enabled;
-            mWifiConnected = connected;
+            mInfo.wifiEnabled = enabled;
+            mInfo.wifiConnected = connected;
+            refreshState(mInfo);
         }
 
         @Override
@@ -164,28 +163,35 @@ public class CellularTile extends QSTile<QSTile.SignalState> {
                 int mobileSignalIconId,
                 String mobileSignalContentDescriptionId, int dataTypeIconId,
                 boolean activityIn, boolean activityOut,
-                String dataTypeContentDescriptionId, String description, boolean noSim,
+                String dataTypeContentDescriptionId, String description,
                 boolean isDataTypeIconWide) {
-            final CallbackInfo info = new CallbackInfo();  // TODO pool?
-            info.enabled = enabled;
-            info.wifiEnabled = mWifiEnabled;
-            info.wifiConnected = mWifiConnected;
-            info.airplaneModeEnabled = mAirplaneModeEnabled;
-            info.mobileSignalIconId = mobileSignalIconId;
-            info.signalContentDescription = mobileSignalContentDescriptionId;
-            info.dataTypeIconId = dataTypeIconId;
-            info.dataContentDescription = dataTypeContentDescriptionId;
-            info.activityIn = activityIn;
-            info.activityOut = activityOut;
-            info.enabledDesc = description;
-            info.noSim = noSim;
-            info.isDataTypeIconWide = isDataTypeIconWide;
-            refreshState(info);
+            mInfo.enabled = enabled;
+            mInfo.mobileSignalIconId = mobileSignalIconId;
+            mInfo.signalContentDescription = mobileSignalContentDescriptionId;
+            mInfo.dataTypeIconId = dataTypeIconId;
+            mInfo.dataContentDescription = dataTypeContentDescriptionId;
+            mInfo.activityIn = activityIn;
+            mInfo.activityOut = activityOut;
+            mInfo.enabledDesc = description;
+            mInfo.isDataTypeIconWide = isDataTypeIconWide;
+            refreshState(mInfo);
+        }
+
+        @Override
+        public void onNoSimVisibleChanged(boolean visible) {
+            mInfo.noSim = visible;
+            if (mInfo.noSim) {
+                // Make sure signal gets cleared out when no sims.
+                mInfo.mobileSignalIconId = 0;
+                mInfo.dataTypeIconId = 0;
+            }
+            refreshState(mInfo);
         }
 
         @Override
         public void onAirplaneModeChanged(boolean enabled) {
-            mAirplaneModeEnabled = enabled;
+            mInfo.airplaneModeEnabled = enabled;
+            refreshState(mInfo);
         }
 
         public void onMobileDataEnabled(boolean enabled) {
@@ -203,7 +209,8 @@ public class CellularTile extends QSTile<QSTile.SignalState> {
         @Override
         public Boolean getToggleState() {
             return mDataController.isMobileDataSupported()
-                    ? mDataController.isMobileDataEnabled() : null;
+                    ? mDataController.isMobileDataEnabled()
+                    : null;
         }
 
         @Override
index bccc753..9744dca 100644 (file)
@@ -85,8 +85,7 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> {
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed()
-                && !mController.isProvisioningNeeded();
+        state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed();
         state.label = mContext.getString(R.string.quick_settings_hotspot_label);
 
         state.value = mController.isHotspotEnabled();
index c524edc..6bad652 100644 (file)
@@ -47,6 +47,10 @@ public class UserDetailView extends PseudoGridView {
         ViewGroupAdapterBridge.link(this, mAdapter);
     }
 
+    public void refreshAdapter() {
+        mAdapter.refresh();
+    }
+
     public static class Adapter extends UserSwitcherController.BaseUserAdapter
             implements OnClickListener {
 
index 4fb1189..5e30622 100644 (file)
@@ -217,11 +217,15 @@ public class WifiTile extends QSTile<QSTile.SignalState> {
                 int mobileSignalIconId,
                 String mobileSignalContentDescriptionId, int dataTypeIconId,
                 boolean activityIn, boolean activityOut,
-                String dataTypeContentDescriptionId, String description, boolean noSim,
+                String dataTypeContentDescriptionId, String description,
                 boolean isDataTypeIconWide) {
             // noop
         }
 
+        public void onNoSimVisibleChanged(boolean noSims) {
+            // noop
+        }
+
         @Override
         public void onAirplaneModeChanged(boolean enabled) {
             // noop
index 29f291d..4f0700e 100644 (file)
@@ -19,6 +19,7 @@ package com.android.systemui.recents;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.ITaskStackListener;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
@@ -56,6 +57,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+
 /** A proxy implementation for the recents component */
 public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener {
 
@@ -79,6 +81,28 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
     final static String sRecentsPackage = "com.android.systemui";
     final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
 
+    /**
+     * An implementation of ITaskStackListener, that allows us to listen for changes to the system
+     * task stacks and update recents accordingly.
+     */
+    class TaskStackListenerImpl extends ITaskStackListener.Stub {
+        @Override
+        public void onTaskStackChanged() {
+            RecentsConfiguration config = RecentsConfiguration.getInstance();
+            if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
+                // Load the next task only if we aren't svelte
+                RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+                RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+                loader.preloadTasks(plan, true /* isTopTaskHome */);
+                RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+                launchOpts.numVisibleTasks = 1;
+                launchOpts.numVisibleTaskThumbnails = 1;
+                launchOpts.onlyLoadForCache = true;
+                loader.loadTasks(mContext, plan, launchOpts);
+            }
+        }
+    }
+
     static RecentsComponent.Callbacks sRecentsComponentCallbacks;
     static RecentsTaskLoadPlan sInstanceLoadPlan;
 
@@ -86,6 +110,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
     LayoutInflater mInflater;
     SystemServicesProxy mSystemServicesProxy;
     Handler mHandler;
+    TaskStackListenerImpl mTaskStackListener;
     boolean mBootCompleted;
     boolean mStartAnimationTriggered;
     boolean mCanReuseTaskStackViews = true;
@@ -116,6 +141,10 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
         mSystemServicesProxy = new SystemServicesProxy(context);
         mHandler = new Handler();
         mTaskStackBounds = new Rect();
+
+        // Register the task stack listener
+        mTaskStackListener = new TaskStackListenerImpl();
+        mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
     }
 
     public void onStart() {
index 6dc2edb..a37bc54 100644 (file)
@@ -167,10 +167,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
             if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 // When the screen turns off, dismiss Recents to Home
                 dismissRecentsToHome(false);
-                // Preload the metadata for all tasks in the background
-                RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-                RecentsTaskLoadPlan plan = loader.createLoadPlan(context);
-                loader.preloadTasks(plan, true /* isTopTaskHome */);
             } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
                 // When the search activity changes, update the Search widget
                 refreshSearchWidget();
@@ -437,22 +433,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
             onEnterAnimationTriggered();
         }
 
-        // Start listening for widget package changes if there is one bound, post it since we don't
-        // want it stalling the startup
-        if (mConfig.searchBarAppWidgetId >= 0) {
-            final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> callback =
-                    new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(this);
-            mRecentsView.post(new Runnable() {
-                @Override
-                public void run() {
-                    RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = callback.get();
-                    if (cb != null) {
-                        mAppWidgetHost.startListening(cb);
-                    }
-                }
-            });
-        }
-
         mStatusBar = ((SystemUIApplication) getApplication())
                 .getComponent(PhoneStatusBar.class);
     }
@@ -539,15 +519,29 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
         unregisterReceiver(mSystemBroadcastReceiver);
 
         // Stop listening for widget package changes if there was one bound
-        if (mAppWidgetHost.isListening()) {
-            mAppWidgetHost.stopListening();
-        }
+        mAppWidgetHost.stopListening();
     }
 
     public void onEnterAnimationTriggered() {
         // Try and start the enter animation (or restart it on configuration changed)
         ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
-        mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
+        ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
+        mRecentsView.startEnterRecentsAnimation(ctx);
+        if (mConfig.searchBarAppWidgetId >= 0) {
+            final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> cbRef =
+                    new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(
+                            RecentsActivity.this);
+            ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    // Start listening for widget package changes if there is one bound
+                    RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = cbRef.get();
+                    if (cb != null) {
+                        mAppWidgetHost.startListening(cb);
+                    }
+                }
+            });
+        }
 
         // Animate the SystemUI scrim views
         mScrimViews.startEnterRecentsAnimation();
index a63e167..5bae37a 100644 (file)
@@ -43,23 +43,23 @@ public class RecentsAppWidgetHost extends AppWidgetHost {
 
     public void startListening(RecentsAppWidgetHostCallbacks cb) {
         mCb = cb;
-        mIsListening = true;
-        super.startListening();
+        if (!mIsListening) {
+            mIsListening = true;
+            super.startListening();
+        }
     }
 
     @Override
     public void stopListening() {
-        super.stopListening();
+        if (mIsListening) {
+            super.stopListening();
+        }
         // Ensure that we release any references to the callbacks
         mCb = null;
         mContext = null;
         mIsListening = false;
     }
 
-    public boolean isListening() {
-        return mIsListening;
-    }
-
     @Override
     protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
         if (mCb == null) return;
index 9a4bd08..3fbd5a6 100644 (file)
@@ -21,6 +21,7 @@ import android.app.ActivityManagerNative;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
+import android.app.ITaskStackListener;
 import android.app.SearchManager;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
@@ -536,4 +537,15 @@ public class SystemServicesProxy {
             e.printStackTrace();
         }
     }
+
+    /** Registers a task stack listener with the system. */
+    public void registerTaskStackListener(ITaskStackListener listener) {
+        if (mIam == null) return;
+
+        try {
+            mIam.registerTaskStackListener(listener);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
 }
index 46a5d8d..746a7df 100644 (file)
@@ -204,6 +204,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
 
     /** Requests all task stacks to start their enter-recents animation */
     public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+        // We have to increment/decrement the post animation trigger in case there are no children
+        // to ensure that it runs
+        ctx.postAnimationTrigger.increment();
+
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
@@ -212,6 +216,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
                 stackView.startEnterRecentsAnimation(ctx);
             }
         }
+        ctx.postAnimationTrigger.decrement();
     }
 
     /** Requests all task stacks to start their exit-recents animation */
index fa44551..0428b48 100644 (file)
@@ -50,7 +50,6 @@ public class SwipeHelper {
     private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
     private int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms
     private int MAX_ESCAPE_ANIMATION_DURATION = 150; // ms
-    private int MAX_DISMISS_VELOCITY = 2000; // dp/sec
     private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 250; // ms
 
     public static float ALPHA_FADE_START = 0.15f; // fraction of thumbnail width
@@ -350,8 +349,7 @@ public class SwipeHelper {
     }
 
     private void endSwipe(VelocityTracker velocityTracker) {
-        float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
-        velocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
+        velocityTracker.computeCurrentVelocity(1000 /* px/sec */);
         float velocity = getVelocity(velocityTracker);
         float perpendicularVelocity = getPerpendicularVelocity(velocityTracker);
         float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
index 2623db3..33a36f6 100644 (file)
@@ -530,11 +530,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
 
     /** Resets the focused task. */
     void resetFocusedTask() {
-        if ((mStack != null) && (0 <= mFocusedTaskIndex) &&
-                (mFocusedTaskIndex < mStack.getTaskCount())) {
+        if ((0 <= mFocusedTaskIndex) && (mFocusedTaskIndex < mStack.getTaskCount())) {
             Task t = mStack.getTasks().get(mFocusedTaskIndex);
             TaskView tv = getChildViewForTask(t);
-            tv.unsetFocusedTask();
+            if (tv != null) {
+                tv.unsetFocusedTask();
+            }
         }
         mFocusedTaskIndex = -1;
     }
index 2b173a9..c59f82c 100644 (file)
@@ -143,22 +143,21 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
                 // Initialize the velocity tracker
                 initOrResetVelocityTracker();
                 mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
-                // Check if the scroller is finished yet
-                mIsScrolling = mScroller.isScrolling();
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
                 if (mActivePointerId == INACTIVE_POINTER_ID) break;
 
+                // Initialize the velocity tracker if necessary
+                initVelocityTrackerIfNotExists();
+                mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
+
                 int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                 int y = (int) ev.getY(activePointerIndex);
                 int x = (int) ev.getX(activePointerIndex);
                 if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
                     // Save the touch move info
                     mIsScrolling = true;
-                    // Initialize the velocity tracker if necessary
-                    initVelocityTrackerIfNotExists();
-                    mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
                     // Disallow parents from intercepting touch events
                     final ViewParent parent = mSv.getParent();
                     if (parent != null) {
@@ -237,6 +236,8 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
             case MotionEvent.ACTION_MOVE: {
                 if (mActivePointerId == INACTIVE_POINTER_ID) break;
 
+                mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
+
                 int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                 int x = (int) ev.getX(activePointerIndex);
                 int y = (int) ev.getY(activePointerIndex);
@@ -246,9 +247,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
                 if (!mIsScrolling) {
                     if (yTotal > mScrollTouchSlop) {
                         mIsScrolling = true;
-                        // Initialize the velocity tracker
-                        initOrResetVelocityTracker();
-                        mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
                         // Disallow parents from intercepting touch events
                         final ViewParent parent = mSv.getParent();
                         if (parent != null) {
@@ -267,11 +265,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
                                 / maxOverScroll));
                     }
                     mScroller.setStackScroll(curStackScroll + deltaP);
-                    if (mScroller.isScrollOutOfBounds()) {
-                        mVelocityTracker.clear();
-                    } else {
-                        mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
-                    }
                 }
                 mLastMotionX = x;
                 mLastMotionY = y;
index 4586f12..e1d80fd 100644 (file)
@@ -27,7 +27,7 @@ public class ViewAnimation {
     public static class TaskViewEnterContext {
         // A trigger to run some logic when all the animations complete.  This works around the fact
         // that it is difficult to coordinate ViewPropertyAnimators
-        ReferenceCountedTrigger postAnimationTrigger;
+        public ReferenceCountedTrigger postAnimationTrigger;
         // An update listener to notify as the enter animation progresses (used for the home transition)
         ValueAnimator.AnimatorUpdateListener updateListener;
 
index 4a16f8d..57ac4b0 100644 (file)
@@ -689,6 +689,12 @@ public abstract class BaseStatusBar extends SystemUI implements
         }
     }
 
+    protected boolean isCurrentProfile(int userId) {
+        synchronized (mCurrentProfiles) {
+            return mCurrentProfiles.get(userId) != null;
+        }
+    }
+
     @Override
     public String getCurrentMediaNotificationKey() {
         return null;
@@ -1642,7 +1648,11 @@ public abstract class BaseStatusBar extends SystemUI implements
     protected void handleVisibleToUserChanged(boolean visibleToUser) {
         try {
             if (visibleToUser) {
-                mBarService.onPanelRevealed();
+                // Only stop blinking, vibrating, ringing when the user went into the shade
+                // manually (SHADE or SHADE_LOCKED).
+                boolean clearNotificationEffects =
+                        (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
+                mBarService.onPanelRevealed(clearNotificationEffects);
             } else {
                 mBarService.onPanelHidden();
             }
index 27da6fd..0faad21 100644 (file)
@@ -244,6 +244,9 @@ public class NotificationContentView extends FrameLayout {
 
     public void notifyContentUpdated() {
         selectLayout(false /* animate */, true /* force */);
+        if (mContractedChild != null) {
+            mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
+        }
     }
 
     public boolean isContentExpandable() {
index 045be3e..0702d7e 100644 (file)
@@ -27,7 +27,6 @@ import com.android.systemui.statusbar.phone.NotificationPanelView;
 public class NotificationCustomViewWrapper extends NotificationViewWrapper {
 
     private final ViewInvertHelper mInvertHelper;
-    private boolean mDark;
 
     protected NotificationCustomViewWrapper(View view) {
         super(view);
@@ -36,13 +35,10 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper {
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
-        if (mDark != dark) {
-            mDark = dark;
-            if (fade) {
-                mInvertHelper.fade(dark, delay);
-            } else {
-                mInvertHelper.update(dark);
-            }
+        if (fade) {
+            mInvertHelper.fade(dark, delay);
+        } else {
+            mInvertHelper.update(dark);
         }
     }
 }
index 8f63a79..953c373 100644 (file)
@@ -24,19 +24,14 @@ import android.view.View;
  */
 public class NotificationMediaViewWrapper extends NotificationTemplateViewWrapper {
 
-    private boolean mDark;
-
     protected NotificationMediaViewWrapper(Context ctx, View view) {
         super(ctx, view);
     }
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
-        if (mDark != dark) {
-            mDark = dark;
 
-            // Only update the large icon, because the rest is already inverted.
-            setPictureGrayscale(dark, fade, delay);
-        }
+        // Only update the large icon, because the rest is already inverted.
+        setPictureGrayscale(dark, fade, delay);
     }
 }
index 8dc14b0..5b6e1cd 100644 (file)
@@ -51,8 +51,6 @@ public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
     private final int mIconBackgroundDarkColor;
     private final Interpolator mLinearOutSlowInInterpolator;
 
-    private boolean mDark;
-
     protected NotificationTemplateViewWrapper(Context ctx, View view) {
         super(view);
         mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
@@ -95,26 +93,23 @@ public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
-        if (mDark != dark) {
-            mDark = dark;
-            if (mInvertHelper != null) {
-                if (fade) {
-                    mInvertHelper.fade(dark, delay);
-                } else {
-                    mInvertHelper.update(dark);
-                }
+        if (mInvertHelper != null) {
+            if (fade) {
+                mInvertHelper.fade(dark, delay);
+            } else {
+                mInvertHelper.update(dark);
             }
-            if (mIcon != null) {
-                if (fade) {
-                    fadeIconColorFilter(mIcon, dark, delay);
-                    fadeIconAlpha(mIcon, dark, delay);
-                } else {
-                    updateIconColorFilter(mIcon, dark);
-                    updateIconAlpha(mIcon, dark);
-                }
+        }
+        if (mIcon != null) {
+            if (fade) {
+                fadeIconColorFilter(mIcon, dark, delay);
+                fadeIconAlpha(mIcon, dark, delay);
+            } else {
+                updateIconColorFilter(mIcon, dark);
+                updateIconAlpha(mIcon, dark);
             }
-            setPictureGrayscale(dark, fade, delay);
         }
+        setPictureGrayscale(dark, fade, delay);
     }
 
     protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
index 418c57f..8e50abe 100644 (file)
 package com.android.systemui.statusbar;
 
 import android.content.Context;
+import android.telephony.SubscriptionInfo;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
@@ -29,6 +31,9 @@ import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
 
+import java.util.ArrayList;
+import java.util.List;
+
 // Intimately tied to the design of res/layout/signal_cluster_view.xml
 public class SignalClusterView
         extends LinearLayout
@@ -41,23 +46,24 @@ public class SignalClusterView
     NetworkControllerImpl mNC;
     SecurityController mSC;
 
+    private boolean mNoSimsVisible = false;
     private boolean mVpnVisible = false;
     private boolean mWifiVisible = false;
     private int mWifiStrengthId = 0;
-    private boolean mMobileVisible = false;
-    private int mMobileStrengthId = 0, mMobileTypeId = 0;
     private boolean mIsAirplaneMode = false;
     private int mAirplaneIconId = 0;
     private int mAirplaneContentDescription;
-    private String mWifiDescription, mMobileDescription, mMobileTypeDescription;
-    private boolean mIsMobileTypeIconWide;
+    private String mWifiDescription;
+    private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
 
-    ViewGroup mWifiGroup, mMobileGroup;
-    ImageView mVpn, mWifi, mMobile, mMobileType, mAirplane;
+    ViewGroup mWifiGroup;
+    ImageView mVpn, mWifi, mAirplane, mNoSims;
     View mWifiAirplaneSpacer;
     View mWifiSignalSpacer;
+    LinearLayout mMobileSignalGroup;
 
     private int mWideTypeIconStartPadding;
+    private int mSecondaryTelephonyPadding;
     private int mEndPadding;
     private int mEndPaddingNothingVisible;
 
@@ -90,6 +96,8 @@ public class SignalClusterView
         super.onFinishInflate();
         mWideTypeIconStartPadding = getContext().getResources().getDimensionPixelSize(
                 R.dimen.wide_type_icon_start_padding);
+        mSecondaryTelephonyPadding = getContext().getResources().getDimensionPixelSize(
+                R.dimen.secondary_telephony_padding);
         mEndPadding = getContext().getResources().getDimensionPixelSize(
                 R.dimen.signal_cluster_battery_padding);
         mEndPaddingNothingVisible = getContext().getResources().getDimensionPixelSize(
@@ -103,12 +111,14 @@ public class SignalClusterView
         mVpn            = (ImageView) findViewById(R.id.vpn);
         mWifiGroup      = (ViewGroup) findViewById(R.id.wifi_combo);
         mWifi           = (ImageView) findViewById(R.id.wifi_signal);
-        mMobileGroup    = (ViewGroup) findViewById(R.id.mobile_combo);
-        mMobile         = (ImageView) findViewById(R.id.mobile_signal);
-        mMobileType     = (ImageView) findViewById(R.id.mobile_type);
         mAirplane       = (ImageView) findViewById(R.id.airplane);
+        mNoSims         = (ImageView) findViewById(R.id.no_sims);
         mWifiAirplaneSpacer =         findViewById(R.id.wifi_airplane_spacer);
         mWifiSignalSpacer =           findViewById(R.id.wifi_signal_spacer);
+        mMobileSignalGroup = (LinearLayout) findViewById(R.id.mobile_signal_group);
+        for (PhoneState state : mPhoneStates) {
+            mMobileSignalGroup.addView(state.mMobileGroup);
+        }
 
         apply();
     }
@@ -118,10 +128,9 @@ public class SignalClusterView
         mVpn            = null;
         mWifiGroup      = null;
         mWifi           = null;
-        mMobileGroup    = null;
-        mMobile         = null;
-        mMobileType     = null;
         mAirplane       = null;
+        mMobileSignalGroup.removeAllViews();
+        mMobileSignalGroup = null;
 
         super.onDetachedFromWindow();
     }
@@ -149,18 +158,56 @@ public class SignalClusterView
 
     @Override
     public void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
-            String contentDescription, String typeContentDescription, boolean isTypeIconWide) {
-        mMobileVisible = visible;
-        mMobileStrengthId = strengthIcon;
-        mMobileTypeId = typeIcon;
-        mMobileDescription = contentDescription;
-        mMobileTypeDescription = typeContentDescription;
-        mIsMobileTypeIconWide = isTypeIconWide;
+            String contentDescription, String typeContentDescription, boolean isTypeIconWide,
+            int subId) {
+        PhoneState state = getOrInflateState(subId);
+        state.mMobileVisible = visible;
+        state.mMobileStrengthId = strengthIcon;
+        state.mMobileTypeId = typeIcon;
+        state.mMobileDescription = contentDescription;
+        state.mMobileTypeDescription = typeContentDescription;
+        state.mIsMobileTypeIconWide = isTypeIconWide;
 
         apply();
     }
 
     @Override
+    public void setNoSims(boolean show) {
+        mNoSimsVisible = show;
+    }
+
+    @Override
+    public void setSubs(List<SubscriptionInfo> subs) {
+        // Clear out all old subIds.
+        mPhoneStates.clear();
+        if (mMobileSignalGroup != null) {
+            mMobileSignalGroup.removeAllViews();
+        }
+        final int n = subs.size();
+        for (int i = 0; i < n; i++) {
+            inflatePhoneState(subs.get(i).getSubscriptionId());
+        }
+    }
+
+    private PhoneState getOrInflateState(int subId) {
+        for (PhoneState state : mPhoneStates) {
+            if (state.mSubId == subId) {
+                return state;
+            }
+        }
+        return inflatePhoneState(subId);
+    }
+
+    private PhoneState inflatePhoneState(int subId) {
+        PhoneState state = new PhoneState(subId, mContext);
+        if (mMobileSignalGroup != null) {
+            mMobileSignalGroup.addView(state.mMobileGroup);
+        }
+        mPhoneStates.add(state);
+        return state;
+    }
+
+    @Override
     public void setIsAirplaneMode(boolean is, int airplaneIconId, int contentDescription) {
         mIsAirplaneMode = is;
         mAirplaneIconId = airplaneIconId;
@@ -175,8 +222,9 @@ public class SignalClusterView
         // ignore content description, so populate manually
         if (mWifiVisible && mWifiGroup != null && mWifiGroup.getContentDescription() != null)
             event.getText().add(mWifiGroup.getContentDescription());
-        if (mMobileVisible && mMobileGroup != null && mMobileGroup.getContentDescription() != null)
-            event.getText().add(mMobileGroup.getContentDescription());
+        for (PhoneState state : mPhoneStates) {
+            state.populateAccessibilityEvent(event);
+        }
         return super.dispatchPopulateAccessibilityEvent(event);
     }
 
@@ -188,12 +236,13 @@ public class SignalClusterView
             mWifi.setImageDrawable(null);
         }
 
-        if (mMobile != null) {
-            mMobile.setImageDrawable(null);
-        }
-
-        if (mMobileType != null) {
-            mMobileType.setImageDrawable(null);
+        for (PhoneState state : mPhoneStates) {
+            if (state.mMobile != null) {
+                state.mMobile.setImageDrawable(null);
+            }
+            if (state.mMobileType != null) {
+                state.mMobileType.setImageDrawable(null);
+            }
         }
 
         if(mAirplane != null) {
@@ -227,19 +276,17 @@ public class SignalClusterView
                     (mWifiVisible ? "VISIBLE" : "GONE"),
                     mWifiStrengthId));
 
-        if (mMobileVisible && !mIsAirplaneMode) {
-            mMobile.setImageResource(mMobileStrengthId);
-            mMobileType.setImageResource(mMobileTypeId);
-            mMobileGroup.setContentDescription(mMobileTypeDescription + " " + mMobileDescription);
-            mMobileGroup.setVisibility(View.VISIBLE);
-        } else {
-            mMobileGroup.setVisibility(View.GONE);
+        boolean anyMobileVisible = false;
+        for (PhoneState state : mPhoneStates) {
+            if (state.apply(anyMobileVisible)) {
+                anyMobileVisible = true;
+            }
         }
 
         if (mIsAirplaneMode) {
             mAirplane.setImageResource(mAirplaneIconId);
             mAirplane.setContentDescription(mAirplaneContentDescription != 0 ?
-                    mContext.getString(mAirplaneContentDescription) : "");
+                    mContext.getString(mAirplaneContentDescription) : null);
             mAirplane.setVisibility(View.VISIBLE);
         } else {
             mAirplane.setVisibility(View.GONE);
@@ -251,23 +298,73 @@ public class SignalClusterView
             mWifiAirplaneSpacer.setVisibility(View.GONE);
         }
 
-        if (mMobileVisible && mMobileTypeId != 0 && mWifiVisible) {
+        if ((anyMobileVisible || mNoSimsVisible) && mWifiVisible) {
             mWifiSignalSpacer.setVisibility(View.VISIBLE);
         } else {
             mWifiSignalSpacer.setVisibility(View.GONE);
         }
 
-        mMobile.setPaddingRelative(mIsMobileTypeIconWide ? mWideTypeIconStartPadding : 0, 0, 0, 0);
+        mNoSims.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE);
 
-        if (DEBUG) Log.d(TAG,
-                String.format("mobile: %s sig=%d typ=%d",
-                    (mMobileVisible ? "VISIBLE" : "GONE"),
-                    mMobileStrengthId, mMobileTypeId));
+        boolean anythingVisible = mNoSimsVisible || mWifiVisible || mIsAirplaneMode
+                || anyMobileVisible || mVpnVisible;
+        setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0);
+    }
 
-        mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
+    private class PhoneState {
+        private final int mSubId;
+        private boolean mMobileVisible = false;
+        private int mMobileStrengthId = 0, mMobileTypeId = 0;
+        private boolean mIsMobileTypeIconWide;
+        private String mMobileDescription, mMobileTypeDescription;
+
+        private ViewGroup mMobileGroup;
+        private ImageView mMobile, mMobileType;
+
+        public PhoneState(int subId, Context context) {
+            ViewGroup root = (ViewGroup) LayoutInflater.from(context)
+                    .inflate(R.layout.mobile_signal_group, null);
+            setViews(root);
+            mSubId = subId;
+        }
 
-        boolean anythingVisible = mWifiVisible || mIsAirplaneMode || mMobileVisible || mVpnVisible;
-        setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0);
+        public void setViews(ViewGroup root) {
+            mMobileGroup    = root;
+            mMobile         = (ImageView) root.findViewById(R.id.mobile_signal);
+            mMobileType     = (ImageView) root.findViewById(R.id.mobile_type);
+        }
+
+        public boolean apply(boolean isSecondaryIcon) {
+            if (mMobileVisible && !mIsAirplaneMode) {
+                mMobile.setImageResource(mMobileStrengthId);
+                mMobileType.setImageResource(mMobileTypeId);
+                mMobileGroup.setContentDescription(mMobileTypeDescription
+                        + " " + mMobileDescription);
+                mMobileGroup.setVisibility(View.VISIBLE);
+            } else {
+                mMobileGroup.setVisibility(View.GONE);
+            }
+
+            // When this isn't next to wifi, give it some extra padding between the signals.
+            mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0,
+                    0, 0, 0);
+            mMobile.setPaddingRelative(mIsMobileTypeIconWide ? mWideTypeIconStartPadding : 0,
+                    0, 0, 0);
+
+            if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
+                        (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
+
+            mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
+
+            return mMobileVisible;
+        }
+
+        public void populateAccessibilityEvent(AccessibilityEvent event) {
+            if (mMobileVisible && mMobileGroup != null
+                    && mMobileGroup.getContentDescription() != null) {
+                event.getText().add(mMobileGroup.getContentDescription());
+            }
+        }
     }
 }
 
index 3d4a1e0..6b167b4 100644 (file)
@@ -46,9 +46,12 @@ public class DozeParameters {
     public void dump(PrintWriter pw) {
         pw.println("  DozeParameters:");
         pw.print("    getDisplayStateSupported(): "); pw.println(getDisplayStateSupported());
-        pw.print("    getPulseDuration(): "); pw.println(getPulseDuration());
-        pw.print("    getPulseInDuration(): "); pw.println(getPulseInDuration());
-        pw.print("    getPulseInDelay(): "); pw.println(getPulseInDelay());
+        pw.print("    getPulseDuration(pickup=false): "); pw.println(getPulseDuration(false));
+        pw.print("    getPulseDuration(pickup=true): "); pw.println(getPulseDuration(true));
+        pw.print("    getPulseInDuration(pickup=false): "); pw.println(getPulseInDuration(false));
+        pw.print("    getPulseInDuration(pickup=true): "); pw.println(getPulseInDuration(true));
+        pw.print("    getPulseInDelay(pickup=false): "); pw.println(getPulseInDelay(false));
+        pw.print("    getPulseInDelay(pickup=true): "); pw.println(getPulseInDelay(true));
         pw.print("    getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration());
         pw.print("    getPulseOutDuration(): "); pw.println(getPulseOutDuration());
         pw.print("    getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion());
@@ -60,22 +63,27 @@ public class DozeParameters {
         pw.print("    getPulseSchedule(): "); pw.println(getPulseSchedule());
         pw.print("    getPulseScheduleResets(): "); pw.println(getPulseScheduleResets());
         pw.print("    getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
+        pw.print("    getPickupPerformsProxCheck(): "); pw.println(getPickupPerformsProxCheck());
     }
 
     public boolean getDisplayStateSupported() {
         return getBoolean("doze.display.supported", R.bool.doze_display_state_supported);
     }
 
-    public int getPulseDuration() {
-        return getPulseInDuration() + getPulseVisibleDuration() + getPulseOutDuration();
+    public int getPulseDuration(boolean pickup) {
+        return getPulseInDuration(pickup) + getPulseVisibleDuration() + getPulseOutDuration();
     }
 
-    public int getPulseInDuration() {
-        return getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
+    public int getPulseInDuration(boolean pickup) {
+        return pickup
+                ? getInt("doze.pulse.duration.in.pickup", R.integer.doze_pulse_duration_in_pickup)
+                : getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
     }
 
-    public int getPulseInDelay() {
-        return getInt("doze.pulse.delay.in", R.integer.doze_pulse_delay_in);
+    public int getPulseInDelay(boolean pickup) {
+        return pickup
+                ? getInt("doze.pulse.delay.in.pickup", R.integer.doze_pulse_delay_in_pickup)
+                : getInt("doze.pulse.delay.in", R.integer.doze_pulse_delay_in);
     }
 
     public int getPulseVisibleDuration() {
@@ -106,6 +114,10 @@ public class DozeParameters {
         return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse);
     }
 
+    public boolean getPickupPerformsProxCheck() {
+        return getBoolean("doze.pickup.proxcheck", R.bool.doze_pickup_performs_proximity_check);
+    }
+
     public boolean getPulseOnNotifications() {
         return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications);
     }
index 022e64e..3e17328 100644 (file)
@@ -21,10 +21,8 @@ import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.content.Context;
-import android.graphics.Color;
 import android.os.Handler;
 import android.util.Log;
-import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
@@ -40,6 +38,7 @@ public class DozeScrimController {
 
     private final DozeParameters mDozeParameters;
     private final Interpolator mPulseInInterpolator = PhoneStatusBar.ALPHA_OUT;
+    private final Interpolator mPulseInInterpolatorPickup;
     private final Interpolator mPulseOutInterpolator = PhoneStatusBar.ALPHA_IN;
     private final Interpolator mDozeAnimationInterpolator;
     private final Handler mHandler = new Handler();
@@ -56,8 +55,8 @@ public class DozeScrimController {
     public DozeScrimController(ScrimController scrimController, Context context) {
         mScrimController = scrimController;
         mDozeParameters = new DozeParameters(context);
-        mDozeAnimationInterpolator = AnimationUtils.loadInterpolator(context,
-                android.R.interpolator.linear_out_slow_in);
+        mDozeAnimationInterpolator = mPulseInInterpolatorPickup =
+                AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
     }
 
     public void setDozing(boolean dozing, boolean animate) {
@@ -219,11 +218,16 @@ public class DozeScrimController {
     private final Runnable mPulseIn = new Runnable() {
         @Override
         public void run() {
-            if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing);
+            if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
+                    + DozeLog.pulseReasonToString(mPulseReason));
             if (!mDozing) return;
             DozeLog.tracePulseStart(mPulseReason);
-            startScrimAnimation(true /* inFront */, 0f, mDozeParameters.getPulseInDuration(),
-                    mPulseInInterpolator, mDozeParameters.getPulseInDelay(), mPulseInFinished);
+            final boolean pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
+            startScrimAnimation(true /* inFront */, 0f,
+                    mDozeParameters.getPulseInDuration(pickup),
+                    pickup ? mPulseInInterpolatorPickup : mPulseInInterpolator,
+                    mDozeParameters.getPulseInDelay(pickup),
+                    mPulseInFinished);
 
             // Signal that the pulse is ready to turn the screen on and draw.
             pulseStarted();
index 15f6dc2..7ec84da 100644 (file)
@@ -60,9 +60,13 @@ public final class NavigationBarTransitions extends BarTransitions {
     @Override
     public void transitionTo(int mode, boolean animate) {
         mRequestedMode = mode;
-        if (mVertical && (mode == MODE_TRANSLUCENT || mode == MODE_TRANSPARENT)) {
+        if (mVertical) {
             // translucent mode not allowed when vertical
-            mode = MODE_OPAQUE;
+            if (mode == MODE_TRANSLUCENT || mode == MODE_TRANSPARENT) {
+                mode = MODE_OPAQUE;
+            } else if (mode == MODE_LIGHTS_OUT_TRANSPARENT) {
+                mode = MODE_LIGHTS_OUT;
+            }
         }
         super.transitionTo(mode, animate);
     }
index 4fb505e..ec2d30c 100644 (file)
@@ -851,14 +851,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
                     }
                 }
             });
-
-            // set up the dynamic hide/show of the label
-            // TODO: uncomment, handle this for the Stack scroller aswell
-//                ((NotificationRowLayout) mStackScroller)
-// .setOnSizeChangedListener(new OnSizeChangedListener() {
-//                @Override
-//                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
-//                    updateCarrierLabelVisibility(false);
         }
 
         mFlashlightController = new FlashlightController(mContext);
@@ -922,7 +914,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
             filter.addAction("fake_artwork");
         }
         filter.addAction(ACTION_DEMO);
-        context.registerReceiver(mBroadcastReceiver, filter);
+        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
 
         // listen for USER_SETUP_COMPLETE setting (per-user)
         resetUserSetupObserver();
@@ -3096,12 +3088,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
             if (DEBUG) Log.v(TAG, "onReceive: " + intent);
             String action = intent.getAction();
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
-                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
-                String reason = intent.getStringExtra("reason");
-                if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
-                    flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
+                if (isCurrentProfile(getSendingUserId())) {
+                    int flags = CommandQueue.FLAG_EXCLUDE_NONE;
+                    String reason = intent.getStringExtra("reason");
+                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
+                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
+                    }
+                    animateCollapsePanels(flags);
                 }
-                animateCollapsePanels(flags);
             }
             else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                 mScreenOn = false;
@@ -3801,6 +3795,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
      * @param state The {@link StatusBarState} to set.
      */
     public void setBarState(int state) {
+        if (state != mState) {
+            EventLogTags.writeSysuiStatusBarState(state);
+
+            // If we're visible and switched to SHADE_LOCKED (the user dragged
+            // down on the lockscreen), clear notification LED, vibration,
+            // ringing.
+            // Other transitions are covered in handleVisibleToUserChanged().
+            if (mVisible && state == StatusBarState.SHADE_LOCKED) {
+                try {
+                    mBarService.clearNotificationEffects();
+                } catch (RemoteException e) {
+                    // Ignore.
+                }
+            }
+        }
         mState = state;
         mStatusBarWindowManager.setStatusBarState(state);
     }
@@ -4132,6 +4141,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
         private final H mHandler = new H();
 
+        // Keeps the last reported state by fireNotificationLight.
+        private boolean mNotificationLightOn;
+
         @Override
         public String toString() {
             return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
@@ -4150,6 +4162,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         }
 
         public void fireNotificationLight(boolean on) {
+            mNotificationLightOn = on;
             for (Callback callback : mCallbacks) {
                 callback.onNotificationLight(on);
             }
@@ -4191,6 +4204,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
             return mBatteryController != null && mBatteryController.isPowerSave();
         }
 
+        @Override
+        public boolean isNotificationLightOn() {
+            return mNotificationLightOn;
+        }
+
         private void handleStartDozing(@NonNull Runnable ready) {
             if (!mDozing) {
                 mDozing = true;
index e4eae38..7cbf13f 100644 (file)
@@ -30,7 +30,7 @@ import com.android.systemui.R;
 public class PhoneStatusBarView extends PanelBar {
     private static final String TAG = "PhoneStatusBarView";
     private static final boolean DEBUG = PhoneStatusBar.DEBUG;
-    private static final boolean DEBUG_GESTURES = true;
+    private static final boolean DEBUG_GESTURES = false;
 
     PhoneStatusBar mBar;
 
index 8ce608c..45a1386 100644 (file)
@@ -122,7 +122,7 @@ public class QSTileHost implements QSTile.Host {
                     tile.userSwitch(newUserId);
                 }
                 mSecurity.onUserSwitched(newUserId);
-                mNetwork.getAccessPointController().onUserSwitched(newUserId);
+                mNetwork.onUserSwitched(newUserId);
                 mObserver.register();
             }
         };
index f9a68d0..479c982 100644 (file)
@@ -67,6 +67,7 @@ public class AccessPointControllerImpl implements NetworkController.AccessPointC
     private final UserManager mUserManager;
     private final Receiver mReceiver = new Receiver();
 
+    private NetworkControllerImpl mNetworkController;
     private boolean mScanning;
     private int mCurrentUser;
 
@@ -77,6 +78,10 @@ public class AccessPointControllerImpl implements NetworkController.AccessPointC
         mCurrentUser = ActivityManager.getCurrentUser();
     }
 
+    void setNetworkController(NetworkControllerImpl networkController) {
+        mNetworkController = networkController;
+    }
+
     public boolean canConfigWifi() {
         return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
                 new UserHandle(mCurrentUser));
@@ -181,7 +186,6 @@ public class AccessPointControllerImpl implements NetworkController.AccessPointC
             ap.isConfigured = config != null;
             ap.networkId = config != null ? config.networkId : AccessPoint.NO_NETWORK;
             ap.ssid = ssid;
-            ap.iconId = ICONS[level];
             // Connected if either:
             // -The network ID in the active WifiInfo matches this network's ID.
             // -The network is ephemeral (no configuration) but the SSID matches.
@@ -189,7 +193,13 @@ public class AccessPointControllerImpl implements NetworkController.AccessPointC
                     && ap.networkId == connectedNetworkId) ||
                     (ap.networkId == WifiConfiguration.INVALID_NETWORK_ID && wifiInfo != null &&
                     ap.ssid.equals(trimDoubleQuotes(wifiInfo.getSSID())));
-            ap.level = level;
+            if (ap.isConnected && mNetworkController != null) {
+                // Ensure we have the connected network's RSSI.
+                ap.level = mNetworkController.getConnectedWifiLevel();
+            } else {
+                ap.level = level;
+            }
+            ap.iconId = ICONS[ap.level];
             // Based on Settings AccessPoint#getSecurity, keep up to date
             // with better methods of determining no security or not.
             ap.hasSecurity = scanResult.capabilities.contains("WEP")
index b7c74e3..63fcbc5 100644 (file)
@@ -33,5 +33,6 @@ public class AccessibilityContentDescriptions {
         R.string.accessibility_wifi_three_bars,
         R.string.accessibility_wifi_signal_full
     };
+
     static final int WIFI_NO_CONNECTION = R.string.accessibility_no_wifi;
 }
index 63c1100..8f40011 100644 (file)
@@ -35,6 +35,8 @@ public class HotspotControllerImpl implements HotspotController {
 
     private static final String TAG = "HotspotController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final String TETHER_ENABLE_PACKAGE = "com.android.settings";
+    private static final String TETHER_ENABLE_CLASS = "com.android.settings.EnableWifiTether";
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final Receiver mReceiver = new Receiver();
@@ -91,20 +93,14 @@ public class HotspotControllerImpl implements HotspotController {
     @Override
     public void setHotspotEnabled(boolean enabled) {
         final ContentResolver cr = mContext.getContentResolver();
-        // This needs to be kept up to date with Settings (WifiApEnabler.setSoftapEnabled)
-        // in case it is turned on in settings and off in qs (or vice versa).
-        // Disable Wifi if enabling tethering.
-        int wifiState = mWifiManager.getWifiState();
-        if (enabled && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||
-                    (wifiState == WifiManager.WIFI_STATE_ENABLED))) {
-            mWifiManager.setWifiEnabled(false);
-            Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1);
-        }
-
-        mWifiManager.setWifiApEnabled(null, enabled);
-
-        // If needed, restore Wifi on tether disable.
-        if (!enabled) {
+        // Call provisioning app which is called when enabling Tethering from Settings
+        if (enabled) {
+            Intent intent = new Intent();
+            intent.setClassName(TETHER_ENABLE_PACKAGE, TETHER_ENABLE_CLASS);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivity(intent);
+        } else {
+            mWifiManager.setWifiApEnabled(null, false);
             if (Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE, 0) == 1) {
                 mWifiManager.setWifiEnabled(true);
                 Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
index 7ee1fc5..1460e5f 100644 (file)
@@ -107,6 +107,7 @@ public class KeyguardUserSwitcher {
     public void show(boolean animate) {
         if (mUserSwitcher != null && mUserSwitcherContainer.getVisibility() != View.VISIBLE) {
             cancelAnimations();
+            mAdapter.refresh();
             mUserSwitcherContainer.setVisibility(View.VISIBLE);
             mStatusBarView.setKeyguardUserSwitcherShowing(true, animate);
             if (animate) {
index 20f0a83..30da9cb 100644 (file)
@@ -33,6 +33,7 @@ import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
 import android.text.format.Time;
@@ -213,7 +214,8 @@ public class MobileDataControllerImpl implements NetworkController.MobileDataCon
 
     private static String getActiveSubscriberId(Context context) {
         final TelephonyManager tele = TelephonyManager.from(context);
-        final String actualSubscriberId = tele.getSubscriberId();
+        final String actualSubscriberId = tele.getSubscriberId(
+                SubscriptionManager.getDefaultDataSubId());
         return actualSubscriberId;
     }
 
index b024f58..bcf08ff 100644 (file)
@@ -22,6 +22,7 @@ public interface NetworkController {
     void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb);
     void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb);
     void setWifiEnabled(boolean enabled);
+    void onUserSwitched(int newUserId);
     AccessPointController getAccessPointController();
     MobileDataController getMobileDataController();
 
@@ -32,8 +33,9 @@ public interface NetworkController {
         void onMobileDataSignalChanged(boolean enabled, int mobileSignalIconId,
                 String mobileSignalContentDescriptionId, int dataTypeIconId,
                 boolean activityIn, boolean activityOut,
-                String dataTypeContentDescriptionId, String description, boolean noSim,
+                String dataTypeContentDescriptionId, String description,
                 boolean isDataTypeIconWide);
+        void onNoSimVisibleChanged(boolean visible);
         void onAirplaneModeChanged(boolean enabled);
         void onMobileDataEnabled(boolean enabled);
     }
@@ -48,7 +50,6 @@ public interface NetworkController {
         void scanForAccessPoints();
         boolean connect(AccessPoint ap);
         boolean canConfigWifi();
-        void onUserSwitched(int newUserId);
 
         public interface AccessPointCallback {
             void onAccessPointsChanged(AccessPoint[] accessPoints);
index 5a97c75..6431ab5 100644 (file)
 
 package com.android.systemui.statusbar.policy;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
 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.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
@@ -35,12 +42,17 @@ import android.provider.Settings;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.cdma.EriInfo;
 import com.android.internal.util.AsyncChannel;
@@ -50,6 +62,9 @@ import com.android.systemui.R;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -64,9 +79,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     // additional diagnostics, but not logspew
     static final boolean CHATTY =  Log.isLoggable(TAG + ".Chat", Log.DEBUG);
-    // Save the previous states of all SignalController state info.
+    // Save the previous SignalController.States of all SignalControllers for dumps.
     static final boolean RECORD_HISTORY = true;
-    // How many to save, must be a power of 2.
+    // If RECORD_HISTORY how many to save, must be a power of 2.
     static final int HISTORY_SIZE = 16;
 
     private static final int INET_CONDITION_THRESHOLD = 50;
@@ -75,29 +90,42 @@ public class NetworkControllerImpl extends BroadcastReceiver
     private final TelephonyManager mPhone;
     private final WifiManager mWifiManager;
     private final ConnectivityManager mConnectivityManager;
+    private final SubscriptionManager mSubscriptionManager;
     private final boolean mHasMobileDataFeature;
+    private final Config mConfig;
 
     // Subcontrollers.
     @VisibleForTesting
     final WifiSignalController mWifiSignalController;
     @VisibleForTesting
-    final MobileSignalController mMobileSignalController;
-    private final AccessPointController mAccessPoints;
+    final Map<Integer, MobileSignalController> mMobileSignalControllers =
+            new HashMap<Integer, MobileSignalController>();
+    // When no SIMs are around at setup, and one is added later, it seems to default to the first
+    // SIM for most actions.  This may be null if there aren't any SIMs around.
+    private MobileSignalController mDefaultSignalController;
+    private final AccessPointControllerImpl mAccessPoints;
     private final MobileDataControllerImpl mMobileDataController;
 
-    // bluetooth
+    // Network types that replace the carrier label if the device does not support mobile data.
     private boolean mBluetoothTethered = false;
+    private boolean mEthernetConnected = false;
 
-    // data connectivity (regardless of state, can we access the internet?)
-    // state of inet connection - 0 not connected, 100 connected
+    // state of inet connection
     private boolean mConnected = false;
-    private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
-    private String mConnectedNetworkTypeName;
     private boolean mInetCondition; // Used for Logging and demo.
 
+    // BitSets indicating which network transport types (e.g., TRANSPORT_WIFI, TRANSPORT_MOBILE) are
+    // connected and validated, respectively.
+    private final BitSet mConnectedTransports = new BitSet();
+    private final BitSet mValidatedTransports = new BitSet();
+
     // States that don't belong to a subcontroller.
     private boolean mAirplaneMode = false;
+    private boolean mHasNoSims;
     private Locale mLocale = null;
+    // This list holds our ordering.
+    private List<SubscriptionInfo> mCurrentSubscriptions
+            = new ArrayList<SubscriptionInfo>();
 
     // All the callbacks.
     private ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<EmergencyListener>();
@@ -106,6 +134,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
     private ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
     private ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
             new ArrayList<NetworkSignalChangedCallback>();
+    private boolean mListening;
+
+    // The current user ID.
+    private int mCurrentUserId;
 
     /**
      * Construct this controller object and register for updates.
@@ -114,18 +146,21 @@ public class NetworkControllerImpl extends BroadcastReceiver
         this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
                 (WifiManager) context.getSystemService(Context.WIFI_SERVICE),
-                Config.readConfig(context), new AccessPointControllerImpl(context),
-                new MobileDataControllerImpl(context));
+                SubscriptionManager.from(context), Config.readConfig(context),
+                new AccessPointControllerImpl(context), new MobileDataControllerImpl(context));
         registerListeners();
     }
 
     @VisibleForTesting
     NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
-            TelephonyManager telephonyManager, WifiManager wifiManager, Config config,
+            TelephonyManager telephonyManager, WifiManager wifiManager,
+            SubscriptionManager subManager, Config config,
             AccessPointControllerImpl accessPointController,
             MobileDataControllerImpl mobileDataController) {
         mContext = context;
+        mConfig = config;
 
+        mSubscriptionManager = subManager;
         mConnectivityManager = connectivityManager;
         mHasMobileDataFeature =
                 mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
@@ -149,15 +184,17 @@ public class NetworkControllerImpl extends BroadcastReceiver
         });
         mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
                 mSignalsChangedCallbacks, mSignalClusters, this);
-        mMobileSignalController = new MobileSignalController(mContext, config,
-                mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, mSignalClusters, this);
 
         // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
-        updateAirplaneMode(true);
+        updateAirplaneMode(true /* force callback */);
+        mAccessPoints.setNetworkController(this);
     }
 
     private void registerListeners() {
-        mMobileSignalController.registerListener();
+        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+            mobileSignalController.registerListener();
+        }
+        mSubscriptionManager.registerOnSubscriptionsChangedListener(mSubscriptionListener);
 
         // broadcasts
         IntentFilter filter = new IntentFilter();
@@ -165,19 +202,32 @@ public class NetworkControllerImpl extends BroadcastReceiver
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
         filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
         filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         mContext.registerReceiver(this, filter);
+        mListening = true;
+
+        updateMobileControllers();
     }
 
     private void unregisterListeners() {
-        mMobileSignalController.unregisterListener();
+        mListening = false;
+        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+            mobileSignalController.unregisterListener();
+        }
+        mSubscriptionManager.unregisterOnSubscriptionsChangedListener(mSubscriptionListener);
         mContext.unregisterReceiver(this);
     }
 
+    public int getConnectedWifiLevel() {
+        return mWifiSignalController.getState().level;
+    }
+
     @Override
     public AccessPointController getAccessPointController() {
         return mAccessPoints;
@@ -190,7 +240,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
 
     public void addEmergencyListener(EmergencyListener listener) {
         mEmergencyListeners.add(listener);
-        refreshCarrierLabel();
+        listener.setEmergencyCallsOnly(isEmergencyOnly());
     }
 
     public void addCarrierLabel(CarrierLabelListener listener) {
@@ -199,7 +249,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
     }
 
     private void notifyMobileDataEnabled(boolean enabled) {
-        int length = mSignalsChangedCallbacks.size();
+        final int length = mSignalsChangedCallbacks.size();
         for (int i = 0; i < length; i++) {
             mSignalsChangedCallbacks.get(i).onMobileDataEnabled(enabled);
         }
@@ -213,12 +263,39 @@ public class NetworkControllerImpl extends BroadcastReceiver
         return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
     }
 
+    private MobileSignalController getDataController() {
+        int dataSubId = SubscriptionManager.getDefaultDataSubId();
+        if (dataSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            if (DEBUG) Log.e(TAG, "No data sim selected");
+            return mDefaultSignalController;
+        }
+        if (mMobileSignalControllers.containsKey(dataSubId)) {
+            return mMobileSignalControllers.get(dataSubId);
+        }
+        Log.e(TAG, "Cannot find controller for data sub: " + dataSubId);
+        return mDefaultSignalController;
+    }
+
     public String getMobileNetworkName() {
-        return mMobileSignalController.mCurrentState.networkName;
+        MobileSignalController controller = getDataController();
+        return controller != null ? controller.getState().networkName : "";
     }
 
     public boolean isEmergencyOnly() {
-        return mMobileSignalController.isEmergencyOnly();
+        int voiceSubId = SubscriptionManager.getDefaultVoiceSubId();
+        if (voiceSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+                if (!mobileSignalController.isEmergencyOnly()) {
+                    return false;
+                }
+            }
+        }
+        if (mMobileSignalControllers.containsKey(voiceSubId)) {
+            return mMobileSignalControllers.get(voiceSubId).isEmergencyOnly();
+        }
+        Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId);
+        // Something is wrong, better assume we can't make calls...
+        return true;
     }
 
     /**
@@ -227,26 +304,35 @@ public class NetworkControllerImpl extends BroadcastReceiver
      */
     void recalculateEmergency() {
         final boolean emergencyOnly = isEmergencyOnly();
-
-        int length = mEmergencyListeners.size();
+        final int length = mEmergencyListeners.size();
         for (int i = 0; i < length; i++) {
             mEmergencyListeners.get(i).setEmergencyCallsOnly(emergencyOnly);
         }
+        // If the emergency has a chance to change, then so does the carrier
+        // label.
+        refreshCarrierLabel();
     }
 
     public void addSignalCluster(SignalCluster cluster) {
         mSignalClusters.add(cluster);
+        cluster.setSubs(mCurrentSubscriptions);
         cluster.setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON,
                 R.string.accessibility_airplane_mode);
+        cluster.setNoSims(mHasNoSims);
         mWifiSignalController.notifyListeners();
-        mMobileSignalController.notifyListeners();
+        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+            mobileSignalController.notifyListeners();
+        }
     }
 
     public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
         mSignalsChangedCallbacks.add(cb);
         cb.onAirplaneModeChanged(mAirplaneMode);
+        cb.onNoSimVisibleChanged(mHasNoSims);
         mWifiSignalController.notifyListeners();
-        mMobileSignalController.notifyListeners();
+        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+            mobileSignalController.notifyListeners();
+        }
     }
 
     public void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
@@ -272,6 +358,14 @@ public class NetworkControllerImpl extends BroadcastReceiver
     }
 
     @Override
+    public void onUserSwitched(int newUserId) {
+        mCurrentUserId = newUserId;
+        mAccessPoints.onUserSwitched(newUserId);
+        updateConnectivity();
+        refreshCarrierLabel();
+    }
+
+    @Override
     public void onReceive(Context context, Intent intent) {
         if (CHATTY) {
             Log.d(TAG, "onReceive: intent=" + intent);
@@ -279,7 +373,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
         final String action = intent.getAction();
         if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE) ||
                 action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
-            updateConnectivity(intent);
+            updateConnectivity();
             refreshCarrierLabel();
         } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
             refreshLocale();
@@ -288,9 +382,121 @@ public class NetworkControllerImpl extends BroadcastReceiver
             refreshLocale();
             updateAirplaneMode(false);
             refreshCarrierLabel();
+        } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED)) {
+            // We are using different subs now, we might be able to make calls.
+            recalculateEmergency();
+        } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+            // Notify every MobileSignalController so they can know whether they are the
+            // data sim or not.
+            for (MobileSignalController controller : mMobileSignalControllers.values()) {
+                controller.handleBroadcast(intent);
+            }
+        } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+            // Might have different subscriptions now.
+            updateMobileControllers();
+        } else {
+            int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                if (mMobileSignalControllers.containsKey(subId)) {
+                    mMobileSignalControllers.get(subId).handleBroadcast(intent);
+                } else {
+                    // Can't find this subscription...  We must be out of date.
+                    updateMobileControllers();
+                }
+            } else {
+                // No sub id, must be for the wifi.
+                mWifiSignalController.handleBroadcast(intent);
+            }
+        }
+    }
+
+    private void updateMobileControllers() {
+        if (!mListening) {
+            return;
+        }
+        List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList();
+        // If there have been no relevant changes to any of the subscriptions, we can leave as is.
+        if (hasCorrectMobileControllers(subscriptions)) {
+            // Even if the controllers are correct, make sure we have the right no sims state.
+            // Such as on boot, don't need any controllers, because there are no sims,
+            // but we still need to update the no sim state.
+            updateNoSims();
+            return;
+        }
+        setCurrentSubscriptions(subscriptions);
+        updateNoSims();
+    }
+
+    @VisibleForTesting
+    protected void updateNoSims() {
+        boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
+        if (hasNoSims != mHasNoSims) {
+            mHasNoSims = hasNoSims;
+            notifyListeners();
         }
-        mWifiSignalController.handleBroadcast(intent);
-        mMobileSignalController.handleBroadcast(intent);
+    }
+
+    @VisibleForTesting
+    void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) {
+        Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() {
+            @Override
+            public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) {
+                return lhs.getSimSlotIndex() == rhs.getSimSlotIndex()
+                        ? lhs.getSubscriptionId() - rhs.getSubscriptionId()
+                        : lhs.getSimSlotIndex() - rhs.getSimSlotIndex();
+            }
+        });
+        final int length = mSignalClusters.size();
+        for (int i = 0; i < length; i++) {
+            mSignalClusters.get(i).setSubs(subscriptions);
+        }
+        mCurrentSubscriptions = subscriptions;
+
+        HashMap<Integer, MobileSignalController> cachedControllers =
+                new HashMap<Integer, MobileSignalController>(mMobileSignalControllers);
+        mMobileSignalControllers.clear();
+        final int num = subscriptions.size();
+        for (int i = 0; i < num; i++) {
+            int subId = subscriptions.get(i).getSubscriptionId();
+            // If we have a copy of this controller already reuse it, otherwise make a new one.
+            if (cachedControllers.containsKey(subId)) {
+                mMobileSignalControllers.put(subId, cachedControllers.get(subId));
+            } else {
+                MobileSignalController controller = new MobileSignalController(mContext, mConfig,
+                        mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, mSignalClusters,
+                        this, subscriptions.get(i));
+                mMobileSignalControllers.put(subId, controller);
+                if (subscriptions.get(i).getSimSlotIndex() == 0) {
+                    mDefaultSignalController = controller;
+                }
+                if (mListening) {
+                    controller.registerListener();
+                }
+            }
+        }
+        if (mListening) {
+            for (Integer key : cachedControllers.keySet()) {
+                if (cachedControllers.get(key) == mDefaultSignalController) {
+                    mDefaultSignalController = null;
+                }
+                cachedControllers.get(key).unregisterListener();
+            }
+        }
+    }
+
+    private boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) {
+        if (allSubscriptions == null) {
+            // If null then the system doesn't know the subscriptions yet, instead just wait
+            // to update the MobileControllers until it knows the state.
+            return true;
+        }
+        for (SubscriptionInfo info : allSubscriptions) {
+            if (!mMobileSignalControllers.containsKey(info.getSubscriptionId())) {
+                return false;
+            }
+        }
+        return true;
     }
 
     private void updateAirplaneMode(boolean force) {
@@ -298,85 +504,87 @@ public class NetworkControllerImpl extends BroadcastReceiver
                 Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
         if (airplaneMode != mAirplaneMode || force) {
             mAirplaneMode = airplaneMode;
-            mMobileSignalController.setAirplaneMode(mAirplaneMode);
-            notifyAirplaneCallbacks();
+            for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+                mobileSignalController.setAirplaneMode(mAirplaneMode);
+            }
+            notifyListeners();
             refreshCarrierLabel();
         }
     }
 
     private void refreshLocale() {
         Locale current = mContext.getResources().getConfiguration().locale;
-        if (current.equals(mLocale)) {
+        if (!current.equals(mLocale)) {
             mLocale = current;
             notifyAllListeners();
         }
     }
 
     /**
-     * Turns inet condition into a boolean indexing for a specific network.
-     * returns 0 for bad connectivity on this network.
-     * returns 1 for good connectivity on this network.
+     * Forces update of all callbacks on both SignalClusters and
+     * NetworkSignalChangedCallbacks.
      */
-    private int inetConditionForNetwork(int networkType, boolean inetCondition) {
-        return (inetCondition && mConnectedNetworkType == networkType) ? 1 : 0;
-    }
-
     private void notifyAllListeners() {
-        // Something changed, trigger everything!
-        notifyAirplaneCallbacks();
-        mMobileSignalController.notifyListeners();
+        notifyListeners();
+        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+            mobileSignalController.notifyListeners();
+        }
         mWifiSignalController.notifyListeners();
     }
 
-    private void notifyAirplaneCallbacks() {
+    /**
+     * Notifies listeners of changes in state of to the NetworkController, but
+     * does not notify for any info on SignalControllers, for that call
+     * notifyAllListeners.
+     */
+    private void notifyListeners() {
         int length = mSignalClusters.size();
         for (int i = 0; i < length; i++) {
             mSignalClusters.get(i).setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON,
                     R.string.accessibility_airplane_mode);
+            mSignalClusters.get(i).setNoSims(mHasNoSims);
         }
-        // update QS
         int signalsChangedLength = mSignalsChangedCallbacks.size();
         for (int i = 0; i < signalsChangedLength; i++) {
             mSignalsChangedCallbacks.get(i).onAirplaneModeChanged(mAirplaneMode);
+            mSignalsChangedCallbacks.get(i).onNoSimVisibleChanged(mHasNoSims);
         }
     }
 
     /**
      * Update the Inet conditions and what network we are connected to.
      */
-    private void updateConnectivity(Intent intent) {
-        final NetworkInfo info = mConnectivityManager.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;
+    private void updateConnectivity() {
+        mConnectedTransports.clear();
+        mValidatedTransports.clear();
+        for (NetworkCapabilities nc :
+                mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
+            for (int transportType : nc.getTransportTypes()) {
+                mConnectedTransports.set(transportType);
+                if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
+                    mValidatedTransports.set(transportType);
+                }
+            }
         }
 
-        int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
-
         if (CHATTY) {
-            Log.d(TAG, "updateConnectivity: networkInfo=" + info);
-            Log.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus);
+            Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports);
+            Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
         }
 
-        mInetCondition = connectionStatus > INET_CONDITION_THRESHOLD;
-
-        if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) {
-            mBluetoothTethered = info.isConnected();
-        } else {
-            mBluetoothTethered = false;
-        }
+        mConnected = !mConnectedTransports.isEmpty();
+        mInetCondition = !mValidatedTransports.isEmpty();
+        mBluetoothTethered = mConnectedTransports.get(TRANSPORT_BLUETOOTH);
+        mEthernetConnected = mConnectedTransports.get(TRANSPORT_ETHERNET);
 
         // We want to update all the icons, all at once, for any condition change
-        mMobileSignalController.setInetCondition(mInetCondition ? 1 : 0,
-                inetConditionForNetwork(mMobileSignalController.getNetworkType(), mInetCondition));
+        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+            mobileSignalController.setInetCondition(
+                    mInetCondition ? 1 : 0,
+                    mValidatedTransports.get(mobileSignalController.getTransportType()) ? 1 : 0);
+        }
         mWifiSignalController.setInetCondition(
-                inetConditionForNetwork(mWifiSignalController.getNetworkType(), mInetCondition));
+                mValidatedTransports.get(mWifiSignalController.getTransportType()) ? 1 : 0);
     }
 
     /**
@@ -386,8 +594,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
         Context context = mContext;
 
         WifiSignalController.WifiState wifiState = mWifiSignalController.getState();
-        MobileSignalController.MobileState mobileState = mMobileSignalController.getState();
-        String label = mMobileSignalController.getLabel("", mConnected, mHasMobileDataFeature);
+        String label = "";
+        for (MobileSignalController controller : mMobileSignalControllers.values()) {
+            label = controller.getLabel(label, mConnected, mHasMobileDataFeature);
+        }
 
         // TODO Simplify this ugliness, some of the flows below shouldn't be possible anymore
         // but stay for the sake of history.
@@ -395,13 +605,11 @@ public class NetworkControllerImpl extends BroadcastReceiver
             label = mContext.getString(R.string.bluetooth_tethered);
         }
 
-        final boolean ethernetConnected =
-                (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET);
-        if (ethernetConnected && !mHasMobileDataFeature) {
+        if (mEthernetConnected && !mHasMobileDataFeature) {
             label = context.getString(R.string.ethernet_label);
         }
 
-        if (mAirplaneMode && (!mobileState.connected && !mobileState.isEmergency)) {
+        if (mAirplaneMode && !isEmergencyOnly()) {
             // combined values from connected wifi take precedence over airplane mode
             if (wifiState.connected && mHasMobileDataFeature) {
                 // Suppress "No internet connection." from mobile if wifi connected.
@@ -412,8 +620,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
                               R.string.status_bar_settings_signal_meter_disconnected);
                  }
             }
-        } else if (!mobileState.dataConnected && !wifiState.connected && !mBluetoothTethered &&
-                 !ethernetConnected && !mHasMobileDataFeature) {
+        } else if (!isMobileDataConnected() && !wifiState.connected && !mBluetoothTethered &&
+                 !mEthernetConnected && !mHasMobileDataFeature) {
             // Pretty much no connection.
             label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
         }
@@ -427,11 +635,14 @@ public class NetworkControllerImpl extends BroadcastReceiver
         }
     }
 
+    private boolean isMobileDataConnected() {
+        MobileSignalController controller = getDataController();
+        return controller != null ? controller.getState().dataConnected : false;
+    }
+
     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());
@@ -441,6 +652,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
         pw.println(mBluetoothTethered);
 
         pw.println("  - connectivity ------");
+        pw.print("  mConnectedTransports=");
+        pw.println(mConnectedTransports);
+        pw.print("  mValidatedTransports=");
+        pw.println(mValidatedTransports);
         pw.print("  mInetCondition=");
         pw.println(mInetCondition);
         pw.print("  mAirplaneMode=");
@@ -448,14 +663,15 @@ public class NetworkControllerImpl extends BroadcastReceiver
         pw.print("  mLocale=");
         pw.println(mLocale);
 
-        mMobileSignalController.dump(pw);
+        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+            mobileSignalController.dump(pw);
+        }
         mWifiSignalController.dump(pw);
     }
 
     private boolean mDemoMode;
     private int mDemoInetCondition;
     private WifiSignalController.WifiState mDemoWifiState;
-    private MobileSignalController.MobileState mDemoMobileState;
 
     @Override
     public void dispatchDemoCommand(String command, Bundle args) {
@@ -465,12 +681,16 @@ public class NetworkControllerImpl extends BroadcastReceiver
             mDemoMode = true;
             mDemoInetCondition = mInetCondition ? 1 : 0;
             mDemoWifiState = mWifiSignalController.getState();
-            mDemoMobileState = mMobileSignalController.getState();
         } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
             if (DEBUG) Log.d(TAG, "Exiting demo mode");
             mDemoMode = false;
+            // Update what MobileSignalControllers, because they may change
+            // to set the number of sim slots.
+            updateMobileControllers();
+            for (MobileSignalController controller : mMobileSignalControllers.values()) {
+                controller.resetLastState();
+            }
             mWifiSignalController.resetLastState();
-            mMobileSignalController.resetLastState();
             registerListeners();
             notifyAllListeners();
             refreshCarrierLabel();
@@ -488,7 +708,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
             if (fully != null) {
                 mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0;
                 mWifiSignalController.setInetCondition(mDemoInetCondition);
-                mMobileSignalController.setInetCondition(mDemoInetCondition, mDemoInetCondition);
+                for (MobileSignalController controller : mMobileSignalControllers.values()) {
+                    controller.setInetCondition(mDemoInetCondition, mDemoInetCondition);
+                }
             }
             String wifi = args.getString("wifi");
             if (wifi != null) {
@@ -502,12 +724,47 @@ public class NetworkControllerImpl extends BroadcastReceiver
                 mDemoWifiState.enabled = show;
                 mWifiSignalController.notifyListeners();
             }
+            String sims = args.getString("sims");
+            if (sims != null) {
+                int num = Integer.parseInt(sims);
+                List<SubscriptionInfo> subs = new ArrayList<SubscriptionInfo>();
+                if (num != mMobileSignalControllers.size()) {
+                    mMobileSignalControllers.clear();
+                    int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
+                    for (int i = start /* get out of normal index range */; i < start + num; i++) {
+                        SubscriptionInfo info = new SubscriptionInfo(i, "", i, "", "", 0, 0, "", 0,
+                                null, 0, 0, "");
+                        subs.add(info);
+                        mMobileSignalControllers.put(i, new MobileSignalController(mContext,
+                                mConfig, mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks,
+                                mSignalClusters, this, info));
+                    }
+                }
+                final int n = mSignalClusters.size();
+                for (int i = 0; i < n; i++) {
+                    mSignalClusters.get(i).setSubs(subs);
+                }
+            }
+            String nosim = args.getString("nosim");
+            if (nosim != null) {
+                boolean show = nosim.equals("show");
+                final int n = mSignalClusters.size();
+                for (int i = 0; i < n; i++) {
+                    mSignalClusters.get(i).setNoSims(show);
+                }
+            }
             String mobile = args.getString("mobile");
             if (mobile != null) {
                 boolean show = mobile.equals("show");
                 String datatype = args.getString("datatype");
+                String slotString = args.getString("slot");
+                int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString);
+                // Hack to index linearly for easy use.
+                MobileSignalController controller = mMobileSignalControllers
+                        .values().toArray(new MobileSignalController[0])[slot];
+                controller.getState().dataSim = datatype != null;
                 if (datatype != null) {
-                    mDemoMobileState.iconGroup =
+                    controller.getState().iconGroup =
                             datatype.equals("1x") ? TelephonyIcons.ONE_X :
                             datatype.equals("3g") ? TelephonyIcons.THREE_G :
                             datatype.equals("4g") ? TelephonyIcons.FOUR_G :
@@ -521,17 +778,25 @@ public class NetworkControllerImpl extends BroadcastReceiver
                 int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
                 String level = args.getString("level");
                 if (level != null) {
-                    mDemoMobileState.level = level.equals("null") ? -1
+                    controller.getState().level = level.equals("null") ? -1
                             : Math.min(Integer.parseInt(level), icons[0].length - 1);
-                    mDemoMobileState.connected = mDemoMobileState.level >= 0;
+                    controller.getState().connected = controller.getState().level >= 0;
                 }
-                mDemoMobileState.enabled = show;
-                mMobileSignalController.notifyListeners();
+                controller.getState().enabled = show;
+                controller.notifyListeners();
             }
             refreshCarrierLabel();
         }
     }
 
+    private final OnSubscriptionsChangedListener mSubscriptionListener =
+            new OnSubscriptionsChangedListener() {
+        public void onSubscriptionInfoChanged() {
+            updateMobileControllers();
+        };
+    };
+
+    // TODO: Move to its own file.
     static class WifiSignalController extends
             SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
         private final WifiManager mWifiManager;
@@ -541,8 +806,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
         public WifiSignalController(Context context, boolean hasMobileData,
                 List<NetworkSignalChangedCallback> signalCallbacks,
                 List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
-            super("WifiSignalController", context, ConnectivityManager.TYPE_WIFI, signalCallbacks,
-                    signalClusters, networkController);
+            super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
+                    signalCallbacks, signalClusters, networkController);
             mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
             mHasMobileData = hasMobileData;
             Handler handler = new WifiHandler();
@@ -566,20 +831,17 @@ public class NetworkControllerImpl extends BroadcastReceiver
         }
 
         @Override
-        public WifiState cleanState() {
+        protected WifiState cleanState() {
             return new WifiState();
         }
 
-        /**
-         * {@inheritDoc}
-         */
         @Override
         public void notifyListeners() {
             // only show wifi in the cluster if connected or if wifi-only
-            boolean wifiEnabled = mCurrentState.enabled
+            boolean wifiVisible = mCurrentState.enabled
                     && (mCurrentState.connected || !mHasMobileData);
-            String wifiDesc = wifiEnabled ? mCurrentState.ssid : null;
-            boolean ssidPresent = wifiEnabled && mCurrentState.ssid != null;
+            String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
+            boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
             String contentDescription = getStringIfExists(getContentDescription());
             int length = mSignalsChangedCallbacks.size();
             for (int i = 0; i < length; i++) {
@@ -591,10 +853,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
 
             int signalClustersLength = mSignalClusters.size();
             for (int i = 0; i < signalClustersLength; i++) {
-                mSignalClusters.get(i).setWifiIndicators(
-                        // only show wifi in the cluster if connected or if wifi-only
-                        mCurrentState.enabled && (mCurrentState.connected || !mHasMobileData),
-                        getCurrentIconId(), contentDescription);
+                mSignalClusters.get(i).setWifiIndicators(wifiVisible, getCurrentIconId(),
+                        contentDescription);
             }
         }
 
@@ -617,7 +877,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
                             ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
                             : mWifiManager.getConnectionInfo();
                     if (info != null) {
-                        mCurrentState.ssid = huntForSsid(info);
+                        mCurrentState.ssid = getSsid(info);
                     } else {
                         mCurrentState.ssid = null;
                     }
@@ -625,6 +885,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
                     mCurrentState.ssid = null;
                 }
             } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+                // Default to -200 as its below WifiManager.MIN_RSSI.
                 mCurrentState.rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
                 mCurrentState.level = WifiManager.calculateSignalLevel(
                         mCurrentState.rssi, WifiIcons.WIFI_LEVEL_COUNT);
@@ -633,7 +894,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
             notifyListenersIfNecessary();
         }
 
-        private String huntForSsid(WifiInfo info) {
+        private String getSsid(WifiInfo info) {
             String ssid = info.getSSID();
             if (ssid != null) {
                 return ssid;
@@ -688,15 +949,15 @@ public class NetworkControllerImpl extends BroadcastReceiver
 
             @Override
             public void copyFrom(State s) {
+                super.copyFrom(s);
                 WifiState state = (WifiState) s;
                 ssid = state.ssid;
-                super.copyFrom(s);
             }
 
             @Override
             protected void toString(StringBuilder builder) {
-                builder.append("ssid=").append(ssid).append(',');
                 super.toString(builder);
+                builder.append(',').append("ssid=").append(ssid);
             }
 
             @Override
@@ -707,12 +968,17 @@ public class NetworkControllerImpl extends BroadcastReceiver
         }
     }
 
+    // TODO: Move to its own file.
     static class MobileSignalController extends SignalController<MobileSignalController.MobileState,
             MobileSignalController.MobileIconGroup> {
         private final Config mConfig;
         private final TelephonyManager mPhone;
         private final String mNetworkNameDefault;
         private final String mNetworkNameSeparator;
+        @VisibleForTesting
+        final PhoneStateListener mPhoneStateListener;
+        // Save entire info for logging, we only use the id.
+        private final SubscriptionInfo mSubscriptionInfo;
 
         // @VisibleForDemoMode
         Map<Integer, MobileIconGroup> mNetworkToIconLookup;
@@ -731,11 +997,15 @@ public class NetworkControllerImpl extends BroadcastReceiver
         // need listener lists anymore.
         public MobileSignalController(Context context, Config config, boolean hasMobileData,
                 TelephonyManager phone, List<NetworkSignalChangedCallback> signalCallbacks,
-                List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
-            super("MobileSignalController", context, ConnectivityManager.TYPE_MOBILE,
-                    signalCallbacks, signalClusters, networkController);
+                List<SignalCluster> signalClusters, NetworkControllerImpl networkController,
+                SubscriptionInfo info) {
+            super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
+                    NetworkCapabilities.TRANSPORT_CELLULAR, signalCallbacks, signalClusters,
+                    networkController);
             mConfig = config;
             mPhone = phone;
+            mSubscriptionInfo = info;
+            mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId());
             mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
             mNetworkNameDefault = getStringIfExists(
                     com.android.internal.R.string.lockscreen_carrier_default);
@@ -745,6 +1015,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
             mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault;
             mLastState.enabled = mCurrentState.enabled = hasMobileData;
             mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
+            // Get initial data sim state.
+            updateDataSim();
         }
 
         /**
@@ -774,15 +1046,19 @@ public class NetworkControllerImpl extends BroadcastReceiver
                         mobileLabel = mCurrentState.networkName;
                     }
                 } else {
-                    mobileLabel = mContext
-                            .getString(R.string.status_bar_settings_signal_meter_disconnected);
+                    mobileLabel = mContext.getString(
+                            R.string.status_bar_settings_signal_meter_disconnected);
                 }
 
+                if (currentLabel.length() != 0) {
+                    currentLabel = currentLabel + mNetworkNameSeparator;
+                }
                 // Now for things that should only be shown when actually using mobile data.
                 if (isMobileLabel) {
-                    return mobileLabel;
+                    return currentLabel + mobileLabel;
                 } else {
-                    return mCurrentState.dataConnected ? mobileLabel : currentLabel;
+                    return currentLabel
+                            + (mCurrentState.dataConnected ? mobileLabel : currentLabel);
                 }
             }
         }
@@ -840,7 +1116,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
 
-            if (!mConfig.showAtLeastThreeGees) {
+            if (!mConfig.showAtLeast3G) {
                 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
                         TelephonyIcons.UNKNOWN);
                 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
@@ -876,31 +1152,31 @@ public class NetworkControllerImpl extends BroadcastReceiver
             }
         }
 
-        /**
-         * {@inheritDoc}
-         */
         @Override
         public void notifyListeners() {
             MobileIconGroup icons = getIcons();
 
             String contentDescription = getStringIfExists(getContentDescription());
             String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
-            int qsTypeIcon = icons.mQsDataType[mCurrentState.inetForNetwork];
-            int length = mSignalsChangedCallbacks.size();
-            for (int i = 0; i < length; i++) {
-                mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled
-                        && !mCurrentState.isEmergency && !mCurrentState.airplaneMode,
-                        getQsCurrentIconId(), contentDescription,
-                        qsTypeIcon,
-                        mCurrentState.dataConnected && mCurrentState.activityIn,
-                        mCurrentState.dataConnected && mCurrentState.activityOut,
-                        dataContentDescription,
-                        mCurrentState.isEmergency ? null : mCurrentState.networkName,
-                        mCurrentState.noSim,
-                        // Only wide if actually showing something.
-                        icons.mIsWide && qsTypeIcon != 0);
+            // Only send data sim callbacks to QS.
+            if (mCurrentState.dataSim) {
+                int qsTypeIcon = mCurrentState.dataConnected ?
+                        icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
+                int length = mSignalsChangedCallbacks.size();
+                for (int i = 0; i < length; i++) {
+                    mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled
+                            && !mCurrentState.isEmergency && !mCurrentState.airplaneMode,
+                            getQsCurrentIconId(), contentDescription,
+                            qsTypeIcon,
+                            mCurrentState.dataConnected && mCurrentState.activityIn,
+                            mCurrentState.dataConnected && mCurrentState.activityOut,
+                            dataContentDescription,
+                            mCurrentState.isEmergency ? null : mCurrentState.networkName,
+                            // Only wide if actually showing something.
+                            icons.mIsWide && qsTypeIcon != 0);
+                }
             }
-            boolean showDataIcon = mCurrentState.inetForNetwork != 0
+            boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
                     || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
             int typeIcon = showDataIcon ? icons.mDataType : 0;
             int signalClustersLength = mSignalClusters.size();
@@ -912,12 +1188,13 @@ public class NetworkControllerImpl extends BroadcastReceiver
                         contentDescription,
                         dataContentDescription,
                         // Only wide if actually showing something.
-                        icons.mIsWide && typeIcon != 0);
+                        icons.mIsWide && typeIcon != 0,
+                        mSubscriptionInfo.getSubscriptionId());
             }
         }
 
         @Override
-        public MobileState cleanState() {
+        protected MobileState cleanState() {
             return new MobileState();
         }
 
@@ -964,42 +1241,32 @@ public class NetworkControllerImpl extends BroadcastReceiver
 
         public void handleBroadcast(Intent intent) {
             String action = intent.getAction();
-            if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
-                String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
-                final String lockedReason =
-                        intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
-                updateSimState(stateExtra, lockedReason);
-                updateTelephony();
-            } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
+            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));
                 notifyListenersIfNecessary();
+            } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+                updateDataSim();
             }
         }
 
-        /**
-         * Determines the current sim state, based on a TelephonyIntents.ACTION_SIM_STATE_CHANGED
-         * broadcast.
-         */
-        private final void updateSimState(String stateExtra, String lockedReason) {
-            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)) {
-                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;
-                }
+        private void updateDataSim() {
+            int defaultDataSub = SubscriptionManager.getDefaultDataSubId();
+            if (SubscriptionManager.isValidSubId(defaultDataSub)) {
+                mCurrentState.dataSim = defaultDataSub == mSubscriptionInfo.getSubscriptionId();
             } else {
-                mSimState = IccCardConstants.State.UNKNOWN;
+                // There doesn't seem to be a data sim selected, however if
+                // there isn't a MobileSignalController with dataSim set, then
+                // QS won't get any callbacks and will be blank.  Instead
+                // lets just assume we are the data sim (which will basically
+                // show one at random) in QS until one is selected.  The user
+                // should pick one soon after, so we shouldn't be in this state
+                // for long.
+                mCurrentState.dataSim = true;
             }
-            if (DEBUG) Log.d(TAG, "updateSimState: mSimState=" + mSimState);
+            notifyListenersIfNecessary();
         }
 
         /**
@@ -1052,16 +1319,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
             }
             mCurrentState.dataConnected = mCurrentState.connected
                     && mDataState == TelephonyManager.DATA_CONNECTED;
-            if (!isCdma()) {
-                if (mSimState == IccCardConstants.State.READY ||
-                        mSimState == IccCardConstants.State.UNKNOWN) {
-                    mCurrentState.noSim = false;
-                } else {
-                    mCurrentState.noSim = true;
-                    // No sim, no data.
-                    mCurrentState.dataConnected = false;
-                }
-            }
 
             if (isRoaming()) {
                 mCurrentState.iconGroup = TelephonyIcons.ROAMING;
@@ -1070,6 +1327,11 @@ public class NetworkControllerImpl extends BroadcastReceiver
                 mCurrentState.isEmergency = isEmergencyOnly();
                 mNetworkController.recalculateEmergency();
             }
+            // Fill in the network name if we think we have it.
+            if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null
+                    && mServiceState.getOperatorAlphaShort() != null) {
+                mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
+            }
             notifyListenersIfNecessary();
         }
 
@@ -1085,17 +1347,22 @@ public class NetworkControllerImpl extends BroadcastReceiver
         @Override
         public void dump(PrintWriter pw) {
             super.dump(pw);
+            pw.println("  mSubscription=" + mSubscriptionInfo + ",");
             pw.println("  mServiceState=" + mServiceState + ",");
             pw.println("  mSignalStrength=" + mSignalStrength + ",");
             pw.println("  mDataState=" + mDataState + ",");
             pw.println("  mDataNetType=" + mDataNetType + ",");
         }
 
-        PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        class MobilePhoneStateListener extends PhoneStateListener {
+            public MobilePhoneStateListener(int subId) {
+                super(subId);
+            }
+
             @Override
             public void onSignalStrengthsChanged(SignalStrength signalStrength) {
                 if (DEBUG) {
-                    Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength +
+                    Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength +
                             ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
                 }
                 mSignalStrength = signalStrength;
@@ -1105,7 +1372,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
             @Override
             public void onServiceStateChanged(ServiceState state) {
                 if (DEBUG) {
-                    Log.d(TAG, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
+                    Log.d(mTag, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
                             + " dataState=" + state.getDataRegState());
                 }
                 mServiceState = state;
@@ -1115,7 +1382,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
             @Override
             public void onDataConnectionStateChanged(int state, int networkType) {
                 if (DEBUG) {
-                    Log.d(TAG, "onDataConnectionStateChanged: state=" + state
+                    Log.d(mTag, "onDataConnectionStateChanged: state=" + state
                             + " type=" + networkType);
                 }
                 mDataState = state;
@@ -1126,7 +1393,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
             @Override
             public void onDataActivity(int direction) {
                 if (DEBUG) {
-                    Log.d(TAG, "onDataActivity: direction=" + direction);
+                    Log.d(mTag, "onDataActivity: direction=" + direction);
                 }
                 setActivity(direction);
             }
@@ -1153,7 +1420,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
 
         static class MobileState extends SignalController.State {
             String networkName;
-            boolean noSim;
+            boolean dataSim;
             boolean dataConnected;
             boolean isEmergency;
             boolean airplaneMode;
@@ -1161,32 +1428,33 @@ public class NetworkControllerImpl extends BroadcastReceiver
 
             @Override
             public void copyFrom(State s) {
+                super.copyFrom(s);
                 MobileState state = (MobileState) s;
-                noSim = state.noSim;
+                dataSim = state.dataSim;
                 networkName = state.networkName;
                 dataConnected = state.dataConnected;
                 inetForNetwork = state.inetForNetwork;
                 isEmergency = state.isEmergency;
                 airplaneMode = state.airplaneMode;
-                super.copyFrom(s);
             }
 
             @Override
             protected void toString(StringBuilder builder) {
-                builder.append("noSim=").append(noSim).append(',');
+                super.toString(builder);
+                builder.append(',');
+                builder.append("dataSim=").append(dataSim).append(',');
                 builder.append("networkName=").append(networkName).append(',');
                 builder.append("dataConnected=").append(dataConnected).append(',');
                 builder.append("inetForNetwork=").append(inetForNetwork).append(',');
                 builder.append("isEmergency=").append(isEmergency).append(',');
-                builder.append("airplaneMode=").append(airplaneMode).append(',');
-                super.toString(builder);
+                builder.append("airplaneMode=").append(airplaneMode);
             }
 
             @Override
             public boolean equals(Object o) {
                 return super.equals(o)
                         && Objects.equals(((MobileState) o).networkName, networkName)
-                        && ((MobileState) o).noSim == noSim
+                        && ((MobileState) o).dataSim == dataSim
                         && ((MobileState) o).dataConnected == dataConnected
                         && ((MobileState) o).isEmergency == isEmergency
                         && ((MobileState) o).airplaneMode == airplaneMode
@@ -1203,7 +1471,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
         protected final String mTag;
         protected final T mCurrentState;
         protected final T mLastState;
-        protected final int mNetworkType;
+        protected final int mTransportType;
         protected final Context mContext;
         // The owner of the SignalController (i.e. NetworkController will maintain the following
         // lists and call notifyListeners whenever the list has changed to ensure everyone
@@ -1220,9 +1488,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
         public SignalController(String tag, Context context, int type,
                 List<NetworkSignalChangedCallback> signalCallbacks,
                 List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
-            mTag = TAG + "::" + tag;
+            mTag = TAG + "." + tag;
             mNetworkController = networkController;
-            mNetworkType = type;
+            mTransportType = type;
             mContext = context;
             mSignalsChangedCallbacks = signalCallbacks;
             mSignalClusters = signalClusters;
@@ -1240,8 +1508,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
             return mCurrentState;
         }
 
-        public int getNetworkType() {
-            return mNetworkType;
+        public int getTransportType() {
+            return mTransportType;
         }
 
         public void setInetCondition(int inetCondition) {
@@ -1249,11 +1517,12 @@ public class NetworkControllerImpl extends BroadcastReceiver
             notifyListenersIfNecessary();
         }
 
-        // @VisibleForDemoMode
         /**
          * Used at the end of demo mode to clear out any ugly state that it has created.
          * Since we haven't had any callbacks, then isDirty will not have been triggered,
          * so we can just take the last good state directly from there.
+         *
+         * Used for demo mode.
          */
         void resetLastState() {
             mCurrentState.copyFrom(mLastState);
@@ -1276,7 +1545,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
 
         public void saveLastState() {
             if (RECORD_HISTORY) {
-                recordLast();
+                recordLastState();
             }
             // Updates the current time.
             mCurrentState.time = System.currentTimeMillis();
@@ -1310,7 +1579,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
         }
 
         /**
-         * Gets the content description for the signal based on current state of connected and
+         * Gets the content description id for the signal based on current state of connected and
          * level.
          */
         public int getContentDescription() {
@@ -1321,7 +1590,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
             }
         }
 
-        protected void notifyListenersIfNecessary() {
+        public void notifyListenersIfNecessary() {
             if (isDirty()) {
                 saveLastState();
                 notifyListeners();
@@ -1344,7 +1613,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
          * Saves the last state of any changes, so we can log the current
          * and last value of any state data.
          */
-        protected void recordLast() {
+        protected void recordLastState() {
             mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState);
         }
 
@@ -1376,7 +1645,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
         /**
          * Generate a blank T.
          */
-        public abstract T cleanState();
+        protected abstract T cleanState();
 
         /*
          * Holds icons for a given state. Arrays are generally indexed as inet
@@ -1485,7 +1754,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
         void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription);
 
         void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
-                String contentDescription, String typeContentDescription, boolean isTypeIconWide);
+                String contentDescription, String typeContentDescription, boolean isTypeIconWide,
+                int subId);
+        void setSubs(List<SubscriptionInfo> subs);
+        void setNoSims(boolean show);
 
         void setIsAirplaneMode(boolean is, int airplaneIcon, int contentDescription);
     }
@@ -1500,7 +1772,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
 
     @VisibleForTesting
     static class Config {
-        boolean showAtLeastThreeGees = false;
+        boolean showAtLeast3G = false;
         boolean alwaysShowCdmaRssi = false;
         boolean show4gForLte = false;
         boolean hspaDataDistinguishable;
@@ -1509,7 +1781,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
             Config config = new Config();
             Resources res = context.getResources();
 
-            config.showAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G);
+            config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
             config.alwaysShowCdmaRssi =
                     res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
             config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
index e5b357a..4ac41a1 100644 (file)
@@ -417,18 +417,6 @@ public class UserSwitcherController {
             }
         }
 
-        public int getSwitchableUsers() {
-            int result = 0;
-            ArrayList<UserRecord> users = mController.mUsers;
-            int N = users.size();
-            for (int i = 0; i < N; i++) {
-                if (users.get(i).info != null) {
-                    result++;
-                }
-            }
-            return result;
-        }
-
         public Drawable getDrawable(Context context, UserRecord item) {
             if (item.isAddUser) {
                 return context.getDrawable(R.drawable.ic_add_circle_qs);
@@ -436,6 +424,10 @@ public class UserSwitcherController {
             return UserIcons.getDefaultUserIcon(item.isGuest ? UserHandle.USER_NULL : item.info.id,
                     /* light= */ true);
         }
+
+        public void refresh() {
+            mController.refreshUsers(UserHandle.USER_NULL);
+        }
     }
 
     public static final class UserRecord {
@@ -500,6 +492,7 @@ public class UserSwitcherController {
             } else {
                 v = (UserDetailView) convertView;
             }
+            v.refreshAdapter();
             return v;
         }
 
index 49fe1e3..020bc00 100644 (file)
@@ -11,6 +11,8 @@ import android.net.wifi.WifiManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
 import android.util.Log;
@@ -25,6 +27,8 @@ import org.mockito.Mockito;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
 
 public class NetworkControllerBaseTest extends AndroidTestCase {
     private static final String TAG = "NetworkControllerBaseTest";
@@ -44,6 +48,7 @@ public class NetworkControllerBaseTest extends AndroidTestCase {
     private ServiceState mServiceState;
     protected ConnectivityManager mMockCm;
     protected WifiManager mMockWm;
+    protected SubscriptionManager mMockSm;
     protected TelephonyManager mMockTm;
     protected Config mConfig;
 
@@ -56,6 +61,7 @@ public class NetworkControllerBaseTest extends AndroidTestCase {
 
         mMockWm = mock(WifiManager.class);
         mMockTm = mock(TelephonyManager.class);
+        mMockSm = mock(SubscriptionManager.class);
         mMockCm = mock(ConnectivityManager.class);
         when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
 
@@ -64,14 +70,22 @@ public class NetworkControllerBaseTest extends AndroidTestCase {
 
         mConfig = new Config();
         mConfig.hspaDataDistinguishable = true;
-        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm,
+        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
                 mConfig, mock(AccessPointControllerImpl.class),
                 mock(MobileDataControllerImpl.class));
         setupNetworkController();
     }
 
     protected void setupNetworkController() {
-        mPhoneStateListener = mNetworkController.mMobileSignalController.mPhoneStateListener;
+        // For now just pretend to be the data sim, so we can test that too.
+        final int subId = SubscriptionManager.getDefaultDataSubId();
+        SubscriptionInfo subscription = mock(SubscriptionInfo.class);
+        List<SubscriptionInfo> subs = new ArrayList<SubscriptionInfo>();
+        when(subscription.getSubscriptionId()).thenReturn(subId);
+        subs.add(subscription);
+        mNetworkController.setCurrentSubscriptions(subs);
+        mPhoneStateListener =
+                mNetworkController.mMobileSignalControllers.get(subId).mPhoneStateListener;
         mSignalCluster = mock(SignalCluster.class);
         mNetworkSignalChangedCallback = mock(NetworkSignalChangedCallback.class);
         mNetworkController.addSignalCluster(mSignalCluster);
@@ -181,14 +195,25 @@ public class NetworkControllerBaseTest extends AndroidTestCase {
         mPhoneStateListener.onDataActivity(dataActivity);
     }
 
+    protected void verifyHasNoSims(boolean hasNoSimsVisible) {
+        ArgumentCaptor<Boolean> hasNoSimsArg = ArgumentCaptor.forClass(Boolean.class);
+
+        Mockito.verify(mSignalCluster, Mockito.atLeastOnce()).setNoSims(hasNoSimsArg.capture());
+        assertEquals("No sims in status bar", hasNoSimsVisible, (boolean) hasNoSimsArg.getValue());
+
+        Mockito.verify(mNetworkSignalChangedCallback, Mockito.atLeastOnce())
+                .onNoSimVisibleChanged(hasNoSimsArg.capture());
+        assertEquals("No sims in quick settings", hasNoSimsVisible,
+                (boolean) hasNoSimsArg.getValue());
+    }
+
     protected void verifyLastQsMobileDataIndicators(boolean visible, int icon, int typeIcon,
-            boolean dataIn, boolean dataOut, boolean noSim) {
+            boolean dataIn, boolean dataOut) {
         ArgumentCaptor<Integer> iconArg = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<Boolean> visibleArg = ArgumentCaptor.forClass(Boolean.class);
         ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class);
         ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<Boolean> noSimArg = ArgumentCaptor.forClass(Boolean.class);
 
         Mockito.verify(mNetworkSignalChangedCallback, Mockito.atLeastOnce())
                 .onMobileDataSignalChanged(visibleArg.capture(), iconArg.capture(),
@@ -198,7 +223,6 @@ public class NetworkControllerBaseTest extends AndroidTestCase {
                         dataOutArg.capture(),
                         ArgumentCaptor.forClass(String.class).capture(),
                         ArgumentCaptor.forClass(String.class).capture(),
-                        noSimArg.capture(),
                         ArgumentCaptor.forClass(Boolean.class).capture());
         assertEquals("Visibility in, quick settings", visible, (boolean) visibleArg.getValue());
         assertEquals("Signal icon in, quick settings", icon, (int) iconArg.getValue());
@@ -207,7 +231,6 @@ public class NetworkControllerBaseTest extends AndroidTestCase {
                 (boolean) dataInArg.getValue());
         assertEquals("Data direction out, in quick settings", dataOut,
                 (boolean) dataOutArg.getValue());
-        assertEquals("Sim state in quick settings", noSim, (boolean) noSimArg.getValue());
     }
 
     protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon) {
@@ -220,7 +243,8 @@ public class NetworkControllerBaseTest extends AndroidTestCase {
                 visibleArg.capture(), iconArg.capture(), typeIconArg.capture(),
                 ArgumentCaptor.forClass(String.class).capture(),
                 ArgumentCaptor.forClass(String.class).capture(),
-                ArgumentCaptor.forClass(Boolean.class).capture());
+                ArgumentCaptor.forClass(Boolean.class).capture(),
+                ArgumentCaptor.forClass(Integer.class).capture());
 
         assertEquals("Signal icon in status bar", icon, (int) iconArg.getValue());
         assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue());
index 146e76d..e327233 100644 (file)
@@ -22,7 +22,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
                 TelephonyIcons.ROAMING_ICON);
         verifyLastQsMobileDataIndicators(true,
                 TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[1][DEFAULT_LEVEL],
-                TelephonyIcons.QS_DATA_R[1], false, false, false);
+                TelephonyIcons.QS_DATA_R[1], false, false);
     }
 
     public void test2gDataIcon() {
@@ -86,14 +86,14 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
 
         verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, DEFAULT_ICON);
         verifyLastQsMobileDataIndicators(true, DEFAULT_QS_SIGNAL_STRENGTH,
-                DEFAULT_QS_ICON, in, out, false);
+                DEFAULT_QS_ICON, in, out);
 
     }
 
     private void verifyDataIndicators(int dataIcon, int qsDataIcon) {
         verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, dataIcon);
         verifyLastQsMobileDataIndicators(true, DEFAULT_QS_SIGNAL_STRENGTH, qsDataIcon, false,
-                false, false);
+                false);
     }
 
 }
index bb2ff7c..33eb4d6 100644 (file)
@@ -17,7 +17,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
         // Turn off mobile network support.
         Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
         // Create a new NetworkController as this is currently handled in constructor.
-        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm,
+        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
                 mConfig, mock(AccessPointControllerImpl.class),
                 mock(MobileDataControllerImpl.class));
         setupNetworkController();
@@ -25,6 +25,30 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
         verifyLastMobileDataIndicators(false, 0, 0);
     }
 
+    public void testNoSimsIconPresent() {
+        // No Subscriptions.
+        mNetworkController.mMobileSignalControllers.clear();
+        mNetworkController.updateNoSims();
+
+        verifyHasNoSims(true);
+    }
+
+    public void testNoSimlessIconWithoutMobile() {
+        // Turn off mobile network support.
+        Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
+        // Create a new NetworkController as this is currently handled in constructor.
+        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
+                mConfig, mock(AccessPointControllerImpl.class),
+                mock(MobileDataControllerImpl.class));
+        setupNetworkController();
+
+        // No Subscriptions.
+        mNetworkController.mMobileSignalControllers.clear();
+        mNetworkController.updateNoSims();
+
+        verifyHasNoSims(false);
+    }
+
     public void testSignalStrength() {
         for (int testStrength = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
                 testStrength <= SignalStrength.SIGNAL_STRENGTH_GREAT; testStrength++) {
@@ -90,7 +114,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
 
             verifyLastQsMobileDataIndicators(true,
                     TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[1][testStrength],
-                    DEFAULT_QS_ICON, false, false, false);
+                    DEFAULT_QS_ICON, false, false);
         }
     }
 
@@ -103,7 +127,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
 
             verifyLastQsMobileDataIndicators(true,
                     TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[1][testStrength],
-                    TelephonyIcons.QS_ICON_1X, false, false, false);
+                    TelephonyIcons.QS_ICON_1X, false, false);
         }
     }
 
index 7467195..b716509 100644 (file)
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Konektatzeko eskaera"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN bidez konektatu nahi da sareko trafikoa kontrolatzeko. Iturburua fidagarria bada bakarrik baimendu. &lt;br /&gt; &lt;br /&gt; VPN konexioa aktibo dagoenean, &lt;img src=vpn_icon /&gt; agertuko da pantailaren goialdean."</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexioa ezarri nahi du sareko trafikoa kontrolatzeko. Iturburua fidagarria bada bakarrik baimendu. &lt;br /&gt; &lt;br /&gt; VPN konexioa aktibo dagoenean, &lt;img src=vpn_icon /&gt; agertuko da pantailaren goialdean."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN sarera konektatuta dago"</string>
     <string name="configure" msgid="4905518375574791375">"Konfiguratu"</string>
     <string name="disconnect" msgid="971412338304200056">"Deskonektatu"</string>
index a77ba3b..cdea95d 100644 (file)
@@ -16,8 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="prompt" msgid="3183836924226407828">"连接请求"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>想要设置一个VPN连接(可被用于监控网络流量)。请只在您信任该来源的情况下才接受此请求。在VPN处于活动状态时,您的屏幕顶部会显示 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; 图标。"</string>
+    <string name="prompt" msgid="3183836924226407828">"网络连接请求"</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>想要设置一个VPN连接(可被用于监控网络流量)。请只在您信任该来源的情况下才接受此请求。在VPN处于活动状态时,您的屏幕顶部会显示 &lt;img src=vpn_icon /&gt; 图标。"</string>
     <string name="legacy_title" msgid="192936250066580964">"已连接VPN"</string>
     <string name="configure" msgid="4905518375574791375">"配置"</string>
     <string name="disconnect" msgid="971412338304200056">"断开连接"</string>
index 1271737..8b53a62 100644 (file)
@@ -4054,10 +4054,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                     // Remove any previous windows with the same appToken.
                     mAppsToBeHidden.remove(appToken);
                     mAppsThatDismissKeyguard.remove(appToken);
-                    if (mAppsToBeHidden.isEmpty() && mKeyguardSecureIncludingHidden) {
-                        mWinShowWhenLocked = win;
-                        mHideLockScreen = true;
-                        mForceStatusBarFromKeyguard = false;
+                    if (mAppsToBeHidden.isEmpty()) {
+                        if (dismissKeyguard && !mKeyguardSecure) {
+                            mAppsThatDismissKeyguard.add(appToken);
+                        } else {
+                            mWinShowWhenLocked = win;
+                            mHideLockScreen = true;
+                            mForceStatusBarFromKeyguard = false;
+                        }
                     }
                 } else if (dismissKeyguard) {
                     if (mKeyguardSecure) {
index 13a649a..5f5b65e 100644 (file)
@@ -240,10 +240,10 @@ nDeviceSetConfig(JNIEnv *_env, jobject _this, jlong dev, jint p, jint value)
 }
 
 static jlong
-nContextCreate(JNIEnv *_env, jobject _this, jlong dev, jint ver, jint sdkVer, jint ct)
+nContextCreate(JNIEnv *_env, jobject _this, jlong dev, jint flags, jint sdkVer, jint contextType)
 {
     LOG_API("nContextCreate");
-    return (jlong)(uintptr_t)rsContextCreate((RsDevice)dev, ver, sdkVer, (RsContextType)ct, 0);
+    return (jlong)(uintptr_t)rsContextCreate((RsDevice)dev, 0, sdkVer, (RsContextType)contextType, flags);
 }
 
 static jlong
index 2781890..89aebe8 100644 (file)
@@ -3314,8 +3314,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
         public int mAccessibilityFocusedWindowId = INVALID_WINDOW_ID;
         public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
 
-        public AccessibilityEvent mShowingFocusedWindowEvent;
-
         private boolean mTouchInteractionInProgress;
 
         private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) {
@@ -3324,19 +3322,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                 // All events that are for changes in a global window
                 // state should *always* be dispatched.
                 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
-                    if (mWindowsForAccessibilityCallback != null) {
-                        // OK, this is fun. Sometimes the focused window is notified
-                        // it has focus before being shown. Historically this event
-                        // means that the window is focused and can be introspected.
-                        // But we still have not gotten the window state from the
-                        // window manager, so delay the notification until then.
-                        AccessibilityWindowInfo window = findWindowById(event.getWindowId());
-                        if (window == null) {
-                            mShowingFocusedWindowEvent = AccessibilityEvent.obtain(event);
-                            return false;
-                        }
-                    }
-                // $fall-through$
                 case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
                 case AccessibilityEvent.TYPE_ANNOUNCEMENT:
                 // All events generated by the user touching the
@@ -3428,18 +3413,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
             }
 
             notifyWindowsChanged();
-
-            // If we are delaying a window state change event as the window
-            // source was showing when it was fired, now is the time to send.
-            if (mShowingFocusedWindowEvent != null) {
-                final int windowId = mShowingFocusedWindowEvent.getWindowId();
-                AccessibilityWindowInfo window = findWindowById(windowId);
-                if (window != null) {
-                    // Sending does the recycle.
-                    sendAccessibilityEvent(mShowingFocusedWindowEvent, mCurrentUserId);
-                }
-                mShowingFocusedWindowEvent = null;
-            }
         }
 
         public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
index 6c2681b..6b8b5b1 100644 (file)
@@ -7664,7 +7664,7 @@ if (MORE_DEBUG) Slog.v(TAG, "   + got " + nRead + "; now wanting " + (size - soF
 
             // If this was a full-system restore, record the ancestral
             // dataset information
-            if (mIsSystemRestore) {
+            if (mIsSystemRestore && mPmAgent != null) {
                 mAncestralPackages = mPmAgent.getRestoredPackages();
                 mAncestralToken = mToken;
                 writeRestoreTokens();
index 831af85..7b3e137 100644 (file)
@@ -1445,12 +1445,9 @@ class AlarmManagerService extends SystemService {
                             maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
                             alarm.repeatInterval, alarm.operation, batch.standalone, true,
                             alarm.workSource, alarm.alarmClock, alarm.userId);
+                }
 
-                    // For now we count this as a wakeup alarm, meaning it needs to be
-                    // delivered immediately.  In the future we should change this, but
-                    // that required delaying when we reschedule the repeat...!
-                    hasWakeup = false;
-                } else if (alarm.wakeup) {
+                if (alarm.wakeup) {
                     hasWakeup = true;
                 }
 
index c3465d1..42a5195 100644 (file)
@@ -29,6 +29,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -53,7 +54,6 @@ import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
@@ -78,10 +78,12 @@ public class AppOpsService extends IAppOpsService.Stub {
     final Handler mHandler;
 
     boolean mWriteScheduled;
+    boolean mFastWriteScheduled;
     final Runnable mWriteRunner = new Runnable() {
         public void run() {
             synchronized (AppOpsService.this) {
                 mWriteScheduled = false;
+                mFastWriteScheduled = false;
                 AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
                     @Override protected Void doInBackground(Void... params) {
                         writeState();
@@ -237,7 +239,7 @@ public class AppOpsService extends IAppOpsService.Stub {
                 }
             }
             if (changed) {
-                scheduleWriteLocked();
+                scheduleFastWriteLocked();
             }
         }
     }
@@ -250,7 +252,7 @@ public class AppOpsService extends IAppOpsService.Stub {
                     if (pkgs.size() <= 0) {
                         mUidOps.remove(uid);
                     }
-                    scheduleWriteLocked();
+                    scheduleFastWriteLocked();
                 }
             }
         }
@@ -260,7 +262,7 @@ public class AppOpsService extends IAppOpsService.Stub {
         synchronized (this) {
             if (mUidOps.indexOfKey(uid) >= 0) {
                 mUidOps.remove(uid);
-                scheduleWriteLocked();
+                scheduleFastWriteLocked();
             }
         }
     }
@@ -400,7 +402,7 @@ public class AppOpsService extends IAppOpsService.Stub {
                         // if there is nothing else interesting in it.
                         pruneOp(op, uid, packageName);
                     }
-                    scheduleWriteNowLocked();
+                    scheduleFastWriteLocked();
                 }
             }
         }
@@ -436,16 +438,20 @@ public class AppOpsService extends IAppOpsService.Stub {
     }
 
     @Override
-    public void resetAllModes() {
-        int callingUid = Binder.getCallingUid();
+    public void resetAllModes(int reqUserId, String reqPackageName) {
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
         mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
-                Binder.getCallingPid(), callingUid, null);
+                callingPid, callingUid, null);
+        reqUserId = ActivityManager.handleIncomingUser(callingPid, callingUid, reqUserId,
+                true, true, "resetAllModes", null);
         HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null;
         synchronized (this) {
             boolean changed = false;
             for (int i=mUidOps.size()-1; i>=0; i--) {
                 HashMap<String, Ops> packages = mUidOps.valueAt(i);
-                if (UserHandle.getUserId(callingUid) != UserHandle.getUserId(mUidOps.keyAt(i))) {
+                if (reqUserId != UserHandle.USER_ALL
+                        && reqUserId != UserHandle.getUserId(mUidOps.keyAt(i))) {
                     // Skip any ops for a different user
                     continue;
                 }
@@ -453,6 +459,10 @@ public class AppOpsService extends IAppOpsService.Stub {
                 while (it.hasNext()) {
                     Map.Entry<String, Ops> ent = it.next();
                     String packageName = ent.getKey();
+                    if (reqPackageName != null && !reqPackageName.equals(packageName)) {
+                        // Skip any ops for a different package
+                        continue;
+                    }
                     Ops pkgOps = ent.getValue();
                     for (int j=pkgOps.size()-1; j>=0; j--) {
                         Op curOp = pkgOps.valueAt(j);
@@ -478,7 +488,7 @@ public class AppOpsService extends IAppOpsService.Stub {
                 }
             }
             if (changed) {
-                scheduleWriteNowLocked();
+                scheduleFastWriteLocked();
             }
         }
         if (callbacks != null) {
@@ -837,12 +847,13 @@ public class AppOpsService extends IAppOpsService.Stub {
         }
     }
 
-    private void scheduleWriteNowLocked() {
-        if (!mWriteScheduled) {
+    private void scheduleFastWriteLocked() {
+        if (!mFastWriteScheduled) {
             mWriteScheduled = true;
+            mFastWriteScheduled = true;
+            mHandler.removeCallbacks(mWriteRunner);
+            mHandler.postDelayed(mWriteRunner, 10*1000);
         }
-        mHandler.removeCallbacks(mWriteRunner);
-        mHandler.post(mWriteRunner);
     }
 
     private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
@@ -1236,12 +1247,11 @@ public class AppOpsService extends IAppOpsService.Stub {
                             pw.print(" ago");
                         }
                         if (op.duration == -1) {
-                            pw.println(" (running)");
-                        } else {
-                            pw.print("; duration=");
-                                    TimeUtils.formatDuration(op.duration, pw);
-                                    pw.println();
+                            pw.print(" (running)");
+                        } else if (op.duration != 0) {
+                            pw.print("; duration="); TimeUtils.formatDuration(op.duration, pw);
                         }
+                        pw.println();
                     }
                 }
             }
index 8b3739d..0f1ed0a 100644 (file)
@@ -797,17 +797,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
         throw new IllegalStateException("No free netIds");
     }
 
-    private int getConnectivityChangeDelay() {
-        final ContentResolver cr = mContext.getContentResolver();
-
-        /** Check system properties for the default value then use secure settings value, if any. */
-        int defaultDelay = SystemProperties.getInt(
-                "conn." + Settings.Global.CONNECTIVITY_CHANGE_DELAY,
-                ConnectivityManager.CONNECTIVITY_CHANGE_DELAY_DEFAULT);
-        return Settings.Global.getInt(cr, Settings.Global.CONNECTIVITY_CHANGE_DELAY,
-                defaultDelay);
-    }
-
     private boolean teardown(NetworkStateTracker netTracker) {
         if (netTracker.teardown()) {
             netTracker.setTeardownRequested(true);
@@ -1065,6 +1054,72 @@ public class ConnectivityService extends IConnectivityManager.Stub
         return result.toArray(new Network[result.size()]);
     }
 
+    private NetworkCapabilities getNetworkCapabilitiesAndValidation(NetworkAgentInfo nai) {
+        if (nai != null) {
+            synchronized (nai) {
+                if (nai.created) {
+                    NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
+                    if (nai.validated) {
+                        nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+                    } else {
+                        nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+                    }
+                    return nc;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
+        // The basic principle is: if an app's traffic could possibly go over a
+        // network, without the app doing anything multinetwork-specific,
+        // (hence, by "default"), then include that network's capabilities in
+        // the array.
+        //
+        // In the normal case, app traffic only goes over the system's default
+        // network connection, so that's the only network returned.
+        //
+        // With a VPN in force, some app traffic may go into the VPN, and thus
+        // over whatever underlying networks the VPN specifies, while other app
+        // traffic may go over the system default network (e.g.: a split-tunnel
+        // VPN, or an app disallowed by the VPN), so the set of networks
+        // returned includes the VPN's underlying networks and the system
+        // default.
+        enforceAccessPermission();
+
+        HashMap<Network, NetworkCapabilities> result = new HashMap<Network, NetworkCapabilities>();
+
+        NetworkAgentInfo nai = getDefaultNetwork();
+        NetworkCapabilities nc = getNetworkCapabilitiesAndValidation(getDefaultNetwork());
+        if (nc != null) {
+            result.put(nai.network, nc);
+        }
+
+        if (!mLockdownEnabled) {
+            synchronized (mVpns) {
+                Vpn vpn = mVpns.get(userId);
+                if (vpn != null) {
+                    Network[] networks = vpn.getUnderlyingNetworks();
+                    if (networks != null) {
+                        for (Network network : networks) {
+                            nai = getNetworkAgentInfoForNetwork(network);
+                            nc = getNetworkCapabilitiesAndValidation(nai);
+                            if (nc != null) {
+                                result.put(nai.network, nc);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        NetworkCapabilities[] out = new NetworkCapabilities[result.size()];
+        out = result.values().toArray(out);
+        return out;
+    }
+
     @Override
     public boolean isNetworkSupported(int networkType) {
         enforceAccessPermission();
@@ -1413,11 +1468,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
     }
 
-    private void sendConnectedBroadcastDelayed(NetworkInfo info, int delayMs) {
-        sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
-        sendGeneralBroadcastDelayed(info, CONNECTIVITY_ACTION, delayMs);
-    }
-
     private void sendInetConditionBroadcast(NetworkInfo info) {
         sendGeneralBroadcast(info, ConnectivityManager.INET_CONDITION_ACTION);
     }
@@ -1449,10 +1499,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
         sendStickyBroadcast(makeGeneralIntent(info, bcastType));
     }
 
-    private void sendGeneralBroadcastDelayed(NetworkInfo info, String bcastType, int delayMs) {
-        sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
-    }
-
     private void sendDataActivityBroadcast(int deviceType, boolean active, long tsNanos) {
         Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
         intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
@@ -1486,19 +1532,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
         }
     }
 
-    private void sendStickyBroadcastDelayed(Intent intent, int delayMs) {
-        if (delayMs <= 0) {
-            sendStickyBroadcast(intent);
-        } else {
-            if (VDBG) {
-                log("sendStickyBroadcastDelayed: delayMs=" + delayMs + ", action="
-                        + intent.getAction());
-            }
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                    EVENT_SEND_STICKY_BROADCAST_INTENT, intent), delayMs);
-        }
-    }
-
     void systemReady() {
         // start network sampling ..
         Intent intent = new Intent(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED);
@@ -2043,6 +2076,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
         }
     }
 
+    // Cancel any lingering so the linger timeout doesn't teardown a network.
+    // This should be called when a network begins satisfying a NetworkRequest.
+    // Note: depending on what state the NetworkMonitor is in (e.g.,
+    // if it's awaiting captive portal login, or if validation failed), this
+    // may trigger a re-evaluation of the network.
+    private void unlinger(NetworkAgentInfo nai) {
+        if (VDBG) log("Canceling linger of " + nai.name());
+        nai.networkLingered.clear();
+        nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+    }
+
     private void handleAsyncChannelHalfConnect(Message msg) {
         AsyncChannel ac = (AsyncChannel) msg.obj;
         if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
@@ -2078,6 +2122,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
             }
         }
     }
+
     private void handleAsyncChannelDisconnected(Message msg) {
         NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
         if (nai != null) {
@@ -2127,11 +2172,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
                     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) &&
+                    for (NetworkAgentInfo existing : mNetworkAgentInfos.values()) {
+                        if (existing.satisfies(request) &&
                                 (alternative == null ||
                                  alternative.getCurrentScore() < existing.getCurrentScore())) {
                             alternative = existing;
@@ -2151,8 +2193,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                 requestNetworkTransitionWakelock(nai.name());
             }
             for (NetworkAgentInfo networkToActivate : toActivate) {
-                networkToActivate.networkLingered.clear();
-                networkToActivate.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+                unlinger(networkToActivate);
                 rematchNetworkAndRequests(networkToActivate, false);
             }
         }
@@ -2187,44 +2228,35 @@ public class ConnectivityService extends IConnectivityManager.Stub
 
     private void handleRegisterNetworkRequest(Message msg) {
         final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
-        final NetworkCapabilities newCap = nri.request.networkCapabilities;
-        int score = 0;
 
         mNetworkRequests.put(nri.request, nri);
 
+        // TODO: This logic may be better replaced with a call to rematchNetworkAndRequests
+
         // Check for the best currently alive network that satisfies this request
         NetworkAgentInfo bestNetwork = null;
         for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
             if (DBG) log("handleRegisterNetworkRequest checking " + network.name());
-            if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) {
+            if (network.satisfies(nri.request)) {
                 if (DBG) log("apparently satisfied.  currentScore=" + network.getCurrentScore());
-                if ((bestNetwork == null) ||
+                if (!nri.isRequest) {
+                    // Not setting bestNetwork here as a listening NetworkRequest may be
+                    // satisfied by multiple Networks.  Instead the request is added to
+                    // each satisfying Network and notified about each.
+                    network.addRequest(nri.request);
+                    notifyNetworkCallback(network, nri);
+                } else if (bestNetwork == null ||
                         bestNetwork.getCurrentScore() < network.getCurrentScore()) {
-                    if (!nri.isRequest) {
-                        // Not setting bestNetwork here as a listening NetworkRequest may be
-                        // satisfied by multiple Networks.  Instead the request is added to
-                        // each satisfying Network and notified about each.
-                        network.addRequest(nri.request);
-                        notifyNetworkCallback(network, nri);
-                    } else {
-                        bestNetwork = network;
-                    }
+                    bestNetwork = network;
                 }
             }
         }
         if (bestNetwork != null) {
             if (DBG) log("using " + bestNetwork.name());
-            if (bestNetwork.networkInfo.isConnected()) {
-                // Cancel any lingering so the linger timeout doesn't teardown this network
-                // even though we have a request for it.
-                bestNetwork.networkLingered.clear();
-                bestNetwork.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
-            }
-            // TODO: This logic may be better replaced with a call to rematchNetworkAndRequests
+            unlinger(bestNetwork);
             bestNetwork.addRequest(nri.request);
             mNetworkForRequestId.put(nri.request.requestId, bestNetwork);
             notifyNetworkCallback(bestNetwork, nri);
-            score = bestNetwork.getCurrentScore();
             if (nri.request.legacyType != TYPE_NONE) {
                 mLegacyTypeTracker.add(nri.request.legacyType, bestNetwork);
             }
@@ -2232,6 +2264,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
 
         if (nri.isRequest) {
             if (DBG) log("sending new NetworkRequest to factories");
+            final int score = bestNetwork == null ? 0 : bestNetwork.getCurrentScore();
             for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
                 nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
                         0, nri.request);
@@ -3544,8 +3577,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
     // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
     private final NetworkRequest mDefaultRequest;
 
+    private NetworkAgentInfo getDefaultNetwork() {
+        return mNetworkForRequestId.get(mDefaultRequest.requestId);
+    }
+
     private boolean isDefaultNetwork(NetworkAgentInfo nai) {
-        return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai;
+        return nai == getDefaultNetwork();
     }
 
     public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
@@ -3593,8 +3630,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
 //            updateMtu(lp, null);
 //        }
         updateTcpBufferSizes(networkAgent);
+
+        // TODO: deprecate and remove mDefaultDns when we can do so safely.
+        // For now, use it only when the network has Internet access. http://b/18327075
+        final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_INTERNET);
         final boolean flushDns = updateRoutes(newLp, oldLp, netId);
-        updateDnses(newLp, oldLp, netId, flushDns);
+        updateDnses(newLp, oldLp, netId, flushDns, useDefaultDns);
+
         updateClat(newLp, oldLp, networkAgent);
         if (isDefaultNetwork(networkAgent)) handleApplyDefaultProxy(newLp.getHttpProxy());
         // TODO - move this check to cover the whole function
@@ -3688,10 +3731,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
         }
         return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty();
     }
-    private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId, boolean flush) {
+    private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId,
+                             boolean flush, boolean useDefaultDns) {
         if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
             Collection<InetAddress> dnses = newLp.getDnsServers();
-            if (dnses.size() == 0 && mDefaultDns != null) {
+            if (dnses.size() == 0 && mDefaultDns != null && useDefaultDns) {
                 dnses = new ArrayList();
                 dnses.add(mDefaultDns);
                 if (DBG) {
@@ -3768,8 +3812,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
             int notificationType) {
         if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE) {
             Intent intent = new Intent();
-            intent.putExtra(ConnectivityManager.EXTRA_NETWORK, nri.request);
-            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, networkAgent.network);
+            intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
+            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
             sendIntent(nri.mPendingIntent, intent);
         }
         // else not handled
@@ -3922,8 +3966,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
 
             // check if it satisfies the NetworkCapabilities
             if (VDBG) log("  checking if request is satisfied: " + nri.request);
-            if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
-                    newNetwork.networkCapabilities)) {
+            if (newNetwork.satisfies(nri.request)) {
                 if (!nri.isRequest) {
                     // This is not a request, it's a callback listener.
                     // Add it to newNetwork regardless of score.
@@ -4003,12 +4046,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                 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");
+                unlinger(nai);
             }
         }
         if (keep) {
@@ -4230,7 +4268,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
         info.setType(type);
         if (connected) {
             info.setDetailedState(DetailedState.CONNECTED, null, info.getExtraInfo());
-            sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
+            sendConnectedBroadcast(info);
         } else {
             info.setDetailedState(DetailedState.DISCONNECTED, null, info.getExtraInfo());
             Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
@@ -4261,10 +4299,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
             final Intent immediateIntent = new Intent(intent);
             immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
             sendStickyBroadcast(immediateIntent);
-            sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
+            sendStickyBroadcast(intent);
             if (newDefaultAgent != null) {
-                sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
-                getConnectivityChangeDelay());
+                sendConnectedBroadcast(newDefaultAgent.networkInfo);
             }
         }
     }
index 8417ccc..f04487e 100644 (file)
@@ -51,8 +51,8 @@ option java_package com.android.server
 # ---------------------------
 # NotificationManagerService.java
 # ---------------------------
-# when a NotificationManager.notify is called
-2750 notification_enqueue (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(notification|3),(update|1)
+# when a NotificationManager.notify is called. status: 0=post, 1=update, 2=ignored
+2750 notification_enqueue (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(notification|3),(status|1)
 # when someone tries to cancel a notification, the notification manager sometimes
 # calls this with flags too
 2751 notification_cancel (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(required_flags|1),(forbidden_flags|1),(reason|1|5),(listener|3)
index b1c4955..83b1919 100644 (file)
@@ -207,20 +207,6 @@ public class MmsServiceBroker extends SystemService {
         return mTelephonyManager;
     }
 
-    /*
-     * Throws a security exception unless the caller has carrier privilege.
-     */
-    private void enforceCarrierPrivilege() {
-        final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
-        for (String pkg : packages) {
-            if (getTelephonyManager().checkCarrierPrivilegesForPackage(pkg) ==
-                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
-                return;
-            }
-        }
-        throw new SecurityException("No carrier privilege");
-    }
-
     private String getCallingPackageName() {
         final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
         if (packages != null && packages.length > 0) {
@@ -270,19 +256,6 @@ public class MmsServiceBroker extends SystemService {
         }
 
         @Override
-        public void updateMmsSendStatus(int messageRef, byte[] pdu, int status)
-                throws RemoteException {
-            enforceCarrierPrivilege();
-            getServiceGuarded().updateMmsSendStatus(messageRef, pdu, status);
-        }
-
-        @Override
-        public void updateMmsDownloadStatus(int messageRef, int status) throws RemoteException {
-            enforceCarrierPrivilege();
-            getServiceGuarded().updateMmsDownloadStatus(messageRef, status);
-        }
-
-        @Override
         public Bundle getCarrierConfigValues(int subId) throws RemoteException {
             Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName());
             return getServiceGuarded().getCarrierConfigValues(subId);
index 215d92d..e0b2307 100644 (file)
@@ -18,12 +18,14 @@ package com.android.server;
 
 import java.util.Calendar;
 
+import android.app.ActivityManagerNative;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.RemoteException;
 import android.util.Slog;
 
 public class MountServiceIdler extends JobService {
@@ -53,6 +55,13 @@ public class MountServiceIdler extends JobService {
 
     @Override
     public boolean onStartJob(JobParameters params) {
+        // First have the activity manager do its idle maintenance.  (Yes this job
+        // is really more than just mount, some day it should be renamed to be system
+        // idleer).
+        try {
+            ActivityManagerNative.getDefault().performIdleMaintenance();
+        } catch (RemoteException e) {
+        }
         // The mount service will run an fstrim operation asynchronously
         // on a designated separate thread, so we provide it with a callback
         // that lets us cleanly end our idle timeslice.  It's safe to call
@@ -98,7 +107,7 @@ public class MountServiceIdler extends JobService {
     private static Calendar tomorrowMidnight() {
         Calendar calendar = Calendar.getInstance();
         calendar.setTimeInMillis(System.currentTimeMillis());
-        calendar.set(Calendar.HOUR, 0);
+        calendar.set(Calendar.HOUR_OF_DAY, 3);
         calendar.set(Calendar.MINUTE, 0);
         calendar.set(Calendar.SECOND, 0);
         calendar.set(Calendar.MILLISECOND, 0);
index 8c3b020..d2dfc7b 100644 (file)
@@ -174,6 +174,8 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
 
                 for (int i = 0; i < count; i++) {
                     if (buffer[i] == 0) {
+                        // Note - do not log this raw message since it may contain
+                        // sensitive data
                         final String rawEvent = new String(
                                 buffer, start, i - start, StandardCharsets.UTF_8);
 
@@ -181,6 +183,9 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
                         try {
                             final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(
                                     rawEvent);
+
+                            log("RCV <- {" + event + "}");
+
                             if (event.isClassUnsolicited()) {
                                 // TODO: migrate to sending NativeDaemonEvent instances
                                 if (mCallbacks.onCheckHoldWakeLock(event.getCode())
@@ -196,6 +201,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
                                 mResponseQueue.add(event.getCmdNumber(), event);
                             }
                         } catch (IllegalArgumentException e) {
+                            log("Problem parsing message " + e);
                         } finally {
                             if (releaseWl) {
                                 mWakeLock.acquire();
@@ -205,8 +211,9 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
                         start = i + 1;
                     }
                 }
+
                 if (start == 0) {
-                    final String rawEvent = new String(buffer, start, count, StandardCharsets.UTF_8);
+                    log("RCV incomplete");
                 }
 
                 // We should end at the amount we read. If not, compact then
index 59d50bd..4e61c0b 100644 (file)
@@ -33,16 +33,21 @@ public class NativeDaemonEvent {
     private final int mCode;
     private final String mMessage;
     private final String mRawEvent;
+    private final String mLogMessage;
     private String[] mParsed;
 
-    private NativeDaemonEvent(int cmdNumber, int code, String message, String rawEvent) {
+    private NativeDaemonEvent(int cmdNumber, int code, String message,
+                              String rawEvent, String logMessage) {
         mCmdNumber = cmdNumber;
         mCode = code;
         mMessage = message;
         mRawEvent = rawEvent;
+        mLogMessage = logMessage;
         mParsed = null;
     }
 
+    static public final String SENSITIVE_MARKER = "{{sensitive}}";
+
     public int getCmdNumber() {
         return mCmdNumber;
     }
@@ -62,7 +67,7 @@ public class NativeDaemonEvent {
 
     @Override
     public String toString() {
-        return mRawEvent;
+        return mLogMessage;
     }
 
     /**
@@ -151,9 +156,15 @@ public class NativeDaemonEvent {
             }
         }
 
+        String logMessage = rawEvent;
+        if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
+            skiplength += parsed[2].length() + 1;
+            logMessage = parsed[0] + " " + parsed[1] + " {}";
+        }
+
         final String message = rawEvent.substring(skiplength);
 
-        return new NativeDaemonEvent(cmdNumber, code, message, rawEvent);
+        return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage);
     }
 
     /**
index 5fe0d1c..748018d 100644 (file)
@@ -1710,14 +1710,18 @@ public class NetworkManagementService extends INetworkManagementService.Stub
     public void setDnsServersForNetwork(int netId, String[] servers, String domains) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        final Command cmd = new Command("resolver", "setnetdns", netId,
-                (domains == null ? "" : domains));
-
-        for (String s : servers) {
-            InetAddress a = NetworkUtils.numericToInetAddress(s);
-            if (a.isAnyLocalAddress() == false) {
-                cmd.appendArg(a.getHostAddress());
+        Command cmd;
+        if (servers.length > 0) {
+            cmd = new Command("resolver", "setnetdns", netId,
+                    (domains == null ? "" : domains));
+            for (String s : servers) {
+                InetAddress a = NetworkUtils.numericToInetAddress(s);
+                if (a.isAnyLocalAddress() == false) {
+                    cmd.appendArg(a.getHostAddress());
+                }
             }
+        } else {
+            cmd = new Command("resolver", "clearnetdns", netId);
         }
 
         try {
index a2f4d56..e52b2bf 100644 (file)
@@ -390,7 +390,7 @@ public class AccountManagerService
             boolean accountDeleted = false;
             Cursor cursor = db.query(TABLE_ACCOUNTS,
                     new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
-                    null, null, null, null, null);
+                    null, null, null, null, ACCOUNTS_ID);
             try {
                 accounts.accountCache.clear();
                 final HashMap<String, ArrayList<String>> accountNamesByType =
index 7e17043..5b1d212 100755 (executable)
@@ -30,6 +30,7 @@ import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 
 import android.Manifest;
 import android.app.AppOpsManager;
@@ -37,6 +38,7 @@ import android.app.ApplicationThreadNative;
 import android.app.IActivityContainer;
 import android.app.IActivityContainerCallback;
 import android.app.IAppTask;
+import android.app.ITaskStackListener;
 import android.app.ProfilerInfo;
 import android.app.admin.DevicePolicyManager;
 import android.app.usage.UsageEvents;
@@ -292,7 +294,7 @@ public final class ActivityManagerService extends ActivityManagerNative
     static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
 
     // Maximum number recent bitmaps to keep in memory.
-    static final int MAX_RECENT_BITMAPS = 5;
+    static final int MAX_RECENT_BITMAPS = 3;
 
     // Amount of time after a call to stopAppSwitches() during which we will
     // prevent further untrusted switches from happening.
@@ -370,6 +372,9 @@ public final class ActivityManagerService extends ActivityManagerNative
 
     static final int LAST_PREBOOT_DELIVERED_FILE_VERSION = 10000;
 
+    // Delay in notifying task stack change listeners (in millis)
+    static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 1000;
+
     /** All system services */
     SystemServiceManager mSystemServiceManager;
 
@@ -378,6 +383,10 @@ public final class ActivityManagerService extends ActivityManagerNative
     /** Run all ActivityStacks through this */
     ActivityStackSupervisor mStackSupervisor;
 
+    /** Task stack change listeners. */
+    private RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
+            new RemoteCallbackList<ITaskStackListener>();
+
     public IntentFirewall mIntentFirewall;
 
     // Whether we should show our dialogs (ANR, crash, etc) or just perform their
@@ -1219,6 +1228,7 @@ public final class ActivityManagerService extends ActivityManagerNative
     static final int START_USER_SWITCH_MSG = 46;
     static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
     static final int DISMISS_DIALOG_MSG = 48;
+    static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 49;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1660,7 +1670,9 @@ public final class ActivityManagerService extends ActivityManagerNative
                 break;
             }
             case REQUEST_ALL_PSS_MSG: {
-                requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
+                synchronized (ActivityManagerService.this) {
+                    requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
+                }
                 break;
             }
             case START_PROFILES_MSG: {
@@ -1738,6 +1750,22 @@ public final class ActivityManagerService extends ActivityManagerNative
                 d.dismiss();
                 break;
             }
+            case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG: {
+                synchronized (ActivityManagerService.this) {
+                    int i = mTaskStackListeners.beginBroadcast();
+                    while (i > 0) {
+                        i--;
+                        try {
+                            // Make a one-way callback to the listener
+                            mTaskStackListeners.getBroadcastItem(i).onTaskStackChanged();
+                        } catch (RemoteException e){
+                            // Handled by the RemoteCallbackList
+                        }
+                    }
+                    mTaskStackListeners.finishBroadcast();
+                }
+                break;
+            }
             }
         }
     };
@@ -2332,6 +2360,16 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
     }
 
+    /** Sets the task stack listener that gets callbacks when a task stack changes. */
+    @Override
+    public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
+        synchronized (ActivityManagerService.this) {
+            if (listener != null) {
+                mTaskStackListeners.register(listener);
+            }
+        }
+    }
+
     @Override
     public void notifyActivityDrawn(IBinder token) {
         if (DEBUG_VISBILITY) Slog.d(TAG, "notifyActivityDrawn: token=" + token);
@@ -2773,12 +2811,12 @@ public final class ActivityManagerService extends ActivityManagerNative
         if (app == null) {
             checkTime(startTime, "startProcess: creating new process record");
             app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
-            app.crashHandler = crashHandler;
             if (app == null) {
                 Slog.w(TAG, "Failed making new process record for "
                         + processName + "/" + info.uid + " isolated=" + isolated);
                 return null;
             }
+            app.crashHandler = crashHandler;
             mProcessNames.put(processName, app.uid, app);
             if (isolated) {
                 mIsolatedProcesses.put(app.uid, app);
@@ -3643,12 +3681,12 @@ public final class ActivityManagerService extends ActivityManagerNative
                 if(DEBUG_TASKS) Slog.i(TAG, "remove RecentTask " + tr
                         + " when finishing user" + userId);
                 mRecentTasks.remove(i);
-                tr.removedFromRecents(mTaskPersister);
+                tr.removedFromRecents();
             }
         }
 
         // Remove tasks from persistent storage.
-        mTaskPersister.wakeup(null, true);
+        notifyTaskPersisterLocked(null, true);
     }
 
     // Sort by taskId
@@ -3660,7 +3698,7 @@ public final class ActivityManagerService extends ActivityManagerNative
     };
 
     // Extract the affiliates of the chain containing mRecentTasks[start].
-    private int processNextAffiliateChain(int start) {
+    private int processNextAffiliateChainLocked(int start) {
         final TaskRecord startTask = mRecentTasks.get(start);
         final int affiliateId = startTask.mAffiliatedTaskId;
 
@@ -3695,7 +3733,7 @@ public final class ActivityManagerService extends ActivityManagerNative
         if (first.mNextAffiliate != null) {
             Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
             first.setNextAffiliate(null);
-            mTaskPersister.wakeup(first, false);
+            notifyTaskPersisterLocked(first, false);
         }
         // Everything in the middle is doubly linked from next to prev.
         final int tmpSize = mTmpRecents.size();
@@ -3706,13 +3744,13 @@ public final class ActivityManagerService extends ActivityManagerNative
                 Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
                         " setting prev=" + prev);
                 next.setPrevAffiliate(prev);
-                mTaskPersister.wakeup(next, false);
+                notifyTaskPersisterLocked(next, false);
             }
             if (prev.mNextAffiliate != next) {
                 Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
                         " setting next=" + next);
                 prev.setNextAffiliate(next);
-                mTaskPersister.wakeup(prev, false);
+                notifyTaskPersisterLocked(prev, false);
             }
             prev.inRecents = true;
         }
@@ -3721,7 +3759,7 @@ public final class ActivityManagerService extends ActivityManagerNative
         if (last.mPrevAffiliate != null) {
             Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
             last.setPrevAffiliate(null);
-            mTaskPersister.wakeup(last, false);
+            notifyTaskPersisterLocked(last, false);
         }
 
         // Insert the group back into mRecentTasks at start.
@@ -3762,7 +3800,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                 if (task.autoRemoveRecents && task.getTopActivity() == null) {
                     // This situation is broken, and we should just get rid of it now.
                     mRecentTasks.remove(i);
-                    task.removedFromRecents(mTaskPersister);
+                    task.removedFromRecents();
                     i--;
                     N--;
                     Slog.w(TAG, "Removing auto-remove without activity: " + task);
@@ -3807,7 +3845,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                         if (app == dummyApp || (app.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
                             // Doesn't exist any more!  Good-bye.
                             mRecentTasks.remove(i);
-                            task.removedFromRecents(mTaskPersister);
+                            task.removedFromRecents();
                             i--;
                             N--;
                             Slog.w(TAG, "Removing no longer valid recent: " + task);
@@ -3843,7 +3881,7 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
 
         // Verify the affiliate chain for each task.
-        for (int i = 0; i < N; i = processNextAffiliateChain(i)) {
+        for (int i = 0; i < N; i = processNextAffiliateChainLocked(i)) {
         }
 
         mTmpRecents.clear();
@@ -3870,7 +3908,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                     + endIndex + " " + cur);
             if (cur == top) {
                 // Verify start of the chain.
-                if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != -1) {
+                if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) {
                     Slog.wtf(TAG, "Bad chain @" + endIndex
                             + ": first task has next affiliate: " + prev);
                     sane = false;
@@ -3889,7 +3927,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                     break;
                 }
             }
-            if (cur.mPrevAffiliateTaskId == -1) {
+            if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) {
                 // Chain ends here.
                 if (cur.mPrevAffiliate != null) {
                     Slog.wtf(TAG, "Bad chain @" + endIndex
@@ -3954,7 +3992,8 @@ public final class ActivityManagerService extends ActivityManagerNative
 
     final void addRecentTaskLocked(TaskRecord task) {
         final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
-                || task.mNextAffiliateTaskId != -1 || task.mPrevAffiliateTaskId != -1;
+                || task.mNextAffiliateTaskId != INVALID_TASK_ID
+                || task.mPrevAffiliateTaskId != INVALID_TASK_ID;
 
         int N = mRecentTasks.size();
         // Quick case: check if the top-most recent task is the same.
@@ -4009,12 +4048,12 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
 
         if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: trimming tasks for " + task);
-        trimRecentsForTask(task, true);
+        trimRecentsForTaskLocked(task, true);
 
         N = mRecentTasks.size();
         while (N >= ActivityManager.getMaxRecentTasksStatic()) {
             final TaskRecord tr = mRecentTasks.remove(N - 1);
-            tr.removedFromRecents(mTaskPersister);
+            tr.removedFromRecents();
             N--;
         }
         task.inRecents = true;
@@ -4079,7 +4118,7 @@ public final class ActivityManagerService extends ActivityManagerNative
      * If needed, remove oldest existing entries in recents that are for the same kind
      * of task as the given one.
      */
-    int trimRecentsForTask(TaskRecord task, boolean doTrim) {
+    int trimRecentsForTaskLocked(TaskRecord task, boolean doTrim) {
         int N = mRecentTasks.size();
         final Intent intent = task.intent;
         final boolean document = intent != null && intent.isDocument();
@@ -4126,7 +4165,7 @@ public final class ActivityManagerService extends ActivityManagerNative
             tr.disposeThumbnail();
             mRecentTasks.remove(i);
             if (task != tr) {
-                tr.removedFromRecents(mTaskPersister);
+                tr.removedFromRecents();
             }
             i--;
             N--;
@@ -5843,6 +5882,7 @@ public final class ActivityManagerService extends ActivityManagerNative
         app.hasShownUi = false;
         app.debugging = false;
         app.cached = false;
+        app.killedByAm = false;
 
         mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
 
@@ -6184,6 +6224,17 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
     }
 
+    @Override
+    public void systemBackupRestored() {
+        synchronized (this) {
+            if (mSystemReady) {
+                mTaskPersister.restoreTasksFromOtherDeviceLocked();
+            } else {
+                Slog.w(TAG, "System backup restored before system is ready");
+            }
+        }
+    }
+
     final void ensureBootCompleted() {
         boolean booting;
         boolean enableScreen;
@@ -7989,10 +8040,6 @@ public final class ActivityManagerService extends ActivityManagerNative
         return list;
     }
 
-    TaskRecord getMostRecentTask() {
-        return mRecentTasks.get(0);
-    }
-
     /**
      * Creates a new RecentTaskInfo from a TaskRecord.
      */
@@ -8002,7 +8049,7 @@ public final class ActivityManagerService extends ActivityManagerNative
 
         // Compose the recent task info
         ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
-        rti.id = tr.getTopActivity() == null ? -1 : tr.taskId;
+        rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
         rti.persistentId = tr.taskId;
         rti.baseIntent = new Intent(tr.getBaseIntent());
         rti.origActivity = tr.origActivity;
@@ -8221,17 +8268,17 @@ public final class ActivityManagerService extends ActivityManagerNative
                 TaskRecord task = new TaskRecord(this, mStackSupervisor.getNextTaskId(), ainfo,
                         intent, description);
 
-                int trimIdx = trimRecentsForTask(task, false);
+                int trimIdx = trimRecentsForTaskLocked(task, false);
                 if (trimIdx >= 0) {
                     // If this would have caused a trim, then we'll abort because that
                     // means it would be added at the end of the list but then just removed.
-                    return -1;
+                    return INVALID_TASK_ID;
                 }
 
                 final int N = mRecentTasks.size();
                 if (N >= (ActivityManager.getMaxRecentTasksStatic()-1)) {
                     final TaskRecord tr = mRecentTasks.remove(N - 1);
-                    tr.removedFromRecents(mTaskPersister);
+                    tr.removedFromRecents();
                 }
 
                 task.inRecents = true;
@@ -8291,7 +8338,7 @@ public final class ActivityManagerService extends ActivityManagerNative
 
     private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess) {
         mRecentTasks.remove(tr);
-        tr.removedFromRecents(mTaskPersister);
+        tr.removedFromRecents();
         ComponentName component = tr.getBaseIntent().getComponent();
         if (component == null) {
             Slog.w(TAG, "No component for base intent of task: " + tr);
@@ -9979,6 +10026,7 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
     }
 
+    /** Pokes the task persister. */
     void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
         if (task != null && task.stack != null && task.stack.isHomeStack()) {
             // Never persist the home stack.
@@ -9987,6 +10035,13 @@ public final class ActivityManagerService extends ActivityManagerNative
         mTaskPersister.wakeup(task, flush);
     }
 
+    /** Notifies all listeners when the task stack has changed. */
+    void notifyTaskStackChangedLocked() {
+        mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
+        Message nmsg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
+        mHandler.sendMessageDelayed(nmsg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY);
+    }
+
     @Override
     public boolean shutdown(int timeout) {
         if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
@@ -10010,12 +10065,12 @@ public final class ActivityManagerService extends ActivityManagerNative
         mBatteryStatsService.shutdown();
         synchronized (this) {
             mProcessStats.shutdownLocked();
+            notifyTaskPersisterLocked(null, true);
         }
-        notifyTaskPersisterLocked(null, true);
 
         return timedout;
     }
-    
+
     public final void activitySlept(IBinder token) {
         if (localLOGV) Slog.v(TAG, "Activity slept: token=" + token);
 
@@ -11104,6 +11159,7 @@ public final class ActivityManagerService extends ActivityManagerNative
 
             if (mRecentTasks == null) {
                 mRecentTasks = mTaskPersister.restoreTasksLocked();
+                mTaskPersister.restoreTasksFromOtherDeviceLocked();
                 if (!mRecentTasks.isEmpty()) {
                     mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks);
                 }
@@ -12844,7 +12900,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                     + PowerManagerInternal.wakefulnessToString(mWakefulness));
             pw.println("  mSleeping=" + mSleeping + " mLockScreenShown="
                     + lockScreenShownToString());
-            pw.print("  mShuttingDown=" + mShuttingDown + " mRunningVoice=" + mRunningVoice);
+            pw.println("  mShuttingDown=" + mShuttingDown + " mRunningVoice=" + mRunningVoice);
         }
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
@@ -15750,6 +15806,9 @@ public final class ActivityManagerService extends ActivityManagerNative
                                     }
                                 } else {
                                     removeTasksByRemovedPackageComponentsLocked(ssp, userId);
+                                    if (userId == UserHandle.USER_OWNER) {
+                                        mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp);
+                                    }
                                 }
                             }
                             break;
@@ -15767,6 +15826,9 @@ public final class ActivityManagerService extends ActivityManagerNative
                         if (replacing) {
                             removeTasksByRemovedPackageComponentsLocked(ssp, userId);
                         }
+                        if (userId == UserHandle.USER_OWNER) {
+                            mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp);
+                        }
                     }
                     break;
                 case Intent.ACTION_TIMEZONE_CHANGED:
index c12cadb..912ca62 100755 (executable)
 
 package com.android.server.am;
 
+import static com.android.server.am.TaskPersister.DEBUG_PERSISTER;
+import static com.android.server.am.TaskPersister.DEBUG_RESTORER;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+
 import android.app.ActivityManager.TaskDescription;
 import android.os.PersistableBundle;
 import android.os.Trace;
@@ -69,7 +73,7 @@ import java.util.Objects;
 final class ActivityRecord {
     static final String TAG = ActivityManagerService.TAG;
     static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
-    final public static String RECENTS_PACKAGE_NAME = "com.android.systemui.recent";
+    final public static String RECENTS_PACKAGE_NAME = "com.android.systemui.recents";
 
     private static final String TAG_ACTIVITY = "activity";
     private static final String ATTR_ID = "id";
@@ -792,7 +796,7 @@ final class ActivityRecord {
         }
     }
 
-    void updateThumbnail(Bitmap newThumbnail, CharSequence description) {
+    void updateThumbnailLocked(Bitmap newThumbnail, CharSequence description) {
         if (newThumbnail != null) {
             if (ActivityManagerService.DEBUG_THUMBNAILS) Slog.i(ActivityManagerService.TAG,
                     "Setting thumbnail of " + this + " to " + newThumbnail);
@@ -1052,12 +1056,12 @@ final class ActivityRecord {
     static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
         final ActivityRecord r = ActivityRecord.forToken(token);
         if (r == null) {
-            return -1;
+            return INVALID_TASK_ID;
         }
         final TaskRecord task = r.task;
         final int activityNdx = task.mActivities.indexOf(r);
         if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) {
-            return -1;
+            return INVALID_TASK_ID;
         }
         return task.taskId;
     }
@@ -1139,7 +1143,7 @@ final class ActivityRecord {
         }
     }
 
-    static ActivityRecord restoreFromXml(XmlPullParser in, int taskId,
+    static ActivityRecord restoreFromXml(XmlPullParser in,
             ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException {
         Intent intent = null;
         PersistableBundle persistentState = null;
@@ -1155,8 +1159,8 @@ final class ActivityRecord {
         for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
             final String attrName = in.getAttributeName(attrNdx);
             final String attrValue = in.getAttributeValue(attrNdx);
-            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "ActivityRecord: attribute name=" +
-                    attrName + " value=" + attrValue);
+            if (DEBUG_PERSISTER || DEBUG_RESTORER) Slog.d(TaskPersister.TAG,
+                        "ActivityRecord: attribute name=" + attrName + " value=" + attrValue);
             if (ATTR_ID.equals(attrName)) {
                 createTime = Long.valueOf(attrValue);
             } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
@@ -1181,15 +1185,15 @@ final class ActivityRecord {
                 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
             if (event == XmlPullParser.START_TAG) {
                 final String name = in.getName();
-                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
-                        "ActivityRecord: START_TAG name=" + name);
+                if (DEBUG_PERSISTER || DEBUG_RESTORER)
+                        Slog.d(TaskPersister.TAG, "ActivityRecord: START_TAG name=" + name);
                 if (TAG_INTENT.equals(name)) {
                     intent = Intent.restoreFromXml(in);
-                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
-                            "ActivityRecord: intent=" + intent);
+                    if (DEBUG_PERSISTER || DEBUG_RESTORER)
+                            Slog.d(TaskPersister.TAG, "ActivityRecord: intent=" + intent);
                 } else if (TAG_PERSISTABLEBUNDLE.equals(name)) {
                     persistentState = PersistableBundle.restoreFromXml(in);
-                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+                    if (DEBUG_PERSISTER || DEBUG_RESTORER) Slog.d(TaskPersister.TAG,
                             "ActivityRecord: persistentState=" + persistentState);
                 } else {
                     Slog.w(TAG, "restoreActivity: unexpected name=" + name);
@@ -1232,7 +1236,7 @@ final class ActivityRecord {
     @Override
     public String toString() {
         if (stringName != null) {
-            return stringName + " t" + (task == null ? -1 : task.taskId) +
+            return stringName + " t" + (task == null ? INVALID_TASK_ID : task.taskId) +
                     (finishing ? " f}" : "}");
         }
         StringBuilder sb = new StringBuilder(128);
index ea694ad..d98f03c 100755 (executable)
@@ -65,7 +65,6 @@ import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Binder;
@@ -838,7 +837,7 @@ final class ActivityStack {
         clearLaunchTime(prev);
         final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
         if (mService.mHasRecents && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) {
-            prev.updateThumbnail(screenshotActivities(prev), null);
+            prev.updateThumbnailLocked(screenshotActivities(prev), null);
         }
         stopFullyDrawnTraceIfNeeded();
 
@@ -950,7 +949,7 @@ final class ActivityStack {
             r.icicle = icicle;
             r.haveState = true;
             r.launchCount = 0;
-            r.updateThumbnail(null, description);
+            r.updateThumbnailLocked(null, description);
         }
         if (!r.stopped) {
             if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r + " (stop complete)");
@@ -1060,6 +1059,9 @@ final class ActivityStack {
             }
             prev.cpuTimeAtResume = 0; // reset it
         }
+
+        // Notfiy when the task stack has changed
+        mService.notifyTaskStackChangedLocked();
     }
 
     /**
@@ -1822,6 +1824,9 @@ final class ActivityStack {
                     // Do over!
                     mStackSupervisor.scheduleResumeTopActivities();
                 }
+                if (next == mLastScreenshotActivity) {
+                    invalidateLastScreenshot();
+                }
                 if (mStackSupervisor.reportResumedActivityLocked(next)) {
                     mNoAnimActivities.clear();
                     if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -4105,7 +4110,7 @@ final class ActivityStack {
                 // Task creator asked to remove this when done, or this task was a voice
                 // interaction, so it should not remain on the recent tasks list.
                 mService.mRecentTasks.remove(task);
-                task.removedFromRecents(mService.mTaskPersister);
+                task.removedFromRecents();
             }
         }
 
index 38809cb..262b4f1 100644 (file)
@@ -1314,7 +1314,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 }
             }
         }
-        ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
 
         final int launchFlags = intent.getFlags();
 
@@ -1391,6 +1390,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
             }
         }
 
+        final ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
+
         if (err != ActivityManager.START_SUCCESS) {
             if (resultRecord != null) {
                 resultStack.sendActivityResultLocked(-1,
@@ -2871,6 +2872,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
         final TaskRecord task = r.task;
         task.setLastThumbnail(task.stack.screenshotActivities(r));
         mService.addRecentTaskLocked(task);
+        mService.notifyTaskStackChangedLocked();
         mWindowManager.setAppVisibility(r.appToken, false);
     }
 
index e01b983..9b7d0b2 100644 (file)
@@ -296,7 +296,7 @@ public final class BroadcastQueue {
     public void skipCurrentReceiverLocked(ProcessRecord app) {
         boolean reschedule = false;
         BroadcastRecord r = app.curReceiver;
-        if (r != null) {
+        if (r != null && r.queue == this) {
             // The current broadcast is waiting for this app's receiver
             // to be finished.  Looks like that's not going to happen, so
             // let the broadcast continue.
@@ -352,7 +352,7 @@ public final class BroadcastQueue {
         }
         r.receiver = null;
         r.intent.setComponent(null);
-        if (r.curApp != null) {
+        if (r.curApp != null && r.curApp.curReceiver == r) {
             r.curApp.curReceiver = null;
         }
         if (r.curFilter != null) {
index 9311f25..629a05d 100644 (file)
 
 package com.android.server.am;
 
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.IPackageManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Debug;
+import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.Xml;
+
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -38,11 +49,18 @@ import java.io.IOException;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.List;
+
+import libcore.io.IoUtils;
+
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 
 public class TaskPersister {
     static final String TAG = "TaskPersister";
-    static final boolean DEBUG = false;
+    static final boolean DEBUG_PERSISTER = false;
+    static final boolean DEBUG_RESTORER = false;
 
     /** When not flushing don't write out files faster than this */
     private static final long INTER_WRITE_DELAY_MS = 500;
@@ -67,12 +85,17 @@ public class TaskPersister {
     // contains subdirs named after TASKS_DIRNAME and IMAGES_DIRNAME mirroring the
     // ancestral device's dataset.  This needs to match the RECENTS_TASK_RESTORE_DIR
     // value in RecentsBackupHelper.
-    private static final String RESTORED_TASKS = "restored_" + TASKS_DIRNAME;
+    private static final String RESTORED_TASKS_DIRNAME = "restored_" + TASKS_DIRNAME;
+
+    // Max time to wait for the application/package of a restored task to be installed
+    // before giving up.
+    private static final long MAX_INSTALL_WAIT_TIME = DateUtils.DAY_IN_MILLIS;
 
     private static final String TAG_TASK = "task";
 
     static File sImagesDir;
     static File sTasksDir;
+    static File sRestoredTasksDir;
 
     private final ActivityManagerService mService;
     private final ActivityStackSupervisor mStackSupervisor;
@@ -105,10 +128,20 @@ public class TaskPersister {
 
     ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
 
+    // Map of tasks that were backed-up on a different device that can be restored on this device.
+    // Data organization: <packageNameOfAffiliateTask, listOfAffiliatedTasksChains>
+    private ArrayMap<String, List<List<OtherDeviceTask>>> mOtherDeviceTasksMap =
+                new ArrayMap<>(10);
+
+    // The next time in milliseconds we will remove expired task from
+    // {@link #mOtherDeviceTasksMap} and disk. Set to {@link Long.MAX_VALUE} to never clean-up
+    // tasks.
+    private long mExpiredTasksCleanupTime = Long.MAX_VALUE;
+
     TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) {
         sTasksDir = new File(systemDir, TASKS_DIRNAME);
         if (!sTasksDir.exists()) {
-            if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
+            if (DEBUG_PERSISTER) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
             if (!sTasksDir.mkdir()) {
                 Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
             }
@@ -116,12 +149,14 @@ public class TaskPersister {
 
         sImagesDir = new File(systemDir, IMAGES_DIRNAME);
         if (!sImagesDir.exists()) {
-            if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
+            if (DEBUG_PERSISTER) Slog.d(TAG, "Creating images directory " + sTasksDir);
             if (!sImagesDir.mkdir()) {
                 Slog.e(TAG, "Failure creating images directory " + sImagesDir);
             }
         }
 
+        sRestoredTasksDir = new File(systemDir, RESTORED_TASKS_DIRNAME);
+
         mStackSupervisor = stackSupervisor;
         mService = stackSupervisor.mService;
 
@@ -138,8 +173,8 @@ public class TaskPersister {
             final WriteQueueItem item = mWriteQueue.get(queueNdx);
             if (item instanceof ImageWriteQueueItem &&
                     ((ImageWriteQueueItem) item).mFilename.startsWith(taskString)) {
-                if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilename +
-                        " from write queue");
+                if (DEBUG_PERSISTER) Slog.d(TAG, "Removing "
+                        + ((ImageWriteQueueItem) item).mFilename + " from write queue");
                 mWriteQueue.remove(queueNdx);
             }
         }
@@ -184,9 +219,9 @@ public class TaskPersister {
             } else if (mNextWriteTime == 0) {
                 mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
             }
-            if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
-                    + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
-                    + " Callers=" + Debug.getCallers(4));
+            if (DEBUG_PERSISTER) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush
+                    + " mNextWriteTime=" + mNextWriteTime + " mWriteQueue.size="
+                    + mWriteQueue.size() + " Callers=" + Debug.getCallers(4));
             notifyAll();
         }
 
@@ -228,7 +263,7 @@ public class TaskPersister {
             } else if (mNextWriteTime == 0) {
                 mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
             }
-            if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
+            if (DEBUG_PERSISTER) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
                     SystemClock.uptimeMillis() + " mNextWriteTime=" +
                     mNextWriteTime + " Callers=" + Debug.getCallers(4));
             notifyAll();
@@ -262,12 +297,12 @@ public class TaskPersister {
     }
 
     private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
-        if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
+        if (DEBUG_PERSISTER) Slog.d(TAG, "saveToXml: task=" + task);
         final XmlSerializer xmlSerializer = new FastXmlSerializer();
         StringWriter stringWriter = new StringWriter();
         xmlSerializer.setOutput(stringWriter);
 
-        if (DEBUG) xmlSerializer.setFeature(
+        if (DEBUG_PERSISTER) xmlSerializer.setFeature(
                     "http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
         // save task
@@ -326,7 +361,7 @@ public class TaskPersister {
 
         for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
             File taskFile = recentFiles[taskNdx];
-            if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
+            if (DEBUG_PERSISTER) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
             BufferedReader reader = null;
             boolean deleteFile = false;
             try {
@@ -339,11 +374,12 @@ public class TaskPersister {
                         event != XmlPullParser.END_TAG) {
                     final String name = in.getName();
                     if (event == XmlPullParser.START_TAG) {
-                        if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
+                        if (DEBUG_PERSISTER)
+                                Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
                         if (TAG_TASK.equals(name)) {
                             final TaskRecord task =
                                     TaskRecord.restoreFromXml(in, mStackSupervisor);
-                            if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
+                            if (DEBUG_PERSISTER) Slog.d(TAG, "restoreTasksLocked: restored task=" +
                                     task);
                             if (task != null) {
                                 task.isPersistable = true;
@@ -371,20 +407,16 @@ public class TaskPersister {
                 Slog.e(TAG, "Failing file: " + fileToString(taskFile));
                 deleteFile = true;
             } finally {
-                if (reader != null) {
-                    try {
-                        reader.close();
-                    } catch (IOException e) {
-                    }
-                }
-                if (!DEBUG && deleteFile) {
-                    if (true || DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
+                IoUtils.closeQuietly(reader);
+                if (!DEBUG_PERSISTER && deleteFile) {
+                    if (true || DEBUG_PERSISTER)
+                            Slog.d(TAG, "Deleting file=" + taskFile.getName());
                     taskFile.delete();
                 }
             }
         }
 
-        if (!DEBUG) {
+        if (!DEBUG_PERSISTER) {
             removeObsoleteFiles(recoveredTaskIds);
         }
 
@@ -415,8 +447,8 @@ public class TaskPersister {
     }
 
     private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
-        if (DEBUG) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds=" + persistentTaskIds +
-                " files=" + files);
+        if (DEBUG_PERSISTER) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds="
+                    + persistentTaskIds + " files=" + files);
         if (files == null) {
             Slog.e(TAG, "File error accessing recents directory (too many files open?).");
             return;
@@ -429,14 +461,14 @@ public class TaskPersister {
                 final int taskId;
                 try {
                     taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
-                    if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
+                    if (DEBUG_PERSISTER) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
                 } catch (Exception e) {
                     Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
                     file.delete();
                     continue;
                 }
                 if (!persistentTaskIds.contains(taskId)) {
-                    if (true || DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" +
+                    if (true || DEBUG_PERSISTER) Slog.d(TAG, "removeObsoleteFile: deleting file=" +
                             file.getName());
                     file.delete();
                 }
@@ -450,10 +482,363 @@ public class TaskPersister {
     }
 
     static Bitmap restoreImage(String filename) {
-        if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
+        if (DEBUG_PERSISTER) Slog.d(TAG, "restoreImage: restoring " + filename);
         return BitmapFactory.decodeFile(sImagesDir + File.separator + filename);
     }
 
+    /**
+     * Tries to restore task that were backed-up on a different device onto this device.
+     */
+    void restoreTasksFromOtherDeviceLocked() {
+        readOtherDeviceTasksFromDisk();
+        addOtherDeviceTasksToRecentsLocked();
+    }
+
+    /**
+     * Read the tasks that were backed-up on a different device and can be restored to this device
+     * from disk and populated {@link #mOtherDeviceTasksMap} with the information. Also sets up
+     * time to clear out other device tasks that have not been restored on this device
+     * within the allotted time.
+     */
+    private void readOtherDeviceTasksFromDisk() {
+        synchronized (mOtherDeviceTasksMap) {
+            // Clear out current map and expiration time.
+            mOtherDeviceTasksMap.clear();
+            mExpiredTasksCleanupTime = Long.MAX_VALUE;
+
+            final File[] taskFiles;
+            if (!sRestoredTasksDir.exists()
+                    || (taskFiles = sRestoredTasksDir.listFiles()) == null) {
+                // Nothing to do if there are no tasks to restore.
+                return;
+            }
+
+            long earliestMtime = System.currentTimeMillis();
+            SparseArray<List<OtherDeviceTask>> tasksByAffiliateIds =
+                        new SparseArray<>(taskFiles.length);
+
+            // Read new tasks from disk
+            for (int i = 0; i < taskFiles.length; ++i) {
+                final File taskFile = taskFiles[i];
+                if (DEBUG_RESTORER) Slog.d(TAG, "readOtherDeviceTasksFromDisk: taskFile="
+                            + taskFile.getName());
+
+                final OtherDeviceTask task = OtherDeviceTask.createFromFile(taskFile);
+
+                if (task == null) {
+                    // Go ahead and remove the file on disk if we are unable to create a task from
+                    // it.
+                    if (DEBUG_RESTORER) Slog.e(TAG, "Unable to create task for file="
+                                + taskFile.getName() + "...deleting file.");
+                    taskFile.delete();
+                    continue;
+                }
+
+                List<OtherDeviceTask> tasks = tasksByAffiliateIds.get(task.mAffiliatedTaskId);
+                if (tasks == null) {
+                    tasks = new ArrayList<>();
+                    tasksByAffiliateIds.put(task.mAffiliatedTaskId, tasks);
+                }
+                tasks.add(task);
+                final long taskMtime = taskFile.lastModified();
+                if (earliestMtime > taskMtime) {
+                    earliestMtime = taskMtime;
+                }
+            }
+
+            if (tasksByAffiliateIds.size() > 0) {
+                // Sort each affiliated tasks chain by taskId which is the order they were created
+                // that should always be correct...Then add to task map.
+                for (int i = 0; i < tasksByAffiliateIds.size(); i++) {
+                    List<OtherDeviceTask> chain = tasksByAffiliateIds.valueAt(i);
+                    Collections.sort(chain);
+                    // Package name of the root task in the affiliate chain.
+                    final String packageName =
+                            chain.get(chain.size()-1).mComponentName.getPackageName();
+                    List<List<OtherDeviceTask>> chains = mOtherDeviceTasksMap.get(packageName);
+                    if (chains == null) {
+                        chains = new ArrayList<>();
+                        mOtherDeviceTasksMap.put(packageName, chains);
+                    }
+                    chains.add(chain);
+                }
+
+                // Set expiration time.
+                mExpiredTasksCleanupTime = earliestMtime + MAX_INSTALL_WAIT_TIME;
+                if (DEBUG_RESTORER) Slog.d(TAG, "Set Expiration time to "
+                            + DateUtils.formatDateTime(mService.mContext, mExpiredTasksCleanupTime,
+                            DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME));
+            }
+        }
+    }
+
+    /**
+     * Removed any expired tasks from {@link #mOtherDeviceTasksMap} and disk if their expiration
+     * time is less than or equal to {@link #mExpiredTasksCleanupTime}.
+     */
+    private void removeExpiredTasksIfNeeded() {
+        synchronized (mOtherDeviceTasksMap) {
+            final long now = System.currentTimeMillis();
+            if (mOtherDeviceTasksMap.isEmpty() || now < mExpiredTasksCleanupTime) {
+                return;
+            }
+
+            long earliestNonExpiredMtime = now;
+            mExpiredTasksCleanupTime = Long.MAX_VALUE;
+
+            // Remove expired backed-up tasks that have not been restored. We only want to
+            // remove task if it is safe to remove all tasks in the affiliation chain.
+            for (int i = mOtherDeviceTasksMap.size() - 1; i >= 0 ; i--) {
+
+                List<List<OtherDeviceTask>> chains = mOtherDeviceTasksMap.valueAt(i);
+                for (int j = chains.size() - 1; j >= 0 ; j--) {
+
+                    List<OtherDeviceTask> chain = chains.get(j);
+                    boolean removeChain = true;
+                    for (int k = chain.size() - 1; k >= 0 ; k--) {
+                        OtherDeviceTask task = chain.get(k);
+                        final long taskLastModified = task.mFile.lastModified();
+                        if ((taskLastModified + MAX_INSTALL_WAIT_TIME) > now) {
+                            // File has not expired yet...but we keep looping to get the earliest
+                            // mtime.
+                            if (earliestNonExpiredMtime > taskLastModified) {
+                                earliestNonExpiredMtime = taskLastModified;
+                            }
+                            removeChain = false;
+                        }
+                    }
+                    if (removeChain) {
+                        for (int k = chain.size() - 1; k >= 0; k--) {
+                            final File file = chain.get(k).mFile;
+                            if (DEBUG_RESTORER) Slog.d(TAG, "Deleting expired file="
+                                    + file.getName() + " mapped to not installed component="
+                                    + chain.get(k).mComponentName);
+                            file.delete();
+                        }
+                        chains.remove(j);
+                    }
+                }
+                if (chains.isEmpty()) {
+                    final String packageName = mOtherDeviceTasksMap.keyAt(i);
+                    mOtherDeviceTasksMap.removeAt(i);
+                    if (DEBUG_RESTORER) Slog.d(TAG, "Removed package=" + packageName
+                                + " from task map");
+                }
+            }
+
+            // Reset expiration time if there is any task remaining.
+            if (!mOtherDeviceTasksMap.isEmpty()) {
+                mExpiredTasksCleanupTime = earliestNonExpiredMtime + MAX_INSTALL_WAIT_TIME;
+                if (DEBUG_RESTORER) Slog.d(TAG, "Reset expiration time to "
+                            + DateUtils.formatDateTime(mService.mContext, mExpiredTasksCleanupTime,
+                            DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME));
+            }
+        }
+    }
+
+    /**
+     * Tries to add all backed-up tasks from another device to this device recent's list.
+     */
+    private void addOtherDeviceTasksToRecentsLocked() {
+        synchronized (mOtherDeviceTasksMap) {
+            for (int i = mOtherDeviceTasksMap.size() - 1; i >= 0; i--) {
+                addOtherDeviceTasksToRecentsLocked(mOtherDeviceTasksMap.keyAt(i));
+            }
+        }
+    }
+
+    /**
+     * Tries to add backed-up tasks that are associated with the input package from
+     * another device to this device recent's list.
+     */
+    void addOtherDeviceTasksToRecentsLocked(String packageName) {
+        synchronized (mOtherDeviceTasksMap) {
+            List<List<OtherDeviceTask>> chains = mOtherDeviceTasksMap.get(packageName);
+            if (chains == null) {
+                return;
+            }
+
+            for (int i = chains.size() - 1; i >= 0; i--) {
+                List<OtherDeviceTask> chain = chains.get(i);
+                if (!canAddOtherDeviceTaskChain(chain)) {
+                    if (DEBUG_RESTORER) Slog.d(TAG, "Can't add task chain at index=" + i
+                            + " for package=" + packageName);
+                    continue;
+                }
+
+                // Generate task records for this chain.
+                List<TaskRecord> tasks = new ArrayList<>();
+                TaskRecord prev = null;
+                for (int j = chain.size() - 1; j >= 0; j--) {
+                    TaskRecord task = createTaskRecordLocked(chain.get(j));
+                    if (task == null) {
+                        // There was a problem in creating one of this task records in this chain.
+                        // There is no way we can continue...
+                        if (DEBUG_RESTORER) Slog.d(TAG, "Can't create task record for file="
+                                + chain.get(j).mFile + " for package=" + packageName);
+                        break;
+                    }
+
+                    // Wire-up affiliation chain.
+                    if (prev == null) {
+                        task.mPrevAffiliate = null;
+                        task.mPrevAffiliateTaskId = INVALID_TASK_ID;
+                        task.mAffiliatedTaskId = task.taskId;
+                    } else {
+                        prev.mNextAffiliate = task;
+                        prev.mNextAffiliateTaskId = task.taskId;
+                        task.mAffiliatedTaskId = prev.mAffiliatedTaskId;
+                        task.mPrevAffiliate = prev;
+                        task.mPrevAffiliateTaskId = prev.taskId;
+                    }
+                    prev = task;
+                    tasks.add(0, task);
+                }
+
+                // Add tasks to recent's if we were able to create task records for all the tasks
+                // in the chain.
+                if (tasks.size() == chain.size()) {
+                    // Make sure there is space in recent's to add the new task. If there is space
+                    // to the to the back.
+                    // TODO: Would be more fancy to interleave the new tasks into recent's based on
+                    // {@link TaskRecord.mLastTimeMoved} and drop the oldest recent's vs. just
+                    // adding to the back of the list.
+                    int spaceLeft =
+                            ActivityManager.getMaxRecentTasksStatic()
+                            - mService.mRecentTasks.size();
+                    if (spaceLeft >= tasks.size()) {
+                        mService.mRecentTasks.addAll(mService.mRecentTasks.size(), tasks);
+                        for (int k = tasks.size() - 1; k >= 0; k--) {
+                            // Persist new tasks.
+                            wakeup(tasks.get(k), false);
+                        }
+
+                        if (DEBUG_RESTORER) Slog.d(TAG, "Added " + tasks.size()
+                                    + " tasks to recent's for" + " package=" + packageName);
+                    } else {
+                        if (DEBUG_RESTORER) Slog.d(TAG, "Didn't add to recents. tasks.size("
+                                    + tasks.size() + ") != chain.size(" + chain.size()
+                                    + ") for package=" + packageName);
+                    }
+                } else {
+                    if (DEBUG_RESTORER) Slog.v(TAG, "Unable to add restored tasks to recents "
+                            + tasks.size() + " tasks for package=" + packageName);
+                }
+
+                // Clean-up structures
+                for (int j = chain.size() - 1; j >= 0; j--) {
+                    chain.get(j).mFile.delete();
+                }
+                chains.remove(i);
+                if (chains.isEmpty()) {
+                    // The fate of all backed-up tasks associated with this package has been
+                    // determine. Go ahead and remove it from the to-process list.
+                    mOtherDeviceTasksMap.remove(packageName);
+                    if (DEBUG_RESTORER)
+                            Slog.d(TAG, "Removed package=" + packageName + " from restore map");
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates and returns {@link TaskRecord} for the task from another device that can be used on
+     * this device. Returns null if the operation failed.
+     */
+    private TaskRecord createTaskRecordLocked(OtherDeviceTask other) {
+        File file = other.mFile;
+        BufferedReader reader = null;
+        TaskRecord task = null;
+        if (DEBUG_RESTORER) Slog.d(TAG, "createTaskRecordLocked: file=" + file.getName());
+
+        try {
+            reader = new BufferedReader(new FileReader(file));
+            final XmlPullParser in = Xml.newPullParser();
+            in.setInput(reader);
+
+            int event;
+            while (((event = in.next()) != XmlPullParser.END_DOCUMENT)
+                    && event != XmlPullParser.END_TAG) {
+                final String name = in.getName();
+                if (event == XmlPullParser.START_TAG) {
+
+                    if (TAG_TASK.equals(name)) {
+                        // Create a task record using a task id that is valid for this device.
+                        task = TaskRecord.restoreFromXml(
+                                in, mStackSupervisor, mStackSupervisor.getNextTaskId());
+                        if (DEBUG_RESTORER)
+                                Slog.d(TAG, "createTaskRecordLocked: restored task=" + task);
+
+                        if (task != null) {
+                            task.isPersistable = true;
+                            task.inRecents = true;
+                            // Task can/should only be backed-up/restored for device owner.
+                            task.userId = UserHandle.USER_OWNER;
+                            // Clear out affiliated ids that are no longer valid on this device.
+                            task.mAffiliatedTaskId = INVALID_TASK_ID;
+                            task.mPrevAffiliateTaskId = INVALID_TASK_ID;
+                            task.mNextAffiliateTaskId = INVALID_TASK_ID;
+                        } else {
+                            Slog.e(TAG, "Unable to create task for backed-up file=" + file + ": "
+                                        + fileToString(file));
+                        }
+                    } else {
+                        Slog.wtf(TAG, "createTaskRecordLocked Unknown xml event=" + event
+                                    + " name=" + name);
+                    }
+                }
+                XmlUtils.skipCurrentTag(in);
+            }
+        } catch (Exception e) {
+            Slog.wtf(TAG, "Unable to parse " + file + ". Error ", e);
+            Slog.e(TAG, "Failing file: " + fileToString(file));
+        } finally {
+            IoUtils.closeQuietly(reader);
+        }
+
+        return task;
+    }
+
+    /**
+     * Returns true if the input task chain backed-up from another device can be restored on this
+     * device.
+     */
+    private boolean canAddOtherDeviceTaskChain(List<OtherDeviceTask> chain) {
+
+        // Get component names of all the tasks in the chain.
+        // Mainly doing this to reduce checking for a component twice if two or more
+        // affiliations belong to the same component which is highly likely.
+        ArraySet<ComponentName> componentsToCheck = new ArraySet<>();
+        for (int i = 0; i < chain.size(); i++) {
+
+            OtherDeviceTask task = chain.get(i);
+            // Quick check, we can't add the task chain if any of its task files don't exist.
+            if (!task.mFile.exists()) {
+                if (DEBUG_RESTORER)
+                        Slog.d(TAG, "Can't add chain due to missing file=" + task.mFile);
+                return false;
+            }
+            componentsToCheck.add(task.mComponentName);
+        }
+
+        boolean canAdd = true;
+        try {
+            // Check to see if all the components for this task chain are installed.
+            final IPackageManager pm = AppGlobals.getPackageManager();
+            for (int i = 0; canAdd && i < componentsToCheck.size(); i++) {
+                ComponentName cn = componentsToCheck.valueAt(i);
+                canAdd &= pm.getActivityInfo(cn, 0, UserHandle.USER_OWNER) != null;
+                if (DEBUG_RESTORER) Slog.d(TAG, "ComponentName=" + cn + " installed=" + canAdd);
+            }
+        } catch (RemoteException e) {
+            // Should not happen???
+            canAdd = false;
+        }
+
+        if (DEBUG_RESTORER) Slog.d(TAG, "canAdd=" + canAdd);
+        return canAdd;
+    }
+
     private class LazyTaskWriterThread extends Thread {
 
         LazyTaskWriterThread(String name) {
@@ -472,21 +857,22 @@ public class TaskPersister {
                     probablyDone = mWriteQueue.isEmpty();
                 }
                 if (probablyDone) {
-                    if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
+                    if (DEBUG_PERSISTER) Slog.d(TAG, "Looking for obsolete files.");
                     persistentTaskIds.clear();
                     synchronized (mService) {
                         final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
-                        if (DEBUG) Slog.d(TAG, "mRecents=" + tasks);
+                        if (DEBUG_PERSISTER) Slog.d(TAG, "mRecents=" + tasks);
                         for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
                             final TaskRecord task = tasks.get(taskNdx);
-                            if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task +
+                            if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: task=" + task +
                                     " persistable=" + task.isPersistable);
                             if ((task.isPersistable || task.inRecents)
-                                    && !task.stack.isHomeStack()) {
-                                if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
+                                    && (task.stack == null || !task.stack.isHomeStack())) {
+                                if (DEBUG_PERSISTER)
+                                        Slog.d(TAG, "adding to persistentTaskIds task=" + task);
                                 persistentTaskIds.add(task.taskId);
                             } else {
-                                if (DEBUG) Slog.d(TAG,
+                                if (DEBUG_PERSISTER) Slog.d(TAG,
                                         "omitting from persistentTaskIds task=" + task);
                             }
                         }
@@ -500,7 +886,7 @@ public class TaskPersister {
                     if (mNextWriteTime != FLUSH_QUEUE) {
                         // The next write we don't have to wait so long.
                         mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
-                        if (DEBUG) Slog.d(TAG, "Next write time may be in " +
+                        if (DEBUG_PERSISTER) Slog.d(TAG, "Next write time may be in " +
                                 INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
                     }
 
@@ -510,8 +896,13 @@ public class TaskPersister {
                             mNextWriteTime = 0; // idle.
                             TaskPersister.this.notifyAll(); // wake up flush() if needed.
                         }
+
+                        // See if we need to remove any expired back-up tasks before waiting.
+                        removeExpiredTasksIfNeeded();
+
                         try {
-                            if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+                            if (DEBUG_PERSISTER)
+                                    Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
                             TaskPersister.this.wait();
                         } catch (InterruptedException e) {
                         }
@@ -521,11 +912,12 @@ public class TaskPersister {
                     item = mWriteQueue.remove(0);
 
                     long now = SystemClock.uptimeMillis();
-                    if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
-                            mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
+                    if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: now=" + now
+                                + " mNextWriteTime=" + mNextWriteTime + " mWriteQueue.size="
+                                + mWriteQueue.size());
                     while (now < mNextWriteTime) {
                         try {
-                            if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
+                            if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: waiting " +
                                     (mNextWriteTime - now));
                             TaskPersister.this.wait(mNextWriteTime - now);
                         } catch (InterruptedException e) {
@@ -540,7 +932,7 @@ public class TaskPersister {
                     ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
                     final String filename = imageWriteQueueItem.mFilename;
                     final Bitmap bitmap = imageWriteQueueItem.mImage;
-                    if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filename);
+                    if (DEBUG_PERSISTER) Slog.d(TAG, "writing bitmap: filename=" + filename);
                     FileOutputStream imageFile = null;
                     try {
                         imageFile = new FileOutputStream(new File(sImagesDir, filename));
@@ -548,23 +940,18 @@ public class TaskPersister {
                     } catch (Exception e) {
                         Slog.e(TAG, "saveImage: unable to save " + filename, e);
                     } finally {
-                        if (imageFile != null) {
-                            try {
-                                imageFile.close();
-                            } catch (IOException e) {
-                            }
-                        }
+                        IoUtils.closeQuietly(imageFile);
                     }
                 } else if (item instanceof TaskWriteQueueItem) {
                     // Write out one task.
                     StringWriter stringWriter = null;
                     TaskRecord task = ((TaskWriteQueueItem) item).mTask;
-                    if (DEBUG) Slog.d(TAG, "Writing task=" + task);
+                    if (DEBUG_PERSISTER) Slog.d(TAG, "Writing task=" + task);
                     synchronized (mService) {
                         if (task.inRecents) {
                             // Still there.
                             try {
-                                if (DEBUG) Slog.d(TAG, "Saving task=" + task);
+                                if (DEBUG_PERSISTER) Slog.d(TAG, "Saving task=" + task);
                                 stringWriter = saveToXml(task);
                             } catch (IOException e) {
                             } catch (XmlPullParserException e) {
@@ -594,4 +981,100 @@ public class TaskPersister {
             }
         }
     }
+
+    /**
+     * Helper class for holding essential information about task that were backed-up on a different
+     * device that can be restored on this device.
+     */
+    private static class OtherDeviceTask implements Comparable<OtherDeviceTask> {
+        final File mFile;
+        // See {@link TaskRecord} for information on the fields below.
+        final ComponentName mComponentName;
+        final int mTaskId;
+        final int mAffiliatedTaskId;
+
+        private OtherDeviceTask(
+                File file, ComponentName componentName, int taskId, int affiliatedTaskId) {
+            mFile = file;
+            mComponentName = componentName;
+            mTaskId = taskId;
+            mAffiliatedTaskId = (affiliatedTaskId == INVALID_TASK_ID) ? taskId: affiliatedTaskId;
+        }
+
+        @Override
+        public int compareTo(OtherDeviceTask another) {
+            return mTaskId - another.mTaskId;
+        }
+
+        /**
+         * Creates a new {@link OtherDeviceTask} object based on the contents of the input file.
+         *
+         * @param file input file that contains the complete task information.
+         * @return new {@link OtherDeviceTask} object or null if we failed to create the object.
+         */
+        static OtherDeviceTask createFromFile(File file) {
+            if (file == null || !file.exists()) {
+                if (DEBUG_RESTORER)
+                    Slog.d(TAG, "createFromFile: file=" + file + " doesn't exist.");
+                return null;
+            }
+
+            BufferedReader reader = null;
+
+            try {
+                reader = new BufferedReader(new FileReader(file));
+                final XmlPullParser in = Xml.newPullParser();
+                in.setInput(reader);
+
+                int event;
+                while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                        event != XmlPullParser.START_TAG) {
+                    // Skip to the start tag or end of document
+                }
+
+                if (event == XmlPullParser.START_TAG) {
+                    final String name = in.getName();
+
+                    if (TAG_TASK.equals(name)) {
+                        ComponentName componentName = null;
+                        int taskId = INVALID_TASK_ID;
+                        int taskAffiliation = INVALID_TASK_ID;
+                        for (int j = in.getAttributeCount() - 1; j >= 0; --j) {
+                            final String attrName = in.getAttributeName(j);
+                            final String attrValue = in.getAttributeValue(j);
+                            if (TaskRecord.ATTR_REALACTIVITY.equals(attrName)) {
+                                componentName = ComponentName.unflattenFromString(attrValue);
+                            } else if (TaskRecord.ATTR_TASKID.equals(attrName)) {
+                                taskId = Integer.valueOf(attrValue);
+                            } else if (TaskRecord.ATTR_TASK_AFFILIATION.equals(attrName)) {
+                                taskAffiliation = Integer.valueOf(attrValue);
+                            }
+                        }
+                        if (componentName == null || taskId == INVALID_TASK_ID) {
+                            if (DEBUG_RESTORER) Slog.e(TAG,
+                                    "createFromFile: FAILED componentName=" + componentName
+                                    + " taskId=" + taskId + " file=" + file);
+                            return null;
+                        }
+                        if (DEBUG_RESTORER) Slog.d(TAG, "creating OtherDeviceTask from file="
+                                + file.getName() + " componentName=" + componentName
+                                + " taskId=" + taskId);
+                        return new OtherDeviceTask(file, componentName, taskId, taskAffiliation);
+                    } else {
+                        Slog.wtf(TAG,
+                                "createFromFile: Unknown xml event=" + event + " name=" + name);
+                    }
+                } else {
+                    Slog.wtf(TAG, "createFromFile: Unable to find start tag in file=" + file);
+                }
+            } catch (IOException | XmlPullParserException e) {
+                Slog.wtf(TAG, "Unable to parse " + file + ". Error ", e);
+            } finally {
+                IoUtils.closeQuietly(reader);
+            }
+
+            // Something went wrong...
+            return null;
+        }
+    }
 }
index ee93233..c3eda71 100644 (file)
@@ -21,6 +21,8 @@ import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
+import static com.android.server.am.TaskPersister.DEBUG_PERSISTER;
+import static com.android.server.am.TaskPersister.DEBUG_RESTORER;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -52,10 +54,10 @@ import java.io.PrintWriter;
 import java.util.ArrayList;
 
 final class TaskRecord {
-    private static final String ATTR_TASKID = "task_id";
+    static final String ATTR_TASKID = "task_id";
     private static final String TAG_INTENT = "intent";
     private static final String TAG_AFFINITYINTENT = "affinity_intent";
-    private static final String ATTR_REALACTIVITY = "real_activity";
+    static final String ATTR_REALACTIVITY = "real_activity";
     private static final String ATTR_ORIGACTIVITY = "orig_activity";
     private static final String TAG_ACTIVITY = "activity";
     private static final String ATTR_AFFINITY = "affinity";
@@ -71,7 +73,7 @@ final class TaskRecord {
     private static final String ATTR_LASTDESCRIPTION = "last_description";
     private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
     private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
-    private static final String ATTR_TASK_AFFILIATION = "task_affiliation";
+    static final String ATTR_TASK_AFFILIATION = "task_affiliation";
     private static final String ATTR_PREV_AFFILIATION = "prev_affiliation";
     private static final String ATTR_NEXT_AFFILIATION = "next_affiliation";
     private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
@@ -82,6 +84,8 @@ final class TaskRecord {
 
     static final boolean IGNORE_RETURN_TO_RECENTS = true;
 
+    static final int INVALID_TASK_ID = -1;
+
     final int taskId;       // Unique identifier for this task.
     String affinity;        // The affinity name for this task, or null; may change identity.
     String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
@@ -151,9 +155,9 @@ final class TaskRecord {
     int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
     int mAffiliatedTaskColor; // color of the parent task affiliation.
     TaskRecord mPrevAffiliate; // previous task in affiliated chain.
-    int mPrevAffiliateTaskId = -1; // previous id for persistence.
+    int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
     TaskRecord mNextAffiliate; // next task in affiliated chain.
-    int mNextAffiliateTaskId = -1; // next id for persistence.
+    int mNextAffiliateTaskId = INVALID_TASK_ID; // next id for persistence.
 
     // For relaunching the task from recents as though it was launched by the original launcher.
     int mCallingUid;
@@ -363,12 +367,12 @@ final class TaskRecord {
 
     void setPrevAffiliate(TaskRecord prevAffiliate) {
         mPrevAffiliate = prevAffiliate;
-        mPrevAffiliateTaskId = prevAffiliate == null ? -1 : prevAffiliate.taskId;
+        mPrevAffiliateTaskId = prevAffiliate == null ? INVALID_TASK_ID : prevAffiliate.taskId;
     }
 
     void setNextAffiliate(TaskRecord nextAffiliate) {
         mNextAffiliate = nextAffiliate;
-        mNextAffiliateTaskId = nextAffiliate == null ? -1 : nextAffiliate.taskId;
+        mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId;
     }
 
     // Close up recents linked list.
@@ -383,12 +387,12 @@ final class TaskRecord {
         setNextAffiliate(null);
     }
 
-    void removedFromRecents(TaskPersister persister) {
+    void removedFromRecents() {
         disposeThumbnail();
         closeRecentsChain();
         if (inRecents) {
             inRecents = false;
-            persister.wakeup(this, false);
+            mService.notifyTaskPersisterLocked(this, false);
         }
     }
 
@@ -875,6 +879,10 @@ final class TaskRecord {
 
     static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
             throws IOException, XmlPullParserException {
+        return restoreFromXml(in, stackSupervisor, INVALID_TASK_ID);
+    }
+    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor,
+            int inTaskId) throws IOException, XmlPullParserException {
         Intent intent = null;
         Intent affinityIntent = null;
         ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
@@ -894,23 +902,23 @@ final class TaskRecord {
         long lastActiveTime = -1;
         long lastTimeOnTop = 0;
         boolean neverRelinquishIdentity = true;
-        int taskId = -1;
+        int taskId = inTaskId;
         final int outerDepth = in.getDepth();
         TaskDescription taskDescription = new TaskDescription();
-        int taskAffiliation = -1;
+        int taskAffiliation = INVALID_TASK_ID;
         int taskAffiliationColor = 0;
-        int prevTaskId = -1;
-        int nextTaskId = -1;
+        int prevTaskId = INVALID_TASK_ID;
+        int nextTaskId = INVALID_TASK_ID;
         int callingUid = -1;
         String callingPackage = "";
 
         for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
             final String attrName = in.getAttributeName(attrNdx);
             final String attrValue = in.getAttributeValue(attrNdx);
-            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
-                    attrName + " value=" + attrValue);
+            if (DEBUG_PERSISTER || DEBUG_RESTORER) Slog.d(TaskPersister.TAG,
+                        "TaskRecord: attribute name=" + attrName + " value=" + attrValue);
             if (ATTR_TASKID.equals(attrName)) {
-                taskId = Integer.valueOf(attrValue);
+                if (taskId == INVALID_TASK_ID) taskId = Integer.valueOf(attrValue);
             } else if (ATTR_REALACTIVITY.equals(attrName)) {
                 realActivity = ComponentName.unflattenFromString(attrValue);
             } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
@@ -966,17 +974,16 @@ final class TaskRecord {
                 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
             if (event == XmlPullParser.START_TAG) {
                 final String name = in.getName();
-                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
-                        name);
+                if (DEBUG_PERSISTER || DEBUG_RESTORER)
+                        Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" + name);
                 if (TAG_AFFINITYINTENT.equals(name)) {
                     affinityIntent = Intent.restoreFromXml(in);
                 } else if (TAG_INTENT.equals(name)) {
                     intent = Intent.restoreFromXml(in);
                 } else if (TAG_ACTIVITY.equals(name)) {
-                    ActivityRecord activity =
-                            ActivityRecord.restoreFromXml(in, taskId, stackSupervisor);
-                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
-                            activity);
+                    ActivityRecord activity = ActivityRecord.restoreFromXml(in, stackSupervisor);
+                    if (DEBUG_PERSISTER || DEBUG_RESTORER)
+                            Slog.d(TaskPersister.TAG, "TaskRecord: activity=" + activity);
                     if (activity != null) {
                         activities.add(activity);
                     }
@@ -1082,8 +1089,9 @@ final class TaskRecord {
                     pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity);
                     pw.print(" mReuseTask="); pw.println(mReuseTask);
         }
-        if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != -1 || mPrevAffiliate != null
-                || mNextAffiliateTaskId != -1 || mNextAffiliate != null) {
+        if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != INVALID_TASK_ID
+                || mPrevAffiliate != null || mNextAffiliateTaskId != INVALID_TASK_ID
+                || mNextAffiliate != null) {
             pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId);
                     pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId);
                     pw.print(" (");
index 4af920a..6feeb7c 100644 (file)
@@ -96,6 +96,12 @@ public class NetworkAgentInfo {
         networkRequests.put(networkRequest.requestId, networkRequest);
     }
 
+    // Does this network satisfy request?
+    public boolean satisfies(NetworkRequest request) {
+        return created &&
+                request.networkCapabilities.satisfiedByNetworkCapabilities(networkCapabilities);
+    }
+
     public boolean isVPN() {
         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
     }
index 225097f..39718ea 100644 (file)
@@ -215,6 +215,8 @@ public class NetworkMonitor extends StateMachine {
     // Default to 5s reevaluation delay.
     private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000;
     private static final int MAX_RETRIES = 10;
+    // Between groups of MAX_RETRIES evaluation attempts, pause 10 mins in hopes ISP outage passes.
+    private static final int REEVALUATE_PAUSE_MS = 10*60*1000;
     private final int mReevaluateDelayMs;
     private int mReevaluateToken = 0;
     private static final int INVALID_UID = -1;
@@ -345,6 +347,7 @@ public class NetworkMonitor extends StateMachine {
         public void enter() {
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                     NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
+            if (!mUserDoesNotWant) sendMessageDelayed(CMD_FORCE_REEVALUATION, REEVALUATE_PAUSE_MS);
         }
 
         @Override
@@ -360,6 +363,15 @@ public class NetworkMonitor extends StateMachine {
                     return NOT_HANDLED;
             }
         }
+
+        @Override
+        public void exit() {
+             // NOTE: This removes the delayed message posted by enter() but will inadvertently
+             // remove any other CMD_FORCE_REEVALUATION in the message queue.  At the moment this
+             // is harmless.  If in the future this becomes problematic a different message could
+             // be used.
+             removeMessages(CMD_FORCE_REEVALUATION);
+        }
     }
 
     // Being in the ValidatedState State indicates a Network is:
index 7c303ff..ef86c6c 100644 (file)
@@ -42,6 +42,7 @@ import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.telephony.Phone;
@@ -428,7 +429,8 @@ public class Tethering extends BaseNetworkObserver {
             if (bluetoothTethered) {
                 showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_general);
             } else {
-                showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi);
+                /* We now have a status bar icon for WifiTethering, so drop the notification */
+                clearTetheredNotification();
             }
         } else if (bluetoothTethered) {
             showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_bluetooth);
@@ -630,8 +632,11 @@ public class Tethering extends BaseNetworkObserver {
     }
 
     public void checkDunRequired() {
-        int secureSetting = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.TETHER_DUN_REQUIRED, 2);
+        int secureSetting = 2;
+        TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        if (tm != null) {
+            secureSetting = tm.getTetherApnRequired();
+        }
         synchronized (mPublicSync) {
             // 2 = not set, 0 = DUN not required, 1 = DUN required
             if (secureSetting != 2) {
index 683212a..61631d4 100644 (file)
@@ -34,6 +34,7 @@ import java.io.PrintWriter;
 abstract class DisplayDevice {
     private final DisplayAdapter mDisplayAdapter;
     private final IBinder mDisplayToken;
+    private final String mUniqueId;
 
     // The display device does not manage these properties itself, they are set by
     // the display manager service.  The display device shouldn't really be looking at these.
@@ -46,9 +47,10 @@ abstract class DisplayDevice {
     // within a transaction from performTraversalInTransactionLocked.
     private Surface mCurrentSurface;
 
-    public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken) {
+    public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId) {
         mDisplayAdapter = displayAdapter;
         mDisplayToken = displayToken;
+        mUniqueId = uniqueId;
     }
 
     /**
@@ -80,6 +82,13 @@ abstract class DisplayDevice {
     }
 
     /**
+     * Returns the unique id of the display device.
+     */
+    public final String getUniqueId() {
+        return mUniqueId;
+    }
+
+    /**
      * Gets information about the display device.
      *
      * The information returned should not change between calls unless the display
@@ -208,6 +217,7 @@ abstract class DisplayDevice {
      */
     public void dumpLocked(PrintWriter pw) {
         pw.println("mAdapter=" + mDisplayAdapter.getName());
+        pw.println("mUniqueId=" + mUniqueId);
         pw.println("mDisplayToken=" + mDisplayToken);
         pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
         pw.println("mCurrentOrientation=" + mCurrentOrientation);
index f48428a..d1e73f0 100644 (file)
@@ -104,12 +104,17 @@ final class DisplayDeviceInfo {
     public static final int TOUCH_EXTERNAL = 2;
 
     /**
-     * Gets the name of the display device, which may be derived from
-     * EDID or other sources.  The name may be displayed to the user.
+     * Gets the name of the display device, which may be derived from EDID or
+     * other sources. The name may be localized and displayed to the user.
      */
     public String name;
 
     /**
+     * Unique Id of display device.
+     */
+    public String uniqueId;
+
+    /**
      * The width of the display in its natural orientation, in pixels.
      * This value is not affected by display rotation.
      */
@@ -235,6 +240,7 @@ final class DisplayDeviceInfo {
     public boolean equals(DisplayDeviceInfo other) {
         return other != null
                 && Objects.equal(name, other.name)
+                && Objects.equal(uniqueId, other.uniqueId)
                 && width == other.width
                 && height == other.height
                 && refreshRate == other.refreshRate
@@ -261,6 +267,7 @@ final class DisplayDeviceInfo {
 
     public void copyFrom(DisplayDeviceInfo other) {
         name = other.name;
+        uniqueId = other.uniqueId;
         width = other.width;
         height = other.height;
         refreshRate = other.refreshRate;
@@ -285,7 +292,8 @@ final class DisplayDeviceInfo {
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append("DisplayDeviceInfo{\"");
-        sb.append(name).append("\": ").append(width).append(" x ").append(height);
+        sb.append(name).append("\": uniqueId=\"").append(uniqueId).append("\", ");
+        sb.append(width).append(" x ").append(height);
         sb.append(", ").append(refreshRate).append(" fps");
         sb.append(", supportedRefreshRates ").append(Arrays.toString(supportedRefreshRates));
         sb.append(", density ").append(densityDpi);
index 24cf423..5ebe64d 100644 (file)
@@ -41,6 +41,8 @@ import java.util.Arrays;
 final class LocalDisplayAdapter extends DisplayAdapter {
     private static final String TAG = "LocalDisplayAdapter";
 
+    private static final String UNIQUE_ID_PREFIX = "local:";
+
     private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
             SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
             SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
@@ -140,7 +142,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
 
         public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
-            super(LocalDisplayAdapter.this, displayToken);
+            super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId);
             mBuiltInDisplayId = builtInDisplayId;
             mPhys = new SurfaceControl.PhysicalDisplayInfo(
                     physicalDisplayInfos[activeDisplayInfo]);
@@ -179,6 +181,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                 mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos;
                 mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos;
                 mInfo.state = mState;
+                mInfo.uniqueId = getUniqueId();
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
                 // support compositing from gralloc protected buffers.
index 00ff1cf..6c57eec 100644 (file)
@@ -118,6 +118,7 @@ final class LogicalDisplay {
                 mInfo.copyFrom(mOverrideDisplayInfo);
                 mInfo.layerStack = mBaseDisplayInfo.layerStack;
                 mInfo.name = mBaseDisplayInfo.name;
+                mInfo.uniqueId = mBaseDisplayInfo.uniqueId;
                 mInfo.state = mBaseDisplayInfo.state;
             } else {
                 mInfo.copyFrom(mBaseDisplayInfo);
@@ -208,6 +209,7 @@ final class LogicalDisplay {
             mBaseDisplayInfo.type = deviceInfo.type;
             mBaseDisplayInfo.address = deviceInfo.address;
             mBaseDisplayInfo.name = deviceInfo.name;
+            mBaseDisplayInfo.uniqueId = deviceInfo.uniqueId;
             mBaseDisplayInfo.appWidth = deviceInfo.width;
             mBaseDisplayInfo.appHeight = deviceInfo.height;
             mBaseDisplayInfo.logicalWidth = deviceInfo.width;
index f514531..5b6f35b 100644 (file)
@@ -61,6 +61,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
     private static final Pattern SETTING_PATTERN =
             Pattern.compile("(\\d+)x(\\d+)/(\\d+)(,[a-z]+)*");
 
+    // Unique id prefix for overlay displays.
+    private static final String UNIQUE_ID_PREFIX = "overlay:";
+
     private final Handler mUiHandler;
     private final ArrayList<OverlayDisplayHandle> mOverlays =
             new ArrayList<OverlayDisplayHandle>();
@@ -160,7 +163,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
                                 + ", densityDpi=" + densityDpi + ", secure=" + secure);
 
                         mOverlays.add(new OverlayDisplayHandle(name,
-                                width, height, densityDpi, gravity, secure));
+                                width, height, densityDpi, gravity, secure, number));
                         continue;
                     }
                 } catch (NumberFormatException ex) {
@@ -203,8 +206,8 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
         public OverlayDisplayDevice(IBinder displayToken, String name,
                 int width, int height, float refreshRate, long presentationDeadlineNanos,
                 int densityDpi, boolean secure, int state,
-                SurfaceTexture surfaceTexture) {
-            super(OverlayDisplayAdapter.this, displayToken);
+                SurfaceTexture surfaceTexture, int number) {
+            super(OverlayDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + number);
             mName = name;
             mWidth = width;
             mHeight = height;
@@ -245,6 +248,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
             if (mInfo == null) {
                 mInfo = new DisplayDeviceInfo();
                 mInfo.name = mName;
+                mInfo.uniqueId = getUniqueId();
                 mInfo.width = mWidth;
                 mInfo.height = mHeight;
                 mInfo.refreshRate = mRefreshRate;
@@ -279,18 +283,20 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
         private final int mDensityDpi;
         private final int mGravity;
         private final boolean mSecure;
+        private final int mNumber;
 
         private OverlayDisplayWindow mWindow;
         private OverlayDisplayDevice mDevice;
 
-        public OverlayDisplayHandle(String name,
-                int width, int height, int densityDpi, int gravity, boolean secure) {
+        public OverlayDisplayHandle(String name, int width,
+                int height, int densityDpi, int gravity, boolean secure, int number) {
             mName = name;
             mWidth = width;
             mHeight = height;
             mDensityDpi = densityDpi;
             mGravity = gravity;
             mSecure = secure;
+            mNumber = number;
 
             mUiHandler.post(mShowRunnable);
         }
@@ -308,7 +314,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
                 IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure);
                 mDevice = new OverlayDisplayDevice(displayToken, mName,
                         mWidth, mHeight, refreshRate, presentationDeadlineNanos,
-                        mDensityDpi, mSecure, state, surfaceTexture);
+                        mDensityDpi, mSecure, state, surfaceTexture, mNumber);
 
                 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
             }
@@ -343,6 +349,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
             pw.println("    mDensityDpi=" + mDensityDpi);
             pw.println("    mGravity=" + mGravity);
             pw.println("    mSecure=" + mSecure);
+            pw.println("    mNumber=" + mNumber);
 
             // Try to dump the window state.
             if (mWindow != null) {
index 28d5fc0..f181cd5 100644 (file)
@@ -34,6 +34,7 @@ import android.view.Surface;
 import android.view.SurfaceControl;
 
 import java.io.PrintWriter;
+import java.util.Iterator;
 
 /**
  * A display adapter that provides virtual displays on behalf of applications.
@@ -45,6 +46,9 @@ final class VirtualDisplayAdapter extends DisplayAdapter {
     static final String TAG = "VirtualDisplayAdapter";
     static final boolean DEBUG = false;
 
+    // Unique id prefix for virtual displays
+    private static final String UNIQUE_ID_PREFIX = "virtual:";
+
     private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
             new ArrayMap<IBinder, VirtualDisplayDevice>();
     private Handler mHandler;
@@ -62,9 +66,12 @@ final class VirtualDisplayAdapter extends DisplayAdapter {
         boolean secure = (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
         IBinder appToken = callback.asBinder();
         IBinder displayToken = SurfaceControl.createDisplay(name, secure);
+        final String baseUniqueId =
+                UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ",";
+        final int uniqueIndex = getNextUniqueIndex(baseUniqueId);
         VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
                 ownerUid, ownerPackageName, name, width, height, densityDpi, surface, flags,
-                new Callback(callback, mHandler));
+                new Callback(callback, mHandler), baseUniqueId + uniqueIndex, uniqueIndex);
 
         mVirtualDisplayDevices.put(appToken, device);
 
@@ -112,6 +119,29 @@ final class VirtualDisplayAdapter extends DisplayAdapter {
         return device;
     }
 
+    /**
+     * Returns the next unique index for the uniqueIdPrefix
+     */
+    private int getNextUniqueIndex(String uniqueIdPrefix) {
+        if (mVirtualDisplayDevices.isEmpty()) {
+            return 0;
+        }
+
+        int nextUniqueIndex = 0;
+        Iterator<VirtualDisplayDevice> it = mVirtualDisplayDevices.values().iterator();
+        while (it.hasNext()) {
+            VirtualDisplayDevice device = it.next();
+            if (device.getUniqueId().startsWith(uniqueIdPrefix)
+                    && device.mUniqueIndex >= nextUniqueIndex) {
+                // Increment the next unique index to be greater than ones we have already ran
+                // across for displays that have the same unique Id prefix.
+                nextUniqueIndex = device.mUniqueIndex + 1;
+            }
+        }
+
+        return nextUniqueIndex;
+    }
+
     private void handleBinderDiedLocked(IBinder appToken) {
         VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
         if (device != null) {
@@ -150,12 +180,13 @@ final class VirtualDisplayAdapter extends DisplayAdapter {
         private int mDisplayState;
         private boolean mStopped;
         private int mPendingChanges;
+        private int mUniqueIndex;
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName,
                 String name, int width, int height, int densityDpi, Surface surface, int flags,
-                Callback callback) {
-            super(VirtualDisplayAdapter.this, displayToken);
+                Callback callback, String uniqueId, int uniqueIndex) {
+            super(VirtualDisplayAdapter.this, displayToken, uniqueId);
             mAppToken = appToken;
             mOwnerUid = ownerUid;
             mOwnerPackageName = ownerPackageName;
@@ -168,6 +199,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter {
             mCallback = callback;
             mDisplayState = Display.STATE_UNKNOWN;
             mPendingChanges |= PENDING_SURFACE_CHANGE;
+            mUniqueIndex = uniqueIndex;
         }
 
         @Override
@@ -255,6 +287,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter {
             if (mInfo == null) {
                 mInfo = new DisplayDeviceInfo();
                 mInfo.name = mName;
+                mInfo.uniqueId = getUniqueId();
                 mInfo.width = mWidth;
                 mInfo.height = mHeight;
                 mInfo.refreshRate = 60;
index 6b010d9..c939861 100644 (file)
@@ -68,6 +68,9 @@ final class WifiDisplayAdapter extends DisplayAdapter {
 
     private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT";
 
+    // Unique id prefix for wifi displays
+    private static final String DISPLAY_NAME_PREFIX = "wifi:";
+
     private final WifiDisplayHandler mHandler;
     private final PersistentDataStore mPersistentDataStore;
     private final boolean mSupportsProtectedBuffers;
@@ -587,7 +590,7 @@ final class WifiDisplayAdapter extends DisplayAdapter {
         public WifiDisplayDevice(IBinder displayToken, String name,
                 int width, int height, float refreshRate, int flags, String address,
                 Surface surface) {
-            super(WifiDisplayAdapter.this, displayToken);
+            super(WifiDisplayAdapter.this, displayToken, DISPLAY_NAME_PREFIX + address);
             mName = name;
             mWidth = width;
             mHeight = height;
@@ -622,6 +625,7 @@ final class WifiDisplayAdapter extends DisplayAdapter {
             if (mInfo == null) {
                 mInfo = new DisplayDeviceInfo();
                 mInfo.name = mName;
+                mInfo.uniqueId = getUniqueId();
                 mInfo.width = mWidth;
                 mInfo.height = mHeight;
                 mInfo.refreshRate = mRefreshRate;
index bb12eae..f6d4efd 100644 (file)
@@ -186,6 +186,7 @@ final class Constants {
 
     static final int INVALID_PORT_ID = HdmiDeviceInfo.PORT_INVALID;
     static final int INVALID_PHYSICAL_ADDRESS = HdmiDeviceInfo.PATH_INVALID;
+    static final int PATH_INTERNAL = HdmiDeviceInfo.PATH_INTERNAL;
 
     // Send result codes. It should be consistent with hdmi_cec.h's send_message error code.
     static final int SEND_RESULT_SUCCESS = 0;
index da404c4..97a6e85 100644 (file)
@@ -38,6 +38,7 @@ import java.util.List;
  *   <li>Gather "OSD (display) name" of all acknowledge devices
  *   <li>Gather "Vendor id" of all acknowledge devices
  * </ol>
+ * We attempt to get OSD name/vendor ID up to 5 times in case the communication fails.
  */
 final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
     private static final String TAG = "DeviceDiscoveryAction";
@@ -87,6 +88,7 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
     private final ArrayList<DeviceInfo> mDevices = new ArrayList<>();
     private final DeviceDiscoveryCallback mCallback;
     private int mProcessedDeviceCount = 0;
+    private int mTimeoutRetry = 0;
 
     /**
      * Constructor.
@@ -309,6 +311,7 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
 
     private void increaseProcessedDeviceCount() {
         mProcessedDeviceCount++;
+        mTimeoutRetry = 0;
     }
 
     private void removeDevice(int index) {
@@ -353,19 +356,23 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
                     return;
             }
         } else {
-            int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
-            switch (mState) {
-                case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
-                    queryPhysicalAddress(address);
-                    return;
-                case STATE_WAITING_FOR_OSD_NAME:
-                    queryOsdName(address);
-                    return;
-                case STATE_WAITING_FOR_VENDOR_ID:
-                    queryVendorId(address);
-                default:
-                    return;
-            }
+            sendQueryCommand();
+        }
+    }
+
+    private void sendQueryCommand() {
+        int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
+        switch (mState) {
+            case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
+                queryPhysicalAddress(address);
+                return;
+            case STATE_WAITING_FOR_OSD_NAME:
+                queryOsdName(address);
+                return;
+            case STATE_WAITING_FOR_VENDOR_ID:
+                queryVendorId(address);
+            default:
+                return;
         }
     }
 
@@ -375,6 +382,11 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
             return;
         }
 
+        if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
+            sendQueryCommand();
+            return;
+        }
+        mTimeoutRetry = 0;
         Slog.v(TAG, "Timeout[State=" + mState + ", Processed=" + mProcessedDeviceCount);
         removeDevice(mProcessedDeviceCount);
         checkAndProceedStage();
index 0e8788a..1486fee 100644 (file)
@@ -129,7 +129,7 @@ final class HdmiCecController {
     }
 
     private void init(long nativePtr) {
-        mIoHandler = new Handler(mService.getServiceLooper());
+        mIoHandler = new Handler(mService.getIoLooper());
         mControlHandler = new Handler(mService.getServiceLooper());
         mNativePtr = nativePtr;
     }
@@ -324,6 +324,7 @@ final class HdmiCecController {
     @ServiceThreadOnly
     void setOption(int flag, int value) {
         assertRunOnServiceThread();
+        HdmiLogger.debug("setOption: [flag:%d, value:%d]", flag, value);
         nativeSetOption(mNativePtr, flag, value);
     }
 
@@ -501,6 +502,19 @@ final class HdmiCecController {
         mControlHandler.post(runnable);
     }
 
+    @ServiceThreadOnly
+    void flush(final Runnable runnable) {
+        assertRunOnServiceThread();
+        runOnIoThread(new Runnable() {
+            @Override
+            public void run() {
+                // This ensures the runnable for cleanup is performed after all the pending
+                // commands are processed by IO thread.
+                runOnServiceThread(runnable);
+            }
+        });
+    }
+
     private boolean isAcceptableAddress(int address) {
         // Can access command targeting devices available in local device or broadcast command.
         if (address == Constants.ADDR_BROADCAST) {
index 046f393..a787c12 100644 (file)
@@ -39,6 +39,10 @@ final class HdmiConfig {
     // Number of retries for polling each device in address allocation mechanism.
     static final int ADDRESS_ALLOCATION_RETRY = 3;
 
+    // Number of retries for sendCommand in actions related to new device discovery.
+    // Number 5 comes from 10 seconds for Chromecast preparation time.
+    static final int TIMEOUT_RETRY = 5;
+
     // CEC spec said that it should try retransmission at least once.
     // The actual number of send request for a single command will be at most
     // RETRANSMISSION_COUNT + 1. Note that it affects only to normal commands
index 8a25f62..5df36b4 100644 (file)
@@ -993,6 +993,19 @@ public final class HdmiControlService extends SystemService {
                 mHotplugEventListenerRecords.remove(this);
             }
         }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof HotplugEventListenerRecord)) return false;
+            if (obj == this) return true;
+            HotplugEventListenerRecord other = (HotplugEventListenerRecord) obj;
+            return other.mListener == this.mListener;
+        }
+
+        @Override
+        public int hashCode() {
+            return mListener.hashCode();
+        }
     }
 
     private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
@@ -1549,8 +1562,8 @@ public final class HdmiControlService extends SystemService {
         source.queryDisplayStatus(callback);
     }
 
-    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
-        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
+    private void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
+        final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
         try {
             listener.asBinder().linkToDeath(record, 0);
         } catch (RemoteException e) {
@@ -1560,6 +1573,24 @@ public final class HdmiControlService extends SystemService {
         synchronized (mLock) {
             mHotplugEventListenerRecords.add(record);
         }
+
+        // Inform the listener of the initial state of each HDMI port by generating
+        // hotplug events.
+        runOnServiceThread(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mLock) {
+                    if (!mHotplugEventListenerRecords.contains(record)) return;
+                }
+                for (HdmiPortInfo port : mPortInfo) {
+                    HdmiHotplugEvent event = new HdmiHotplugEvent(port.getId(),
+                            mCecController.isConnected(port.getId()));
+                    synchronized (mLock) {
+                        invokeHotplugEventListenerLocked(listener, event);
+                    }
+                }
+            }
+        });
     }
 
     private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
@@ -2021,30 +2052,52 @@ public final class HdmiControlService extends SystemService {
     void setControlEnabled(boolean enabled) {
         assertRunOnServiceThread();
 
-        if (!enabled) {
-            // Call the vendor handler before the service is disabled.
-            invokeVendorCommandListenersOnControlStateChanged(false,
-                    HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
-        }
-        int value = toInt(enabled);
-        mCecController.setOption(OPTION_CEC_ENABLE, value);
-        mMhlController.setOption(OPTION_MHL_ENABLE, value);
-
         synchronized (mLock) {
             mHdmiControlEnabled = enabled;
         }
 
         if (enabled) {
-            initializeCec(INITIATED_BY_ENABLE_CEC);
-        } else {
-            disableDevices(new PendingActionClearedCallback() {
-                @Override
-                public void onCleared(HdmiCecLocalDevice device) {
-                    assertRunOnServiceThread();
-                    clearLocalDevices();
-                }
-            });
+            enableHdmiControlService();
+            return;
         }
+        // Call the vendor handler before the service is disabled.
+        invokeVendorCommandListenersOnControlStateChanged(false,
+                HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
+        // Post the remained tasks in the service thread again to give the vendor-issued-tasks
+        // a chance to run.
+        runOnServiceThread(new Runnable() {
+            @Override
+            public void run() {
+                disableHdmiControlService();
+            }
+        });
+        return;
+    }
+
+    @ServiceThreadOnly
+    private void enableHdmiControlService() {
+        mCecController.setOption(OPTION_CEC_ENABLE, ENABLED);
+        mMhlController.setOption(OPTION_MHL_ENABLE, ENABLED);
+
+        initializeCec(INITIATED_BY_ENABLE_CEC);
+    }
+
+    @ServiceThreadOnly
+    private void disableHdmiControlService() {
+        disableDevices(new PendingActionClearedCallback() {
+            @Override
+            public void onCleared(HdmiCecLocalDevice device) {
+                assertRunOnServiceThread();
+                mCecController.flush(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCecController.setOption(OPTION_CEC_ENABLE, DISABLED);
+                        mMhlController.setOption(OPTION_MHL_ENABLE, DISABLED);
+                        clearLocalDevices();
+                    }
+                });
+            }
+        });
     }
 
     @ServiceThreadOnly
index 64f0703..3d64cc5 100644 (file)
@@ -51,6 +51,7 @@ final class NewDeviceAction extends HdmiCecFeatureAction {
 
     private int mVendorId;
     private String mDisplayName;
+    private int mTimeoutRetry;
 
     /**
      * Constructor.
@@ -71,15 +72,22 @@ final class NewDeviceAction extends HdmiCecFeatureAction {
 
     @Override
     public boolean start() {
+        requestOsdName(true);
+        return true;
+    }
+
+    private void requestOsdName(boolean firstTry) {
+        if (firstTry) {
+            mTimeoutRetry = 0;
+        }
         mState = STATE_WAITING_FOR_SET_OSD_NAME;
         if (mayProcessCommandIfCached(mDeviceLogicalAddress, Constants.MESSAGE_SET_OSD_NAME)) {
-            return true;
+            return;
         }
 
         sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(),
                 mDeviceLogicalAddress));
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
-        return true;
     }
 
     @Override
@@ -103,12 +111,12 @@ final class NewDeviceAction extends HdmiCecFeatureAction {
                 } catch (UnsupportedEncodingException e) {
                     Slog.e(TAG, "Failed to get OSD name: " + e.getMessage());
                 }
-                requestVendorId();
+                requestVendorId(true);
                 return true;
             } else if (opcode == Constants.MESSAGE_FEATURE_ABORT) {
                 int requestOpcode = params[0] & 0xFF;
                 if (requestOpcode == Constants.MESSAGE_GIVE_OSD_NAME) {
-                    requestVendorId();
+                    requestVendorId(true);
                     return true;
                 }
             }
@@ -138,7 +146,10 @@ final class NewDeviceAction extends HdmiCecFeatureAction {
         return false;
     }
 
-    private void requestVendorId() {
+    private void requestVendorId(boolean firstTry) {
+        if (firstTry) {
+            mTimeoutRetry = 0;
+        }
         // At first, transit to waiting status for <Device Vendor Id>.
         mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
         // If the message is already in cache, process it.
@@ -176,9 +187,17 @@ final class NewDeviceAction extends HdmiCecFeatureAction {
             return;
         }
         if (state == STATE_WAITING_FOR_SET_OSD_NAME) {
+            if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
+                requestOsdName(false);
+                return;
+            }
             // Osd name request timed out. Try vendor id
-            requestVendorId();
+            requestVendorId(true);
         } else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) {
+            if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
+                requestVendorId(false);
+                return;
+            }
             // vendor id timed out. Go ahead creating the device info what we've got so far.
             addDeviceInfo();
             finish();
index 1e29fd6..fd7a7f9 100644 (file)
@@ -64,10 +64,11 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
 
     @Override
     boolean processCommand(HdmiCecMessage cmd) {
-        if (mState != STATE_WAIT_FOR_REPORT_POWER_STATUS) {
-            return false;
+        if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
+                && cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) {
+            return handleReportPowerStatus(cmd);
         }
-        return handleReportPowerStatus(cmd);
+        return false;
     }
 
     private boolean handleReportPowerStatus(HdmiCecMessage cmd) {
index 6023354..a209cd0 100644 (file)
@@ -90,10 +90,9 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
     }
 
     private void sendSystemAudioModeRequestInternal() {
-        int avrPhysicalAddress = tv().getAvrDeviceInfo().getPhysicalAddress();
         HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
                 getSourceAddress(),
-                mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
+                mAvrLogicalAddress, getSystemAudioModeRequestParam(), mTargetAudioStatus);
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
             public void onSendCompleted(int error) {
@@ -108,6 +107,20 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
         addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
     }
 
+    private int getSystemAudioModeRequestParam() {
+        // <System Audio Mode Request> takes the physical address of the source device
+        // as a parameter. Get it from following candidates, in the order listed below:
+        // 1) physical address of the active source
+        // 2) active routing path
+        // 3) physical address of TV
+        if (tv().getActiveSource().isValid()) {
+            return tv().getActiveSource().physicalAddress;
+        }
+        int param = tv().getActivePath();
+        return param != Constants.INVALID_PHYSICAL_ADDRESS
+                ? param : Constants.PATH_INTERNAL;
+    }
+
     private void handleSendSystemAudioModeRequestTimeout() {
         if (!mTargetAudioStatus  // Don't retry for Off case.
                 || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
index 053c988..f11a3f9 100644 (file)
@@ -443,7 +443,7 @@ public class MediaSessionService extends SystemService implements Monitor {
         synchronized (mLock) {
             List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
             int size = records.size();
-            if (size > 0) {
+            if (size > 0 && records.get(0).isPlaybackActive(false)) {
                 rememberMediaButtonReceiverLocked(records.get(0));
             }
             ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
@@ -702,8 +702,12 @@ public class MediaSessionService extends SystemService implements Monitor {
 
             try {
                 synchronized (mLock) {
+                    // If we don't have a media button receiver to fall back on
+                    // include non-playing sessions for dispatching
+                    boolean useNotPlayingSessions = mUserRecords.get(
+                            ActivityManager.getCurrentUser()).mLastMediaButtonReceiver == null;
                     MediaSessionRecord session = mPriorityStack
-                            .getDefaultMediaButtonSession(mCurrentUserId);
+                            .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
                     if (isVoiceKey(keyEvent.getKeyCode())) {
                         handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
                     } else {
index c48a075..611718e 100644 (file)
@@ -51,6 +51,9 @@ public class MediaSessionStack {
 
     private MediaSessionRecord mGlobalPrioritySession;
 
+    // The last record that either entered one of the playing states or was
+    // added.
+    private MediaSessionRecord mLastInterestingRecord;
     private MediaSessionRecord mCachedButtonReceiver;
     private MediaSessionRecord mCachedDefault;
     private MediaSessionRecord mCachedVolumeDefault;
@@ -65,6 +68,7 @@ public class MediaSessionStack {
     public void addSession(MediaSessionRecord record) {
         mSessions.add(record);
         clearCache();
+        mLastInterestingRecord = record;
     }
 
     /**
@@ -93,6 +97,9 @@ public class MediaSessionStack {
             mSessions.remove(record);
             mSessions.add(0, record);
             clearCache();
+            // This becomes the last interesting record since it entered a
+            // playing state
+            mLastInterestingRecord = record;
             return true;
         } else if (!MediaSession.isActiveState(newState)) {
             // Just clear the volume cache when a state goes inactive
@@ -168,9 +175,11 @@ public class MediaSessionStack {
      * Get the highest priority session that can handle media buttons.
      *
      * @param userId The user to check.
+     * @param includeNotPlaying Return a non-playing session if nothing else is
+     *            available
      * @return The default media button session or null.
      */
-    public MediaSessionRecord getDefaultMediaButtonSession(int userId) {
+    public MediaSessionRecord getDefaultMediaButtonSession(int userId, boolean includeNotPlaying) {
         if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
             return mGlobalPrioritySession;
         }
@@ -180,7 +189,25 @@ public class MediaSessionStack {
         ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
                 MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId);
         if (records.size() > 0) {
-            mCachedButtonReceiver = records.get(0);
+            MediaSessionRecord record = records.get(0);
+            if (record.isPlaybackActive(false)) {
+                // Since we're going to send a button event to this record make
+                // it the last interesting one.
+                mLastInterestingRecord = record;
+                mCachedButtonReceiver = record;
+            } else if (mLastInterestingRecord != null) {
+                if (records.contains(mLastInterestingRecord)) {
+                    mCachedButtonReceiver = mLastInterestingRecord;
+                } else {
+                    // That record is no longer used. Clear its reference.
+                    mLastInterestingRecord = null;
+                }
+            }
+            if (includeNotPlaying && mCachedButtonReceiver == null) {
+                // If we really want a record and we didn't find one yet use the
+                // highest priority session even if it's not playing.
+                mCachedButtonReceiver = record;
+            }
         }
         return mCachedButtonReceiver;
     }
index 24fc455..fdb443e 100644 (file)
@@ -26,8 +26,9 @@ public interface NotificationDelegate {
     void onNotificationError(int callingUid, int callingPid,
             String pkg, String tag, int id,
             int uid, int initialPid, String message, int userId);
-    void onPanelRevealed();
+    void onPanelRevealed(boolean clearEffects);
     void onPanelHidden();
+    void clearEffects();
     void onNotificationVisibilityChanged(
             String[] newlyVisibleKeys, String[] noLongerVisibleKeys);
     void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
index 70d0e6a..aec20bc 100644 (file)
@@ -116,13 +116,14 @@ import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 
 /** {@hide} */
 public class NotificationManagerService extends SystemService {
     static final String TAG = "NotificationService";
-    static final boolean DBG = false;
+    static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     static final int MAX_PACKAGE_NOTIFICATIONS = 50;
 
@@ -139,6 +140,7 @@ public class NotificationManagerService extends SystemService {
     static final int SHORT_DELAY = 2000; // 2 seconds
 
     static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
+
     static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
 
     static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
@@ -166,6 +168,15 @@ public class NotificationManagerService extends SystemService {
     static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
             ValidateNotificationPeople.STARRED_CONTACT;
 
+    /** notification_enqueue status value for a newly enqueued notification. */
+    private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
+
+    /** notification_enqueue status value for an existing notification. */
+    private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
+
+    /** notification_enqueue status value for an ignored notification. */
+    private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
+
     private IActivityManager mAm;
     AudioManager mAudioManager;
     StatusBarManagerInternal mStatusBar;
@@ -190,8 +201,8 @@ public class NotificationManagerService extends SystemService {
 
     private boolean mDisableNotificationEffects;
     private int mCallState;
-    NotificationRecord mSoundNotification;
-    NotificationRecord mVibrateNotification;
+    private String mSoundNotificationKey;
+    private String mVibrateNotificationKey;
 
     private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
     private ComponentName mEffectsSuppressor;
@@ -209,9 +220,10 @@ public class NotificationManagerService extends SystemService {
     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
             new ArrayMap<String, NotificationRecord>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
+    final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
 
-    ArrayList<String> mLights = new ArrayList<String>();
-    NotificationRecord mLedNotification;
+    // The last key in this list owns the hardware.
+    ArrayList<String> mLights = new ArrayList<>();
 
     private AppOpsManager mAppOps;
 
@@ -251,6 +263,7 @@ public class NotificationManagerService extends SystemService {
     private static final int REASON_LISTENER_CANCEL = 10;
     private static final int REASON_LISTENER_CANCEL_ALL = 11;
     private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
+    private static final int REASON_GROUP_OPTIMIZATION = 13;
 
     private static class Archive {
         final int mBufferSize;
@@ -564,11 +577,25 @@ public class NotificationManagerService extends SystemService {
         }
 
         @Override
-        public void onPanelRevealed() {
+        public void onPanelRevealed(boolean clearEffects) {
             EventLogTags.writeNotificationPanelRevealed();
+            if (clearEffects) {
+                clearEffects();
+            }
+        }
+
+        @Override
+        public void onPanelHidden() {
+            EventLogTags.writeNotificationPanelHidden();
+        }
+
+        @Override
+        public void clearEffects() {
             synchronized (mNotificationList) {
+                if (DBG) Slog.d(TAG, "clearEffects");
+
                 // sound
-                mSoundNotification = null;
+                mSoundNotificationKey = null;
 
                 long identity = Binder.clearCallingIdentity();
                 try {
@@ -582,7 +609,7 @@ public class NotificationManagerService extends SystemService {
                 }
 
                 // vibrate
-                mVibrateNotification = null;
+                mVibrateNotificationKey = null;
                 identity = Binder.clearCallingIdentity();
                 try {
                     mVibrator.cancel();
@@ -592,17 +619,11 @@ public class NotificationManagerService extends SystemService {
 
                 // light
                 mLights.clear();
-                mLedNotification = null;
                 updateLightsLocked();
             }
         }
 
         @Override
-        public void onPanelHidden() {
-            EventLogTags.writeNotificationPanelHidden();
-        }
-
-        @Override
         public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
                 int uid, int initialPid, String message, int userId) {
             Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
@@ -740,8 +761,10 @@ public class NotificationManagerService extends SystemService {
                 // Keep track of screen on/off state, but do not turn off the notification light
                 // until user passes through the lock screen or views the notification.
                 mScreenOn = true;
+                updateNotificationPulse();
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 mScreenOn = false;
+                updateNotificationPulse();
             } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
                 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
                         .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
@@ -1600,14 +1623,19 @@ public class NotificationManagerService extends SystemService {
                     if (N > 0) {
                         pw.println("  Lights List:");
                         for (int i=0; i<N; i++) {
-                            pw.println("    " + mLights.get(i));
+                            if (i == N - 1) {
+                                pw.print("  > ");
+                            } else {
+                                pw.print("    ");
+                            }
+                            pw.println(mLights.get(i));
                         }
                         pw.println("  ");
                     }
                     pw.println("  mUseAttentionLight=" + mUseAttentionLight);
                     pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
-                    pw.println("  mSoundNotification=" + mSoundNotification);
-                    pw.println("  mVibrateNotification=" + mVibrateNotification);
+                    pw.println("  mSoundNotificationKey=" + mSoundNotificationKey);
+                    pw.println("  mVibrateNotificationKey=" + mVibrateNotificationKey);
                     pw.println("  mDisableNotificationEffects=" + mDisableNotificationEffects);
                     pw.println("  mCallState=" + callStateToString(mCallState));
                     pw.println("  mSystemReady=" + mSystemReady);
@@ -1658,6 +1686,16 @@ public class NotificationManagerService extends SystemService {
 
             pw.println("\n  Condition providers:");
             mConditionProviders.dump(pw, filter);
+
+            pw.println("\n  Group summaries:");
+            for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
+                NotificationRecord r = entry.getValue();
+                pw.println("    " + entry.getKey() + " -> " + r.getKey());
+                if (mNotificationsByKey.get(r.getKey()) != r) {
+                    pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
+                    r.dump(pw, "      ", getContext());
+                }
+            }
         }
     }
 
@@ -1779,16 +1817,34 @@ public class NotificationManagerService extends SystemService {
                         // Retain ranking information from previous record
                         r.copyRankingInformation(old);
                     }
-                    mRankingHelper.extractSignals(r);
+
+                    // Handle grouped notifications and bail out early if we
+                    // can to avoid extracting signals.
+                    handleGroupedNotificationLocked(r, old, callingUid, callingPid);
+                    boolean ignoreNotification =
+                            removeUnusedGroupedNotificationLocked(r, callingUid, callingPid);
 
                     // This conditional is a dirty hack to limit the logging done on
                     //     behalf of the download manager without affecting other apps.
                     if (!pkg.equals("com.android.providers.downloads")
                             || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
+                        int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
+                        if (ignoreNotification) {
+                            enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
+                        } else if (old != null) {
+                            enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
+                        }
                         EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
                                 pkg, id, tag, userId, notification.toString(),
-                                (old != null) ? 1 : 0);
+                                enqueueStatus);
                     }
+
+                    if (ignoreNotification) {
+                        return;
+                    }
+
+                    mRankingHelper.extractSignals(r);
+
                     // 3. Apply local rules
 
                     // blocked apps
@@ -1805,16 +1861,6 @@ public class NotificationManagerService extends SystemService {
                         return;
                     }
 
-                    // Clear out group children of the old notification if the update causes the
-                    // group summary to go away. This happens when the old notification was a
-                    // summary and the new one isn't, or when the old notification was a summary
-                    // and its group key changed.
-                    if (old != null && old.getNotification().isGroupSummary() &&
-                            (!notification.isGroupSummary() ||
-                                    !old.getGroupKey().equals(r.getGroupKey()))) {
-                        cancelGroupChildrenLocked(old, callingUid, callingPid, null);
-                    }
-
                     int index = indexOfNotificationLocked(n.getKey());
                     if (index < 0) {
                         mNotificationList.add(r);
@@ -1864,6 +1910,90 @@ public class NotificationManagerService extends SystemService {
         idOut[0] = id;
     }
 
+    /**
+     * Ensures that grouped notification receive their special treatment.
+     *
+     * <p>Cancels group children if the new notification causes a group to lose
+     * its summary.</p>
+     *
+     * <p>Updates mSummaryByGroupKey.</p>
+     */
+    private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
+            int callingUid, int callingPid) {
+        StatusBarNotification sbn = r.sbn;
+        Notification n = sbn.getNotification();
+        String group = sbn.getGroupKey();
+        boolean isSummary = n.isGroupSummary();
+
+        Notification oldN = old != null ? old.sbn.getNotification() : null;
+        String oldGroup = old != null ? old.sbn.getGroupKey() : null;
+        boolean oldIsSummary = old != null && oldN.isGroupSummary();
+
+        if (oldIsSummary) {
+            NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
+            if (removedSummary != old) {
+                String removedKey =
+                        removedSummary != null ? removedSummary.getKey() : "<null>";
+                Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
+                        ", removed=" + removedKey);
+            }
+        }
+        if (isSummary) {
+            mSummaryByGroupKey.put(group, r);
+        }
+
+        // Clear out group children of the old notification if the update
+        // causes the group summary to go away. This happens when the old
+        // notification was a summary and the new one isn't, or when the old
+        // notification was a summary and its group key changed.
+        if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
+            cancelGroupChildrenLocked(old, callingUid, callingPid, null,
+                    REASON_GROUP_SUMMARY_CANCELED);
+        }
+    }
+
+    /**
+     * Performs group notification optimizations if SysUI is the only active
+     * notification listener and returns whether the given notification should
+     * be ignored.
+     *
+     * <p>Returns true if the given notification is a child of a group with a
+     * summary, which means that SysUI will never show it, and hence the new
+     * notification can be safely ignored.</p>
+     *
+     * <p>For summaries, cancels all children of that group, as SysUI will
+     * never show them anymore.</p>
+     *
+     * @return true if the given notification can be ignored as an optimization
+     */
+    private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
+            int callingUid, int callingPid) {
+        // No optimizations are possible if listeners want groups.
+        if (mListeners.notificationGroupsDesired()) {
+            return false;
+        }
+
+        StatusBarNotification sbn = r.sbn;
+        String group = sbn.getGroupKey();
+        boolean isSummary = sbn.getNotification().isGroupSummary();
+        boolean isChild = sbn.getNotification().isGroupChild();
+
+        NotificationRecord summary = mSummaryByGroupKey.get(group);
+        if (isChild && summary != null) {
+            // Child with an active summary -> ignore
+            if (DBG) {
+                Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
+                        + summary.getKey());
+            }
+            return true;
+        } else if (isSummary) {
+            // Summary -> cancel children
+            cancelGroupChildrenLocked(r, callingUid, callingPid, null,
+                    REASON_GROUP_OPTIMIZATION);
+        }
+        return false;
+    }
+
     private void buzzBeepBlinkLocked(NotificationRecord record) {
         boolean buzzBeepBlinked = false;
         final Notification notification = record.sbn.getNotification();
@@ -1932,7 +2062,7 @@ public class NotificationManagerService extends SystemService {
                 boolean looping =
                         (notification.flags & Notification.FLAG_INSISTENT) != 0;
                 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
-                mSoundNotification = record;
+                mSoundNotificationKey = record.getKey();
                 // do not play notifications if stream volume is 0 (typically because
                 // ringer mode is silent) or if there is a user of exclusive audio focus
                 if ((mAudioManager.getStreamVolume(
@@ -1975,7 +2105,7 @@ public class NotificationManagerService extends SystemService {
             if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
                     && !(mAudioManager.getRingerModeInternal()
                             == AudioManager.RINGER_MODE_SILENT)) {
-                mVibrateNotification = record;
+                mVibrateNotificationKey = record.getKey();
 
                 if (useDefaultVibrate || convertSoundToVibration) {
                     // Escalate privileges so we can use the vibrator even if the
@@ -2006,9 +2136,6 @@ public class NotificationManagerService extends SystemService {
         // light
         // release the light
         boolean wasShowLights = mLights.remove(record.getKey());
-        if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
-            mLedNotification = null;
-        }
         if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
             mLights.add(record.getKey());
             updateLightsLocked();
@@ -2332,9 +2459,11 @@ public class NotificationManagerService extends SystemService {
             mListeners.notifyRemovedLocked(r.sbn);
         }
 
+        final String canceledKey = r.getKey();
+
         // sound
-        if (mSoundNotification == r) {
-            mSoundNotification = null;
+        if (canceledKey.equals(mSoundNotificationKey)) {
+            mSoundNotificationKey = null;
             final long identity = Binder.clearCallingIdentity();
             try {
                 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
@@ -2348,8 +2477,8 @@ public class NotificationManagerService extends SystemService {
         }
 
         // vibrate
-        if (mVibrateNotification == r) {
-            mVibrateNotification = null;
+        if (canceledKey.equals(mVibrateNotificationKey)) {
+            mVibrateNotificationKey = null;
             long identity = Binder.clearCallingIdentity();
             try {
                 mVibrator.cancel();
@@ -2360,10 +2489,7 @@ public class NotificationManagerService extends SystemService {
         }
 
         // light
-        mLights.remove(r.getKey());
-        if (mLedNotification == r) {
-            mLedNotification = null;
-        }
+        mLights.remove(canceledKey);
 
         // Record usage stats
         switch (reason) {
@@ -2386,11 +2512,16 @@ public class NotificationManagerService extends SystemService {
         }
 
         mNotificationsByKey.remove(r.sbn.getKey());
+        String groupKey = r.getGroupKey();
+        NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
+        if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
+            mSummaryByGroupKey.remove(groupKey);
+        }
 
         // Save it for users of getHistoricalNotifications()
         mArchive.record(r.sbn);
 
-        EventLogTags.writeNotificationCanceled(r.getKey(), reason);
+        EventLogTags.writeNotificationCanceled(canceledKey, reason);
     }
 
     /**
@@ -2433,7 +2564,8 @@ public class NotificationManagerService extends SystemService {
                         mNotificationList.remove(index);
 
                         cancelNotificationLocked(r, sendDelete, reason);
-                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
+                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
+                                REASON_GROUP_SUMMARY_CANCELED);
                         updateLightsLocked();
                     }
                 }
@@ -2512,7 +2644,7 @@ public class NotificationManagerService extends SystemService {
                 final int M = canceledNotifications.size();
                 for (int i = 0; i < M; i++) {
                     cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
-                            listenerName);
+                            listenerName, REASON_GROUP_SUMMARY_CANCELED);
                 }
             }
             if (canceledNotifications != null) {
@@ -2556,14 +2688,14 @@ public class NotificationManagerService extends SystemService {
         int M = canceledNotifications != null ? canceledNotifications.size() : 0;
         for (int i = 0; i < M; i++) {
             cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
-                    listenerName);
+                    listenerName, REASON_GROUP_SUMMARY_CANCELED);
         }
         updateLightsLocked();
     }
 
     // Warning: The caller is responsible for invoking updateLightsLocked().
     private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
-            String listenerName) {
+            String listenerName, int reason) {
         Notification n = r.getNotification();
         if (!n.isGroupSummary()) {
             return;
@@ -2583,11 +2715,10 @@ public class NotificationManagerService extends SystemService {
             StatusBarNotification childSbn = childR.sbn;
             if (childR.getNotification().isGroupChild() &&
                     childR.getGroupKey().equals(r.getGroupKey())) {
-                EventLogTags.writeNotificationCancel(callingUid, callingPid,
-                        pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
-                        REASON_GROUP_SUMMARY_CANCELED, listenerName);
+                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
+                        childSbn.getTag(), userId, 0, 0, reason, listenerName);
                 mNotificationList.remove(i);
-                cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
+                cancelNotificationLocked(childR, false, reason);
             }
         }
     }
@@ -2596,20 +2727,22 @@ public class NotificationManagerService extends SystemService {
     void updateLightsLocked()
     {
         // handle notification lights
-        if (mLedNotification == null) {
-            // get next notification, if any
-            int n = mLights.size();
-            if (n > 0) {
-                mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
+        NotificationRecord ledNotification = null;
+        while (ledNotification == null && !mLights.isEmpty()) {
+            final String owner = mLights.get(mLights.size() - 1);
+            ledNotification = mNotificationsByKey.get(owner);
+            if (ledNotification == null) {
+                Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
+                mLights.remove(owner);
             }
         }
 
         // Don't flash while we are in a call or screen is on
-        if (mLedNotification == null || mInCall || mScreenOn) {
+        if (ledNotification == null || mInCall || mScreenOn) {
             mNotificationLight.turnOff();
             mStatusBar.notificationLightOff();
         } else {
-            final Notification ledno = mLedNotification.sbn.getNotification();
+            final Notification ledno = ledNotification.sbn.getNotification();
             int ledARGB = ledno.ledARGB;
             int ledOnMS = ledno.ledOnMS;
             int ledOffMS = ledno.ledOffMS;
@@ -2783,6 +2916,7 @@ public class NotificationManagerService extends SystemService {
     public class NotificationListeners extends ManagedServices {
 
         private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
+        private boolean mNotificationGroupsDesired;
 
         public NotificationListeners() {
             super(getContext(), mHandler, mNotificationList, mUserProfiles);
@@ -2810,6 +2944,7 @@ public class NotificationManagerService extends SystemService {
             final INotificationListener listener = (INotificationListener) info.service;
             final NotificationRankingUpdate update;
             synchronized (mNotificationList) {
+                updateNotificationGroupsDesiredLocked();
                 update = makeRankingUpdateLocked(info);
             }
             try {
@@ -2823,8 +2958,10 @@ public class NotificationManagerService extends SystemService {
         protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
             if (mListenersDisablingEffects.remove(removed)) {
                 updateListenerHintsLocked();
+                updateEffectsSuppressorLocked();
             }
             mLightTrimListeners.remove(removed);
+            updateNotificationGroupsDesiredLocked();
         }
 
         public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
@@ -3028,6 +3165,31 @@ public class NotificationManagerService extends SystemService {
             }
             return false;
         }
+
+        /**
+         * Returns whether any of the currently registered listeners wants to receive notification
+         * groups.
+         *
+         * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
+         */
+        public boolean notificationGroupsDesired() {
+            return mNotificationGroupsDesired;
+        }
+
+        private void updateNotificationGroupsDesiredLocked() {
+            mNotificationGroupsDesired = true;
+            // No listeners, no groups.
+            if (mServices.isEmpty()) {
+                mNotificationGroupsDesired = false;
+                return;
+            }
+            // One listener: Check whether it's SysUI.
+            if (mServices.size() == 1 &&
+                    mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
+                mNotificationGroupsDesired = false;
+                return;
+            }
+        }
     }
 
     public static final class DumpFilter {
index aeddecb..6a96f85 100644 (file)
@@ -227,23 +227,8 @@ public class RankingHelper implements RankingConfig {
             notificationList.get(i).setGlobalSortKey(null);
         }
 
-        try {
-            // rank each record individually
-            Collections.sort(notificationList, mPreliminaryComparator);
-        } catch (RuntimeException ex) {
-            // Don't crash the system server if something bad happened.
-            Log.e(TAG, "Extreme badness during notification sort", ex);
-            Log.e(TAG, "Current notification list: ");
-            for (int i = 0; i < N; i++) {
-                NotificationRecord nr = notificationList.get(i);
-                Log.e(TAG, String.format(
-                        "  [%d] %s (group %s, rank %d, sortkey %s)",
-                        i, nr, nr.getGroupKey(), nr.getAuthoritativeRank(),
-                        nr.getNotification().getSortKey()));
-            }
-            // STOPSHIP: remove once b/16626175 is found
-            throw ex;
-        }
+        // rank each record individually
+        Collections.sort(notificationList, mPreliminaryComparator);
 
         synchronized (mProxyByGroupTmp) {
             // record individual ranking result and nominate proxies for each group
index 3e6d15a..6f1e851 100644 (file)
@@ -4285,6 +4285,14 @@ public class PackageManagerService extends IPackageManager.Stub {
         boolean updatedPkgBetter = false;
         // First check if this is a system package that may involve an update
         if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
+            // If new package is not located in "/system/priv-app" (e.g. due to an OTA),
+            // it needs to drop FLAG_PRIVILEGED.
+            if (locationIsPrivileged(scanFile)) {
+                updatedPkg.pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED;
+            } else {
+                updatedPkg.pkgFlags &= ~ApplicationInfo.FLAG_PRIVILEGED;
+            }
+
             if (ps != null && !ps.codePath.equals(scanFile)) {
                 // The path has changed from what was last scanned...  check the
                 // version of the new path against what we have stored to determine
@@ -4302,12 +4310,6 @@ public class PackageManagerService extends IPackageManager.Stub {
                                 + " to " + scanFile);
                         updatedPkg.codePath = scanFile;
                         updatedPkg.codePathString = scanFile.toString();
-                        // This is the point at which we know that the system-disk APK
-                        // for this package has moved during a reboot (e.g. due to an OTA),
-                        // so we need to reevaluate it for privilege policy.
-                        if (locationIsPrivileged(scanFile)) {
-                            updatedPkg.pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED;
-                        }
                     }
                     updatedPkg.pkg = pkg;
                     throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, null);
@@ -4537,26 +4539,35 @@ public class PackageManagerService extends IPackageManager.Stub {
         try {
             IMountService ms = PackageHelper.getMountService();
             if (ms != null) {
-                final long interval = android.provider.Settings.Global.getLong(
-                        mContext.getContentResolver(),
-                        android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
-                        DEFAULT_MANDATORY_FSTRIM_INTERVAL);
-                if (interval > 0) {
-                    final long timeSinceLast = System.currentTimeMillis() - ms.lastMaintenance();
-                    if (timeSinceLast > interval) {
-                        Slog.w(TAG, "No disk maintenance in " + timeSinceLast
-                                + "; running immediately");
-                        if (!isFirstBoot()) {
-                            try {
-                                ActivityManagerNative.getDefault().showBootMessage(
-                                        mContext.getResources().getString(
-                                                R.string.android_upgrading_fstrim), true);
-                            } catch (RemoteException e) {
-                            }
+                final boolean isUpgrade = isUpgrade();
+                boolean doTrim = isUpgrade;
+                if (doTrim) {
+                    Slog.w(TAG, "Running disk maintenance immediately due to system update");
+                } else {
+                    final long interval = android.provider.Settings.Global.getLong(
+                            mContext.getContentResolver(),
+                            android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
+                            DEFAULT_MANDATORY_FSTRIM_INTERVAL);
+                    if (interval > 0) {
+                        final long timeSinceLast = System.currentTimeMillis() - ms.lastMaintenance();
+                        if (timeSinceLast > interval) {
+                            doTrim = true;
+                            Slog.w(TAG, "No disk maintenance in " + timeSinceLast
+                                    + "; running immediately");
                         }
-                        ms.runMaintenance();
                     }
                 }
+                if (doTrim) {
+                    if (!isFirstBoot()) {
+                        try {
+                            ActivityManagerNative.getDefault().showBootMessage(
+                                    mContext.getResources().getString(
+                                            R.string.android_upgrading_fstrim), true);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    ms.runMaintenance();
+                }
             } else {
                 Slog.e(TAG, "Mount service unavailable!");
             }
@@ -7202,7 +7213,9 @@ public class PackageManagerService extends IPackageManager.Stub {
                         // If the original was granted this permission, we take
                         // that grant decision as read and propagate it to the
                         // update.
-                        allowed = true;
+                        if (sysPs.isPrivileged()) {
+                            allowed = true;
+                        }
                     } else {
                         // The system apk may have been updated with an older
                         // version of the one on the data partition, but which
index 4fd9fa7..524f638 100644 (file)
@@ -3324,6 +3324,18 @@ final class Settings {
             pw.print(",");
             pw.print(ps.installerPackageName != null ? ps.installerPackageName : "?");
             pw.println();
+            if (ps.pkg != null) {
+                pw.print(checkinTag); pw.print("-"); pw.print("splt,");
+                pw.print("base,");
+                pw.println(ps.pkg.baseRevisionCode);
+                if (ps.pkg.splitNames != null) {
+                    for (int i = 0; i < ps.pkg.splitNames.length; i++) {
+                        pw.print(checkinTag); pw.print("-"); pw.print("splt,");
+                        pw.print(ps.pkg.splitNames[i]); pw.print(",");
+                        pw.println(ps.pkg.splitRevisionCodes[i]);
+                    }
+                }
+            }
             for (UserInfo user : users) {
                 pw.print(checkinTag);
                 pw.print("-");
@@ -3374,6 +3386,7 @@ final class Settings {
         pw.println();
         if (ps.pkg != null) {
             pw.print(prefix); pw.print("  versionName="); pw.println(ps.pkg.mVersionName);
+            pw.print(prefix); pw.print("  splits="); dumpSplitNames(pw, ps.pkg); pw.println();
             pw.print(prefix); pw.print("  applicationInfo=");
                 pw.println(ps.pkg.applicationInfo.toString());
             pw.print(prefix); pw.print("  flags="); printFlags(pw, ps.pkg.applicationInfo.flags,
@@ -3646,4 +3659,27 @@ final class Settings {
         pw.println("Settings parse messages:");
         pw.print(mReadMessages.toString());
     }
+
+    private static void dumpSplitNames(PrintWriter pw, PackageParser.Package pkg) {
+        if (pkg == null) {
+            pw.print("unknown");
+        } else {
+            // [base:10, config.mdpi, config.xhdpi:12]
+            pw.print("[");
+            pw.print("base");
+            if (pkg.baseRevisionCode != 0) {
+                pw.print(":"); pw.print(pkg.baseRevisionCode);
+            }
+            if (pkg.splitNames != null) {
+                for (int i = 0; i < pkg.splitNames.length; i++) {
+                    pw.print(", ");
+                    pw.print(pkg.splitNames[i]);
+                    if (pkg.splitRevisionCodes[i] != 0) {
+                        pw.print(":"); pw.print(pkg.splitRevisionCodes[i]);
+                    }
+                }
+            }
+            pw.print("]");
+        }
+    }
 }
index db0f53b..be3251c 100644 (file)
@@ -224,6 +224,7 @@ public class UserManagerService extends IUserManager.Stub {
                         |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                         -1, -1);
                 mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
+                initDefaultGuestRestrictions();
                 readUserListLocked();
                 // Prune out any partially created/partially removed users.
                 ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
@@ -469,7 +470,7 @@ public class UserManagerService extends IUserManager.Stub {
     private void initDefaultGuestRestrictions() {
         if (mGuestRestrictions.isEmpty()) {
             mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
-            writeUserListLocked();
+            mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);
         }
     }
 
@@ -653,8 +654,15 @@ public class UserManagerService extends IUserManager.Stub {
                             }
                         }
                     } else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
-                        mGuestRestrictions.clear();
-                        readRestrictionsLocked(parser, mGuestRestrictions);
+                        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                                && type != XmlPullParser.END_TAG) {
+                            if (type == XmlPullParser.START_TAG) {
+                                if (parser.getName().equals(TAG_RESTRICTIONS)) {
+                                    readRestrictionsLocked(parser, mGuestRestrictions);
+                                }
+                                break;
+                            }
+                        }
                     }
                 }
             }
index 9828cd4..cf2ed07 100644 (file)
@@ -26,7 +26,6 @@ import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.util.Slog;
-import android.view.WindowManager;
 
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.IStatusBarService;
@@ -495,16 +494,26 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
     }
 
     /**
-     * The status bar service should call this each time the user brings the panel from
-     * invisible to visible in order to clear the notification light.
+     * @param clearNotificationEffects whether to consider notifications as "shown" and stop
+     *     LED, vibration, and ringing
      */
     @Override
-    public void onPanelRevealed() {
+    public void onPanelRevealed(boolean clearNotificationEffects) {
         enforceStatusBarService();
         long identity = Binder.clearCallingIdentity();
         try {
-            // tell the notification manager to turn off the lights.
-            mNotificationDelegate.onPanelRevealed();
+            mNotificationDelegate.onPanelRevealed(clearNotificationEffects);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void clearNotificationEffects() throws RemoteException {
+        enforceStatusBarService();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mNotificationDelegate.clearEffects();
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
index dc355c4..5ab3fa1 100644 (file)
@@ -19,6 +19,7 @@ package com.android.server.tv;
 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
 import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
+import static android.media.tv.TvInputManager.INPUT_STATE_UNKNOWN;
 
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
@@ -779,6 +780,22 @@ public final class TvInputManagerService extends SystemService {
         }
 
         @Override
+        public int getTvInputState(String inputId, int userId) {
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "getTvInputState");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    UserState userState = getUserStateLocked(resolvedUserId);
+                    TvInputState state = userState.inputMap.get(inputId);
+                    return state == null ? INPUT_STATE_UNKNOWN : state.state;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "getTvContentRatingSystemList");
@@ -816,10 +833,6 @@ public final class TvInputManagerService extends SystemService {
                     } catch (RemoteException e) {
                         Slog.e(TAG, "client process has already died", e);
                     }
-                    for (TvInputState state : userState.inputMap.values()) {
-                        notifyInputStateChangedLocked(userState, state.info.getId(), state.state,
-                                callback);
-                    }
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
index 0cbf03a..08754f9 100644 (file)
@@ -159,11 +159,15 @@ final class AccessibilityController {
         }
     }
 
-    public void onWindowFocusChangedLocked() {
+    public void onWindowFocusChangedNotLocked() {
         // Not relevant for the display magnifier.
 
-        if (mWindowsForAccessibilityObserver != null) {
-            mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
+        WindowsForAccessibilityObserver observer = null;
+        synchronized (mWindowManagerService) {
+            observer = mWindowsForAccessibilityObserver;
+        }
+        if (observer != null) {
+            observer.performComputeChangedWindowsNotLocked();
         }
     }
 
@@ -937,14 +941,13 @@ final class AccessibilityController {
             computeChangedWindows();
         }
 
+        public void performComputeChangedWindowsNotLocked() {
+            mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
+            computeChangedWindows();
+        }
+
         public void scheduleComputeChangedWindowsLocked() {
-            // If focus changed, compute changed windows immediately as the focused window
-            // is used by the accessibility manager service to determine the active window.
-            if (mWindowManagerService.mCurrentFocus != null
-                    && mWindowManagerService.mCurrentFocus != mWindowManagerService.mLastFocus) {
-                mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
-                computeChangedWindows();
-            } else if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
+            if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
                 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
                         mRecurringAccessibilityEventsIntervalMillis);
             }
@@ -955,6 +958,9 @@ final class AccessibilityController {
                 Slog.i(LOG_TAG, "computeChangedWindows()");
             }
 
+            boolean windowsChanged = false;
+            List<WindowInfo> windows = new ArrayList<WindowInfo>();
+
             synchronized (mWindowManagerService.mWindowMap) {
                 // Do not send the windows if there is no current focus as
                 // the window manager is still looking for where to put it.
@@ -975,8 +981,6 @@ final class AccessibilityController {
                 SparseArray<WindowState> visibleWindows = mTempWindowStates;
                 populateVisibleWindowsOnScreenLocked(visibleWindows);
 
-                List<WindowInfo> windows = new ArrayList<WindowInfo>();
-
                 Set<IBinder> addedWindows = mTempBinderSet;
                 addedWindows.clear();
 
@@ -1074,7 +1078,6 @@ final class AccessibilityController {
                 addedWindows.clear();
 
                 // We computed the windows and if they changed notify the client.
-                boolean windowsChanged = false;
                 if (mOldWindows.size() != windows.size()) {
                     // Different size means something changed.
                     windowsChanged = true;
@@ -1096,22 +1099,24 @@ final class AccessibilityController {
                 }
 
                 if (windowsChanged) {
-                    if (DEBUG) {
-                        Log.i(LOG_TAG, "Windows changed:" + windows);
-                    }
-                    // Remember the old windows to detect changes.
                     cacheWindows(windows);
-                    // Announce the change.
-                    mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
-                            windows).sendToTarget();
-                } else {
-                    if (DEBUG) {
-                        Log.i(LOG_TAG, "No windows changed.");
-                    }
-                    // Recycle the nodes as we do not need them.
-                    clearAndRecycleWindows(windows);
                 }
             }
+
+            // Now we do not hold the lock, so send the windows over.
+            if (windowsChanged) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Windows changed:" + windows);
+                }
+                mCallback.onWindowsForAccessibilityChanged(windows);
+            } else {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "No windows changed.");
+                }
+            }
+
+            // Recycle the windows as we do not need them.
+            clearAndRecycleWindows(windows);
         }
 
         private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
@@ -1217,7 +1222,7 @@ final class AccessibilityController {
             return false;
         }
 
-        private void clearAndRecycleWindows(List<WindowInfo> windows) {
+        private static void clearAndRecycleWindows(List<WindowInfo> windows) {
             final int windowCount = windows.size();
             for (int i = windowCount - 1; i >= 0; i--) {
                 windows.remove(i).recycle();
@@ -1254,7 +1259,6 @@ final class AccessibilityController {
 
         private class MyHandler extends Handler {
             public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
-            public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 2;
 
             public MyHandler(Looper looper) {
                 super(looper, null, false);
@@ -1267,12 +1271,6 @@ final class AccessibilityController {
                     case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
                         computeChangedWindows();
                     } break;
-
-                    case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
-                        List<WindowInfo> windows = (List<WindowInfo>) message.obj;
-                        mCallback.onWindowsForAccessibilityChanged(windows);
-                        clearAndRecycleWindows(windows);
-                    } break;
                 }
             }
         }
index f6e8bcf..c0d54e1 100644 (file)
@@ -118,7 +118,7 @@ public class AppTransition implements Dump {
     private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.3f;
 
     private static final int DEFAULT_APP_TRANSITION_DURATION = 250;
-    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 300;
+    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 325;
     private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 325;
 
     private final Context mContext;
index 34d1a64..ba995f2 100644 (file)
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import android.content.Context;
 import android.graphics.Rect;
 import android.os.Environment;
 import android.util.AtomicFile;
@@ -41,7 +40,6 @@ import java.util.HashMap;
 public class DisplaySettings {
     private static final String TAG = WindowManagerService.TAG;
 
-    private final Context mContext;
     private final AtomicFile mFile;
     private final HashMap<String, Entry> mEntries = new HashMap<String, Entry>();
 
@@ -57,15 +55,19 @@ public class DisplaySettings {
         }
     }
 
-    public DisplaySettings(Context context) {
-        mContext = context;
+    public DisplaySettings() {
         File dataDir = Environment.getDataDirectory();
         File systemDir = new File(dataDir, "system");
         mFile = new AtomicFile(new File(systemDir, "display_settings.xml"));
     }
 
-    public void getOverscanLocked(String name, Rect outRect) {
-        Entry entry = mEntries.get(name);
+    public void getOverscanLocked(String name, String uniqueId, Rect outRect) {
+        // Try to get the entry with the unique if possible.
+        // Else, fall back on the display name.
+        Entry entry;
+        if (uniqueId == null || (entry = mEntries.get(uniqueId)) == null) {
+            entry = mEntries.get(name);
+        }
         if (entry != null) {
             outRect.left = entry.overscanLeft;
             outRect.top = entry.overscanTop;
@@ -110,7 +112,7 @@ public class DisplaySettings {
             int type;
             while ((type = parser.next()) != XmlPullParser.START_TAG
                     && type != XmlPullParser.END_DOCUMENT) {
-                ;
+                // Do nothing.
             }
 
             if (type != XmlPullParser.START_TAG) {
index bcfd7f0..2750941 100644 (file)
@@ -825,7 +825,7 @@ public class WindowManagerService extends IWindowManager.Stub
                 com.android.internal.R.bool.config_hasPermanentDpad);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-        mDisplaySettings = new DisplaySettings(context);
+        mDisplaySettings = new DisplaySettings();
         mDisplaySettings.readSettingsLocked();
 
         LocalServices.addService(WindowManagerPolicy.class, mPolicy);
@@ -7610,7 +7610,15 @@ public class WindowManagerService extends IWindowManager.Stub
                     WindowState lastFocus;
                     WindowState newFocus;
 
+                    AccessibilityController accessibilityController = null;
+
                     synchronized(mWindowMap) {
+                        // TODO(multidisplay): Accessibility supported only of default desiplay.
+                        if (mAccessibilityController != null && getDefaultDisplayContentLocked()
+                                .getDisplayId() == Display.DEFAULT_DISPLAY) {
+                            accessibilityController = mAccessibilityController;
+                        }
+
                         lastFocus = mLastFocus;
                         newFocus = mCurrentFocus;
                         if (lastFocus == newFocus) {
@@ -7628,6 +7636,12 @@ public class WindowManagerService extends IWindowManager.Stub
                         }
                     }
 
+                    // First notify the accessibility manager for the change so it has
+                    // the windows before the newly focused one starts firing eventgs.
+                    if (accessibilityController != null) {
+                        accessibilityController.onWindowFocusChangedNotLocked();
+                    }
+
                     //System.out.println("Changing focus from " + lastFocus
                     //                   + " to " + newFocus);
                     if (newFocus != null) {
@@ -8476,7 +8490,7 @@ public class WindowManagerService extends IWindowManager.Stub
             displayInfo.overscanBottom = bottom;
         }
 
-        mDisplaySettings.setOverscanLocked(displayInfo.name, left, top, right, bottom);
+        mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, left, top, right, bottom);
         mDisplaySettings.writeSettingsLocked();
 
         reconfigureDisplayLocked(displayContent);
@@ -9539,6 +9553,9 @@ public class WindowManagerService extends IWindowManager.Stub
                                     + " interesting=" + numInteresting
                                     + " drawn=" + wtoken.numDrawnWindows);
                             wtoken.allDrawn = true;
+                            // Force an additional layout pass where WindowStateAnimator#
+                            // commitFinishDrawingLocked() will call performShowLocked().
+                            displayContent.layoutNeeded = true;
                             mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, wtoken.token).sendToTarget();
                         }
                     }
@@ -9761,7 +9778,7 @@ public class WindowManagerService extends IWindowManager.Stub
                     w.mContentChanged = false;
 
                     // Moved from updateWindowsAndWallpaperLocked().
-                    if (w.mHasSurface) {
+                    if (w.mHasSurface && !w.isHiddenFromUserLocked()) {
                         // Take care of the window being ready to display.
                         final boolean committed =
                                 winAnimator.commitFinishDrawingLocked(currentTime);
@@ -10399,12 +10416,6 @@ public class WindowManagerService extends IWindowManager.Stub
             mCurrentFocus = newFocus;
             mLosingFocus.remove(newFocus);
 
-            // TODO(multidisplay): Accessibilty supported only of default desiplay.
-            if (mAccessibilityController != null
-                    && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                mAccessibilityController.onWindowFocusChangedLocked();
-            }
-
             int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
 
             if (imWindowChanged && oldFocus != mInputMethodWindow) {
@@ -11458,7 +11469,7 @@ public class WindowManagerService extends IWindowManager.Stub
 
         DisplayInfo displayInfo = displayContent.getDisplayInfo();
         final Rect rect = new Rect();
-        mDisplaySettings.getOverscanLocked(displayInfo.name, rect);
+        mDisplaySettings.getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
         synchronized (displayContent.mDisplaySizeLock) {
             displayInfo.overscanLeft = rect.left;
             displayInfo.overscanTop = rect.top;
index 87d420f..c2d8004 100644 (file)
@@ -455,6 +455,14 @@ class WindowStateAnimator {
             }
         }
 
+        if (!isWindowAnimating()) {
+            //TODO (multidisplay): Accessibility is supported only for the default display.
+            if (mService.mAccessibilityController != null
+                    && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+            }
+        }
+
         if (!mWin.mExiting) {
             return;
         }
index bd63e00..3271ebf 100644 (file)
@@ -91,7 +91,7 @@ public final class AudioState implements Parcelable {
     @Override
     public String toString() {
         return String.format(Locale.US,
-                "[AudioState isMuted: %b, route; %s, supportedRouteMask: %s]",
+                "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s]",
                 isMuted,
                 audioRouteToString(route),
                 audioRouteToString(supportedRouteMask));
index 7df40f1..bbf3384 100644 (file)
@@ -153,10 +153,10 @@ public final class Call {
         public static final int CAPABILITY_SUPPORTS_VT_REMOTE = 0x00000200;
 
         /**
-         * Call is using voice over LTE.
+         * Call is using high definition audio.
          * @hide
          */
-        public static final int CAPABILITY_VoLTE = 0x00000400;
+        public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00000400;
 
         /**
          * Call is using voice over WIFI.
@@ -255,8 +255,8 @@ public final class Call {
             if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE)) {
                 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE");
             }
-            if (can(capabilities, CAPABILITY_VoLTE)) {
-                builder.append(" CAPABILITY_VoLTE");
+            if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
+                builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
             }
             if (can(capabilities, CAPABILITY_VoWIFI)) {
                 builder.append(" CAPABILITY_VoWIFI");
index 125ada0..f3b0586 100644 (file)
@@ -118,10 +118,10 @@ public abstract class Connection implements IConferenceable {
     public static final int CAPABILITY_SUPPORTS_VT_REMOTE = 0x00000200;
 
     /**
-     * Connection is using voice over LTE.
+     * Connection is using high definition audio.
      * @hide
      */
-    public static final int CAPABILITY_VoLTE = 0x00000400;
+    public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00000400;
 
     /**
      * Connection is using voice over WIFI.
@@ -224,8 +224,8 @@ public abstract class Connection implements IConferenceable {
         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE)) {
             builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE");
         }
-        if (can(capabilities, CAPABILITY_VoLTE)) {
-            builder.append(" CAPABILITY_VoLTE");
+        if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
+            builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
         }
         if (can(capabilities, CAPABILITY_VoWIFI)) {
             builder.append(" CAPABILITY_VoWIFI");
@@ -838,6 +838,11 @@ public abstract class Connection implements IConferenceable {
         return mConnectionCapabilities;
     }
 
+    /** @hide */
+    @SystemApi @Deprecated public final int getCallCapabilities() {
+        return getConnectionCapabilities();
+    }
+
     /**
      * Sets the value of the {@link #getAddress()} property.
      *
@@ -1009,7 +1014,7 @@ public abstract class Connection implements IConferenceable {
     }
 
     /** @hide */
-    @Deprecated public final void setCapabilities(int connectionCapabilities) {
+    @SystemApi @Deprecated public final void setCallCapabilities(int connectionCapabilities) {
         setConnectionCapabilities(connectionCapabilities);
     }
 
@@ -1310,12 +1315,16 @@ public abstract class Connection implements IConferenceable {
     }
 
     private static class FailureSignalingConnection extends Connection {
+        private boolean mImmutable = false;
         public FailureSignalingConnection(DisconnectCause disconnectCause) {
             setDisconnected(disconnectCause);
+            mImmutable = true;
         }
 
         public void checkImmutable() {
-            throw new UnsupportedOperationException("Connection is immutable");
+            if (mImmutable) {
+                throw new UnsupportedOperationException("Connection is immutable");
+            }
         }
     }
 
index 2a3d7ab..6621726 100644 (file)
@@ -732,6 +732,26 @@ public class TelecomManager {
     }
 
     /**
+     * Return whether a given phone account has a voicemail number configured.
+     *
+     * @param accountHandle The handle for the account to check for a voicemail number.
+     * @return {@code true} If the given phone account has a voicemail number.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean hasVoiceMailNumber(PhoneAccountHandle accountHandle) {
+        try {
+            if (isServiceConnected()) {
+                return getTelecomService().hasVoiceMailNumber(accountHandle);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling isInCall().", e);
+        }
+        return false;
+    }
+
+    /**
      * Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding
      * states).
      * <p>
index cbd9d69..f8d7539 100644 (file)
@@ -121,6 +121,11 @@ interface ITelecomService {
     boolean isVoiceMailNumber(in PhoneAccountHandle accountHandle, String number);
 
     /**
+     * @see TelecomServiceImpl#hasVoiceMailNumber
+     */
+    boolean hasVoiceMailNumber(in PhoneAccountHandle accountHandle);
+
+    /**
      * @see TelecomServiceImpl#getDefaultPhoneApp
      */
     ComponentName getDefaultPhoneApp();
index 8c2a4eb..559a58c 100644 (file)
@@ -167,10 +167,40 @@ public class ServiceState implements Parcelable {
 
     private int mVoiceRegState = STATE_OUT_OF_SERVICE;
     private int mDataRegState = STATE_OUT_OF_SERVICE;
-    private boolean mRoaming;
-    private String mOperatorAlphaLong;
-    private String mOperatorAlphaShort;
-    private String mOperatorNumeric;
+
+    /**
+     * Roaming type
+     * HOME : in home network
+     * @hide
+     */
+    public static final int ROAMING_TYPE_NOT_ROAMING = 0;
+    /**
+     * Roaming type
+     * UNKNOWN : in a roaming network, but we can not tell if it's domestic or international
+     * @hide
+     */
+    public static final int ROAMING_TYPE_UNKNOWN = 1;
+    /**
+     * Roaming type
+     * DOMESTIC : in domestic roaming network
+     * @hide
+     */
+    public static final int ROAMING_TYPE_DOMESTIC = 2;
+    /**
+     * Roaming type
+     * INTERNATIONAL : in international roaming network
+     * @hide
+     */
+    public static final int ROAMING_TYPE_INTERNATIONAL = 3;
+
+    private int mVoiceRoamingType;
+    private int mDataRoamingType;
+    private String mVoiceOperatorAlphaLong;
+    private String mVoiceOperatorAlphaShort;
+    private String mVoiceOperatorNumeric;
+    private String mDataOperatorAlphaLong;
+    private String mDataOperatorAlphaShort;
+    private String mDataOperatorNumeric;
     private boolean mIsManualNetworkSelection;
 
     private boolean mIsEmergencyOnly;
@@ -187,6 +217,29 @@ public class ServiceState implements Parcelable {
     private int mCdmaEriIconMode;
 
     /**
+     * get String description of roaming type
+     * @hide
+     */
+    public static final String getRoamingLogString(int roamingType) {
+        switch (roamingType) {
+            case ROAMING_TYPE_NOT_ROAMING:
+                return "home";
+
+            case ROAMING_TYPE_UNKNOWN:
+                return "roaming";
+
+            case ROAMING_TYPE_DOMESTIC:
+                return "Domestic Roaming";
+
+            case ROAMING_TYPE_INTERNATIONAL:
+                return "International Roaming";
+
+            default:
+                return "UNKNOWN";
+        }
+    }
+
+    /**
      * Create a new ServiceState from a intent notifier Bundle
      *
      * This method is used by PhoneStateIntentReceiver and maybe by
@@ -221,10 +274,14 @@ public class ServiceState implements Parcelable {
     protected void copyFrom(ServiceState s) {
         mVoiceRegState = s.mVoiceRegState;
         mDataRegState = s.mDataRegState;
-        mRoaming = s.mRoaming;
-        mOperatorAlphaLong = s.mOperatorAlphaLong;
-        mOperatorAlphaShort = s.mOperatorAlphaShort;
-        mOperatorNumeric = s.mOperatorNumeric;
+        mVoiceRoamingType = s.mVoiceRoamingType;
+        mDataRoamingType = s.mDataRoamingType;
+        mVoiceOperatorAlphaLong = s.mVoiceOperatorAlphaLong;
+        mVoiceOperatorAlphaShort = s.mVoiceOperatorAlphaShort;
+        mVoiceOperatorNumeric = s.mVoiceOperatorNumeric;
+        mDataOperatorAlphaLong = s.mDataOperatorAlphaLong;
+        mDataOperatorAlphaShort = s.mDataOperatorAlphaShort;
+        mDataOperatorNumeric = s.mDataOperatorNumeric;
         mIsManualNetworkSelection = s.mIsManualNetworkSelection;
         mRilVoiceRadioTechnology = s.mRilVoiceRadioTechnology;
         mRilDataRadioTechnology = s.mRilDataRadioTechnology;
@@ -244,10 +301,14 @@ public class ServiceState implements Parcelable {
     public ServiceState(Parcel in) {
         mVoiceRegState = in.readInt();
         mDataRegState = in.readInt();
-        mRoaming = in.readInt() != 0;
-        mOperatorAlphaLong = in.readString();
-        mOperatorAlphaShort = in.readString();
-        mOperatorNumeric = in.readString();
+        mVoiceRoamingType = in.readInt();
+        mDataRoamingType = in.readInt();
+        mVoiceOperatorAlphaLong = in.readString();
+        mVoiceOperatorAlphaShort = in.readString();
+        mVoiceOperatorNumeric = in.readString();
+        mDataOperatorAlphaLong = in.readString();
+        mDataOperatorAlphaShort = in.readString();
+        mDataOperatorNumeric = in.readString();
         mIsManualNetworkSelection = in.readInt() != 0;
         mRilVoiceRadioTechnology = in.readInt();
         mRilDataRadioTechnology = in.readInt();
@@ -264,10 +325,14 @@ public class ServiceState implements Parcelable {
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mVoiceRegState);
         out.writeInt(mDataRegState);
-        out.writeInt(mRoaming ? 1 : 0);
-        out.writeString(mOperatorAlphaLong);
-        out.writeString(mOperatorAlphaShort);
-        out.writeString(mOperatorNumeric);
+        out.writeInt(mVoiceRoamingType);
+        out.writeInt(mDataRoamingType);
+        out.writeString(mVoiceOperatorAlphaLong);
+        out.writeString(mVoiceOperatorAlphaShort);
+        out.writeString(mVoiceOperatorNumeric);
+        out.writeString(mDataOperatorAlphaLong);
+        out.writeString(mDataOperatorAlphaShort);
+        out.writeString(mDataOperatorNumeric);
         out.writeInt(mIsManualNetworkSelection ? 1 : 0);
         out.writeInt(mRilVoiceRadioTechnology);
         out.writeInt(mRilDataRadioTechnology);
@@ -337,10 +402,45 @@ public class ServiceState implements Parcelable {
      *
      * @return true if TS 27.007 7.2 roaming is true
      *              and ONS is different from SPN
-     *
      */
     public boolean getRoaming() {
-        return mRoaming;
+        return getVoiceRoaming() || getDataRoaming();
+    }
+
+    /**
+     * Get current voice network roaming status
+     * @return roaming status
+     * @hide
+     */
+    public boolean getVoiceRoaming() {
+        return mVoiceRoamingType != ROAMING_TYPE_NOT_ROAMING;
+    }
+
+    /**
+     * Get current voice network roaming type
+     * @return roaming type
+     * @hide
+     */
+    public int getVoiceRoamingType() {
+        return mVoiceRoamingType;
+    }
+
+    /**
+     * Get current data network roaming type
+     * @return roaming type
+     * @hide
+     */
+    public boolean getDataRoaming() {
+        return mDataRoamingType != ROAMING_TYPE_NOT_ROAMING;
+    }
+
+    /**
+     * Get current data network roaming type
+     * @return roaming type
+     * @hide
+     */
+    public int getDataRoamingType() {
+        return mDataRoamingType;
     }
 
     /**
@@ -387,7 +487,25 @@ public class ServiceState implements Parcelable {
      * @return long name of operator, null if unregistered or unknown
      */
     public String getOperatorAlphaLong() {
-        return mOperatorAlphaLong;
+        return mVoiceOperatorAlphaLong;
+    }
+
+    /**
+     * Get current registered voice network operator name in long alphanumeric format.
+     * @return long name of operator
+     * @hide
+     */
+    public String getVoiceOperatorAlphaLong() {
+        return mVoiceOperatorAlphaLong;
+    }
+
+    /**
+     * Get current registered data network operator name in long alphanumeric format.
+     * @return long name of voice operator
+     * @hide
+     */
+    public String getDataOperatorAlphaLong() {
+        return mDataOperatorAlphaLong;
     }
 
     /**
@@ -398,7 +516,25 @@ public class ServiceState implements Parcelable {
      * @return short name of operator, null if unregistered or unknown
      */
     public String getOperatorAlphaShort() {
-        return mOperatorAlphaShort;
+        return mVoiceOperatorAlphaShort;
+    }
+
+    /**
+     * Get current registered voice network operator name in short alphanumeric format.
+     * @return short name of operator, null if unregistered or unknown
+     * @hide
+     */
+    public String getVoiceOperatorAlphaShort() {
+        return mVoiceOperatorAlphaShort;
+    }
+
+    /**
+     * Get current registered data network operator name in short alphanumeric format.
+     * @return short name of operator, null if unregistered or unknown
+     * @hide
+     */
+    public String getDataOperatorAlphaShort() {
+        return mDataOperatorAlphaShort;
     }
 
     /**
@@ -414,7 +550,25 @@ public class ServiceState implements Parcelable {
      * {@link com.android.internal.telephony.MccTable#countryCodeForMcc(int)}.
      */
     public String getOperatorNumeric() {
-        return mOperatorNumeric;
+        return mVoiceOperatorNumeric;
+    }
+
+    /**
+     * Get current registered voice network operator numeric id.
+     * @return numeric format of operator, null if unregistered or unknown
+     * @hide
+     */
+    public String getVoiceOperatorNumeric() {
+        return mVoiceOperatorNumeric;
+    }
+
+    /**
+     * Get current registered data network operator numeric id.
+     * @return numeric format of operator, null if unregistered or unknown
+     * @hide
+     */
+    public String getDataOperatorNumeric() {
+        return mDataOperatorNumeric;
     }
 
     /**
@@ -430,11 +584,15 @@ public class ServiceState implements Parcelable {
     public int hashCode() {
         return ((mVoiceRegState * 31)
                 + (mDataRegState * 37)
-                + (mRoaming ? 1 : 0)
+                + mVoiceRoamingType
+                + mDataRoamingType
                 + (mIsManualNetworkSelection ? 1 : 0)
-                + ((null == mOperatorAlphaLong) ? 0 : mOperatorAlphaLong.hashCode())
-                + ((null == mOperatorAlphaShort) ? 0 : mOperatorAlphaShort.hashCode())
-                + ((null == mOperatorNumeric) ? 0 : mOperatorNumeric.hashCode())
+                + ((null == mVoiceOperatorAlphaLong) ? 0 : mVoiceOperatorAlphaLong.hashCode())
+                + ((null == mVoiceOperatorAlphaShort) ? 0 : mVoiceOperatorAlphaShort.hashCode())
+                + ((null == mVoiceOperatorNumeric) ? 0 : mVoiceOperatorNumeric.hashCode())
+                + ((null == mDataOperatorAlphaLong) ? 0 : mDataOperatorAlphaLong.hashCode())
+                + ((null == mDataOperatorAlphaShort) ? 0 : mDataOperatorAlphaShort.hashCode())
+                + ((null == mDataOperatorNumeric) ? 0 : mDataOperatorNumeric.hashCode())
                 + mCdmaRoamingIndicator
                 + mCdmaDefaultRoamingIndicator
                 + (mIsEmergencyOnly ? 1 : 0));
@@ -456,11 +614,15 @@ public class ServiceState implements Parcelable {
 
         return (mVoiceRegState == s.mVoiceRegState
                 && mDataRegState == s.mDataRegState
-                && mRoaming == s.mRoaming
                 && mIsManualNetworkSelection == s.mIsManualNetworkSelection
-                && equalsHandlesNulls(mOperatorAlphaLong, s.mOperatorAlphaLong)
-                && equalsHandlesNulls(mOperatorAlphaShort, s.mOperatorAlphaShort)
-                && equalsHandlesNulls(mOperatorNumeric, s.mOperatorNumeric)
+                && mVoiceRoamingType == s.mVoiceRoamingType
+                && mDataRoamingType == s.mDataRoamingType
+                && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
+                && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
+                && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
+                && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong)
+                && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort)
+                && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric)
                 && equalsHandlesNulls(mRilVoiceRadioTechnology, s.mRilVoiceRadioTechnology)
                 && equalsHandlesNulls(mRilDataRadioTechnology, s.mRilDataRadioTechnology)
                 && equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
@@ -548,10 +710,17 @@ public class ServiceState implements Parcelable {
         String radioTechnology = rilRadioTechnologyToString(mRilVoiceRadioTechnology);
         String dataRadioTechnology = rilRadioTechnologyToString(mRilDataRadioTechnology);
 
-        return (mVoiceRegState + " " + mDataRegState + " " + (mRoaming ? "roaming" : "home")
-                + " " + mOperatorAlphaLong
-                + " " + mOperatorAlphaShort
-                + " " + mOperatorNumeric
+        return (mVoiceRegState + " " + mDataRegState
+                + " "
+                + "voice " + getRoamingLogString(mVoiceRoamingType)
+                + " "
+                + "data " + getRoamingLogString(mDataRoamingType)
+                + " " + mVoiceOperatorAlphaLong
+                + " " + mVoiceOperatorAlphaShort
+                + " " + mVoiceOperatorNumeric
+                + " " + mDataOperatorAlphaLong
+                + " " + mDataOperatorAlphaShort
+                + " " + mDataOperatorNumeric
                 + " " + (mIsManualNetworkSelection ? "(manual)" : "")
                 + " " + radioTechnology
                 + " " + dataRadioTechnology
@@ -567,10 +736,14 @@ public class ServiceState implements Parcelable {
         if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setNullState=" + state);
         mVoiceRegState = state;
         mDataRegState = state;
-        mRoaming = false;
-        mOperatorAlphaLong = null;
-        mOperatorAlphaShort = null;
-        mOperatorNumeric = null;
+        mVoiceRoamingType = ROAMING_TYPE_NOT_ROAMING;
+        mDataRoamingType = ROAMING_TYPE_NOT_ROAMING;
+        mVoiceOperatorAlphaLong = null;
+        mVoiceOperatorAlphaShort = null;
+        mVoiceOperatorNumeric = null;
+        mDataOperatorAlphaLong = null;
+        mDataOperatorAlphaShort = null;
+        mDataOperatorNumeric = null;
         mIsManualNetworkSelection = false;
         mRilVoiceRadioTechnology = 0;
         mRilDataRadioTechnology = 0;
@@ -610,9 +783,29 @@ public class ServiceState implements Parcelable {
     }
 
     public void setRoaming(boolean roaming) {
-        mRoaming = roaming;
+        mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
+        mDataRoamingType = mVoiceRoamingType;
     }
 
+    /** @hide */
+    public void setVoiceRoaming(boolean roaming) {
+        mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
+    }
+
+    /** @hide */
+    public void setVoiceRoamingType(int type) {
+        mVoiceRoamingType = type;
+    }
+
+    /** @hide */
+    public void setDataRoaming(boolean dataRoaming) {
+        mDataRoamingType = (dataRoaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
+    }
+
+    /** @hide */
+    public void setDataRoamingType(int type) {
+        mDataRoamingType = type;
+    }
 
     /**
      * @hide
@@ -650,9 +843,26 @@ public class ServiceState implements Parcelable {
     }
 
     public void setOperatorName(String longName, String shortName, String numeric) {
-        mOperatorAlphaLong = longName;
-        mOperatorAlphaShort = shortName;
-        mOperatorNumeric = numeric;
+        mVoiceOperatorAlphaLong = longName;
+        mVoiceOperatorAlphaShort = shortName;
+        mVoiceOperatorNumeric = numeric;
+        mDataOperatorAlphaLong = longName;
+        mDataOperatorAlphaShort = shortName;
+        mDataOperatorNumeric = numeric;
+    }
+
+    /** @hide */
+    public void setVoiceOperatorName(String longName, String shortName, String numeric) {
+        mVoiceOperatorAlphaLong = longName;
+        mVoiceOperatorAlphaShort = shortName;
+        mVoiceOperatorNumeric = numeric;
+    }
+
+    /** @hide */
+    public void setDataOperatorName(String longName, String shortName, String numeric) {
+        mDataOperatorAlphaLong = longName;
+        mDataOperatorAlphaShort = shortName;
+        mDataOperatorNumeric = numeric;
     }
 
     /**
@@ -662,7 +872,18 @@ public class ServiceState implements Parcelable {
      * @hide
      */
     public void setOperatorAlphaLong(String longName) {
-        mOperatorAlphaLong = longName;
+        mVoiceOperatorAlphaLong = longName;
+        mDataOperatorAlphaLong = longName;
+    }
+
+    /** @hide */
+    public void setVoiceOperatorAlphaLong(String longName) {
+        mVoiceOperatorAlphaLong = longName;
+    }
+
+    /** @hide */
+    public void setDataOperatorAlphaLong(String longName) {
+        mDataOperatorAlphaLong = longName;
     }
 
     public void setIsManualSelection(boolean isManual) {
@@ -689,10 +910,14 @@ public class ServiceState implements Parcelable {
     private void setFromNotifierBundle(Bundle m) {
         mVoiceRegState = m.getInt("voiceRegState");
         mDataRegState = m.getInt("dataRegState");
-        mRoaming = m.getBoolean("roaming");
-        mOperatorAlphaLong = m.getString("operator-alpha-long");
-        mOperatorAlphaShort = m.getString("operator-alpha-short");
-        mOperatorNumeric = m.getString("operator-numeric");
+        mVoiceRoamingType = m.getInt("voiceRoamingType");
+        mDataRoamingType = m.getInt("dataRoamingType");
+        mVoiceOperatorAlphaLong = m.getString("operator-alpha-long");
+        mVoiceOperatorAlphaShort = m.getString("operator-alpha-short");
+        mVoiceOperatorNumeric = m.getString("operator-numeric");
+        mDataOperatorAlphaLong = m.getString("data-operator-alpha-long");
+        mDataOperatorAlphaShort = m.getString("data-operator-alpha-short");
+        mDataOperatorNumeric = m.getString("data-operator-numeric");
         mIsManualNetworkSelection = m.getBoolean("manual");
         mRilVoiceRadioTechnology = m.getInt("radioTechnology");
         mRilDataRadioTechnology = m.getInt("dataRadioTechnology");
@@ -713,10 +938,14 @@ public class ServiceState implements Parcelable {
     public void fillInNotifierBundle(Bundle m) {
         m.putInt("voiceRegState", mVoiceRegState);
         m.putInt("dataRegState", mDataRegState);
-        m.putBoolean("roaming", Boolean.valueOf(mRoaming));
-        m.putString("operator-alpha-long", mOperatorAlphaLong);
-        m.putString("operator-alpha-short", mOperatorAlphaShort);
-        m.putString("operator-numeric", mOperatorNumeric);
+        m.putInt("voiceRoamingType", mVoiceRoamingType);
+        m.putInt("dataRoamingType", mDataRoamingType);
+        m.putString("operator-alpha-long", mVoiceOperatorAlphaLong);
+        m.putString("operator-alpha-short", mVoiceOperatorAlphaShort);
+        m.putString("operator-numeric", mVoiceOperatorNumeric);
+        m.putString("data-operator-alpha-long", mDataOperatorAlphaLong);
+        m.putString("data-operator-alpha-short", mDataOperatorAlphaShort);
+        m.putString("data-operator-numeric", mDataOperatorNumeric);
         m.putBoolean("manual", Boolean.valueOf(mIsManualNetworkSelection));
         m.putInt("radioTechnology", mRilVoiceRadioTechnology);
         m.putInt("dataRadioTechnology", mRilDataRadioTechnology);
index c8b782f..e57f9e3 100644 (file)
@@ -27,6 +27,7 @@ import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.DisplayMetrics;
 
 /**
  * A Parcelable class for Subscription Information.
@@ -36,7 +37,7 @@ public class SubscriptionInfo implements Parcelable {
     /**
      * Size of text to render on the icon.
      */
-    private static final int TEXT_SIZE = 22;
+    private static final int TEXT_SIZE = 16;
 
     /**
      * Subscription Identifier, this is a device unique number
@@ -197,10 +198,10 @@ public class SubscriptionInfo implements Parcelable {
     public Bitmap createIconBitmap(Context context) {
         int width = mIconBitmap.getWidth();
         int height = mIconBitmap.getHeight();
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
 
         // Create a new bitmap of the same size because it will be modified.
-        Bitmap workingBitmap = Bitmap.createBitmap(context.getResources().getDisplayMetrics(),
-                width, height, mIconBitmap.getConfig());
+        Bitmap workingBitmap = Bitmap.createBitmap(metrics, width, height, mIconBitmap.getConfig());
 
         Canvas canvas = new Canvas(workingBitmap);
         Paint paint = new Paint();
@@ -214,8 +215,10 @@ public class SubscriptionInfo implements Parcelable {
         paint.setAntiAlias(true);
         paint.setTypeface(Typeface.create("sans-serif", Typeface.NORMAL));
         paint.setColor(Color.WHITE);
-        paint.setTextSize(TEXT_SIZE);
-        final String index = Integer.toString(mSimSlotIndex + 1);
+        // Set text size scaled by density
+        paint.setTextSize(TEXT_SIZE * metrics.density);
+        // Convert sim slot index to localized string
+        final String index = String.format("%d", mSimSlotIndex + 1);
         final Rect textBound = new Rect();
         paint.getTextBounds(index, 0, 1, textBound);
         final float xOffset = (width / 2.f) - textBound.centerX();
index 40d2e77..abf1ead 100644 (file)
@@ -1103,5 +1103,35 @@ public class SubscriptionManager {
         return Boolean.parseBoolean(TelephonyManager.getTelephonyProperty(phoneId,
                 TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, null));
     }
+
+    /**
+     * Returns a constant indicating the state of sim for the subscription.
+     *
+     * @param subId
+     *
+     * {@See TelephonyManager#SIM_STATE_UNKNOWN}
+     * {@See TelephonyManager#SIM_STATE_ABSENT}
+     * {@See TelephonyManager#SIM_STATE_PIN_REQUIRED}
+     * {@See TelephonyManager#SIM_STATE_PUK_REQUIRED}
+     * {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED}
+     * {@See TelephonyManager#SIM_STATE_READY}
+     * {@See TelephonyManager#SIM_STATE_NOT_READY}
+     * {@See TelephonyManager#SIM_STATE_PERM_DISABLED}
+     * {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR}
+     *
+     * {@hide}
+     */
+    public static int getSimStateForSubscriber(int subId) {
+        int simState;
+
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            simState = iSub.getSimStateForSubscriber(subId);
+        } catch (RemoteException ex) {
+            simState = TelephonyManager.SIM_STATE_UNKNOWN;
+        }
+        logd("getSimStateForSubscriber: simState=" + simState + " subId=" + subId);
+        return simState;
+    }
 }
 
index 1b54eec..751e11b 100644 (file)
@@ -1416,10 +1416,14 @@ public class TelephonyManager {
     //
     //
 
-    /** SIM card state: Unknown. Signifies that the SIM is in transition
-     *  between states. For example, when the user inputs the SIM pin
-     *  under PIN_REQUIRED state, a query for sim status returns
-     *  this state before turning to SIM_STATE_READY. */
+    /**
+     * SIM card state: Unknown. Signifies that the SIM is in transition
+     * between states. For example, when the user inputs the SIM pin
+     * under PIN_REQUIRED state, a query for sim status returns
+     * this state before turning to SIM_STATE_READY.
+     *
+     * These are the ordinal value of IccCardConstants.State.
+     */
     public static final int SIM_STATE_UNKNOWN = 0;
     /** SIM card state: no SIM card is available in the device */
     public static final int SIM_STATE_ABSENT = 1;
@@ -1427,14 +1431,22 @@ public class TelephonyManager {
     public static final int SIM_STATE_PIN_REQUIRED = 2;
     /** SIM card state: Locked: requires the user's SIM PUK to unlock */
     public static final int SIM_STATE_PUK_REQUIRED = 3;
-    /** SIM card state: Locked: requries a network PIN to unlock */
+    /** SIM card state: Locked: requires a network PIN to unlock */
     public static final int SIM_STATE_NETWORK_LOCKED = 4;
     /** SIM card state: Ready */
     public static final int SIM_STATE_READY = 5;
-    /** SIM card state: SIM Card Error, Sim Card is present but faulty
+    /** SIM card state: SIM Card is NOT READY
+     *@hide
+     */
+    public static final int SIM_STATE_NOT_READY = 6;
+    /** SIM card state: SIM Card Error, permanently disabled
+     *@hide
+     */
+    public static final int SIM_STATE_PERM_DISABLED = 7;
+    /** SIM card state: SIM Card Error, present but faulty
      *@hide
      */
-    public static final int SIM_STATE_CARD_IO_ERROR = 6;
+    public static final int SIM_STATE_CARD_IO_ERROR = 8;
 
     /**
      * @return true if a ICC card is present
@@ -1464,8 +1476,7 @@ public class TelephonyManager {
     }
 
     /**
-     * Returns a constant indicating the state of the
-     * device SIM card.
+     * Returns a constant indicating the state of the default SIM card.
      *
      * @see #SIM_STATE_UNKNOWN
      * @see #SIM_STATE_ABSENT
@@ -1473,6 +1484,8 @@ public class TelephonyManager {
      * @see #SIM_STATE_PUK_REQUIRED
      * @see #SIM_STATE_NETWORK_LOCKED
      * @see #SIM_STATE_READY
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_CARD_IO_ERROR
      */
     public int getSimState() {
@@ -1480,10 +1493,9 @@ public class TelephonyManager {
     }
 
     /**
-     * Returns a constant indicating the state of the
-     * device SIM card in a slot.
+     * Returns a constant indicating the state of the device SIM card in a slot.
      *
-     * @param slotId
+     * @param slotIdx
      *
      * @see #SIM_STATE_UNKNOWN
      * @see #SIM_STATE_ABSENT
@@ -1491,39 +1503,20 @@ public class TelephonyManager {
      * @see #SIM_STATE_PUK_REQUIRED
      * @see #SIM_STATE_NETWORK_LOCKED
      * @see #SIM_STATE_READY
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_PERM_DISABLED
+     * @see #SIM_STATE_CARD_IO_ERROR
      */
     /** {@hide} */
-    // FIXME the argument to pass is subId ??
-    public int getSimState(int slotId) {
-        int[] subId = SubscriptionManager.getSubId(slotId);
+    public int getSimState(int slotIdx) {
+        int[] subId = SubscriptionManager.getSubId(slotIdx);
         if (subId == null || subId.length == 0) {
-            return SIM_STATE_ABSENT;
-        }
-        // FIXME Do not use a property to determine SIM_STATE, call
-        // appropriate method on some object.
-        int phoneId = SubscriptionManager.getPhoneId(subId[0]);
-        String prop = getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_SIM_STATE, "");
-        if ("ABSENT".equals(prop)) {
-            return SIM_STATE_ABSENT;
-        }
-        else if ("PIN_REQUIRED".equals(prop)) {
-            return SIM_STATE_PIN_REQUIRED;
-        }
-        else if ("PUK_REQUIRED".equals(prop)) {
-            return SIM_STATE_PUK_REQUIRED;
-        }
-        else if ("NETWORK_LOCKED".equals(prop)) {
-            return SIM_STATE_NETWORK_LOCKED;
-        }
-        else if ("READY".equals(prop)) {
-            return SIM_STATE_READY;
-        }
-        else if ("CARD_IO_ERROR".equals(prop)) {
-            return SIM_STATE_CARD_IO_ERROR;
-        }
-        else {
+            Rlog.d(TAG, "getSimState:- empty subId return SIM_STATE_ABSENT");
             return SIM_STATE_UNKNOWN;
         }
+        int simState = SubscriptionManager.getSimStateForSubscriber(subId[0]);
+        Rlog.d(TAG, "getSimState: simState=" + simState + " slotIdx=" + slotIdx);
+        return simState;
     }
 
     /**
@@ -1535,7 +1528,7 @@ public class TelephonyManager {
      * @see #getSimState
      */
     public String getSimOperator() {
-        int subId = mSubscriptionManager.getDefaultDataSubId();
+        int subId = SubscriptionManager.getDefaultDataSubId();
         if (!SubscriptionManager.isUsableSubIdValue(subId)) {
             subId = SubscriptionManager.getDefaultSmsSubId();
             if (!SubscriptionManager.isUsableSubIdValue(subId)) {
@@ -1811,9 +1804,10 @@ public class TelephonyManager {
      *
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
+     * @return true if the operation was executed correctly.
      */
-    public void setLine1NumberForDisplay(String alphaTag, String number) {
-        setLine1NumberForDisplayForSubscriber(getDefaultSubscription(), alphaTag, number);
+    public boolean setLine1NumberForDisplay(String alphaTag, String number) {
+        return setLine1NumberForDisplayForSubscriber(getDefaultSubscription(), alphaTag, number);
     }
 
     /**
@@ -1828,14 +1822,16 @@ public class TelephonyManager {
      * @param subId the subscriber that the alphatag and dialing number belongs to.
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
+     * @return true if the operation was executed correctly.
      * @hide
      */
-    public void setLine1NumberForDisplayForSubscriber(int subId, String alphaTag, String number) {
+    public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag, String number) {
         try {
-            getITelephony().setLine1NumberForDisplayForSubscriber(subId, alphaTag, number);
+            return getITelephony().setLine1NumberForDisplayForSubscriber(subId, alphaTag, number);
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
         }
+        return false;
     }
 
     /**
@@ -2755,8 +2751,6 @@ public class TelephonyManager {
      * @hide
      */
     public static void setTelephonyProperty(int phoneId, String property, String value) {
-        Rlog.d(TAG, "setTelephonyProperty property: " + property + " phoneId: " + phoneId +
-                " value: " + value);
         String propVal = "";
         String p[] = null;
         String prop = SystemProperties.get(property);
@@ -2770,7 +2764,8 @@ public class TelephonyManager {
         }
 
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
-            Rlog.d(TAG, "setTelephonyProperty invalid phone id");
+            Rlog.d(TAG, "setTelephonyProperty: invalid phoneId=" + phoneId +
+                    " property=" + property + " value: " + value + " prop=" + prop);
             return;
         }
 
@@ -2789,13 +2784,15 @@ public class TelephonyManager {
             }
         }
 
-        // TODO: workaround for QC
-        if (property.length() > SystemProperties.PROP_NAME_MAX || propVal.length() > SystemProperties.PROP_VALUE_MAX) {
-            Rlog.d(TAG, "setTelephonyProperty length too long:" + property + ", " + propVal);
+        if (property.length() > SystemProperties.PROP_NAME_MAX
+                || propVal.length() > SystemProperties.PROP_VALUE_MAX) {
+            Rlog.d(TAG, "setTelephonyProperty: property to long phoneId=" + phoneId +
+                    " property=" + property + " value: " + value + " propVal=" + propVal);
             return;
         }
 
-        Rlog.d(TAG, "setTelephonyProperty property=" + property + " propVal=" + propVal);
+        Rlog.d(TAG, "setTelephonyProperty: success phoneId=" + phoneId +
+                " property=" + property + " value: " + value + " propVal=" + propVal);
         SystemProperties.set(property, propVal);
     }
 
@@ -2901,13 +2898,16 @@ public class TelephonyManager {
                 propVal = values[phoneId];
             }
         }
+        Rlog.d(TAG, "getTelephonyProperty: return propVal='" + propVal + "' phoneId=" + phoneId
+                + " property='" + property + "' defaultVal='" + defaultVal + "' prop=" + prop);
         return propVal == null ? defaultVal : propVal;
     }
 
     /** @hide */
     public int getSimCount() {
+        // FIXME Need to get it from Telephony Dev Controller when that gets implemented!
+        // and then this method shouldn't be used at all!
         if(isMultiSimEnabled()) {
-            //FIXME Need to get it from Telephony Devcontroller
             return 2;
         } else {
             return 1;
@@ -3084,6 +3084,26 @@ public class TelephonyManager {
     }
 
     /**
+     * Check TETHER_DUN_REQUIRED and TETHER_DUN_APN settings, net.tethering.noprovisioning
+     * SystemProperty, and config_tether_apndata to decide whether DUN APN is required for
+     * tethering.
+     *
+     * @return 0: Not required. 1: required. 2: Not set.
+     * @hide
+     */
+    public int getTetherApnRequired() {
+        try {
+            return getITelephony().getTetherApnRequired();
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "hasMatchedTetherApnSetting RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "hasMatchedTetherApnSetting NPE", ex);
+        }
+        return 2;
+    }
+
+
+    /**
      * Values used to return status for hasCarrierPrivileges call.
      */
     /** @hide */
index 2ab9648..2482249 100644 (file)
@@ -195,6 +195,10 @@ public class ImsReasonInfo implements Parcelable {
     public static final int CODE_USER_IGNORE = 503;
     // User declines an incoming call
     public static final int CODE_USER_DECLINE = 504;
+    // Device declines/ends a call due to low battery
+    public static final int CODE_LOW_BATTERY = 505;
+    // Device declines call due to blacklisted call ID
+    public static final int CODE_BLACKLISTED_CALL_ID = 506;
     // IMS -> Telephony
     // The call is terminated by the network or remote user
     public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
index b9cee42..30c48d7 100644 (file)
@@ -32,7 +32,7 @@ import android.os.Message;
  * {@hide}
  */
 interface IImsService {
-    int open(int serviceClass, in PendingIntent incomingCallIntent,
+    int open(int phoneId, int serviceClass, in PendingIntent incomingCallIntent,
             in IImsRegistrationListener listener);
     void close(int serviceId);
     boolean isConnected(int serviceId, int serviceType, int callType);
@@ -53,18 +53,18 @@ interface IImsService {
     /**
      * Config interface to get/set IMS service/capability parameters.
      */
-    IImsConfig getConfigInterface();
+    IImsConfig getConfigInterface(int phoneId);
 
     /**
      * Used for turning on IMS when its in OFF state.
      */
-    void turnOnIms();
+    void turnOnIms(int phoneId);
 
     /**
      * Used for turning off IMS when its in ON state.
      * When IMS is OFF, device will behave as CSFB'ed.
      */
-    void turnOffIms();
+    void turnOffIms(int phoneId);
 
     /**
      * ECBM interface for Emergency Callback mode mechanism.
index 0322499..49ac400 100644 (file)
@@ -61,40 +61,6 @@ interface IMms {
             in PendingIntent downloadedIntent);
 
     /**
-     * Update the status of a pending (send-by-IP) MMS message handled by the carrier app.
-     * If the carrier app fails to send this message, it may be resent via carrier network
-     * depending on the status code.
-     *
-     * The caller should have carrier privileges.
-     * @see android.telephony.TelephonyManager.hasCarrierPrivileges
-     *
-     * @param messageRef the reference number of the MMS message.
-     * @param pdu non-empty (contains the SendConf PDU) if the message was sent successfully,
-     *   otherwise, this param should be null.
-     * @param status send status. It can be Activity.RESULT_OK or one of the MMS error codes.
-     *   If status is Activity.RESULT_OK, the MMS was sent successfully.
-     *   If status is MMS_ERROR_RETRY, this message would be resent via carrier
-     *   network. The message will not be resent for other MMS error statuses.
-     */
-    void updateMmsSendStatus(int messageRef, in byte[] pdu, in int status);
-
-    /**
-     * Update the status of a pending (download-by-IP) MMS message handled by the carrier app.
-     * If the carrier app fails to download this message, it may be re-downloaded via carrier
-     * network depending on the status code.
-     *
-     * The caller should have carrier privileges.
-     * @see android.telephony.TelephonyManager.hasCarrierPrivileges
-     *
-     * @param messageRef the reference number of the MMS message.
-     * @param status download status.  It can be Activity.RESULT_OK or one of the MMS error codes.
-     *   If status is Activity.RESULT_OK, the MMS was downloaded successfully.
-     *   If status is MMS_ERROR_RETRY, this message would be re-downloaded via carrier
-     *   network. The message will not be re-downloaded for other MMS error statuses.
-     */
-    void updateMmsDownloadStatus(int messageRef, in int status);
-
-    /**
      * Get carrier-dependent configuration values.
      *
      * @param subId the SIM id
index 15fa340..6fdf121 100644 (file)
@@ -224,17 +224,6 @@ interface ISms {
     void injectSmsPdu(in byte[] pdu, String format, in PendingIntent receivedIntent);
 
     /**
-     * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
-     * This outbound message was handled by the carrier app. If the carrier app fails to send
-     * this message, it would be resent by PSTN.
-     *
-     * @param messageRef the reference number of the SMS message.
-     * @param success True if and only if the message was sent successfully. If its value is
-     *  false, this message should be resent via PSTN.
-     */
-    void updateSmsSendStatus(int messageRef, boolean success);
-
-    /**
      * Send a multi-part text based SMS.
      *
      * @param destinationAddress the address to send the message to
index ca82083..acbc0aa 100755 (executable)
@@ -164,4 +164,11 @@ interface ISub {
     void clearDefaultsForInactiveSubIds();
 
     int[] getActiveSubIdList();
+
+    /**
+     * Get the SIM state for the subscriber
+     * @return SIM state as the ordinal of IccCardConstants.State
+     */
+    int getSimStateForSubscriber(int subId);
+
 }
index adb3bc4..bf7f332 100644 (file)
@@ -657,6 +657,15 @@ interface ITelephony {
     int getPreferredNetworkType();
 
     /**
+     * Check TETHER_DUN_REQUIRED and TETHER_DUN_APN settings, net.tethering.noprovisioning
+     * SystemProperty, and config_tether_apndata to decide whether DUN APN is required for
+     * tethering.
+     *
+     * @return 0: Not required. 1: required. 2: Not set.
+     */
+    int getTetherApnRequired();
+
+    /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
@@ -740,8 +749,9 @@ interface ITelephony {
      * @param subId the subscriber that the alphatag and dialing number belongs to.
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
+     * @return true if the operation was executed correctly.
      */
-    void setLine1NumberForDisplayForSubscriber(int subId, String alphaTag, String number);
+    boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag, String number);
 
     /**
      * Returns the displayed dialing number string if it was set previously via
index 8029713..c1e2518 100644 (file)
  */
 package com.android.internal.telephony;
 
+import android.telephony.TelephonyManager;
+
 /**
  * {@hide}
  */
 public class IccCardConstants {
 
-    /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
+    /* The extra data for broadcasting intent INTENT_ICC_STATE_CHANGE */
     public static final String INTENT_KEY_ICC_STATE = "ss";
     /* UNKNOWN means the ICC state is unknown */
     public static final String INTENT_VALUE_ICC_UNKNOWN = "UNKNOWN";
@@ -32,13 +34,17 @@ public class IccCardConstants {
     static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR";
     /* LOCKED means ICC is locked by pin or by network */
     public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
+    //TODO: we can remove this state in the future if Bug 18489776 analysis
+    //#42's first race condition is resolved
+    /* INTERNAL LOCKED means ICC is locked by pin or by network */
+    public static final String INTENT_VALUE_ICC_INTERNAL_LOCKED = "INTERNAL_LOCKED";
     /* READY means ICC is ready to access */
     public static final String INTENT_VALUE_ICC_READY = "READY";
     /* IMSI means ICC IMSI is ready in property */
     public static final String INTENT_VALUE_ICC_IMSI = "IMSI";
     /* LOADED means all ICC records, including IMSI, are loaded */
     public static final String INTENT_VALUE_ICC_LOADED = "LOADED";
-    /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
+    /* The extra data for broadcasting intent INTENT_ICC_STATE_CHANGE */
     public static final String INTENT_KEY_LOCKED_REASON = "reason";
     /* PIN means ICC is locked on PIN1 */
     public static final String INTENT_VALUE_LOCKED_ON_PIN = "PIN";
@@ -56,17 +62,19 @@ public class IccCardConstants {
      * UNKNOWN is a transient state, for example, after user inputs ICC pin under
      * PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
      * turns to READY
+     *
+     * The ordinal values much match {@link TelephonyManager#SIM_STATE_UNKNOWN} ...
      */
     public enum State {
-        UNKNOWN,
-        ABSENT,
-        PIN_REQUIRED,
-        PUK_REQUIRED,
-        NETWORK_LOCKED,
-        READY,
-        NOT_READY,
-        PERM_DISABLED,
-        CARD_IO_ERROR;
+        UNKNOWN,        /** ordinal(0) == {@See TelephonyManager#SIM_STATE_UNKNOWN} */
+        ABSENT,         /** ordinal(1) == {@See TelephonyManager#SIM_STATE_ABSENT} */
+        PIN_REQUIRED,   /** ordinal(2) == {@See TelephonyManager#SIM_STATE_PIN_REQUIRED} */
+        PUK_REQUIRED,   /** ordinal(3) == {@See TelephonyManager#SIM_STATE_PUK_REQUIRED} */
+        NETWORK_LOCKED, /** ordinal(4) == {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED} */
+        READY,          /** ordinal(5) == {@See TelephonyManager#SIM_STATE_READY} */
+        NOT_READY,      /** ordinal(6) == {@See TelephonyManager#SIM_STATE_NOT_READY} */
+        PERM_DISABLED,  /** ordinal(7) == {@See TelephonyManager#SIM_STATE_PERM_DISABLED} */
+        CARD_IO_ERROR;  /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */
 
         public boolean isPinLocked() {
             return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
@@ -77,5 +85,21 @@ public class IccCardConstants {
                     || (this == NETWORK_LOCKED) || (this == READY)
                     || (this == PERM_DISABLED) || (this == CARD_IO_ERROR));
         }
+
+        public static State intToState(int state) throws IllegalArgumentException {
+            switch(state) {
+                case 0: return UNKNOWN;
+                case 1: return ABSENT;
+                case 2: return PIN_REQUIRED;
+                case 3: return PUK_REQUIRED;
+                case 4: return NETWORK_LOCKED;
+                case 5: return READY;
+                case 6: return NOT_READY;
+                case 7: return PERM_DISABLED;
+                case 8: return CARD_IO_ERROR;
+                default:
+                    throw new IllegalArgumentException();
+            }
+        }
     }
 }
index 513f622..33d40ad 100644 (file)
@@ -57,6 +57,8 @@
         </service>
         <receiver android:name="UserTarget">
         </receiver>
+        <service android:name="IsolatedService" android:isolatedProcess="true">
+        </service>
         <receiver android:name="StartEmpty" android:exported="true">
             <intent-filter>
                 <action android:name="com.example.START_EMPTY" />
index b065b88..4281c68 100644 (file)
@@ -62,6 +62,8 @@ public class ActivityTestMain extends Activity {
 
     ArrayList<ServiceConnection> mConnections = new ArrayList<ServiceConnection>();
 
+    ServiceConnection mIsolatedConnection;
+
     static final int MSG_SPAM = 1;
 
     final Handler mHandler = new Handler() {
@@ -207,6 +209,34 @@ public class ActivityTestMain extends Activity {
                 return true;
             }
         });
+        menu.add("Rebind Isolated!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+            @Override public boolean onMenuItemClick(MenuItem item) {
+                Intent intent = new Intent(ActivityTestMain.this, IsolatedService.class);
+                ServiceConnection conn = new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        Log.i(TAG, "Isolated service connected " + name + " " + service);
+                    }
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        Log.i(TAG, "Isolated service disconnected " + name);
+                    }
+                };
+                if (mIsolatedConnection != null) {
+                    Log.i(TAG, "Unbinding existing service: " + mIsolatedConnection);
+                    unbindService(mIsolatedConnection);
+                    mIsolatedConnection = null;
+                }
+                Log.i(TAG, "Binding new service: " + conn);
+                if (bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
+                    mIsolatedConnection = conn;
+                } else {
+                    Toast.makeText(ActivityTestMain.this, "Failed to bind",
+                            Toast.LENGTH_LONG).show();
+                }
+                return true;
+            }
+        });
         menu.add("Send!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
             @Override public boolean onMenuItemClick(MenuItem item) {
                 Intent intent = new Intent(ActivityTestMain.this, SingleUserReceiver.class);
@@ -417,6 +447,10 @@ public class ActivityTestMain extends Activity {
             unbindService(conn);
         }
         mConnections.clear();
+        if (mIsolatedConnection != null) {
+            unbindService(mIsolatedConnection);
+            mIsolatedConnection = null;
+        }
     }
 
     @Override
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/IsolatedService.java b/tests/ActivityTests/src/com/google/android/test/activity/IsolatedService.java
new file mode 100644 (file)
index 0000000..f3bf42e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.google.android.test.activity;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Log;
+
+public class IsolatedService extends Service {
+    Binder mBinder = new Binder();
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i("IsolatedService", "Service created in pid " + android.os.Process.myPid());
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.i("IsolatedService", "Service destroyed in pid " + android.os.Process.myPid());
+    }
+}
index 0d8db13..e3a0200 100644 (file)
@@ -2985,17 +2985,26 @@ status_t
 writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
 {
     status_t err;
+    const char* kClass = "class";
+    const char* kFragment = "fragment";
+    const String8 kTransition("transition");
+    const String8 kTransitionPrefix("transition-");
 
     // tag:attribute pairs that should be checked in layout files.
     KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs;
-    addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
-    addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
-    addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
+    addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, kClass);
+    addTagAttrPair(&kLayoutTagAttrPairs, kFragment, NULL, kClass);
+    addTagAttrPair(&kLayoutTagAttrPairs, kFragment, RESOURCES_ANDROID_NAMESPACE, "name");
 
     // tag:attribute pairs that should be checked in xml files.
     KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs;
-    addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
-    addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
+    addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, kFragment);
+    addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, kFragment);
+
+    // tag:attribute pairs that should be checked in transition files.
+    KeyedVector<String8, Vector<NamespaceAttributePair> > kTransitionTagAttrPairs;
+    addTagAttrPair(&kTransitionTagAttrPairs, kTransition.string(), NULL, kClass);
+    addTagAttrPair(&kTransitionTagAttrPairs, "pathMotion", NULL, kClass);
 
     const Vector<sp<AaptDir> >& dirs = assets->resDirs();
     const size_t K = dirs.size();
@@ -3014,6 +3023,9 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
         } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
             startTags.add(String8("menu"));
             tagAttrPairs = NULL;
+        } else if (dirName == kTransition || (strncmp(dirName.string(), kTransitionPrefix.string(),
+                        kTransitionPrefix.size()) == 0)) {
+            tagAttrPairs = &kTransitionTagAttrPairs;
         } else {
             continue;
         }
index bdc6586..6d03311 100644 (file)
@@ -12,6 +12,7 @@
 #include "ResourceIdCache.h"
 #include "SdkConstants.h"
 
+#include <algorithm>
 #include <androidfw/ResourceTypes.h>
 #include <utils/ByteOrder.h>
 #include <utils/TypeHelpers.h>
@@ -19,6 +20,8 @@
 
 #define NOISY(x) //x
 
+static const char* kAttrPrivateType = "^attr-private";
+
 status_t compileXmlFile(const Bundle* bundle,
                         const sp<AaptAssets>& assets,
                         const String16& resourceName,
@@ -2131,8 +2134,16 @@ uint32_t ResourceTable::getResId(const String16& package,
     if (p == NULL) return 0;
     sp<Type> t = p->getTypes().valueFor(type);
     if (t == NULL) return 0;
-    sp<ConfigList> c =  t->getConfigs().valueFor(name);
-    if (c == NULL) return 0;
+    sp<ConfigList> c = t->getConfigs().valueFor(name);
+    if (c == NULL) {
+        if (type != String16("attr")) {
+            return 0;
+        }
+        t = p->getTypes().valueFor(String16(kAttrPrivateType));
+        if (t == NULL) return 0;
+        c = t->getConfigs().valueFor(name);
+        if (c == NULL) return 0;
+    }
     int32_t ei = c->getEntryIndex();
     if (ei < 0) return 0;
 
@@ -2266,7 +2277,15 @@ uint32_t ResourceTable::getCustomResource(
     sp<Type> t = p->getTypes().valueFor(type);
     if (t == NULL) return 0;
     sp<ConfigList> c =  t->getConfigs().valueFor(name);
-    if (c == NULL) return 0;
+    if (c == NULL) {
+        if (type != String16("attr")) {
+            return 0;
+        }
+        t = p->getTypes().valueFor(String16(kAttrPrivateType));
+        if (t == NULL) return 0;
+        c = t->getConfigs().valueFor(name);
+        if (c == NULL) return 0;
+    }
     int32_t ei = c->getEntryIndex();
     if (ei < 0) return 0;
     return getResId(p, t, ei);
@@ -2470,6 +2489,10 @@ status_t ResourceTable::assignResourceIds()
             continue;
         }
 
+        if (mPackageType == System) {
+            p->movePrivateAttrs();
+        }
+
         // This has no sense for packages being built as AppFeature (aka with a non-zero offset).
         status_t err = p->applyPublicTypeOrder();
         if (err != NO_ERROR && firstError == NO_ERROR) {
@@ -2540,15 +2563,20 @@ status_t ResourceTable::assignResourceIds()
             }
         }
 
+
         // Assign resource IDs to keys in bags...
         for (size_t ti = 0; ti < typeCount; ti++) {
             sp<Type> t = p->getOrderedTypes().itemAt(ti);
             if (t == NULL) {
                 continue;
             }
+
             const size_t N = t->getOrderedConfigs().size();
             for (size_t ci=0; ci<N; ci++) {
                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
+                if (c == NULL) {
+                    continue;
+                }
                 //printf("Ordered config #%d: %p\n", ci, c.get());
                 const size_t N = c->getEntries().size();
                 for (size_t ei=0; ei<N; ei++) {
@@ -2586,9 +2614,15 @@ status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
             if (t == NULL) {
                 continue;
             }
+
             const size_t N = t->getOrderedConfigs().size();
-            sp<AaptSymbols> typeSymbols =
-                    outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
+            sp<AaptSymbols> typeSymbols;
+            if (t->getName() == String16(kAttrPrivateType)) {
+                typeSymbols = outSymbols->addNestedSymbol(String8("attr"), t->getPos());
+            } else {
+                typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
+            }
+
             if (typeSymbols == NULL) {
                 return UNKNOWN_ERROR;
             }
@@ -2954,6 +2988,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>&
 
                 for (size_t ei=0; ei<N; ei++) {
                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
+                    if (cl == NULL) {
+                        continue;
+                    }
+
                     if (cl->getPublic()) {
                         typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
                     }
@@ -2984,12 +3022,13 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>&
 
             // We need to write one type chunk for each configuration for
             // which we have entries in this type.
-            const size_t NC = t->getUniqueConfigs().size();
+            const SortedVector<ConfigDescription> uniqueConfigs(t->getUniqueConfigs());
+            const size_t NC = uniqueConfigs.size();
             
             const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
             
             for (size_t ci=0; ci<NC; ci++) {
-                ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
+                const ConfigDescription& config = uniqueConfigs[ci];
 
                 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
                      "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
@@ -3061,7 +3100,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>&
                 // Build the entries inside of this type.
                 for (size_t ei=0; ei<N; ei++) {
                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
-                    sp<Entry> e = cl->getEntries().valueFor(config);
+                    sp<Entry> e = NULL;
+                    if (cl != NULL) {
+                        e = cl->getEntries().valueFor(config);
+                    }
 
                     // Set the offset for this entry in its type.
                     uint32_t* index = (uint32_t*)
@@ -3096,9 +3138,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>&
                 for (size_t i = 0; i < N; ++i) {
                     if (!validResources[i]) {
                         sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
-                        fprintf(stderr, "%s: no entries written for %s/%s (0x%08x)\n", log_prefix,
-                                String8(typeName).string(), String8(c->getName()).string(),
-                                Res_MAKEID(p->getAssignedId() - 1, ti, i));
+                        if (c != NULL) {
+                            fprintf(stderr, "%s: no entries written for %s/%s (0x%08x)\n", log_prefix,
+                                    String8(typeName).string(), String8(c->getName()).string(),
+                                    Res_MAKEID(p->getAssignedId() - 1, ti, i));
+                        }
                         missing_entry = true;
                     }
                 }
@@ -3807,11 +3851,45 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
         */
     }
     
-    mUniqueConfigs.add(cdesc);
-    
     return e;
 }
 
+sp<ResourceTable::ConfigList> ResourceTable::Type::removeEntry(const String16& entry) {
+    ssize_t idx = mConfigs.indexOfKey(entry);
+    if (idx < 0) {
+        return NULL;
+    }
+
+    sp<ConfigList> removed = mConfigs.valueAt(idx);
+    mConfigs.removeItemsAt(idx);
+
+    Vector<sp<ConfigList> >::iterator iter = std::find(
+            mOrderedConfigs.begin(), mOrderedConfigs.end(), removed);
+    if (iter != mOrderedConfigs.end()) {
+        mOrderedConfigs.erase(iter);
+    }
+
+    mPublic.removeItem(entry);
+    return removed;
+}
+
+SortedVector<ConfigDescription> ResourceTable::Type::getUniqueConfigs() const {
+    SortedVector<ConfigDescription> unique;
+    const size_t entryCount = mOrderedConfigs.size();
+    for (size_t i = 0; i < entryCount; i++) {
+        if (mOrderedConfigs[i] == NULL) {
+            continue;
+        }
+        const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configs =
+                mOrderedConfigs[i]->getEntries();
+        const size_t configCount = configs.size();
+        for (size_t j = 0; j < configCount; j++) {
+            unique.add(configs.keyAt(j));
+        }
+    }
+    return unique;
+}
+
 status_t ResourceTable::Type::applyPublicEntryOrder()
 {
     size_t N = mOrderedConfigs.size();
@@ -3838,11 +3916,10 @@ status_t ResourceTable::Type::applyPublicEntryOrder()
             //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
             if (e->getName() == name) {
                 if (idx >= (int32_t)mOrderedConfigs.size()) {
-                    p.sourcePos.error("Public entry identifier 0x%x entry index "
-                            "is larger than available symbols (index %d, total symbols %d).\n",
-                            p.ident, idx, mOrderedConfigs.size());
-                    hasError = true;
-                } else if (mOrderedConfigs.itemAt(idx) == NULL) {
+                    mOrderedConfigs.resize(idx + 1);
+                }
+
+                if (mOrderedConfigs.itemAt(idx) == NULL) {
                     e->setPublic(true);
                     e->setPublicSourcePos(p.sourcePos);
                     mOrderedConfigs.replaceAt(e, idx);
@@ -4018,6 +4095,61 @@ status_t ResourceTable::Package::applyPublicTypeOrder()
     return NO_ERROR;
 }
 
+void ResourceTable::Package::movePrivateAttrs() {
+    sp<Type> attr = mTypes.valueFor(String16("attr"));
+    if (attr == NULL) {
+        // Nothing to do.
+        return;
+    }
+
+    Vector<sp<ConfigList> > privateAttrs;
+
+    bool hasPublic = false;
+    const Vector<sp<ConfigList> >& configs = attr->getOrderedConfigs();
+    const size_t configCount = configs.size();
+    for (size_t i = 0; i < configCount; i++) {
+        if (configs[i] == NULL) {
+            continue;
+        }
+
+        if (attr->isPublic(configs[i]->getName())) {
+            hasPublic = true;
+        } else {
+            privateAttrs.add(configs[i]);
+        }
+    }
+
+    // Only if we have public attributes do we create a separate type for
+    // private attributes.
+    if (!hasPublic) {
+        return;
+    }
+
+    // Create a new type for private attributes.
+    sp<Type> privateAttrType = getType(String16(kAttrPrivateType), SourcePos());
+
+    const size_t privateAttrCount = privateAttrs.size();
+    for (size_t i = 0; i < privateAttrCount; i++) {
+        const sp<ConfigList>& cl = privateAttrs[i];
+
+        // Remove the private attributes from their current type.
+        attr->removeEntry(cl->getName());
+
+        // Add it to the new type.
+        const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries = cl->getEntries();
+        const size_t entryCount = entries.size();
+        for (size_t j = 0; j < entryCount; j++) {
+            const sp<Entry>& oldEntry = entries[j];
+            sp<Entry> entry = privateAttrType->getEntry(
+                    cl->getName(), oldEntry->getPos(), &entries.keyAt(j));
+            *entry = *oldEntry;
+        }
+
+        // Move the symbols to the new type.
+
+    }
+}
+
 sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
 {
     if (package != mAssetsPackage) {
index db392c8..81590bc 100644 (file)
@@ -470,6 +470,14 @@ public:
                            bool overlay = false,
                            bool autoAddOverlay = false);
 
+        bool isPublic(const String16& entry) const {
+            return mPublic.indexOfKey(entry) >= 0;
+        }
+
+        sp<ConfigList> removeEntry(const String16& entry);
+
+        SortedVector<ConfigDescription> getUniqueConfigs() const;
+
         const SourcePos& getFirstPublicSourcePos() const { return *mFirstPublicSourcePos; }
 
         int32_t getPublicIndex() const { return mPublicIndex; }
@@ -479,19 +487,16 @@ public:
 
         status_t applyPublicEntryOrder();
 
-        const SortedVector<ConfigDescription>& getUniqueConfigs() const { return mUniqueConfigs; }
-        
         const DefaultKeyedVector<String16, sp<ConfigList> >& getConfigs() const { return mConfigs; }
         const Vector<sp<ConfigList> >& getOrderedConfigs() const { return mOrderedConfigs; }
-
         const SortedVector<String16>& getCanAddEntries() const { return mCanAddEntries; }
         
         const SourcePos& getPos() const { return mPos; }
+
     private:
         String16 mName;
         SourcePos* mFirstPublicSourcePos;
         DefaultKeyedVector<String16, Public> mPublic;
-        SortedVector<ConfigDescription> mUniqueConfigs;
         DefaultKeyedVector<String16, sp<ConfigList> > mConfigs;
         Vector<sp<ConfigList> > mOrderedConfigs;
         SortedVector<String16> mCanAddEntries;
@@ -527,6 +532,8 @@ public:
         const DefaultKeyedVector<String16, sp<Type> >& getTypes() const { return mTypes; }
         const Vector<sp<Type> >& getOrderedTypes() const { return mOrderedTypes; }
 
+        void movePrivateAttrs();
+
     private:
         status_t setStrings(const sp<AaptFile>& data,
                             ResStringPool* strings,
index 93814b2..c41a4ee 100644 (file)
@@ -23,7 +23,7 @@ import android.content.res.AssetManager;
 public class BridgeAssetManager extends AssetManager {
 
     /**
-     * This initializes the static field {@link AssetManager#mSystem} which is used
+     * This initializes the static field {@link AssetManager#sSystem} which is used
      * by methods who get a global asset manager using {@link AssetManager#getSystem()}.
      * <p/>
      * They will end up using our bridge asset manager.
index 4993262..ab79664 100644 (file)
@@ -282,7 +282,7 @@ public class FontFamily_Delegate {
     @LayoutlibDelegate
     /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "FontFamily.addFontFromAsset is not supported.", null, null);
+                "Typeface.createFromAsset is not supported.", null, null);
         return false;
     }
 
diff --git a/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java b/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java
deleted file mode 100644 (file)
index ed8498f..0000000
+++ /dev/null
@@ -1,75 +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.text.format;
-
-import java.util.Calendar;
-import java.util.TimeZone;
-import java.util.UnknownFormatConversionException;
-import java.util.regex.Pattern;
-
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate used to provide new implementation for native methods of {@link Time}
- *
- * Through the layoutlib_create tool, some native methods of Time have been replaced by calls to
- * methods of the same name in this delegate class.
- */
-public class Time_Delegate {
-
-    // Regex to match odd number of '%'.
-    private static final Pattern p = Pattern.compile("(?<!%)(%%)*%(?!%)");
-
-    // Format used by toString()
-    private static final String FORMAT = "%1$tY%1$tm%1$tdT%1$tH%1$tM%1$tS<%1$tZ>";
-
-    // ---- private helper methods ----
-
-    private static Calendar timeToCalendar(Time time) {
-        Calendar calendar = getCalendarInstance(time);
-        calendar.set(time.year, time.month, time.monthDay, time.hour, time.minute, time.second);
-        return calendar;
-    }
-
-    private static void calendarToTime(Calendar c, Time time) {
-        time.timezone = c.getTimeZone().getID();
-        time.set(c.get(Calendar.SECOND), c.get(Calendar.MINUTE), c.get(Calendar.HOUR_OF_DAY),
-                c.get(Calendar.DATE), c.get(Calendar.MONTH), c.get(Calendar.YEAR));
-        time.weekDay = c.get(Calendar.DAY_OF_WEEK);
-        time.yearDay = c.get(Calendar.DAY_OF_YEAR);
-        time.isDst = c.getTimeZone().inDaylightTime(c.getTime()) ? 1 : 0;
-        // gmtoff is in seconds and TimeZone.getOffset() returns milliseconds.
-        time.gmtoff = c.getTimeZone().getOffset(c.getTimeInMillis()) / DateUtils.SECOND_IN_MILLIS;
-    }
-
-    /**
-     * Return a calendar instance with the correct timezone.
-     *
-     * @param time Time to obtain the timezone from.
-     */
-    private static Calendar getCalendarInstance(Time time) {
-        // TODO: Check platform code to make sure the behavior is same for null/invalid timezone.
-        if (time == null || time.timezone == null) {
-            // Default to local timezone.
-            return Calendar.getInstance();
-        }
-        // If timezone is invalid, use GMT.
-        return Calendar.getInstance(TimeZone.getTimeZone(time.timezone));
-    }
-}
index ec78712..0af04ec 100644 (file)
@@ -36,6 +36,7 @@ import com.android.tools.layoutlib.create.MethodAdapter;
 import com.android.tools.layoutlib.create.OverrideMethod;
 import com.android.util.Pair;
 import com.ibm.icu.util.ULocale;
+import libcore.io.MemoryMappedFile_Delegate;
 
 import android.content.res.BridgeAssetManager;
 import android.graphics.Bitmap;
@@ -252,6 +253,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
 
         // load the fonts.
         FontFamily_Delegate.setFontLocation(fontLocation.getAbsolutePath());
+        MemoryMappedFile_Delegate.setDataDir(fontLocation.getAbsoluteFile().getParentFile());
 
         // now parse com.android.internal.R (and only this one as android.R is a subset of
         // the internal version), and put the content in the maps.
index e1f24ce..e74debb 100644 (file)
@@ -483,7 +483,7 @@ public final class BridgeContext extends Context {
             // In some cases, style may not be a dynamic id, so we do a full search.
             ResourceReference ref = resolveId(resid);
             if (ref != null) {
-                mRenderResources.getStyle(ref.getName(), ref.isFramework());
+                style = mRenderResources.getStyle(ref.getName(), ref.isFramework());
             }
         }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/libcore/io/BridgeBufferIterator.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/libcore/io/BridgeBufferIterator.java
new file mode 100644 (file)
index 0000000..7e361a1
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.layoutlib.bridge.libcore.io;
+
+import java.nio.ByteBuffer;
+
+import libcore.io.BufferIterator;
+
+/**
+ * Provides an implementation of {@link BufferIterator} over a {@link ByteBuffer}.
+ */
+public class BridgeBufferIterator extends BufferIterator {
+
+    private final long mSize;
+    private final ByteBuffer mByteBuffer;
+
+    public BridgeBufferIterator(long size, ByteBuffer buffer) {
+        mSize = size;
+        mByteBuffer = buffer;
+    }
+
+    @Override
+    public void seek(int offset) {
+        assert offset <= mSize;
+        mByteBuffer.position(offset);
+    }
+
+    @Override
+    public void skip(int byteCount) {
+        int newPosition = mByteBuffer.position() + byteCount;
+        assert newPosition <= mSize;
+        mByteBuffer.position(newPosition);
+    }
+
+    @Override
+    public void readByteArray(byte[] dst, int dstOffset, int byteCount) {
+        assert dst.length >= dstOffset + byteCount;
+        mByteBuffer.get(dst, dstOffset, byteCount);
+    }
+
+    @Override
+    public byte readByte() {
+        return mByteBuffer.get();
+    }
+
+    @Override
+    public int readInt() {
+        return mByteBuffer.getInt();
+    }
+
+    @Override
+    public void readIntArray(int[] dst, int dstOffset, int intCount) {
+        while (--intCount >= 0) {
+            dst[dstOffset++] = mByteBuffer.getInt();
+        }
+    }
+
+    @Override
+    public short readShort() {
+        return mByteBuffer.getShort();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/libcore/io/MemoryMappedFile_Delegate.java b/tools/layoutlib/bridge/src/libcore/io/MemoryMappedFile_Delegate.java
new file mode 100644 (file)
index 0000000..723d5c4
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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 libcore.io;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.libcore.io.BridgeBufferIterator;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.system.ErrnoException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteOrder;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel.MapMode;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Delegate used to provide alternate implementation of select methods of {@link MemoryMappedFile}.
+ */
+public class MemoryMappedFile_Delegate {
+
+    private static final DelegateManager<MemoryMappedFile_Delegate> sManager = new
+            DelegateManager<MemoryMappedFile_Delegate>(MemoryMappedFile_Delegate.class);
+
+    private static final Map<MemoryMappedFile, Long> sMemoryMappedFileMap =
+            new HashMap<MemoryMappedFile, Long>();
+
+    private final MappedByteBuffer mMappedByteBuffer;
+    private final long mSize;
+
+    /** Path on the target device where the data file is available. */
+    private static final String TARGET_PATH = System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo";
+    /** Path on the host (inside the SDK) where the data files are available. */
+    private static File sRootPath;
+
+    @LayoutlibDelegate
+    static MemoryMappedFile mmapRO(String path) throws ErrnoException {
+        if (!path.startsWith(TARGET_PATH)) {
+            throw new ErrnoException("Custom timezone data files are not supported.", 1);
+        }
+        if (sRootPath == null) {
+            throw new ErrnoException("Bridge has not been initialized properly.", 1);
+        }
+        path = path.substring(TARGET_PATH.length());
+        try {
+            File f = new File(sRootPath, path);
+            if (!f.exists()) {
+                throw new ErrnoException("File not found: " + f.getPath(), 1);
+            }
+            RandomAccessFile file = new RandomAccessFile(f, "r");
+            try {
+                long size = file.length();
+                MemoryMappedFile_Delegate newDelegate = new MemoryMappedFile_Delegate(file);
+                long filePointer = file.getFilePointer();
+                MemoryMappedFile mmFile = new MemoryMappedFile(filePointer, size);
+                long delegateIndex = sManager.addNewDelegate(newDelegate);
+                sMemoryMappedFileMap.put(mmFile, delegateIndex);
+                return mmFile;
+            } finally {
+                file.close();
+            }
+        } catch (IOException e) {
+            throw new ErrnoException("mmapRO", 1, e);
+        }
+    }
+
+    @LayoutlibDelegate
+    static void close(MemoryMappedFile thisFile) throws ErrnoException {
+        Long index = sMemoryMappedFileMap.get(thisFile);
+        if (index != null) {
+            sMemoryMappedFileMap.remove(thisFile);
+            sManager.removeJavaReferenceFor(index);
+        }
+    }
+
+    @LayoutlibDelegate
+    static BufferIterator bigEndianIterator(MemoryMappedFile file) {
+        MemoryMappedFile_Delegate delegate = getDelegate(file);
+        return new BridgeBufferIterator(delegate.mSize, delegate.mMappedByteBuffer.duplicate());
+    }
+
+    // TODO: implement littleEndianIterator()
+
+    public MemoryMappedFile_Delegate(RandomAccessFile file) throws IOException {
+        mSize = file.length();
+        // It's weird that map() takes size as long, but returns MappedByteBuffer which uses an int
+        // to store the marker to the position.
+        mMappedByteBuffer = file.getChannel().map(MapMode.READ_ONLY, 0, mSize);
+        assert mMappedByteBuffer.order() == ByteOrder.BIG_ENDIAN;
+    }
+
+    public static void setDataDir(File path) {
+        sRootPath = path;
+    }
+
+    private static MemoryMappedFile_Delegate getDelegate(MemoryMappedFile file) {
+        Long index = sMemoryMappedFileMap.get(file);
+        return index == null ? null : sManager.getDelegate(index);
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/libcore/util/ZoneInfo_WallTime_Delegate.java b/tools/layoutlib/bridge/src/libcore/util/ZoneInfo_WallTime_Delegate.java
new file mode 100644 (file)
index 0000000..f29c5c0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 libcore.util;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.util.GregorianCalendar;
+
+/**
+ * Delegate used to provide alternate implementation of select methods in {@link ZoneInfo.WallTime}
+ */
+public class ZoneInfo_WallTime_Delegate {
+
+    @LayoutlibDelegate
+    static GregorianCalendar createGregorianCalendar() {
+        return new GregorianCalendar();
+    }
+}
index 4e6f456..5a979e8 100644 (file)
@@ -21,6 +21,7 @@ import com.android.tools.layoutlib.java.AutoCloseable;
 import com.android.tools.layoutlib.java.Charsets;
 import com.android.tools.layoutlib.java.IntegralToString;
 import com.android.tools.layoutlib.java.Objects;
+import com.android.tools.layoutlib.java.System_Delegate;
 import com.android.tools.layoutlib.java.UnsafeByteSequence;
 
 import java.util.Arrays;
@@ -131,6 +132,7 @@ public final class CreateInfo implements ICreateInfo {
             IntegralToString.class,
             UnsafeByteSequence.class,
             Charsets.class,
+            System_Delegate.class,
         };
 
     /**
@@ -170,7 +172,11 @@ public final class CreateInfo implements ICreateInfo {
         "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
         "com.android.internal.util.XmlUtils#convertValueToInt",
         "com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
-        "dalvik.system.VMRuntime#newUnpaddedArray"
+        "dalvik.system.VMRuntime#newUnpaddedArray",
+        "libcore.io.MemoryMappedFile#mmapRO",
+        "libcore.io.MemoryMappedFile#close",
+        "libcore.io.MemoryMappedFile#bigEndianIterator",
+        "libcore.util.ZoneInfo$WallTime#createGregorianCalendar",
     };
 
     /**
@@ -261,6 +267,7 @@ public final class CreateInfo implements ICreateInfo {
             "java.nio.charset.Charsets",                       "com.android.tools.layoutlib.java.Charsets",
             "java.lang.IntegralToString",                      "com.android.tools.layoutlib.java.IntegralToString",
             "java.lang.UnsafeByteSequence",                    "com.android.tools.layoutlib.java.UnsafeByteSequence",
+            "java.nio.charset.StandardCharsets",               "com.android.tools.layoutlib.java.Charsets",
         };
 
     private final static String[] EXCLUDED_CLASSES =
index 9c6fbac..1e2623f 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.android.tools.layoutlib.create;
 
+import com.android.tools.layoutlib.java.System_Delegate;
+
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -47,24 +49,25 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
     private static final String ANDROID_LOCALE_CLASS =
             "com/android/layoutlib/bridge/android/AndroidLocale";
 
-    private static final String JAVA_LOCALE_CLASS = "java/util/Locale";
+    private static final String JAVA_LOCALE_CLASS = Type.getInternalName(java.util.Locale.class);
     private static final Type STRING = Type.getType(String.class);
 
+    private static final String JAVA_LANG_SYSTEM = Type.getInternalName(System.class);
+
     // Static initialization block to initialize METHOD_REPLACERS.
     static {
         // Case 1: java.lang.System.arraycopy()
         METHOD_REPLACERS.add(new MethodReplacer() {
             @Override
             public boolean isNeeded(String owner, String name, String desc) {
-                return "java/lang/System".equals(owner) && "arraycopy".equals(name) &&
+                return JAVA_LANG_SYSTEM.equals(owner) && "arraycopy".equals(name) &&
                         ARRAYCOPY_DESCRIPTORS.contains(desc);
             }
 
             @Override
-            public void replace(int[] opcode, String[] methodInformation) {
-                assert methodInformation.length == 3 && isNeeded(methodInformation[0], methodInformation[1], methodInformation[2])
-                        && opcode.length == 1;
-                methodInformation[2] = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
+            public void replace(MethodInformation mi) {
+                assert isNeeded(mi.owner, mi.name, mi.desc);
+                mi.desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
             }
         });
 
@@ -80,12 +83,11 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
             }
 
             @Override
-            public void replace(int[] opcode, String[] methodInformation) {
-                assert methodInformation.length == 3 && isNeeded(methodInformation[0], methodInformation[1], methodInformation[2])
-                        && opcode.length == 1;
-                opcode[0] = Opcodes.INVOKESTATIC;
-                methodInformation[0] = ANDROID_LOCALE_CLASS;
-                methodInformation[2] = LOCALE_TO_STRING;
+            public void replace(MethodInformation mi) {
+                assert isNeeded(mi.owner, mi.name, mi.desc);
+                mi.opcode = Opcodes.INVOKESTATIC;
+                mi.owner = ANDROID_LOCALE_CLASS;
+                mi.desc = LOCALE_TO_STRING;
             }
         });
 
@@ -104,10 +106,27 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
             }
 
             @Override
-            public void replace(int[] opcode, String[] methodInformation) {
-                assert methodInformation.length == 3 && isNeeded(methodInformation[0], methodInformation[1], methodInformation[2])
-                        && opcode.length == 1;
-                methodInformation[0] = ANDROID_LOCALE_CLASS;
+            public void replace(MethodInformation mi) {
+                assert isNeeded(mi.owner, mi.name, mi.desc);
+                mi.owner = ANDROID_LOCALE_CLASS;
+            }
+        });
+
+        // Case 4: java.lang.System.log?()
+        METHOD_REPLACERS.add(new MethodReplacer() {
+            @Override
+            public boolean isNeeded(String owner, String name, String desc) {
+                return JAVA_LANG_SYSTEM.equals(owner) && name.length() == 4
+                        && name.startsWith("log");
+            }
+
+            @Override
+            public void replace(MethodInformation mi) {
+                assert isNeeded(mi.owner, mi.name, mi.desc);
+                assert mi.desc.equals("(Ljava/lang/String;Ljava/lang/Throwable;)V")
+                        || mi.desc.equals("(Ljava/lang/String;)V");
+                mi.name = "log";
+                mi.owner = Type.getInternalName(System_Delegate.class);
             }
         });
     }
@@ -141,13 +160,12 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
         public void visitMethodInsn(int opcode, String owner, String name, String desc) {
             for (MethodReplacer replacer : METHOD_REPLACERS) {
                 if (replacer.isNeeded(owner, name, desc)) {
-                    String[] methodInformation = {owner, name, desc};
-                    int[] opcodeOut = {opcode};
-                    replacer.replace(opcodeOut, methodInformation);
-                    opcode = opcodeOut[0];
-                    owner = methodInformation[0];
-                    name = methodInformation[1];
-                    desc = methodInformation[2];
+                    MethodInformation mi = new MethodInformation(opcode, owner, name, desc);
+                    replacer.replace(mi);
+                    opcode = mi.opcode;
+                    owner = mi.owner;
+                    name = mi.name;
+                    desc = mi.desc;
                     break;
                 }
             }
@@ -155,19 +173,28 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
         }
     }
 
+    private static class MethodInformation {
+        public int opcode;
+        public String owner;
+        public String name;
+        public String desc;
+
+        public MethodInformation(int opcode, String owner, String name, String desc) {
+            this.opcode = opcode;
+            this.owner = owner;
+            this.name = name;
+            this.desc = desc;
+        }
+    }
+
     private interface MethodReplacer {
         public boolean isNeeded(String owner, String name, String desc);
 
         /**
-         * This method must update the arrays with the new values of the method attributes -
+         * Updates the MethodInformation with the new values of the method attributes -
          * opcode, owner, name and desc.
-         * @param opcode This array should contain the original value of the opcode. The value is
-         *               modified by the method if needed. The size of the array must be 1.
          *
-         * @param methodInformation This array should contain the original values of the method
-         *                          attributes - owner, name and desc in that order. The values
-         *                          may be modified as needed. The size of the array must be 3.
          */
-        public void replace(int[] opcode, String[] methodInformation);
+        public void replace(MethodInformation mi);
     }
 }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java
new file mode 100644 (file)
index 0000000..613c8d9
--- /dev/null
@@ -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.
+ */
+
+package com.android.tools.layoutlib.java;
+
+import com.android.tools.layoutlib.create.ReplaceMethodCallsAdapter;
+
+/**
+ * Provides dummy implementation of methods that don't exist on the host VM.
+ *
+ * @see ReplaceMethodCallsAdapter
+ */
+public class System_Delegate {
+    public static void log(String message) {
+        // ignore.
+    }
+
+    public static void log(String message, Throwable th) {
+        // ignore.
+    }
+}