OSDN Git Service

Merge "Map KEY_VOICECOMMAND to KEYCODE_VOICE_ASSIST" into lmp-dev
authorMichael Wright <michaelwr@google.com>
Mon, 8 Sep 2014 23:22:27 +0000 (23:22 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Mon, 8 Sep 2014 23:22:28 +0000 (23:22 +0000)
187 files changed:
Android.mk
api/current.txt
api/removed.txt
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
core/java/android/animation/AnimatorSet.java
core/java/android/app/ActivityManager.java
core/java/android/app/ActivityManagerNative.java
core/java/android/app/ActivityOptions.java
core/java/android/app/ActivityThread.java
core/java/android/app/ActivityTransitionCoordinator.java
core/java/android/app/ApplicationPackageManager.java
core/java/android/app/ApplicationThreadNative.java
core/java/android/app/EnterTransitionCoordinator.java
core/java/android/app/ExitTransitionCoordinator.java
core/java/android/app/IActivityManager.java
core/java/android/app/IApplicationThread.java
core/java/android/app/Notification.java
core/java/android/app/admin/DevicePolicyManager.java
core/java/android/content/pm/ActivityInfo.java
core/java/android/content/pm/IPackageInstallerCallback.aidl
core/java/android/content/pm/LauncherApps.java
core/java/android/content/pm/PackageInstaller.java
core/java/android/content/pm/PackageManager.java
core/java/android/content/pm/PackageParser.java
core/java/android/content/res/AssetManager.java
core/java/android/content/res/Resources.java
core/java/android/hardware/soundtrigger/SoundTrigger.java
core/java/android/os/BatteryStats.java
core/java/android/os/FileBridge.java
core/java/android/os/Message.java
core/java/android/print/PrintManager.java
core/java/android/provider/CallLog.java
core/java/android/service/notification/INotificationListener.aidl
core/java/android/service/notification/IStatusBarNotificationHolder.aidl [new file with mode: 0644]
core/java/android/service/notification/NotificationListenerService.java
core/java/android/service/voice/AlwaysOnHotwordDetector.java
core/java/android/speech/tts/TextToSpeech.java
core/java/android/speech/tts/TextToSpeechService.java
core/java/android/speech/tts/Voice.java
core/java/android/transition/Transition.java
core/java/android/transition/TransitionSet.java
core/java/android/transition/Visibility.java
core/java/android/view/AccessibilityInteractionController.java
core/java/android/view/IWindowManager.aidl
core/java/android/view/RenderNode.java
core/java/android/view/RenderNodeAnimator.java
core/java/android/view/View.java
core/java/android/view/ViewDebug.java
core/java/android/view/ViewGroup.java
core/java/android/view/ViewRootImpl.java
core/java/android/view/accessibility/AccessibilityInteractionClient.java
core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
core/java/android/view/inputmethod/CursorAnchorInfo.java
core/java/android/widget/DatePicker.java
core/java/android/widget/DatePickerCalendarDelegate.java
core/java/android/widget/DatePickerController.java
core/java/android/widget/Editor.java
core/java/com/android/internal/content/PackageHelper.java
core/java/com/android/internal/os/BatteryStatsImpl.java
core/java/com/android/internal/util/XmlUtils.java
core/jni/android_hardware_SoundTrigger.cpp
core/jni/android_util_AssetManager.cpp
core/jni/android_view_RenderNode.cpp
core/jni/android_view_RenderNodeAnimator.cpp
core/jni/android_view_ThreadedRenderer.cpp
core/res/res/layout/notification_template_material_inbox.xml
core/res/res/values-mcc310-mnc260/config.xml
core/res/res/values-mcc311-mnc480/config.xml
core/res/res/values/attrs.xml
core/res/res/values/attrs_manifest.xml
core/res/res/values/colors_material.xml
core/res/res/values/config.xml
core/res/res/values/public.xml
core/res/res/values/symbols.xml
core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
core/tests/inputmethodtests/run_core_inputmethod_test.sh
core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
docs/html/tools/sdk/ndk/index.jd
graphics/java/android/graphics/drawable/Ripple.java
graphics/java/android/graphics/drawable/RippleBackground.java
graphics/java/android/graphics/drawable/RippleDrawable.java
libs/hwui/AnimationContext.cpp
libs/hwui/AnimationContext.h
libs/hwui/Animator.h
libs/hwui/AnimatorManager.cpp
libs/hwui/RenderNode.cpp
libs/hwui/RenderProperties.h
libs/hwui/RenderState.cpp
libs/hwui/RenderState.h
libs/hwui/renderthread/CanvasContext.cpp
location/java/android/location/LocationManager.java
location/java/com/android/internal/location/GpsNetInitiatedHandler.java
media/java/android/media/AudioAttributes.java
media/java/android/media/AudioManager.java
media/java/android/media/MediaCodec.java
media/java/android/media/MediaCodecInfo.java
media/java/android/media/MediaCodecList.java
media/java/android/media/session/MediaSession.java
media/java/android/media/session/MediaSessionLegacyHelper.java
media/java/android/media/tv/TvInputService.java
packages/PrintSpooler/res/layout/preview_page_loading.xml [new file with mode: 0644]
packages/PrintSpooler/res/values-land/constants.xml
packages/PrintSpooler/res/values/strings.xml
packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
packages/SystemUI/AndroidManifest.xml
packages/SystemUI/res/drawable/stat_sys_vpn_ic.xml [new file with mode: 0644]
packages/SystemUI/res/layout/recents_empty.xml
packages/SystemUI/res/layout/signal_cluster_view.xml
packages/SystemUI/res/values/config.xml
packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.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/RecentsConfiguration.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/TaskView.java
packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
services/core/java/com/android/server/NativeDaemonEvent.java
services/core/java/com/android/server/NsdService.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/location/GpsLocationProvider.java
services/core/java/com/android/server/notification/DowntimeConditionProvider.java
services/core/java/com/android/server/notification/NotificationManagerService.java
services/core/java/com/android/server/notification/ZenModeHelper.java
services/core/java/com/android/server/pm/PackageInstallerService.java
services/core/java/com/android/server/pm/PackageInstallerSession.java
services/core/java/com/android/server/pm/PackageManagerService.java
services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
services/core/java/com/android/server/wm/AppTransition.java
services/core/java/com/android/server/wm/WindowManagerService.java
services/core/java/com/android/server/wm/WindowStateAnimator.java
services/core/jni/com_android_server_UsbHostManager.cpp
services/usage/java/com/android/server/usage/IntervalStats.java
services/usage/java/com/android/server/usage/UnixCalendar.java [new file with mode: 0644]
services/usage/java/com/android/server/usage/UsageStatsDatabase.java
services/usage/java/com/android/server/usage/UsageStatsService.java
services/usage/java/com/android/server/usage/UsageStatsUtils.java [deleted file]
services/usage/java/com/android/server/usage/UsageStatsXml.java
services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
services/usage/java/com/android/server/usage/UserUsageStatsService.java
telecomm/java/android/telecomm/AudioState.java
telecomm/java/android/telecomm/Call.java
telecomm/java/android/telecomm/Conference.java
telecomm/java/android/telecomm/Connection.java
telecomm/java/android/telecomm/ConnectionRequest.java
telecomm/java/android/telecomm/ConnectionService.java
telecomm/java/android/telecomm/InCallAdapter.java
telecomm/java/android/telecomm/PhoneAccount.java
telecomm/java/android/telecomm/PhoneCapabilities.java
telecomm/java/android/telecomm/RemoteConference.java
telecomm/java/android/telecomm/RemoteConnection.java
telecomm/java/android/telecomm/RemoteConnectionService.java
telecomm/java/android/telecomm/StatusHints.java
telecomm/java/android/telecomm/TelecommManager.java
telecomm/java/android/telecomm/VideoCallImpl.java
telecomm/java/android/telecomm/VideoCallbackServant.java [new file with mode: 0644]
telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
telecomm/java/com/android/internal/telecomm/IVideoProvider.aidl
telephony/java/android/telephony/PhoneNumberUtils.java
telephony/java/android/telephony/TelephonyManager.java
telephony/java/com/android/internal/telephony/ITelephony.aidl
telephony/java/com/android/internal/telephony/TelephonyProperties.java
tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
tests/HwAccelerationTest/src/com/android/test/hwui/RevealActivity.java
tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
wifi/java/android/net/wifi/WifiConfiguration.java

index 03aa762..35d13d7 100644 (file)
@@ -204,6 +204,7 @@ LOCAL_SRC_FILES += \
        core/java/android/os/IUserManager.aidl \
        core/java/android/os/IVibratorService.aidl \
        core/java/android/service/notification/INotificationListener.aidl \
+       core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
        core/java/android/service/notification/IConditionListener.aidl \
        core/java/android/service/notification/IConditionProvider.aidl \
        core/java/android/print/ILayoutResultCallback.aidl \
index eb1aedc..0efa148 100644 (file)
@@ -1062,6 +1062,7 @@ package android {
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
     field public static final int restrictedAccountType = 16843733; // 0x10103d5
     field public static final int restrictionType = 16843923; // 0x1010493
+    field public static final int resumeWhilePausing = 16843955; // 0x10104b3
     field public static final int reversible = 16843851; // 0x101044b
     field public static final int right = 16843183; // 0x10101af
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
@@ -5525,8 +5526,8 @@ package android.app.admin {
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_PORT = "android.app.extra.PROVISIONING_WIFI_PROXY_PORT";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE = "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SSID = "android.app.extra.PROVISIONING_WIFI_SSID";
-    field public static int FLAG_MANAGED_CAN_ACCESS_PARENT;
-    field public static int FLAG_PARENT_CAN_ACCESS_MANAGED;
+    field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
+    field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
     field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
     field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
     field public static final int KEYGUARD_DISABLE_FINGERPRINT = 32; // 0x20
@@ -8411,6 +8412,7 @@ package android.content.pm {
     field public static final int FLAG_MULTIPROCESS = 1; // 0x1
     field public static final int FLAG_NO_HISTORY = 128; // 0x80
     field public static final int FLAG_RELINQUISH_TASK_IDENTITY = 4096; // 0x1000
+    field public static final int FLAG_RESUME_WHILE_PAUSING = 16384; // 0x4000
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10
     field public static final int LAUNCH_MULTIPLE = 0; // 0x0
@@ -8615,21 +8617,16 @@ package android.content.pm {
 
   public class LauncherApps {
     method public void addCallback(android.content.pm.LauncherApps.Callback);
-    method public void addCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
-    method public void addOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
-    method public void addOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback, android.os.Handler);
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
-    method public boolean isActivityEnabledForProfile(android.content.ComponentName, android.os.UserHandle);
     method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
-    method public boolean isPackageEnabledForProfile(java.lang.String, android.os.UserHandle);
+    method public void registerCallback(android.content.pm.LauncherApps.Callback);
+    method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
     method public void removeCallback(android.content.pm.LauncherApps.Callback);
-    method public void removeOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
     method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
-    method public void showAppDetailsForProfile(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
-    method public void startActivityForProfile(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+    method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
   }
 
   public static abstract class LauncherApps.Callback {
@@ -8641,10 +8638,6 @@ package android.content.pm {
     method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
   }
 
-  public static abstract class LauncherApps.OnAppsChangedCallback extends android.content.pm.LauncherApps.Callback {
-    ctor public LauncherApps.OnAppsChangedCallback();
-  }
-
   public class PackageInfo implements android.os.Parcelable {
     ctor public PackageInfo();
     method public int describeContents();
@@ -8686,7 +8679,7 @@ package android.content.pm {
     method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllSessions();
     method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
     method public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
-    method public android.content.pm.PackageInstaller.Session openSession(int);
+    method public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
     method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
     method public void uninstall(java.lang.String, android.content.IntentSender);
@@ -8719,16 +8712,15 @@ package android.content.pm {
     method public java.lang.String[] getNames() throws java.io.IOException;
     method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
     method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
-    method public void setProgress(float);
+    method public void setStagingProgress(float);
   }
 
   public static abstract class PackageInstaller.SessionCallback {
     ctor public PackageInstaller.SessionCallback();
+    method public abstract void onActiveChanged(int, boolean);
     method public abstract void onBadgingChanged(int);
-    method public abstract void onClosed(int);
     method public abstract void onCreated(int);
     method public abstract void onFinished(int, boolean);
-    method public abstract void onOpened(int);
     method public abstract void onProgressChanged(int, float);
   }
 
@@ -8741,7 +8733,7 @@ package android.content.pm {
     method public java.lang.String getInstallerPackageName();
     method public float getProgress();
     method public int getSessionId();
-    method public boolean isOpen();
+    method public boolean isActive();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
@@ -8881,6 +8873,7 @@ package android.content.pm {
     field public static final java.lang.String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
     field public static final java.lang.String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
+    field public static final java.lang.String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
     field public static final java.lang.String FEATURE_BACKUP = "android.software.backup";
     field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
     field public static final java.lang.String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
@@ -14051,6 +14044,7 @@ package android.media {
     field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
     field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
     field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+    field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
     field public static final int USAGE_ALARM = 4; // 0x4
     field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
@@ -14236,9 +14230,9 @@ package android.media {
     field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
-    field public static final java.lang.String EXTRA_AUDIO_PLUG_STATE = "android.media.extra.audio_plug_state";
-    field public static final java.lang.String EXTRA_ENCODINGS = "android.media.extra.encodings";
-    field public static final java.lang.String EXTRA_MAX_CHANNEL_COUNT = "android.media.extra.max_channel_count";
+    field public static final java.lang.String EXTRA_AUDIO_PLUG_STATE = "android.media.extra.AUDIO_PLUG_STATE";
+    field public static final java.lang.String EXTRA_ENCODINGS = "android.media.extra.ENCODINGS";
+    field public static final java.lang.String EXTRA_MAX_CHANNEL_COUNT = "android.media.extra.MAX_CHANNEL_COUNT";
     field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE";
     field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
     field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE";
@@ -14710,7 +14704,7 @@ package android.media {
 
   public static final class MediaCodecInfo.CodecCapabilities {
     ctor public MediaCodecInfo.CodecCapabilities();
-    method public static android.media.MediaCodecInfo.CodecCapabilities CreateFromProfileLevel(java.lang.String, int, int);
+    method public static android.media.MediaCodecInfo.CodecCapabilities createFromProfileLevel(java.lang.String, int, int);
     method public android.media.MediaCodecInfo.AudioCapabilities getAudioCapabilities();
     method public android.media.MediaFormat getDefaultFormat();
     method public android.media.MediaCodecInfo.EncoderCapabilities getEncoderCapabilities();
@@ -23701,7 +23695,6 @@ package android.provider {
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER";
     field public static final java.lang.String FEATURES = "features";
-    field public static final int FEATURES_NONE = 0; // 0x0
     field public static final int FEATURES_VIDEO = 1; // 0x1
     field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
     field public static final int INCOMING_TYPE = 1; // 0x1
@@ -27425,11 +27418,12 @@ package android.speech.tts {
     ctor public TextToSpeech(android.content.Context, android.speech.tts.TextToSpeech.OnInitListener);
     ctor public TextToSpeech(android.content.Context, android.speech.tts.TextToSpeech.OnInitListener, java.lang.String);
     method public int addEarcon(java.lang.String, java.lang.String, int);
-    method public int addEarcon(java.lang.String, java.lang.String);
+    method public deprecated int addEarcon(java.lang.String, java.lang.String);
+    method public int addEarcon(java.lang.String, java.io.File);
     method public int addSpeech(java.lang.String, java.lang.String, int);
     method public int addSpeech(java.lang.CharSequence, java.lang.String, int);
     method public int addSpeech(java.lang.String, java.lang.String);
-    method public int addSpeech(java.lang.CharSequence, java.lang.String);
+    method public int addSpeech(java.lang.CharSequence, java.io.File);
     method public deprecated boolean areDefaultsEnforced();
     method public java.util.Set<java.util.Locale> getAvailableLanguages();
     method public java.lang.String getDefaultEngine();
@@ -27443,7 +27437,7 @@ package android.speech.tts {
     method public java.util.Set<android.speech.tts.Voice> getVoices();
     method public int isLanguageAvailable(java.util.Locale);
     method public boolean isSpeaking();
-    method public int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public int playEarcon(java.lang.String, int, android.os.Bundle, java.lang.String);
     method public deprecated int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
     method public deprecated int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
@@ -27456,10 +27450,10 @@ package android.speech.tts {
     method public int setSpeechRate(float);
     method public int setVoice(android.speech.tts.Voice);
     method public void shutdown();
-    method public int speak(java.lang.CharSequence, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public int speak(java.lang.CharSequence, int, android.os.Bundle, java.lang.String);
     method public deprecated int speak(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public int stop();
-    method public int synthesizeToFile(java.lang.CharSequence, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String, java.lang.String);
+    method public int synthesizeToFile(java.lang.CharSequence, android.os.Bundle, java.io.File, java.lang.String);
     method public deprecated int synthesizeToFile(java.lang.String, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
     field public static final java.lang.String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
     field public static final int ERROR = -1; // 0xffffffff
@@ -27532,15 +27526,15 @@ package android.speech.tts {
 
   public abstract class TextToSpeechService extends android.app.Service {
     ctor public TextToSpeechService();
-    method protected int isValidVoiceName(java.lang.String);
     method public android.os.IBinder onBind(android.content.Intent);
     method protected java.lang.String onGetDefaultVoiceNameFor(java.lang.String, java.lang.String, java.lang.String);
     method protected java.util.Set<java.lang.String> onGetFeaturesForLanguage(java.lang.String, java.lang.String, java.lang.String);
     method protected abstract java.lang.String[] onGetLanguage();
-    method protected java.util.List<android.speech.tts.Voice> onGetVoices();
+    method public java.util.List<android.speech.tts.Voice> onGetVoices();
     method protected abstract int onIsLanguageAvailable(java.lang.String, java.lang.String, java.lang.String);
+    method public int onIsValidVoiceName(java.lang.String);
     method protected abstract int onLoadLanguage(java.lang.String, java.lang.String, java.lang.String);
-    method protected int onLoadVoice(java.lang.String);
+    method public int onLoadVoice(java.lang.String);
     method protected abstract void onStop();
     method protected abstract void onSynthesizeText(android.speech.tts.SynthesisRequest, android.speech.tts.SynthesisCallback);
   }
@@ -27561,7 +27555,7 @@ package android.speech.tts {
     method public java.util.Locale getLocale();
     method public java.lang.String getName();
     method public int getQuality();
-    method public boolean getRequiresNetworkConnection();
+    method public boolean isNetworkConnectionRequired();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int LATENCY_HIGH = 400; // 0x190
     field public static final int LATENCY_LOW = 200; // 0xc8
@@ -28218,7 +28212,6 @@ package android.telecomm {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final int ROUTE_ALL = 15; // 0xf
     field public static final int ROUTE_BLUETOOTH = 2; // 0x2
     field public static final int ROUTE_EARPIECE = 1; // 0x1
     field public static final int ROUTE_SPEAKER = 8; // 0x8
@@ -28231,17 +28224,19 @@ package android.telecomm {
 
   public abstract class Conference {
     ctor public Conference(android.telecomm.PhoneAccountHandle);
-    method public boolean addConnection(android.telecomm.Connection);
-    method public void destroy();
+    method public final boolean addConnection(android.telecomm.Connection);
+    method public final void destroy();
     method public final int getCapabilities();
     method public final java.util.List<android.telecomm.Connection> getConnections();
-    method public final android.telecomm.PhoneAccountHandle getPhoneAccount();
+    method public final android.telecomm.PhoneAccountHandle getPhoneAccountHandle();
     method public final int getState();
     method public void onDisconnect();
     method public void onHold();
+    method public void onMerge();
     method public void onSeparate(android.telecomm.Connection);
+    method public void onSwap();
     method public void onUnhold();
-    method public void removeConnection(android.telecomm.Connection);
+    method public final void removeConnection(android.telecomm.Connection);
     method public final void setActive();
     method public final void setCapabilities(int);
     method public final void setDisconnected(int, java.lang.String);
@@ -28308,12 +28303,11 @@ package android.telecomm {
   }
 
   public final class ConnectionRequest implements android.os.Parcelable {
-    ctor public ConnectionRequest(android.telecomm.PhoneAccountHandle, android.net.Uri, int, android.os.Bundle);
+    ctor public ConnectionRequest(android.telecomm.PhoneAccountHandle, android.net.Uri, android.os.Bundle);
     method public int describeContents();
     method public android.telecomm.PhoneAccountHandle getAccountHandle();
+    method public android.net.Uri getAddress();
     method public android.os.Bundle getExtras();
-    method public android.net.Uri getHandle();
-    method public int getHandlePresentation();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
@@ -28344,16 +28338,16 @@ package android.telecomm {
   }
 
   public class PhoneAccount implements android.os.Parcelable {
-    method public static android.telecomm.PhoneAccount.Builder builder();
+    method public static android.telecomm.PhoneAccount.Builder builder(android.telecomm.PhoneAccountHandle, java.lang.CharSequence);
     method public int describeContents();
     method public android.telecomm.PhoneAccountHandle getAccountHandle();
+    method public android.net.Uri getAddress();
     method public int getCapabilities();
-    method public android.net.Uri getHandle();
     method public android.graphics.drawable.Drawable getIcon(android.content.Context);
     method public int getIconResId();
     method public java.lang.CharSequence getLabel();
     method public java.lang.CharSequence getShortDescription();
-    method public java.lang.String getSubscriptionNumber();
+    method public android.net.Uri getSubscriptionAddress();
     method public java.util.List<java.lang.String> getSupportedUriSchemes();
     method public boolean supportsUriScheme(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
@@ -28366,17 +28360,14 @@ package android.telecomm {
   }
 
   public static class PhoneAccount.Builder {
-    ctor public PhoneAccount.Builder();
+    ctor public PhoneAccount.Builder(android.telecomm.PhoneAccountHandle, java.lang.CharSequence);
     method public android.telecomm.PhoneAccount build();
-    method public android.telecomm.PhoneAccount.Builder withAccountHandle(android.telecomm.PhoneAccountHandle);
-    method public android.telecomm.PhoneAccount.Builder withCapabilities(int);
-    method public android.telecomm.PhoneAccount.Builder withHandle(android.net.Uri);
-    method public android.telecomm.PhoneAccount.Builder withIconResId(int);
-    method public android.telecomm.PhoneAccount.Builder withLabel(java.lang.CharSequence);
-    method public android.telecomm.PhoneAccount.Builder withShortDescription(java.lang.CharSequence);
-    method public android.telecomm.PhoneAccount.Builder withSubscriptionNumber(java.lang.String);
-    method public android.telecomm.PhoneAccount.Builder withSupportedUriScheme(java.lang.String);
-    method public android.telecomm.PhoneAccount.Builder withSupportedUriSchemes(java.util.List<java.lang.String>);
+    method public android.telecomm.PhoneAccount.Builder setAddress(android.net.Uri);
+    method public android.telecomm.PhoneAccount.Builder setCapabilities(int);
+    method public android.telecomm.PhoneAccount.Builder setIconResId(int);
+    method public android.telecomm.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
+    method public android.telecomm.PhoneAccount.Builder setSubscriptionAddress(android.net.Uri);
+    method public android.telecomm.PhoneAccount.Builder setSupportedUriSchemes(java.util.List<java.lang.String>);
   }
 
   public class PhoneAccountHandle implements android.os.Parcelable {
@@ -28392,13 +28383,13 @@ package android.telecomm {
     method public static java.lang.String toString(int);
     field public static final int ADD_CALL = 16; // 0x10
     field public static final int ALL = 255; // 0xff
-    field public static final int GENERIC_CONFERENCE = 128; // 0x80
     field public static final int HOLD = 1; // 0x1
-    field public static final int MERGE_CALLS = 4; // 0x4
+    field public static final int MANAGE_CONFERENCE = 128; // 0x80
+    field public static final int MERGE_CONFERENCE = 4; // 0x4
     field public static final int MUTE = 64; // 0x40
     field public static final int RESPOND_VIA_TEXT = 32; // 0x20
     field public static final int SUPPORT_HOLD = 2; // 0x2
-    field public static final int SWAP_CALLS = 8; // 0x8
+    field public static final int SWAP_CONFERENCE = 8; // 0x8
   }
 
   public class PropertyPresentation {
@@ -28410,7 +28401,7 @@ package android.telecomm {
   }
 
   public final class RemoteConference {
-    method public final void addListener(android.telecomm.RemoteConference.Listener);
+    method public final void addCallback(android.telecomm.RemoteConference.Callback);
     method public void disconnect();
     method public final int getCallCapabilities();
     method public final java.util.List<android.telecomm.RemoteConnection> getConnections();
@@ -28418,13 +28409,13 @@ package android.telecomm {
     method public java.lang.String getDisconnectMessage();
     method public final int getState();
     method public void hold();
-    method public final void removeListener(android.telecomm.RemoteConference.Listener);
+    method public final void removeCallback(android.telecomm.RemoteConference.Callback);
     method public void separate(android.telecomm.RemoteConnection);
     method public void unhold();
   }
 
-  public static abstract class RemoteConference.Listener {
-    ctor public RemoteConference.Listener();
+  public static abstract class RemoteConference.Callback {
+    ctor public RemoteConference.Callback();
     method public void onCapabilitiesChanged(android.telecomm.RemoteConference, int);
     method public void onConnectionAdded(android.telecomm.RemoteConference, android.telecomm.RemoteConnection);
     method public void onConnectionRemoved(android.telecomm.RemoteConference, android.telecomm.RemoteConnection);
@@ -28491,11 +28482,11 @@ package android.telecomm {
   public final class StatusHints implements android.os.Parcelable {
     ctor public StatusHints(android.content.ComponentName, java.lang.CharSequence, int, android.os.Bundle);
     method public int describeContents();
-    method public android.content.ComponentName getComponentName();
     method public android.os.Bundle getExtras();
     method public android.graphics.drawable.Drawable getIcon(android.content.Context);
     method public int getIconResId();
     method public java.lang.CharSequence getLabel();
+    method public android.content.ComponentName getPackageName();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
@@ -28515,17 +28506,17 @@ package android.telecomm {
     method public void registerPhoneAccount(android.telecomm.PhoneAccount);
     method public void showInCallScreen(boolean);
     method public void unregisterPhoneAccount(android.telecomm.PhoneAccountHandle);
-    field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecomm.intent.action.CHANGE_PHONE_ACCOUNTS";
-    field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.intent.action.CONNECTION_SERVICE_CONFIGURE";
-    field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecomm.intent.action.SHOW_CALL_SETTINGS";
+    field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecomm.action.CHANGE_PHONE_ACCOUNTS";
+    field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecomm.action.CONNECTION_SERVICE_CONFIGURE";
+    field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecomm.action.SHOW_CALL_SETTINGS";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
     field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecomm.extra.CALL_BACK_NUMBER";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecomm.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
     field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecomm.extra.CONNECTION_SERVICE";
-    field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.intent.extra.PHONE_ACCOUNT_HANDLE";
-    field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
+    field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecomm.extra.PHONE_ACCOUNT_HANDLE";
+    field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecomm.extra.START_CALL_WITH_SPEAKERPHONE";
   }
 
 }
@@ -28889,8 +28880,6 @@ package android.telephony {
     method public void downloadMultimediaMessage(java.lang.String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
     method public android.os.Bundle getCarrierConfigValues();
     method public static android.telephony.SmsManager getDefault();
-    method public static android.telephony.SmsManager getSmsManagerUsingSubId(long);
-    method public long getSubId();
     method public void injectSmsPdu(byte[], java.lang.String, android.app.PendingIntent);
     method public void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
     method public void sendMultimediaMessage(android.net.Uri, java.lang.String, android.os.Bundle, android.app.PendingIntent);
@@ -29004,10 +28993,7 @@ package android.telephony {
   }
 
   public class TelephonyManager {
-    method public void enableSimplifiedNetworkSettings(boolean);
-    method public void enableSimplifiedNetworkSettings(long, boolean);
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
-    method public int getCalculatedPreferredNetworkType();
     method public int getCallState();
     method public android.telephony.CellLocation getCellLocation();
     method public int getDataActivity();
@@ -29024,14 +29010,11 @@ package android.telephony {
     method public java.lang.String getNetworkOperatorName();
     method public int getNetworkType();
     method public int getPhoneType();
-    method public int getPreferredNetworkType();
     method public java.lang.String getSimCountryIso();
     method public java.lang.String getSimOperator();
     method public java.lang.String getSimOperatorName();
     method public java.lang.String getSimSerialNumber();
     method public int getSimState();
-    method public boolean getSimplifiedNetworkSettingsEnabled();
-    method public boolean getSimplifiedNetworkSettingsEnabled(long);
     method public java.lang.String getSubscriberId();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
@@ -29046,11 +29029,10 @@ package android.telephony {
     method public boolean isSmsCapable();
     method public void listen(android.telephony.PhoneStateListener, int);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
-    method public boolean setCdmaSubscription(int);
+    method public boolean setGlobalPreferredNetworkType();
     method public void setLine1NumberForDisplay(java.lang.String, java.lang.String);
     method public void setLine1NumberForDisplay(long, java.lang.String, java.lang.String);
-    method public boolean setOperatorBrandOverride(java.lang.String, java.lang.String);
-    method public boolean setPreferredNetworkType(int);
+    method public boolean setOperatorBrandOverride(java.lang.String);
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
     field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
     field public static final int CALL_STATE_IDLE = 0; // 0x0
@@ -35901,8 +35883,8 @@ package android.view.inputmethod {
   public final class CursorAnchorInfo implements android.os.Parcelable {
     ctor public CursorAnchorInfo(android.os.Parcel);
     method public int describeContents();
-    method public android.graphics.RectF getCharacterRect(int);
-    method public int getCharacterRectFlags(int);
+    method public android.graphics.RectF getCharacterBounds(int);
+    method public int getCharacterBoundsFlags(int);
     method public java.lang.CharSequence getComposingText();
     method public int getComposingTextStart();
     method public float getInsertionMarkerBaseline();
@@ -35917,11 +35899,12 @@ package android.view.inputmethod {
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int FLAG_HAS_INVISIBLE_REGION = 2; // 0x2
     field public static final int FLAG_HAS_VISIBLE_REGION = 1; // 0x1
+    field public static final int FLAG_IS_RTL = 4; // 0x4
   }
 
   public static final class CursorAnchorInfo.Builder {
     ctor public CursorAnchorInfo.Builder();
-    method public android.view.inputmethod.CursorAnchorInfo.Builder addCharacterRect(int, float, float, float, float, int);
+    method public android.view.inputmethod.CursorAnchorInfo.Builder addCharacterBounds(int, float, float, float, float, int);
     method public android.view.inputmethod.CursorAnchorInfo build();
     method public void reset();
     method public android.view.inputmethod.CursorAnchorInfo.Builder setComposingText(int, java.lang.CharSequence);
@@ -37564,6 +37547,7 @@ package android.widget {
     method public android.widget.CalendarView getCalendarView();
     method public boolean getCalendarViewShown();
     method public int getDayOfMonth();
+    method public int getFirstDayOfWeek();
     method public long getMaxDate();
     method public long getMinDate();
     method public int getMonth();
@@ -37571,6 +37555,7 @@ package android.widget {
     method public int getYear();
     method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
     method public void setCalendarViewShown(boolean);
+    method public void setFirstDayOfWeek(int);
     method public void setMaxDate(long);
     method public void setMinDate(long);
     method public void setSpinnersShown(boolean);
index a272cf4..3c16276 100644 (file)
@@ -1,11 +1,3 @@
-package android {
-
-  public static final class R.attr {
-    field public static final int __removed1 = 16843955; // 0x10104b3
-  }
-
-}
-
 package android.media {
 
   public class AudioFormat {
@@ -53,6 +45,8 @@ package android.view.inputmethod {
   }
 
   public final class CursorAnchorInfo implements android.os.Parcelable {
+    method public android.graphics.RectF getCharacterRect(int);
+    method public int getCharacterRectFlags(int);
     method public boolean isInsertionMarkerClipped();
     field public static final int CHARACTER_RECT_TYPE_FULLY_VISIBLE = 1; // 0x1
     field public static final int CHARACTER_RECT_TYPE_INVISIBLE = 3; // 0x3
@@ -63,6 +57,7 @@ package android.view.inputmethod {
   }
 
   public static final class CursorAnchorInfo.Builder {
+    method public android.view.inputmethod.CursorAnchorInfo.Builder addCharacterRect(int, float, float, float, float, int);
     method public android.view.inputmethod.CursorAnchorInfo.Builder setInsertionMarkerLocation(float, float, float, float, boolean);
   }
 
@@ -78,11 +73,3 @@ package android.view.inputmethod {
 
 }
 
-package com.android.internal {
-
-  public static final class R.attr {
-    field public static final int __removed1 = 16843955; // 0x10104b3
-  }
-
-}
-
index 5f7a17d..27a03b6 100644 (file)
@@ -54,6 +54,10 @@ interface IAccessibilityServiceConnection {
         int action, in Bundle arguments, int interactionId,
         IAccessibilityInteractionConnectionCallback callback, long threadId);
 
+    boolean computeClickPointInScreen(int accessibilityWindowId, long accessibilityNodeId,
+        int interactionId, IAccessibilityInteractionConnectionCallback callback,
+        long threadId);
+
     AccessibilityWindowInfo getWindow(int windowId);
 
     List<AccessibilityWindowInfo> getWindows();
index 9156eeb..7c13dbe 100644 (file)
@@ -504,6 +504,10 @@ public final class AnimatorSet extends Animator {
         mStarted = true;
         mPaused = false;
 
+        for (Node node : mNodes) {
+            node.animation.setAllowRunningAsynchronously(false);
+        }
+
         if (mDuration >= 0) {
             // If the duration was set on this AnimatorSet, pass it along to all child animations
             for (Node node : mNodes) {
index 3e03893..9486a72 100644 (file)
@@ -2656,17 +2656,24 @@ public class ActivityManager {
 
         /**
          * Start an activity in this task.  Brings the task to the foreground.  If this task
-         * is not currently active (that is, its id < 0), then the activity being started
-         * needs to be started as a new task and the Intent's ComponentName must match the
-         * base ComponenentName of the recent task entry.  Otherwise, the activity being
-         * started must <b>not</b> be launched as a new task -- not through explicit intent
-         * flags nor implicitly as the singleTask or singleInstance launch modes.
+         * is not currently active (that is, its id < 0), then a new activity for the given
+         * Intent will be launched as the root of the task and the task brought to the
+         * foreground.  Otherwise, if this task is currently active and the Intent does not specify
+         * an activity to launch in a new task, then a new activity for the given Intent will
+         * be launched on top of the task and the task brought to the foreground.  If this
+         * task is currently active and the Intent specifies {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+         * or would otherwise be launched in to a new task, then the activity not launched but
+         * this task be brought to the foreground and a new intent delivered to the top
+         * activity if appropriate.
          *
-         * <p>See {@link Activity#startActivity(android.content.Intent, android.os.Bundle)
-         * Activity.startActivity} for more information.</p>
+         * <p>In other words, you generally want to use an Intent here that does not specify
+         * {@link Intent#FLAG_ACTIVITY_NEW_TASK} or {@link Intent#FLAG_ACTIVITY_NEW_DOCUMENT},
+         * and let the system do the right thing.</p>
          *
          * @param intent The Intent describing the new activity to be launched on the task.
          * @param options Optional launch options.
+         *
+         * @see Activity#startActivity(android.content.Intent, android.os.Bundle)
          */
         public void startActivity(Context context, Intent intent, Bundle options) {
             ActivityThread thread = ActivityThread.currentActivityThread();
index 1f7e450..394b183 100644 (file)
@@ -509,8 +509,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
         case ACTIVITY_PAUSED_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            PersistableBundle persistentState = data.readPersistableBundle();
-            activityPaused(token, persistentState);
+            activityPaused(token);
             reply.writeNoException();
             return true;
         }
@@ -2829,13 +2828,12 @@ class ActivityManagerProxy implements IActivityManager
         data.recycle();
         reply.recycle();
     }
-    public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException
+    public void activityPaused(IBinder token) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        data.writePersistableBundle(persistentState);
         mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
index 9dd4605..ffffb6c 100644 (file)
@@ -84,13 +84,13 @@ public class ActivityOptions {
      * Initial width of the animation.
      * @hide
      */
-    public static final String KEY_ANIM_START_WIDTH = "android:animStartWidth";
+    public static final String KEY_ANIM_WIDTH = "android:animWidth";
 
     /**
      * Initial height of the animation.
      * @hide
      */
-    public static final String KEY_ANIM_START_HEIGHT = "android:animStartHeight";
+    public static final String KEY_ANIM_HEIGHT = "android:animHeight";
 
     /**
      * Callback for when animation is started.
@@ -140,8 +140,8 @@ public class ActivityOptions {
     private Bitmap mThumbnail;
     private int mStartX;
     private int mStartY;
-    private int mStartWidth;
-    private int mStartHeight;
+    private int mWidth;
+    private int mHeight;
     private IRemoteCallback mAnimationStartedListener;
     private ResultReceiver mTransitionReceiver;
     private boolean mIsReturning;
@@ -238,13 +238,13 @@ public class ActivityOptions {
      * defines the coordinate space for <var>startX</var> and <var>startY</var>.
      * @param startX The x starting location of the new activity, relative to <var>source</var>.
      * @param startY The y starting location of the activity, relative to <var>source</var>.
-     * @param startWidth The initial width of the new activity.
-     * @param startHeight The initial height of the new activity.
+     * @param width The initial width of the new activity.
+     * @param height The initial height of the new activity.
      * @return Returns a new ActivityOptions object that you can use to
      * supply these options as the options Bundle when starting an activity.
      */
     public static ActivityOptions makeScaleUpAnimation(View source,
-            int startX, int startY, int startWidth, int startHeight) {
+            int startX, int startY, int width, int height) {
         ActivityOptions opts = new ActivityOptions();
         opts.mPackageName = source.getContext().getPackageName();
         opts.mAnimationType = ANIM_SCALE_UP;
@@ -252,8 +252,8 @@ public class ActivityOptions {
         source.getLocationOnScreen(pts);
         opts.mStartX = pts[0] + startX;
         opts.mStartY = pts[1] + startY;
-        opts.mStartWidth = startWidth;
-        opts.mStartHeight = startHeight;
+        opts.mWidth = width;
+        opts.mHeight = height;
         return opts;
     }
 
@@ -359,9 +359,10 @@ public class ActivityOptions {
      * @hide
      */
     public static ActivityOptions makeThumbnailAspectScaleUpAnimation(View source,
-            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
-        return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, listener,
-                true);
+            Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
+            OnAnimationStartedListener listener) {
+        return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
+                targetWidth, targetHeight, listener, true);
     }
 
     /**
@@ -382,13 +383,15 @@ public class ActivityOptions {
      * @hide
      */
     public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
-            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
-        return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, listener,
-                false);
+            Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight,
+            OnAnimationStartedListener listener) {
+        return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY,
+                targetWidth, targetHeight, listener, false);
     }
 
     private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail,
-            int startX, int startY, OnAnimationStartedListener listener, boolean scaleUp) {
+            int startX, int startY, int targetWidth, int targetHeight,
+            OnAnimationStartedListener listener, boolean scaleUp) {
         ActivityOptions opts = new ActivityOptions();
         opts.mPackageName = source.getContext().getPackageName();
         opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP :
@@ -398,6 +401,8 @@ public class ActivityOptions {
         source.getLocationOnScreen(pts);
         opts.mStartX = pts[0] + startX;
         opts.mStartY = pts[1] + startY;
+        opts.mWidth = targetWidth;
+        opts.mHeight = targetHeight;
         opts.setOnAnimationStartedListener(source.getHandler(), listener);
         return opts;
     }
@@ -543,8 +548,8 @@ public class ActivityOptions {
             case ANIM_SCALE_UP:
                 mStartX = opts.getInt(KEY_ANIM_START_X, 0);
                 mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
-                mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0);
-                mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0);
+                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
+                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
                 break;
 
             case ANIM_THUMBNAIL_SCALE_UP:
@@ -554,6 +559,8 @@ public class ActivityOptions {
                 mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL);
                 mStartX = opts.getInt(KEY_ANIM_START_X, 0);
                 mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
+                mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
+                mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0);
                 mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
                         opts.getBinder(KEY_ANIM_START_LISTENER));
                 break;
@@ -605,13 +612,13 @@ public class ActivityOptions {
     }
 
     /** @hide */
-    public int getStartWidth() {
-        return mStartWidth;
+    public int getWidth() {
+        return mWidth;
     }
 
     /** @hide */
-    public int getStartHeight() {
-        return mStartHeight;
+    public int getHeight() {
+        return mHeight;
     }
 
     /** @hide */
@@ -690,8 +697,8 @@ public class ActivityOptions {
             case ANIM_SCALE_UP:
                 mStartX = otherOptions.mStartX;
                 mStartY = otherOptions.mStartY;
-                mStartWidth = otherOptions.mStartWidth;
-                mStartHeight = otherOptions.mStartHeight;
+                mWidth = otherOptions.mWidth;
+                mHeight = otherOptions.mHeight;
                 if (mAnimationStartedListener != null) {
                     try {
                         mAnimationStartedListener.sendResult(null);
@@ -707,6 +714,8 @@ public class ActivityOptions {
                 mThumbnail = otherOptions.mThumbnail;
                 mStartX = otherOptions.mStartX;
                 mStartY = otherOptions.mStartY;
+                mWidth = otherOptions.mWidth;
+                mHeight = otherOptions.mHeight;
                 if (mAnimationStartedListener != null) {
                     try {
                         mAnimationStartedListener.sendResult(null);
@@ -755,8 +764,8 @@ public class ActivityOptions {
             case ANIM_SCALE_UP:
                 b.putInt(KEY_ANIM_START_X, mStartX);
                 b.putInt(KEY_ANIM_START_Y, mStartY);
-                b.putInt(KEY_ANIM_START_WIDTH, mStartWidth);
-                b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight);
+                b.putInt(KEY_ANIM_WIDTH, mWidth);
+                b.putInt(KEY_ANIM_HEIGHT, mHeight);
                 break;
             case ANIM_THUMBNAIL_SCALE_UP:
             case ANIM_THUMBNAIL_SCALE_DOWN:
@@ -765,6 +774,8 @@ public class ActivityOptions {
                 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
                 b.putInt(KEY_ANIM_START_X, mStartX);
                 b.putInt(KEY_ANIM_START_Y, mStartY);
+                b.putInt(KEY_ANIM_WIDTH, mWidth);
+                b.putInt(KEY_ANIM_HEIGHT, mHeight);
                 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
                         != null ? mAnimationStartedListener.asBinder() : null);
                 break;
index 4f2a3bc..3a39900 100644 (file)
@@ -563,11 +563,11 @@ public final class ActivityThread {
         }
 
         public final void schedulePauseActivity(IBinder token, boolean finished,
-                boolean userLeaving, int configChanges) {
+                boolean userLeaving, int configChanges, boolean dontReport) {
             sendMessage(
                     finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                     token,
-                    (userLeaving ? 1 : 0),
+                    (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
                     configChanges);
         }
 
@@ -1283,13 +1283,15 @@ public final class ActivityThread {
                 } break;
                 case PAUSE_ACTIVITY:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
-                    handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
+                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
+                            (msg.arg1&2) != 0);
                     maybeSnapshot();
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case PAUSE_ACTIVITY_FINISHING:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
-                    handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
+                    handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,
+                            (msg.arg1&1) != 0);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case STOP_ACTIVITY_SHOW:
@@ -3142,7 +3144,7 @@ public final class ActivityThread {
     }
 
     private void handlePauseActivity(IBinder token, boolean finished,
-            boolean userLeaving, int configChanges) {
+            boolean userLeaving, int configChanges, boolean dontReport) {
         ActivityClientRecord r = mActivities.get(token);
         if (r != null) {
             //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
@@ -3159,9 +3161,11 @@ public final class ActivityThread {
             }
 
             // Tell the activity manager we have paused.
-            try {
-                ActivityManagerNative.getDefault().activityPaused(token, r.persistentState);
-            } catch (RemoteException ex) {
+            if (!dontReport) {
+                try {
+                    ActivityManagerNative.getDefault().activityPaused(token);
+                } catch (RemoteException ex) {
+                }
             }
             mSomeActivitiesChanged = true;
         }
@@ -4283,6 +4287,8 @@ public final class ActivityThread {
             AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
         }
 
+        Message.updateCheckRecycle(data.appInfo.targetSdkVersion);
+
         /*
          * Before spawning a new process, reset the time zone to be the system time zone.
          * This needs to be done because the system time zone could have changed after the
index 9e80a4b..9f49194 100644 (file)
@@ -205,6 +205,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
     private boolean mIsStartingTransition;
     private ArrayList<GhostViewListeners> mGhostViewListeners =
             new ArrayList<GhostViewListeners>();
+    private ArrayMap<View, Float> mOriginalAlphas = new ArrayMap<View, Float>();
 
     public ActivityTransitionCoordinator(Window window,
             ArrayList<String> allSharedElementNames,
@@ -580,6 +581,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
         mWindow = null;
         mSharedElements.clear();
         mTransitioningViews.clear();
+        mOriginalAlphas.clear();
         mResultReceiver = null;
         mPendingTransition = null;
         mListener = null;
@@ -589,10 +591,27 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
         return getWindow().getTransitionBackgroundFadeDuration();
     }
 
-    protected static void setTransitionAlpha(ArrayList<View> views, float alpha) {
+    protected void hideViews(ArrayList<View> views) {
         int count = views.size();
         for (int i = 0; i < count; i++) {
-            views.get(i).setTransitionAlpha(alpha);
+            View view = views.get(i);
+            if (!mOriginalAlphas.containsKey(view)) {
+                mOriginalAlphas.put(view, view.getAlpha());
+            }
+            view.setAlpha(0f);
+            view.setTransitionAlpha(0f);
+        }
+    }
+
+    protected void showViews(ArrayList<View> views) {
+        int count = views.size();
+        for (int i = 0; i < count; i++) {
+            View view = views.get(i);
+            Float alpha = mOriginalAlphas.remove(view);
+            if (alpha != null) {
+                view.setAlpha(alpha);
+                view.setTransitionAlpha(1f);
+            }
         }
     }
 
index 9cd6d49..404268c 100644 (file)
@@ -1693,7 +1693,7 @@ final class ApplicationPackageManager extends PackageManager {
         if (dr == null) {
             dr = itemInfo.loadDefaultIcon(this);
         }
-        return getUserBadgedDrawableForDensity(dr, new UserHandle(mContext.getUserId()), null, 0);
+        return getUserBadgedIcon(dr, new UserHandle(mContext.getUserId()));
     }
 
     private Drawable getBadgedDrawable(Drawable drawable, Drawable badgeDrawable,
index 63e8707..0123e16 100644 (file)
@@ -78,7 +78,8 @@ public abstract class ApplicationThreadNative extends Binder
             boolean finished = data.readInt() != 0;
             boolean userLeaving = data.readInt() != 0;
             int configChanges = data.readInt();
-            schedulePauseActivity(b, finished, userLeaving, configChanges);
+            boolean dontReport = data.readInt() != 0;
+            schedulePauseActivity(b, finished, userLeaving, configChanges, dontReport);
             return true;
         }
 
@@ -689,13 +690,14 @@ class ApplicationThreadProxy implements IApplicationThread {
     }
     
     public final void schedulePauseActivity(IBinder token, boolean finished,
-            boolean userLeaving, int configChanges) throws RemoteException {
+            boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
         data.writeInt(finished ? 1 : 0);
         data.writeInt(userLeaving ? 1 :0);
         data.writeInt(configChanges);
+        data.writeInt(dontReport ? 1 : 0);
         mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
index f432c49..f38c108 100644 (file)
@@ -112,9 +112,9 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
     protected void viewsReady(ArrayMap<String, View> sharedElements) {
         super.viewsReady(sharedElements);
         mIsReadyForTransition = true;
-        setTransitionAlpha(mSharedElements, 0);
+        hideViews(mSharedElements);
         if (getViewsTransition() != null) {
-            setTransitionAlpha(mTransitioningViews, 0);
+            hideViews(mTransitioningViews);
         }
         if (mIsReturning) {
             sendSharedElementDestination();
@@ -240,7 +240,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
         if (!mIsCanceled) {
             mIsCanceled = true;
             if (getViewsTransition() == null || mIsViewsTransitionStarted) {
-                setTransitionAlpha(mSharedElements, 1);
+                showViews(mSharedElements);
             } else {
                 mTransitioningViews.addAll(mSharedElements);
             }
@@ -300,7 +300,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
         // Now start shared element transition
         ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
                 mSharedElementNames);
-        setTransitionAlpha(mSharedElements, 1);
+        showViews(mSharedElements);
         scheduleSetSharedElementEnd(sharedElementSnapshots);
         ArrayList<SharedElementOriginalState> originalImageViewState =
                 setSharedElementState(sharedElementState, sharedElementSnapshots);
@@ -411,7 +411,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
                     @Override
                     public void onTransitionStart(Transition transition) {
                         mEnterViewsTransition = transition;
-                        setTransitionAlpha(mTransitioningViews, 1);
+                        showViews(mTransitioningViews);
                         super.onTransitionStart(transition);
                     }
 
index a59a927..982bbc4 100644 (file)
@@ -126,8 +126,8 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
     }
 
     public void resetViews() {
-        setTransitionAlpha(mTransitioningViews, 1);
-        setTransitionAlpha(mSharedElements, 1);
+        showViews(mTransitioningViews);
+        showViews(mSharedElements);
         mIsHidden = true;
         if (!mIsReturning && getDecor() != null) {
             getDecor().suppressLayout(false);
@@ -187,7 +187,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
     private void hideSharedElements() {
         moveSharedElementsFromOverlay();
         if (!mIsHidden) {
-            setTransitionAlpha(mSharedElements, 0);
+            hideViews(mSharedElements);
         }
         mSharedElementsHidden = true;
         finishIfNecessary();
@@ -296,7 +296,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
                     transition.removeListener(this);
                     exitTransitionComplete();
                     if (mIsHidden) {
-                        setTransitionAlpha(mTransitioningViews, 1);
+                        showViews(mTransitioningViews);
                     }
                     if (mSharedElementBundle != null) {
                         delayCancel();
@@ -323,7 +323,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
                     transition.removeListener(this);
                     sharedElementTransitionComplete();
                     if (mIsHidden) {
-                        setTransitionAlpha(mSharedElements, 1);
+                        showViews(mSharedElements);
                     }
                 }
             });
index 99428e8..9483680 100644 (file)
@@ -111,7 +111,7 @@ public interface IActivityManager extends IInterface {
     public void activityResumed(IBinder token) throws RemoteException;
     public void activityIdle(IBinder token, Configuration config,
             boolean stopProfiling) throws RemoteException;
-    public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException;
+    public void activityPaused(IBinder token) throws RemoteException;
     public void activityStopped(IBinder token, Bundle state,
             PersistableBundle persistentState, CharSequence description) throws RemoteException;
     public void activitySlept(IBinder token) throws RemoteException;
index a7546d9..f53075c 100644 (file)
@@ -49,7 +49,7 @@ import java.util.Map;
  */
 public interface IApplicationThread extends IInterface {
     void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving,
-            int configChanges) throws RemoteException;
+            int configChanges, boolean dontReport) throws RemoteException;
     void scheduleStopActivity(IBinder token, boolean showWindow,
             int configChanges) throws RemoteException;
     void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
index 1083943..c831c25 100644 (file)
@@ -3386,8 +3386,16 @@ public class Notification implements Parcelable
      */
     public static abstract class Style {
         private CharSequence mBigContentTitle;
-        private CharSequence mSummaryText = null;
-        private boolean mSummaryTextSet = false;
+
+        /**
+         * @hide
+         */
+        protected CharSequence mSummaryText = null;
+
+        /**
+         * @hide
+         */
+        protected boolean mSummaryTextSet = false;
 
         protected Builder mBuilder;
 
@@ -3679,6 +3687,11 @@ public class Notification implements Parcelable
      * @see Notification#bigContentView
      */
     public static class BigTextStyle extends Style {
+
+        private static final int MAX_LINES = 13;
+        private static final int LINES_CONSUMED_BY_ACTIONS = 3;
+        private static final int LINES_CONSUMED_BY_SUMMARY = 2;
+
         private CharSequence mBigText;
 
         public BigTextStyle() {
@@ -3745,6 +3758,7 @@ public class Notification implements Parcelable
 
             contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
             contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
+            contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
             contentView.setViewVisibility(R.id.text2, View.GONE);
 
             applyTopPadding(contentView);
@@ -3756,6 +3770,24 @@ public class Notification implements Parcelable
             return contentView;
         }
 
+        private int calculateMaxLines() {
+            int lineCount = MAX_LINES;
+            boolean hasActions = mBuilder.mActions.size() > 0;
+            boolean hasSummary = (mSummaryTextSet ? mSummaryText : mBuilder.mSubText) != null;
+            if (hasActions) {
+                lineCount -= LINES_CONSUMED_BY_ACTIONS;
+            }
+            if (hasSummary) {
+                lineCount -= LINES_CONSUMED_BY_SUMMARY;
+            }
+
+            // If we have less top padding at the top, we can fit less lines.
+            if (!mBuilder.mHasThreeLines) {
+                lineCount--;
+            }
+            return lineCount;
+        }
+
         /**
          * @hide
          */
index 112fc82..926a348 100644 (file)
@@ -441,13 +441,13 @@ public class DevicePolicyManager {
      * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from a
      * managed profile to its parent.
      */
-    public static int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001;
+    public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001;
 
     /**
      * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from the
      * parent to its managed profile.
      */
-    public static int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002;
+    public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002;
 
     /**
      * Return true if the given administrator component is currently
index dbf49c5..8d3126d 100644 (file)
@@ -255,16 +255,22 @@ public class ActivityInfo extends ComponentInfo
      * Bit in {@link #flags}: If set, a task rooted at this activity will have its
      * baseIntent replaced by the activity immediately above this. Each activity may further
      * relinquish its identity to the activity above it using this flag. Set from the
-     * android.R.attr#relinquishTaskIdentity attribute.
+     * {@link android.R.attr#relinquishTaskIdentity} attribute.
      */
     public static final int FLAG_RELINQUISH_TASK_IDENTITY = 0x1000;
     /**
      * Bit in {@link #flags} indicating that tasks started with this activity are to be
      * removed from the recent list of tasks when the last activity in the task is finished.
-     * {@link android.R.attr#autoRemoveFromRecents}
+     * Corresponds to {@link android.R.attr#autoRemoveFromRecents}
      */
     public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 0x2000;
     /**
+     * Bit in {@link #flags} indicating that this activity can start is creation/resume
+     * while the previous activity is still pausing.  Corresponds to
+     * {@link android.R.attr#resumeWhilePausing}
+     */
+    public static final int FLAG_RESUME_WHILE_PAUSING = 0x4000;
+    /**
      * @hide Bit in {@link #flags}: If set, this component will only be seen
      * by the primary user.  Only works with broadcast receivers.  Set from the
      * android.R.attr#primaryUserOnly attribute.
index fe98ee7..974eb1e 100644 (file)
@@ -20,8 +20,7 @@ package android.content.pm;
 oneway interface IPackageInstallerCallback {
     void onSessionCreated(int sessionId);
     void onSessionBadgingChanged(int sessionId);
-    void onSessionOpened(int sessionId);
+    void onSessionActiveChanged(int sessionId, boolean active);
     void onSessionProgressChanged(int sessionId, float progress);
-    void onSessionClosed(int sessionId);
     void onSessionFinished(int sessionId, boolean success);
 }
index f9370b3..d49bc50 100644 (file)
@@ -278,21 +278,21 @@ public class LauncherApps {
 
 
     /**
-     * Adds a callback for changes to packages in current and managed profiles.
+     * Registers a callback for changes to packages in current and managed profiles.
      *
-     * @param callback The callback to add.
+     * @param callback The callback to register.
      */
-    public void addCallback(Callback callback) {
-        addCallback(callback, null);
+    public void registerCallback(Callback callback) {
+        registerCallback(callback, null);
     }
 
     /**
-     * Adds a callback for changes to packages in current and managed profiles.
+     * Registers a callback for changes to packages in current and managed profiles.
      *
-     * @param callback The callback to add.
+     * @param callback The callback to register.
      * @param handler that should be used to post callbacks on, may be null.
      */
-    public void addCallback(Callback callback, Handler handler) {
+    public void registerCallback(Callback callback, Handler handler) {
         synchronized (this) {
             if (callback != null && !mCallbacks.contains(callback)) {
                 boolean addedFirstCallback = mCallbacks.size() == 0;
@@ -308,12 +308,12 @@ public class LauncherApps {
     }
 
     /**
-     * Removes a callback that was previously added.
+     * Unregisters a callback that was previously registered.
      *
-     * @param callback The callback to remove.
-     * @see #addCallback(Callback)
+     * @param callback The callback to unregister.
+     * @see #registerCallback(Callback)
      */
-    public void removeCallback(Callback callback) {
+    public void unregisterCallback(Callback callback) {
         synchronized (this) {
             removeCallbackLocked(callback);
             if (mCallbacks.size() == 0) {
@@ -500,44 +500,12 @@ public class LauncherApps {
         }
     }
 
-    /** Remove after unbundled apps have migrated STOP SHIP */
-    public static abstract class OnAppsChangedCallback extends Callback {
-    }
-
-    /** Remove after unbundled apps have migrated STOP SHIP */
-    public void addOnAppsChangedCallback(OnAppsChangedCallback callback) {
-        addCallback(callback, null);
-    }
-
-    /** Remove after unbundled apps have migrated STOP SHIP */
-    public void addOnAppsChangedCallback(OnAppsChangedCallback callback, Handler handler) {
-        addCallback(callback, handler);
-    }
-
-    /** Remove after unbundled apps have migrated STOP SHIP */
-    public void removeOnAppsChangedCallback(OnAppsChangedCallback callback) {
-        removeCallback(callback);
-    }
-
-    /** Remove after unbundled apps have migrated STOP SHIP */
-    public void startActivityForProfile(ComponentName component, UserHandle user, Rect sourceBounds,
-            Bundle opts) {
-        startMainActivity(component, user, sourceBounds, opts);
-    }
-
-    /** Remove after unbundled apps have migrated STOP SHIP */
-    public void showAppDetailsForProfile(ComponentName component, UserHandle user,
-            Rect sourceBounds, Bundle opts) {
-        startAppDetailsActivity(component, user, sourceBounds, opts);
-    }
-
-    /** Remove after unbundled apps have migrated STOP SHIP */
-    public boolean isPackageEnabledForProfile(String packageName, UserHandle user) {
-        return isPackageEnabled(packageName, user);
+    /** STOPSHIP remove when launcher 3 has been updated */
+    public void addCallback(Callback callback) {
+        registerCallback(callback);
     }
-
-    /** Remove after unbundled apps have migrated STOP SHIP */
-    public boolean isActivityEnabledForProfile(ComponentName component, UserHandle user) {
-        return isActivityEnabled(component, user);
+    /** STOPSHIP remove when launcher 3 has been updated */
+    public void removeCallback(Callback callback) {
+        unregisterCallback(callback);
     }
 }
index 7c34a65..06d4c4a 100644 (file)
@@ -285,6 +285,9 @@ public class PackageInstaller {
      *
      * @throws IOException if parameters were unsatisfiable, such as lack of
      *             disk space or unavailable media.
+     * @throws SecurityException when installation services are unavailable,
+     *             such as when called from a restricted user.
+     * @throws IllegalArgumentException when {@link SessionParams} is invalid.
      * @return positive, non-zero unique ID that represents the created session.
      *         This ID remains consistent across device reboots until the
      *         session is finalized. IDs are not reused during a given boot.
@@ -303,10 +306,18 @@ public class PackageInstaller {
     /**
      * Open an existing session to actively perform work. To succeed, the caller
      * must be the owner of the install session.
+     *
+     * @throws IOException if parameters were unsatisfiable, such as lack of
+     *             disk space or unavailable media.
+     * @throws SecurityException when the caller does not own the session, or
+     *             the session is invalid.
      */
-    public @NonNull Session openSession(int sessionId) {
+    public @NonNull Session openSession(int sessionId) throws IOException {
         try {
             return new Session(mInstaller.openSession(sessionId));
+        } catch (RuntimeException e) {
+            ExceptionUtils.maybeUnwrapIOException(e);
+            throw e;
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -316,6 +327,9 @@ public class PackageInstaller {
      * Update the icon representing the app being installed in a specific
      * session. This should be roughly
      * {@link ActivityManager#getLauncherLargeIconSize()} in both dimensions.
+     *
+     * @throws SecurityException when the caller does not own the session, or
+     *             the session is invalid.
      */
     public void updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon) {
         try {
@@ -328,6 +342,9 @@ public class PackageInstaller {
     /**
      * Update the label representing the app being installed in a specific
      * session.
+     *
+     * @throws SecurityException when the caller does not own the session, or
+     *             the session is invalid.
      */
     public void updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel) {
         try {
@@ -338,6 +355,15 @@ public class PackageInstaller {
         }
     }
 
+    /**
+     * Completely abandon the given session, destroying all staged data and
+     * rendering it invalid. Abandoned sessions will be reported to
+     * {@link SessionCallback} listeners as failures. This is equivalent to
+     * opening the session and calling {@link Session#abandon()}.
+     *
+     * @throws SecurityException when the caller does not own the session, or
+     *             the session is invalid.
+     */
     public void abandonSession(int sessionId) {
         try {
             mInstaller.abandonSession(sessionId);
@@ -347,7 +373,11 @@ public class PackageInstaller {
     }
 
     /**
-     * Return details for a specific session.
+     * Return details for a specific session. No special permissions are
+     * required to retrieve these details.
+     *
+     * @return details for the requested session, or {@code null} if the session
+     *         does not exist.
      */
     public @Nullable SessionInfo getSessionInfo(int sessionId) {
         try {
@@ -358,12 +388,12 @@ public class PackageInstaller {
     }
 
     /**
-     * Return list of all active install sessions, regardless of the installer.
+     * Return list of all known install sessions, regardless of the installer.
      */
     public @NonNull List<SessionInfo> getAllSessions() {
         final ApplicationInfo info = mContext.getApplicationInfo();
         if ("com.google.android.googlequicksearchbox".equals(info.packageName)
-                && info.versionCode <= 300400070) {
+                && info.versionCode <= 300400110) {
             Log.d(TAG, "Ignoring callback request from old prebuilt");
             return Collections.EMPTY_LIST;
         }
@@ -376,7 +406,7 @@ public class PackageInstaller {
     }
 
     /**
-     * Return list of all install sessions owned by the calling app.
+     * Return list of all known install sessions owned by the calling app.
      */
     public @NonNull List<SessionInfo> getMySessions() {
         try {
@@ -436,27 +466,32 @@ public class PackageInstaller {
         public abstract void onBadgingChanged(int sessionId);
 
         /**
-         * Session has been opened. A session is usually opened when the
-         * installer is actively writing data.
+         * Active state for session has been changed.
+         * <p>
+         * A session is considered active whenever there is ongoing forward
+         * progress being made, such as the installer holding an open
+         * {@link Session} instance while streaming data into place, or the
+         * system optimizing code as the result of
+         * {@link Session#commit(IntentSender)}.
+         * <p>
+         * If the installer closes the {@link Session} without committing, the
+         * session is considered inactive until the installer opens the session
+         * again.
          */
-        public abstract void onOpened(int sessionId);
+        public abstract void onActiveChanged(int sessionId, boolean active);
 
         /**
          * Progress for given session has been updated.
          * <p>
          * Note that this progress may not directly correspond to the value
-         * reported by {@link PackageInstaller.Session#setProgress(float)}, as
-         * the system may carve out a portion of the overall progress to
-         * represent its own internal installation work.
+         * reported by
+         * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
+         * system may carve out a portion of the overall progress to represent
+         * its own internal installation work.
          */
         public abstract void onProgressChanged(int sessionId, float progress);
 
         /**
-         * Session has been closed.
-         */
-        public abstract void onClosed(int sessionId);
-
-        /**
          * Session has completely finished, either with success or failure.
          */
         public abstract void onFinished(int sessionId, boolean success);
@@ -467,10 +502,9 @@ public class PackageInstaller {
             Handler.Callback {
         private static final int MSG_SESSION_CREATED = 1;
         private static final int MSG_SESSION_BADGING_CHANGED = 2;
-        private static final int MSG_SESSION_OPENED = 3;
+        private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
         private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
-        private static final int MSG_SESSION_CLOSED = 5;
-        private static final int MSG_SESSION_FINISHED = 6;
+        private static final int MSG_SESSION_FINISHED = 5;
 
         final SessionCallback mCallback;
         final Handler mHandler;
@@ -482,24 +516,23 @@ public class PackageInstaller {
 
         @Override
         public boolean handleMessage(Message msg) {
+            final int sessionId = msg.arg1;
             switch (msg.what) {
                 case MSG_SESSION_CREATED:
-                    mCallback.onCreated(msg.arg1);
+                    mCallback.onCreated(sessionId);
                     return true;
                 case MSG_SESSION_BADGING_CHANGED:
-                    mCallback.onBadgingChanged(msg.arg1);
+                    mCallback.onBadgingChanged(sessionId);
                     return true;
-                case MSG_SESSION_OPENED:
-                    mCallback.onOpened(msg.arg1);
+                case MSG_SESSION_ACTIVE_CHANGED:
+                    final boolean active = msg.arg2 != 0;
+                    mCallback.onActiveChanged(sessionId, active);
                     return true;
                 case MSG_SESSION_PROGRESS_CHANGED:
-                    mCallback.onProgressChanged(msg.arg1, (float) msg.obj);
-                    return true;
-                case MSG_SESSION_CLOSED:
-                    mCallback.onClosed(msg.arg1);
+                    mCallback.onProgressChanged(sessionId, (float) msg.obj);
                     return true;
                 case MSG_SESSION_FINISHED:
-                    mCallback.onFinished(msg.arg1, msg.arg2 != 0);
+                    mCallback.onFinished(sessionId, msg.arg2 != 0);
                     return true;
             }
             return false;
@@ -516,8 +549,9 @@ public class PackageInstaller {
         }
 
         @Override
-        public void onSessionOpened(int sessionId) {
-            mHandler.obtainMessage(MSG_SESSION_OPENED, sessionId, 0).sendToTarget();
+        public void onSessionActiveChanged(int sessionId, boolean active) {
+            mHandler.obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, active ? 1 : 0)
+                    .sendToTarget();
         }
 
         @Override
@@ -527,11 +561,6 @@ public class PackageInstaller {
         }
 
         @Override
-        public void onSessionClosed(int sessionId) {
-            mHandler.obtainMessage(MSG_SESSION_CLOSED, sessionId, 0).sendToTarget();
-        }
-
-        @Override
         public void onSessionFinished(int sessionId, boolean success) {
             mHandler.obtainMessage(MSG_SESSION_FINISHED, sessionId, success ? 1 : 0)
                     .sendToTarget();
@@ -545,7 +574,8 @@ public class PackageInstaller {
     }
 
     /**
-     * Register to watch for session lifecycle events.
+     * Register to watch for session lifecycle events. No special permissions
+     * are required to watch for these events.
      */
     public void registerSessionCallback(@NonNull SessionCallback callback) {
         registerSessionCallback(callback, new Handler());
@@ -558,7 +588,8 @@ public class PackageInstaller {
     }
 
     /**
-     * Register to watch for session lifecycle events.
+     * Register to watch for session lifecycle events. No special permissions
+     * are required to watch for these events.
      *
      * @param handler to dispatch callback events through, otherwise uses
      *            calling thread.
@@ -567,7 +598,7 @@ public class PackageInstaller {
         // TODO: remove this temporary guard once we have new prebuilts
         final ApplicationInfo info = mContext.getApplicationInfo();
         if ("com.google.android.googlequicksearchbox".equals(info.packageName)
-                && info.versionCode <= 300400070) {
+                && info.versionCode <= 300400110) {
             Log.d(TAG, "Ignoring callback request from old prebuilt");
             return;
         }
@@ -591,7 +622,7 @@ public class PackageInstaller {
     }
 
     /**
-     * Unregister an existing callback.
+     * Unregister a previously registered callback.
      */
     public void unregisterSessionCallback(@NonNull SessionCallback callback) {
         synchronized (mDelegates) {
@@ -629,10 +660,22 @@ public class PackageInstaller {
             mSession = session;
         }
 
+        /** {@hide} */
+        @Deprecated
+        public void setProgress(float progress) {
+            setStagingProgress(progress);
+        }
+
         /**
-         * Set current progress. Valid values are anywhere between 0 and 1.
+         * Set current progress of staging this session. Valid values are
+         * anywhere between 0 and 1.
+         * <p>
+         * Note that this progress may not directly correspond to the value
+         * reported by {@link SessionCallback#onProgressChanged(int, float)}, as
+         * the system may carve out a portion of the overall progress to
+         * represent its own internal installation work.
          */
-        public void setProgress(float progress) {
+        public void setStagingProgress(float progress) {
             try {
                 mSession.setClientProgress(progress);
             } catch (RemoteException e) {
@@ -672,6 +715,12 @@ public class PackageInstaller {
          *            start at the beginning of the file.
          * @param lengthBytes total size of the file being written, used to
          *            preallocate the underlying disk space, or -1 if unknown.
+         *            The system may clear various caches as needed to allocate
+         *            this space.
+         * @throws IOException if trouble opening the file for writing, such as
+         *             lack of disk space or unavailable media.
+         * @throws SecurityException if called after the session has been
+         *             committed or abandoned.
          */
         public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
                 long lengthBytes) throws IOException {
@@ -705,6 +754,9 @@ public class PackageInstaller {
          * <p>
          * This returns all names which have been previously written through
          * {@link #openWrite(String, long, long)} as part of this session.
+         *
+         * @throws SecurityException if called after the session has been
+         *             committed or abandoned.
          */
         public @NonNull String[] getNames() throws IOException {
             try {
@@ -724,6 +776,9 @@ public class PackageInstaller {
          * through {@link #openWrite(String, long, long)} as part of this
          * session. For example, this stream may be used to calculate a
          * {@link MessageDigest} of a written APK before committing.
+         *
+         * @throws SecurityException if called after the session has been
+         *             committed or abandoned.
          */
         public @NonNull InputStream openRead(@NonNull String name) throws IOException {
             try {
@@ -745,6 +800,9 @@ public class PackageInstaller {
          * Once this method is called, no additional mutations may be performed
          * on the session. If the device reboots before the session has been
          * finalized, you may commit the session again.
+         *
+         * @throws SecurityException if streams opened through
+         *             {@link #openWrite(String, long, long)} are still open.
          */
         public void commit(@NonNull IntentSender statusReceiver) {
             try {
@@ -769,7 +827,9 @@ public class PackageInstaller {
 
         /**
          * Completely abandon this session, destroying all staged data and
-         * rendering it invalid.
+         * rendering it invalid. Abandoned sessions will be reported to
+         * {@link SessionCallback} listeners as failures. This is equivalent to
+         * opening the session and calling {@link Session#abandon()}.
          */
         public void abandon() {
             try {
@@ -923,6 +983,18 @@ public class PackageInstaller {
         }
 
         /** {@hide} */
+        public void setInstallFlagsInternal() {
+            installFlags |= PackageManager.INSTALL_INTERNAL;
+            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
+        }
+
+        /** {@hide} */
+        public void setInstallFlagsExternal() {
+            installFlags |= PackageManager.INSTALL_EXTERNAL;
+            installFlags &= ~PackageManager.INSTALL_INTERNAL;
+        }
+
+        /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
             pw.printHexPair("installFlags", installFlags);
@@ -986,7 +1058,7 @@ public class PackageInstaller {
         /** {@hide} */
         public boolean sealed;
         /** {@hide} */
-        public boolean open;
+        public boolean active;
 
         /** {@hide} */
         public int mode;
@@ -1010,7 +1082,7 @@ public class PackageInstaller {
             resolvedBaseCodePath = source.readString();
             progress = source.readFloat();
             sealed = source.readInt() != 0;
-            open = source.readInt() != 0;
+            active = source.readInt() != 0;
 
             mode = source.readInt();
             sizeBytes = source.readLong();
@@ -1036,20 +1108,37 @@ public class PackageInstaller {
         /**
          * Return current overall progress of this session, between 0 and 1.
          * <p>
-         * Note that this progress may not directly correspond to the value reported
-         * by {@link PackageInstaller.Session#setProgress(float)}, as the system may
-         * carve out a portion of the overall progress to represent its own internal
-         * installation work.
+         * Note that this progress may not directly correspond to the value
+         * reported by
+         * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
+         * system may carve out a portion of the overall progress to represent
+         * its own internal installation work.
          */
         public float getProgress() {
             return progress;
         }
 
         /**
-         * Return if this session is currently open.
+         * Return if this session is currently active.
+         * <p>
+         * A session is considered active whenever there is ongoing forward
+         * progress being made, such as the installer holding an open
+         * {@link Session} instance while streaming data into place, or the
+         * system optimizing code as the result of
+         * {@link Session#commit(IntentSender)}.
+         * <p>
+         * If the installer closes the {@link Session} without committing, the
+         * session is considered inactive until the installer opens the session
+         * again.
          */
+        public boolean isActive() {
+            return active;
+        }
+
+        /** {@hide} */
+        @Deprecated
         public boolean isOpen() {
-            return open;
+            return isActive();
         }
 
         /**
@@ -1105,7 +1194,7 @@ public class PackageInstaller {
             dest.writeString(resolvedBaseCodePath);
             dest.writeFloat(progress);
             dest.writeInt(sealed ? 1 : 0);
-            dest.writeInt(open ? 1 : 0);
+            dest.writeInt(active ? 1 : 0);
 
             dest.writeInt(mode);
             dest.writeLong(sizeBytes);
index e87adda..8e64e36 100644 (file)
@@ -976,6 +976,14 @@ public abstract class PackageManager {
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes at least one form of audio
+     * output, such as speakers, audio jack or streaming over bluetooth
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device is capable of communicating with
      * other devices via Bluetooth.
      */
@@ -1546,6 +1554,15 @@ public abstract class PackageManager {
     public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has all of the inputs necessary to be considered a compatible game controller, or
+     * includes a compatible game controller in the box.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
+
+
+    /**
      * Action to external storage service to clean out removed apps.
      * @hide
      */
index 6d40dcf..e0fd532 100644 (file)
@@ -3110,6 +3110,12 @@ public class PackageParser {
                     false)) {
                 a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
             }
+
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestActivity_resumeWhilePausing,
+                    false)) {
+                a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+            }
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
index 2684e6c..e578822 100644 (file)
@@ -773,6 +773,7 @@ public final class AssetManager implements AutoCloseable {
     private native final String[] getArrayStringResource(int arrayRes);
     private native final int[] getArrayStringInfo(int arrayRes);
     /*package*/ native final int[] getArrayIntResource(int arrayRes);
+    /*package*/ native final int[] getStyleAttributes(int themeRes);
 
     private native final void init(boolean isSystem);
     private native final void destroy();
index 31813c1..cabe228 100644 (file)
@@ -1572,6 +1572,16 @@ public class Resources {
         }
 
         /**
+         * Gets all of the attribute ids associated with this {@link Theme}. For debugging only.
+         *
+         * @return The int array containing attribute ids associated with this {@link Theme}.
+         * @hide
+         */
+        public int[] getAllAttributes() {
+            return mAssets.getStyleAttributes(getAppliedStyleResId());
+        }
+
+        /**
          * Returns the resources to which this theme belongs.
          *
          * @return Resources to which this theme belongs.
index 7a49eb5..7390e2b 100644 (file)
@@ -480,7 +480,7 @@ public class SoundTrigger {
             int capturePreambleMs = in.readInt();
             boolean triggerInData = in.readByte() == 1;
             AudioFormat captureFormat = null;
-            if (triggerInData) {
+            if (in.readByte() == 1) {
                 int sampleRate = in.readInt();
                 int encoding = in.readInt();
                 int channelMask = in.readInt();
@@ -508,7 +508,8 @@ public class SoundTrigger {
             dest.writeInt(captureSession);
             dest.writeInt(captureDelayMs);
             dest.writeInt(capturePreambleMs);
-            if (triggerInData && (captureFormat != null)) {
+            dest.writeByte((byte) (triggerInData ? 1 : 0));
+            if (captureFormat != null) {
                 dest.writeByte((byte)1);
                 dest.writeInt(captureFormat.getSampleRate());
                 dest.writeInt(captureFormat.getEncoding());
index fe9f79b..461469c 100644 (file)
@@ -1667,7 +1667,7 @@ public abstract class BatteryStats implements Parcelable {
      */
     public abstract long[] getChargeStepDurationsArray();
 
-    public abstract Map<String, ? extends LongCounter> getWakeupReasonStats();
+    public abstract Map<String, ? extends Timer> getWakeupReasonStats();
 
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
 
@@ -2045,11 +2045,15 @@ public abstract class BatteryStats implements Parcelable {
                             sb.toString());
                 }
             }
-            Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+            Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
             if (wakeupReasons.size() > 0) {
-                for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+                for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
+                    // Not doing the regular wake lock formatting to remain compatible
+                    // with the old checkin format.
+                    long totalTimeMicros = ent.getValue().getTotalTimeLocked(rawRealtime, which);
+                    int count = ent.getValue().getCountLocked(which);
                     dumpLine(pw, 0 /* uid */, category, WAKEUP_REASON_DATA,
-                            "\"" + ent.getKey() + "\"", ent.getValue().getCountLocked(which));
+                            "\"" + ent.getKey() + "\"", (totalTimeMicros + 500) / 1000, count);
                 }
             }
         }
@@ -2921,14 +2925,14 @@ public abstract class BatteryStats implements Parcelable {
                 pw.println();
             }
 
-            Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+            Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
             if (wakeupReasons.size() > 0) {
                 pw.print(prefix); pw.println("  All wakeup reasons:");
                 final ArrayList<TimerEntry> reasons = new ArrayList<TimerEntry>();
-                for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
-                    BatteryStats.LongCounter counter = ent.getValue();
-                    reasons.add(new TimerEntry(ent.getKey(), 0, null,
-                            ent.getValue().getCountLocked(which)));
+                for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
+                    Timer timer = ent.getValue();
+                    reasons.add(new TimerEntry(ent.getKey(), 0, timer,
+                            timer.getCountLocked(which)));
                 }
                 Collections.sort(reasons, timerComparator);
                 for (int i=0; i<reasons.size(); i++) {
@@ -2938,9 +2942,8 @@ public abstract class BatteryStats implements Parcelable {
                     sb.append(prefix);
                     sb.append("  Wakeup reason ");
                     sb.append(timer.mName);
-                    sb.append(": ");
-                    formatTimeMs(sb, timer.mTime);
-                    sb.append("realtime");
+                    printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
+                    sb.append(" realtime");
                     pw.println(sb.toString());
                 }
                 pw.println();
@@ -3138,7 +3141,7 @@ public abstract class BatteryStats implements Parcelable {
             }
 
             Map<String, ? extends Timer> jobs = u.getJobStats();
-            if (syncs.size() > 0) {
+            if (jobs.size() > 0) {
                 for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
                     Timer timer = ent.getValue();
                     // Convert from microseconds to milliseconds with rounding
@@ -3952,7 +3955,7 @@ public abstract class BatteryStats implements Parcelable {
         prepareForDumpLocked();
 
         dumpLine(pw, 0 /* uid */, "i" /* category */, VERSION_DATA,
-                "10", getParcelVersion(), getStartPlatformVersion(), getEndPlatformVersion());
+                "11", getParcelVersion(), getStartPlatformVersion(), getEndPlatformVersion());
 
         long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
 
index 022a106..0acf24b 100644 (file)
@@ -75,6 +75,13 @@ public class FileBridge extends Thread {
         return mClosed;
     }
 
+    public void forceClose() {
+        IoUtils.closeQuietly(mTarget);
+        IoUtils.closeQuietly(mServer);
+        IoUtils.closeQuietly(mClient);
+        mClosed = true;
+    }
+
     public void setTargetFile(FileDescriptor target) {
         mTarget = target;
     }
@@ -89,7 +96,6 @@ public class FileBridge extends Thread {
         try {
             while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
                 final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
-
                 if (cmd == CMD_WRITE) {
                     // Shuttle data into local file
                     int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
@@ -118,15 +124,10 @@ public class FileBridge extends Thread {
                 }
             }
 
-        } catch (ErrnoException e) {
-            Log.wtf(TAG, "Failed during bridge", e);
-        } catch (IOException e) {
+        } catch (ErrnoException | IOException e) {
             Log.wtf(TAG, "Failed during bridge", e);
         } finally {
-            IoUtils.closeQuietly(mTarget);
-            IoUtils.closeQuietly(mServer);
-            IoUtils.closeQuietly(mClient);
-            mClosed = true;
+            forceClose();
         }
     }
 
@@ -151,6 +152,7 @@ public class FileBridge extends Thread {
                 writeCommandAndBlock(CMD_CLOSE, "close()");
             } finally {
                 IoBridge.closeAndSignalBlockedThreads(mClient);
+                IoUtils.closeQuietly(mClientPfd);
             }
         }
 
index 1a5811c..b6b70cc 100644 (file)
@@ -113,6 +113,8 @@ public final class Message implements Parcelable {
 
     private static final int MAX_POOL_SIZE = 50;
 
+    private static boolean gCheckRecycle = true;
+
     /**
      * Return a new Message instance from the global pool. Allows us to
      * avoid allocating new objects in many cases.
@@ -256,6 +258,13 @@ public final class Message implements Parcelable {
         return m;
     }
 
+    /** @hide */
+    public static void updateCheckRecycle(int targetSdkVersion) {
+        if (targetSdkVersion < Build.VERSION_CODES.L) {
+            gCheckRecycle = false;
+        }
+    }
+
     /**
      * Return a Message instance to the global pool.
      * <p>
@@ -266,8 +275,11 @@ public final class Message implements Parcelable {
      */
     public void recycle() {
         if (isInUse()) {
-            throw new IllegalStateException("This message cannot be recycled because it "
-                    + "is still in use.");
+            if (gCheckRecycle) {
+                throw new IllegalStateException("This message cannot be recycled because it "
+                        + "is still in use.");
+            }
+            return;
         }
         recycleUnchecked();
     }
index 9361286..bf8ac65 100644 (file)
@@ -535,7 +535,7 @@ public final class PrintManager {
                 destroyed = isDestroyedLocked();
             }
 
-            if (destroyed) {
+            if (destroyed && observer != null) {
                 try {
                     observer.onDestroy();
                 } catch (RemoteException re) {
index 5fa1cc9..a5e86d8 100644 (file)
@@ -167,8 +167,6 @@ public class CallLog {
          */
         public static final String FEATURES = "features";
 
-        /** Call had no associated features (e.g. voice-only). */
-        public static final int FEATURES_NONE = 0x0;
         /** Call had video. */
         public static final int FEATURES_VIDEO = 0x1;
 
index 8ca9b6c..e6bf6ba 100644 (file)
@@ -16,6 +16,7 @@
 
 package android.service.notification;
 
+import android.service.notification.IStatusBarNotificationHolder;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.NotificationRankingUpdate;
 
@@ -23,9 +24,9 @@ import android.service.notification.NotificationRankingUpdate;
 oneway interface INotificationListener
 {
     void onListenerConnected(in NotificationRankingUpdate update);
-    void onNotificationPosted(in StatusBarNotification notification,
+    void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
             in NotificationRankingUpdate update);
-    void onNotificationRemoved(in StatusBarNotification notification,
+    void onNotificationRemoved(in IStatusBarNotificationHolder notificationHolder,
             in NotificationRankingUpdate update);
     void onNotificationRankingUpdate(in NotificationRankingUpdate update);
     void onListenerHintsChanged(int hints);
diff --git a/core/java/android/service/notification/IStatusBarNotificationHolder.aidl b/core/java/android/service/notification/IStatusBarNotificationHolder.aidl
new file mode 100644 (file)
index 0000000..fd6b59e
--- /dev/null
@@ -0,0 +1,24 @@
+/**
+ * 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.notification;
+
+import android.service.notification.StatusBarNotification;
+
+/** @hide */
+interface IStatusBarNotificationHolder {
+    StatusBarNotification get();
+}
index 2ca8098..d744070 100644 (file)
@@ -603,8 +603,15 @@ public abstract class NotificationListenerService extends Service {
 
     private class INotificationListenerWrapper extends INotificationListener.Stub {
         @Override
-        public void onNotificationPosted(StatusBarNotification sbn,
+        public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
                 NotificationRankingUpdate update) {
+            StatusBarNotification sbn;
+            try {
+                sbn = sbnHolder.get();
+            } catch (RemoteException e) {
+                Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
+                return;
+            }
             Notification.Builder.rebuild(getContext(), sbn.getNotification());
 
             // protect subclass from concurrent modifications of (@link mNotificationKeys}.
@@ -618,8 +625,15 @@ public abstract class NotificationListenerService extends Service {
             }
         }
         @Override
-        public void onNotificationRemoved(StatusBarNotification sbn,
+        public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
                 NotificationRankingUpdate update) {
+            StatusBarNotification sbn;
+            try {
+                sbn = sbnHolder.get();
+            } catch (RemoteException e) {
+                Log.w(TAG, "onNotificationRemoved: Error receiving StatusBarNotification", e);
+                return;
+            }
             // protect subclass from concurrent modifications of (@link mNotificationKeys}.
             synchronized (mWrapper) {
                 applyUpdate(update);
index 4de5f41..8aa2689 100644 (file)
@@ -468,19 +468,6 @@ public class AlwaysOnHotwordDetector {
     }
 
     /**
-     * FIXME: Remove once the prebuilts are updated.
-     *
-     * @hide
-     */
-    @Deprecated
-    public Intent createIntentToEnroll() {
-        if (DBG) Slog.d(TAG, "createIntentToEnroll");
-        synchronized (mLock) {
-            return getManageIntentLocked(MANAGE_ACTION_ENROLL);
-        }
-    }
-
-    /**
      * Creates an intent to start the un-enrollment for the associated keyphrase.
      * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
      * Starting re-enrollment is only valid if the keyphrase is already enrolled,
@@ -502,19 +489,6 @@ public class AlwaysOnHotwordDetector {
     }
 
     /**
-     * FIXME: Remove once the prebuilts are updated.
-     *
-     * @hide
-     */
-    @Deprecated
-    public Intent createIntentToUnEnroll() {
-        if (DBG) Slog.d(TAG, "createIntentToUnEnroll");
-        synchronized (mLock) {
-            return getManageIntentLocked(MANAGE_ACTION_UN_ENROLL);
-        }
-    }
-
-    /**
      * Creates an intent to start the re-enrollment for the associated keyphrase.
      * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
      * Starting re-enrollment is only valid if the keyphrase is already enrolled,
@@ -535,19 +509,6 @@ public class AlwaysOnHotwordDetector {
         }
     }
 
-    /**
-     * FIXME: Remove once the prebuilts are updated.
-     *
-     * @hide
-     */
-    @Deprecated
-    public Intent createIntentToReEnroll() {
-        if (DBG) Slog.d(TAG, "createIntentToReEnroll");
-        synchronized (mLock) {
-            return getManageIntentLocked(MANAGE_ACTION_RE_ENROLL);
-        }
-    }
-
     private Intent getManageIntentLocked(int action) {
         if (mAvailability == STATE_INVALID) {
             throw new IllegalStateException("getManageIntent called on an invalid detector");
index 7245975..a4b6e92 100644 (file)
@@ -578,7 +578,7 @@ public class TextToSpeech {
          *
          * @deprecated Starting from API level 20, to select network synthesis, call
          * ({@link TextToSpeech#getVoices()}, find a suitable network voice
-         * ({@link Voice#getRequiresNetworkConnection()}) and pass it
+         * ({@link Voice#isNetworkConnectionRequired()}) and pass it
          * to {@link TextToSpeech#setVoice(Voice)}).
          */
         @Deprecated
@@ -596,7 +596,7 @@ public class TextToSpeech {
 
          * @deprecated Starting from API level 20, to select embedded synthesis, call
          * ({@link TextToSpeech#getVoices()}, find a suitable embedded voice
-         * ({@link Voice#getRequiresNetworkConnection()}) and pass it
+         * ({@link Voice#isNetworkConnectionRequired()}) and pass it
          * to {@link TextToSpeech#setVoice(Voice)}).
          */
         @Deprecated
@@ -957,20 +957,18 @@ public class TextToSpeech {
      *
      * @param text
      *            The string of text. Example: <code>"south_south_east"</code>
-     * @param filename
-     *            The full path to the sound file (for example:
-     *            "/sdcard/mysounds/hello.wav")
+     * @param file
+     *            File object pointing to the sound file.
      *
      * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
      */
-    public int addSpeech(CharSequence text, String filename) {
+    public int addSpeech(CharSequence text, File file) {
         synchronized (mStartLock) {
-            mUtterances.put(text, Uri.parse(filename));
+            mUtterances.put(text, Uri.fromFile(file));
             return SUCCESS;
         }
     }
 
-
     /**
      * Adds a mapping between a string of text and a sound resource in a
      * package. Use this to add custom earcons.
@@ -1017,7 +1015,11 @@ public class TextToSpeech {
      *            "/sdcard/mysounds/tick.wav")
      *
      * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     *
+     * @deprecated As of API level 20, replaced by
+     *         {@link #addEarcon(String, File)}.
      */
+    @Deprecated
     public int addEarcon(String earcon, String filename) {
         synchronized(mStartLock) {
             mEarcons.put(earcon, Uri.parse(filename));
@@ -1025,6 +1027,27 @@ public class TextToSpeech {
         }
     }
 
+    /**
+     * Adds a mapping between a string of text and a sound file.
+     * Use this to add custom earcons.
+     *
+     * @see #playEarcon(String, int, HashMap)
+     *
+     * @param earcon
+     *            The name of the earcon.
+     *            Example: <code>"[tick]"</code>
+     * @param file
+     *            File object pointing to the sound file.
+     *
+     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     */
+    public int addEarcon(String earcon, File file) {
+        synchronized(mStartLock) {
+            mEarcons.put(earcon, Uri.fromFile(file));
+            return SUCCESS;
+        }
+    }
+
     private Uri makeResourceUri(String packageName, int resourceId) {
         return new Uri.Builder()
                 .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
@@ -1061,7 +1084,7 @@ public class TextToSpeech {
      */
     public int speak(final CharSequence text,
                      final int queueMode,
-                     final HashMap<String, String> params,
+                     final Bundle params,
                      final String utteranceId) {
         return runAction(new Action<Integer>() {
             @Override
@@ -1103,11 +1126,11 @@ public class TextToSpeech {
      *
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
      * @deprecated As of API level 20, replaced by
-     *         {@link #speak(CharSequence, int, HashMap, String)}.
+     *         {@link #speak(CharSequence, int, Bundle, String)}.
      */
     @Deprecated
     public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
-        return speak(text, queueMode, params,
+        return speak(text, queueMode, convertParamsHashMaptoBundle(params),
                      params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
     }
 
@@ -1135,7 +1158,7 @@ public class TextToSpeech {
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
      */
     public int playEarcon(final String earcon, final int queueMode,
-            final HashMap<String, String> params, final String utteranceId) {
+            final Bundle params, final String utteranceId) {
         return runAction(new Action<Integer>() {
             @Override
             public Integer run(ITextToSpeechService service) throws RemoteException {
@@ -1173,12 +1196,12 @@ public class TextToSpeech {
      *
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
      * @deprecated As of API level 20, replaced by
-     *         {@link #playEarcon(String, int, HashMap, String)}.
+     *         {@link #playEarcon(String, int, Bundle, String)}.
      */
     @Deprecated
     public int playEarcon(final String earcon, final int queueMode,
             final HashMap<String, String> params) {
-        return playEarcon(earcon, queueMode, params,
+        return playEarcon(earcon, queueMode, convertParamsHashMaptoBundle(params),
                           params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
     }
 
@@ -1757,22 +1780,20 @@ public class TextToSpeech {
      *            must be prefixed by the name of the engine they are intended for. For example
      *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
      *            engine named "com.svox.pico" if it is being used.
-     * @param filename Absolute file filename to write the generated audio data to.It should be
-     *            something like "/sdcard/myappsounds/mysound.wav".
+     * @param file File to write the generated audio data to.
      * @param utteranceId An unique identifier for this request.
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
      */
-    public int synthesizeToFile(final CharSequence text, final HashMap<String, String> params,
-            final String filename, final String utteranceId) {
+    public int synthesizeToFile(final CharSequence text, final Bundle params,
+            final File file, final String utteranceId) {
         return runAction(new Action<Integer>() {
             @Override
             public Integer run(ITextToSpeechService service) throws RemoteException {
                 ParcelFileDescriptor fileDescriptor;
                 int returnValue;
                 try {
-                    File file = new File(filename);
                     if(file.exists() && !file.canWrite()) {
-                        Log.e(TAG, "Can't write to " + filename);
+                        Log.e(TAG, "Can't write to " + file);
                         return ERROR;
                     }
                     fileDescriptor = ParcelFileDescriptor.open(file,
@@ -1784,10 +1805,10 @@ public class TextToSpeech {
                     fileDescriptor.close();
                     return returnValue;
                 } catch (FileNotFoundException e) {
-                    Log.e(TAG, "Opening file " + filename + " failed", e);
+                    Log.e(TAG, "Opening file " + file + " failed", e);
                     return ERROR;
                 } catch (IOException e) {
-                    Log.e(TAG, "Closing file " + filename + " failed", e);
+                    Log.e(TAG, "Closing file " + file + " failed", e);
                     return ERROR;
                 }
             }
@@ -1817,16 +1838,18 @@ public class TextToSpeech {
      *
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
      * @deprecated As of API level 20, replaced by
-     *         {@link #synthesizeToFile(CharSequence, HashMap, String, String)}.
+     *         {@link #synthesizeToFile(CharSequence, Bundle, File, String)}.
      */
+    @Deprecated
     public int synthesizeToFile(final String text, final HashMap<String, String> params,
             final String filename) {
-        return synthesizeToFile(text, params, filename, params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+        return synthesizeToFile(text, convertParamsHashMaptoBundle(params),
+                new File(filename), params.get(Engine.KEY_PARAM_UTTERANCE_ID));
     }
 
-    private Bundle getParams(HashMap<String, String> params) {
+    private Bundle convertParamsHashMaptoBundle(HashMap<String, String> params) {
         if (params != null && !params.isEmpty()) {
-            Bundle bundle = new Bundle(mParams);
+            Bundle bundle = new Bundle();
             copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
             copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID);
             copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
@@ -1852,11 +1875,85 @@ public class TextToSpeech {
             }
 
             return bundle;
+        }
+        return null;
+    }
+
+    private Bundle getParams(Bundle params) {
+        if (params != null && !params.isEmpty()) {
+            Bundle bundle = new Bundle(mParams);
+            bundle.putAll(params);
+
+            verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_STREAM);
+            verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_SESSION_ID);
+            verifyStringBundleParam(bundle, Engine.KEY_PARAM_UTTERANCE_ID);
+            verifyFloatBundleParam(bundle, Engine.KEY_PARAM_VOLUME);
+            verifyFloatBundleParam(bundle, Engine.KEY_PARAM_PAN);
+
+            // Copy feature strings defined by the framework.
+            verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
+            verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
+            verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_TIMEOUT_MS);
+            verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_RETRIES_COUNT);
+
+            return bundle;
         } else {
             return mParams;
         }
     }
 
+    private static boolean verifyIntegerBundleParam(Bundle bundle, String key) {
+        if (bundle.containsKey(key)) {
+            if (!(bundle.get(key) instanceof Integer ||
+                    bundle.get(key) instanceof Long)) {
+                bundle.remove(key);
+                Log.w(TAG, "Synthesis request paramter " + key + " containst value "
+                        + " with invalid type. Should be an Integer or a Long");
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean verifyStringBundleParam(Bundle bundle, String key) {
+        if (bundle.containsKey(key)) {
+            if (!(bundle.get(key) instanceof String)) {
+                bundle.remove(key);
+                Log.w(TAG, "Synthesis request paramter " + key + " containst value "
+                        + " with invalid type. Should be a String");
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean verifyBooleanBundleParam(Bundle bundle, String key) {
+        if (bundle.containsKey(key)) {
+            if (!(bundle.get(key) instanceof Boolean ||
+                    bundle.get(key) instanceof String)) {
+                bundle.remove(key);
+                Log.w(TAG, "Synthesis request paramter " + key + " containst value "
+                        + " with invalid type. Should be a Boolean or String");
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    private static boolean verifyFloatBundleParam(Bundle bundle, String key) {
+        if (bundle.containsKey(key)) {
+            if (!(bundle.get(key) instanceof Float ||
+                    bundle.get(key) instanceof Double)) {
+                bundle.remove(key);
+                Log.w(TAG, "Synthesis request paramter " + key + " containst value "
+                        + " with invalid type. Should be a Float or a Double");
+                return false;
+            }
+        }
+        return true;
+    }
+
     private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) {
         String value = params.get(key);
         if (value != null) {
index 4fea109..d00a433 100644 (file)
@@ -84,7 +84,7 @@ import java.util.Set;
  * the following methods:
  * <ul>
  * <li>{@link #onGetVoices()}</li>
- * <li>{@link #isValidVoiceName(String)}</li>
+ * <li>{@link #onIsValidVoiceName(String)}</li>
  * <li>{@link #onLoadVoice(String)}</li>
  * <li>{@link #onGetDefaultVoiceNameFor(String, String, String)}</li>
  * </ul>
@@ -278,7 +278,7 @@ public abstract class TextToSpeechService extends Service {
      *
      * @return A list of voices supported.
      */
-    protected List<Voice> onGetVoices() {
+    public List<Voice> onGetVoices() {
         // Enumerate all locales and check if they are available
         ArrayList<Voice> voices = new ArrayList<Voice>();
         for (Locale locale : Locale.getAvailableLocales()) {
@@ -335,7 +335,7 @@ public abstract class TextToSpeechService extends Service {
         }
         Locale properLocale = TtsEngines.normalizeTTSLocale(iso3Locale);
         String voiceName = properLocale.toLanguageTag();
-        if (isValidVoiceName(voiceName) == TextToSpeech.SUCCESS) {
+        if (onIsValidVoiceName(voiceName) == TextToSpeech.SUCCESS) {
             return voiceName;
         } else {
             return null;
@@ -357,7 +357,7 @@ public abstract class TextToSpeechService extends Service {
      * @param voiceName Name of the voice.
      * @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}.
      */
-    protected int onLoadVoice(String voiceName) {
+    public int onLoadVoice(String voiceName) {
         Locale locale = Locale.forLanguageTag(voiceName);
         if (locale == null) {
             return TextToSpeech.ERROR;
@@ -388,7 +388,7 @@ public abstract class TextToSpeechService extends Service {
      * @param voiceName Name of the voice.
      * @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}.
      */
-    protected int isValidVoiceName(String voiceName) {
+    public int onIsValidVoiceName(String voiceName) {
         Locale locale = Locale.forLanguageTag(voiceName);
         if (locale == null) {
             return TextToSpeech.ERROR;
@@ -1275,7 +1275,7 @@ public abstract class TextToSpeechService extends Service {
             if (!checkNonNull(voiceName)) {
                 return TextToSpeech.ERROR;
             }
-            int retVal = isValidVoiceName(voiceName);
+            int retVal = onIsValidVoiceName(voiceName);
 
             if (retVal == TextToSpeech.SUCCESS) {
                 SpeechItem item = new LoadVoiceItem(caller, Binder.getCallingUid(),
index a97141c..a1fa51d 100644 (file)
@@ -162,7 +162,7 @@ public class Voice implements Parcelable {
     /**
      * @return Does the Voice require a network connection to work.
      */
-    public boolean getRequiresNetworkConnection() {
+    public boolean isNetworkConnectionRequired() {
         return mRequiresNetworkConnection;
     }
 
index 0d1b568..40bb6ec 100644 (file)
@@ -1417,9 +1417,9 @@ public abstract class Transition implements Cloneable {
                     }
                     capturePropagationValues(values);
                     if (start) {
-                        addViewValues(mStartValues, view, values);
+                        addViewValues(mStartValues, view, values, true);
                     } else {
-                        addViewValues(mEndValues, view, values);
+                        addViewValues(mEndValues, view, values, true);
                     }
                 }
             }
@@ -1460,7 +1460,7 @@ public abstract class Transition implements Cloneable {
     }
 
     static void addViewValues(TransitionValuesMaps transitionValuesMaps,
-            View view, TransitionValues transitionValues) {
+            View view, TransitionValues transitionValues, boolean setTransientState) {
         transitionValuesMaps.viewValues.put(view, transitionValues);
         int id = view.getId();
         if (id >= 0) {
@@ -1489,11 +1489,15 @@ public abstract class Transition implements Cloneable {
                     // Duplicate item IDs: cannot match by item ID.
                     View alreadyMatched = transitionValuesMaps.itemIdValues.get(itemId);
                     if (alreadyMatched != null) {
-                        alreadyMatched.setHasTransientState(false);
+                        if (setTransientState) {
+                            alreadyMatched.setHasTransientState(false);
+                        }
                         transitionValuesMaps.itemIdValues.put(itemId, null);
                     }
                 } else {
-                    view.setHasTransientState(true);
+                    if (setTransientState) {
+                        view.setHasTransientState(true);
+                    }
                     transitionValuesMaps.itemIdValues.put(itemId, view);
                 }
             }
@@ -1560,9 +1564,9 @@ public abstract class Transition implements Cloneable {
             }
             capturePropagationValues(values);
             if (start) {
-                addViewValues(mStartValues, view, values);
+                addViewValues(mStartValues, view, values, true);
             } else {
-                addViewValues(mEndValues, view, values);
+                addViewValues(mEndValues, view, values, true);
             }
         }
         if (view instanceof ViewGroup) {
index f6499ae..56db674 100644 (file)
@@ -408,7 +408,7 @@ public class TransitionSet extends Transition {
         for (int i = 0; i < numValues; i++) {
             View view = values.viewValues.keyAt(i);
             if (isValidTarget(view)) {
-                addViewValues(included, view, values.viewValues.valueAt(i));
+                addViewValues(included, view, values.viewValues.valueAt(i), false);
             }
         }
         return included;
index 0b02552..d648ca6 100644 (file)
@@ -358,12 +358,16 @@ public abstract class Visibility extends Transition {
                     overlayView = startView;
                 } else if (startView.getParent() instanceof View) {
                     View startParent = (View) startView.getParent();
-                    if (!isValidTarget(startParent)) {
-                        if (startView.isAttachedToWindow()) {
-                            overlayView = copyViewImage(startView);
-                        } else {
-                            overlayView = startView;
-                        }
+                    VisibilityInfo parentVisibilityInfo = null;
+                    TransitionValues endParentValues = getMatchedTransitionValues(startParent,
+                            true);
+                    if (endParentValues != null) {
+                        TransitionValues startParentValues = getTransitionValues(startParent, true);
+                        parentVisibilityInfo =
+                                getVisibilityChangeInfo(startParentValues, endParentValues);
+                    }
+                    if (parentVisibilityInfo == null || !parentVisibilityInfo.visibilityChange) {
+                        overlayView = copyViewImage(startView);
                     } else if (startParent.getParent() == null) {
                         int id = startParent.getId();
                         if (id != View.NO_ID && sceneRoot.findViewById(id) != null
index a10dda3..a283b91 100644 (file)
@@ -19,7 +19,6 @@ package android.view;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -101,7 +100,7 @@ final class AccessibilityInteractionController {
             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
             long interrogatingTid, MagnificationSpec spec) {
         Message message = mHandler.obtainMessage();
-        message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
+        message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
         message.arg1 = flags;
 
         SomeArgs args = SomeArgs.obtain();
@@ -176,7 +175,7 @@ final class AccessibilityInteractionController {
             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
             long interrogatingTid, MagnificationSpec spec) {
         Message message = mHandler.obtainMessage();
-        message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID;
+        message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
         message.arg1 = flags;
         message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
 
@@ -261,7 +260,7 @@ final class AccessibilityInteractionController {
             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
             long interrogatingTid, MagnificationSpec spec) {
         Message message = mHandler.obtainMessage();
-        message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
+        message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
         message.arg1 = flags;
 
         SomeArgs args = SomeArgs.obtain();
@@ -637,6 +636,95 @@ final class AccessibilityInteractionController {
         }
     }
 
+    public void computeClickPointInScreenClientThread(long accessibilityNodeId,
+            Region interactiveRegion, int interactionId,
+            IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+            long interrogatingTid, MagnificationSpec spec) {
+        Message message = mHandler.obtainMessage();
+        message.what = PrivateHandler.MSG_COMPUTE_CLICK_POINT_IN_SCREEN;
+
+        SomeArgs args = SomeArgs.obtain();
+        args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
+        args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
+        args.argi3 = interactionId;
+        args.arg1 = callback;
+        args.arg2 = spec;
+        args.arg3 = interactiveRegion;
+
+        message.obj = args;
+
+        // If the interrogation is performed by the same thread as the main UI
+        // thread in this process, set the message as a static reference so
+        // after this call completes the same thread but in the interrogating
+        // client can handle the message to generate the result.
+        if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
+            AccessibilityInteractionClient.getInstanceForThread(
+                    interrogatingTid).setSameThreadMessage(message);
+        } else {
+            mHandler.sendMessage(message);
+        }
+    }
+
+    private void computeClickPointInScreenUiThread(Message message) {
+        SomeArgs args = (SomeArgs) message.obj;
+        final int accessibilityViewId = args.argi1;
+        final int virtualDescendantId = args.argi2;
+        final int interactionId = args.argi3;
+        final IAccessibilityInteractionConnectionCallback callback =
+                (IAccessibilityInteractionConnectionCallback) args.arg1;
+        final MagnificationSpec spec = (MagnificationSpec) args.arg2;
+        final Region interactiveRegion = (Region) args.arg3;
+        args.recycle();
+
+        boolean succeeded = false;
+        Point point = mTempPoint;
+        try {
+            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+                return;
+            }
+            View target = null;
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                target = findViewByAccessibilityId(accessibilityViewId);
+            } else {
+                target = mViewRootImpl.mView;
+            }
+            if (target != null && isShown(target)) {
+                AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
+                if (provider != null) {
+                    // For virtual views just use the center of the bounds in screen.
+                    AccessibilityNodeInfo node = null;
+                    if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                        node = provider.createAccessibilityNodeInfo(virtualDescendantId);
+                    } else {
+                        node = provider.createAccessibilityNodeInfo(
+                                AccessibilityNodeProvider.HOST_VIEW_ID);
+                    }
+                    if (node != null) {
+                        succeeded = true;
+                        Rect boundsInScreen = mTempRect;
+                        node.getBoundsInScreen(boundsInScreen);
+                        point.set(boundsInScreen.centerX(), boundsInScreen.centerY());
+                    }
+                } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                    // For a real view, ask the view to compute the click point.
+                    succeeded = target.computeClickPointInScreenForAccessibility(
+                            interactiveRegion, point);
+                }
+            }
+        } finally {
+            try {
+                Point result = null;
+                if (succeeded) {
+                    applyAppScaleAndMagnificationSpecIfNeeded(point, spec);
+                    result = point;
+                }
+                callback.setComputeClickPointInScreenActionResult(result, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+    }
+
     private View findViewByAccessibilityId(int accessibilityId) {
         View root = mViewRootImpl.mView;
         if (root == null) {
@@ -688,6 +776,26 @@ final class AccessibilityInteractionController {
         }
     }
 
+    private void applyAppScaleAndMagnificationSpecIfNeeded(Point point,
+            MagnificationSpec spec) {
+        final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
+        if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
+            return;
+        }
+
+        if (applicationScale != 1.0f) {
+            point.x *= applicationScale;
+            point.y *= applicationScale;
+        }
+
+        if (spec != null) {
+            point.x *= spec.scale;
+            point.y *= spec.scale;
+            point.x += (int) spec.offsetX;
+            point.y += (int) spec.offsetY;
+        }
+    }
+
     private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
             MagnificationSpec spec) {
         if (info == null) {
@@ -1080,11 +1188,12 @@ final class AccessibilityInteractionController {
 
     private class PrivateHandler extends Handler {
         private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
-        private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
-        private final static int MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID = 3;
-        private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4;
+        private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
+        private final static int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
+        private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
         private final static int MSG_FIND_FOCUS = 5;
         private final static int MSG_FOCUS_SEARCH = 6;
+        private final static int MSG_COMPUTE_CLICK_POINT_IN_SCREEN = 7;
 
         public PrivateHandler(Looper looper) {
             super(looper);
@@ -1096,16 +1205,18 @@ final class AccessibilityInteractionController {
             switch (type) {
                 case MSG_PERFORM_ACCESSIBILITY_ACTION:
                     return "MSG_PERFORM_ACCESSIBILITY_ACTION";
-                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
-                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
-                case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID:
-                    return "MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID";
-                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
-                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
+                case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
+                    return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
+                case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
+                    return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
+                case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
+                    return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
                 case MSG_FIND_FOCUS:
                     return "MSG_FIND_FOCUS";
                 case MSG_FOCUS_SEARCH:
                     return "MSG_FOCUS_SEARCH";
+                case MSG_COMPUTE_CLICK_POINT_IN_SCREEN:
+                    return "MSG_COMPUTE_CLICK_POINT_IN_SCREEN";
                 default:
                     throw new IllegalArgumentException("Unknown message type: " + type);
             }
@@ -1115,16 +1226,16 @@ final class AccessibilityInteractionController {
         public void handleMessage(Message message) {
             final int type = message.what;
             switch (type) {
-                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
+                case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
                     findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
                 } break;
                 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
                     perfromAccessibilityActionUiThread(message);
                 } break;
-                case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: {
+                case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
                     findAccessibilityNodeInfosByViewIdUiThread(message);
                 } break;
-                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
+                case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
                     findAccessibilityNodeInfosByTextUiThread(message);
                 } break;
                 case MSG_FIND_FOCUS: {
@@ -1133,6 +1244,9 @@ final class AccessibilityInteractionController {
                 case MSG_FOCUS_SEARCH: {
                     focusSearchUiThread(message);
                 } break;
+                case MSG_COMPUTE_CLICK_POINT_IN_SCREEN: {
+                    computeClickPointInScreenUiThread(message);
+                } break;
                 default:
                     throw new IllegalArgumentException("Unknown message type: " + type);
             }
index 5d6d998..6aa86c7 100644 (file)
@@ -94,7 +94,8 @@ interface IWindowManager
     void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
             IRemoteCallback startedCallback, boolean scaleUp);
     void overridePendingAppTransitionAspectScaledThumb(in Bitmap srcThumb, int startX,
-            int startY, IRemoteCallback startedCallback, boolean scaleUp);
+            int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
+            boolean scaleUp);
     void executeAppTransition();
     void setAppStartingWindow(IBinder token, String pkg, int theme,
             in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
index 9dc9766..47f72a8 100644 (file)
@@ -367,6 +367,10 @@ public class RenderNode {
         throw new IllegalArgumentException("Unrecognized outline?");
     }
 
+    public boolean hasShadow() {
+        return nHasShadow(mNativeRenderNode);
+    }
+
     /**
      * Enables or disables clipping to the outline.
      *
@@ -861,6 +865,7 @@ public class RenderNode {
             float alpha);
     private static native boolean nSetOutlineEmpty(long renderNode);
     private static native boolean nSetOutlineNone(long renderNode);
+    private static native boolean nHasShadow(long renderNode);
     private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
     private static native boolean nSetRevealClip(long renderNode,
             boolean shouldClip, float x, float y, float radius);
index fa4a13a..debf45d 100644 (file)
@@ -453,6 +453,12 @@ public class RenderNodeAnimator extends Animator {
         throw new IllegalStateException("Cannot clone this animator");
     }
 
+    @Override
+    public void setAllowRunningAsynchronously(boolean mayRunAsync) {
+        checkMutable();
+        nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
+    }
+
     private static native long nCreateAnimator(int property, float finalValue);
     private static native long nCreateCanvasPropertyFloatAnimator(
             long canvasProperty, float finalValue);
@@ -466,6 +472,7 @@ public class RenderNodeAnimator extends Animator {
     private static native long nGetDuration(long nativePtr);
     private static native void nSetStartDelay(long nativePtr, long startDelay);
     private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
+    private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
 
     private static native void nStart(long animPtr, RenderNodeAnimator finishListener);
     private static native void nEnd(long animPtr);
index 92ad4e8..82c5425 100644 (file)
@@ -35,6 +35,8 @@ import android.graphics.LinearGradient;
 import android.graphics.Matrix;
 import android.graphics.Outline;
 import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
@@ -5731,6 +5733,136 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     }
 
     /**
+     * Computes a point on which a sequence of a down/up event can be sent to
+     * trigger clicking this view. This method is for the exclusive use by the
+     * accessibility layer to determine where to send a click event in explore
+     * by touch mode.
+     *
+     * @param interactiveRegion The interactive portion of this window.
+     * @param outPoint The point to populate.
+     * @return True of such a point exists.
+     */
+    boolean computeClickPointInScreenForAccessibility(Region interactiveRegion,
+            Point outPoint) {
+        // Since the interactive portion of the view is a region but as a view
+        // may have a transformation matrix which cannot be applied to a
+        // region we compute the view bounds rectangle and all interactive
+        // predecessor's and sibling's (siblings of predecessors included)
+        // rectangles that intersect the view bounds. At the
+        // end if the view was partially covered by another interactive
+        // view we compute the view's interactive region and pick a point
+        // on its boundary path as regions do not offer APIs to get inner
+        // points. Note that the the code is optimized to fail early and
+        // avoid unnecessary allocations plus computations.
+
+        // The current approach has edge cases that may produce false
+        // positives or false negatives. For example, a portion of the
+        // view may be covered by an interactive descendant of a
+        // predecessor, which we do not compute. Also a view may be handling
+        // raw touch events instead registering click listeners, which
+        // we cannot compute. Despite these limitations this approach will
+        // work most of the time and it is a huge improvement over just
+        // blindly sending the down and up events in the center of the
+        // view.
+
+        // Cannot click on an unattached view.
+        if (mAttachInfo == null) {
+            return false;
+        }
+
+        // Attached to an invisible window means this view is not visible.
+        if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
+            return false;
+        }
+
+        RectF bounds = mAttachInfo.mTmpTransformRect;
+        bounds.set(0, 0, getWidth(), getHeight());
+        List<RectF> intersections = mAttachInfo.mTmpRectList;
+        intersections.clear();
+
+        if (mParent instanceof ViewGroup) {
+            ViewGroup parentGroup = (ViewGroup) mParent;
+            if (!parentGroup.translateBoundsAndIntersectionsInWindowCoordinates(
+                    this, bounds, intersections)) {
+                intersections.clear();
+                return false;
+            }
+        }
+
+        // Take into account the window location.
+        final int dx = mAttachInfo.mWindowLeft;
+        final int dy = mAttachInfo.mWindowTop;
+        bounds.offset(dx, dy);
+        offsetRects(intersections, dx, dy);
+
+        if (intersections.isEmpty() && interactiveRegion == null) {
+            outPoint.set((int) bounds.centerX(), (int) bounds.centerY());
+        } else {
+            // This view is partially covered by other views, then compute
+            // the not covered region and pick a point on its boundary.
+            Region region = new Region();
+            region.set((int) bounds.left, (int) bounds.top,
+                    (int) bounds.right, (int) bounds.bottom);
+
+            final int intersectionCount = intersections.size();
+            for (int i = intersectionCount - 1; i >= 0; i--) {
+                RectF intersection = intersections.remove(i);
+                region.op((int) intersection.left, (int) intersection.top,
+                        (int) intersection.right, (int) intersection.bottom,
+                        Region.Op.DIFFERENCE);
+            }
+
+            // If the view is completely covered, done.
+            if (region.isEmpty()) {
+                return false;
+            }
+
+            // Take into account the interactive portion of the window
+            // as the rest is covered by other windows. If no such a region
+            // then the whole window is interactive.
+            if (interactiveRegion != null) {
+                region.op(interactiveRegion, Region.Op.INTERSECT);
+            }
+
+            // If the view is completely covered, done.
+            if (region.isEmpty()) {
+                return false;
+            }
+
+            // Try a shortcut here.
+            if (region.isRect()) {
+                Rect regionBounds = mAttachInfo.mTmpInvalRect;
+                region.getBounds(regionBounds);
+                outPoint.set(regionBounds.centerX(), regionBounds.centerY());
+                return true;
+            }
+
+            // Get the a point on the region boundary path.
+            Path path = region.getBoundaryPath();
+            PathMeasure pathMeasure = new PathMeasure(path, false);
+            final float[] coordinates = mAttachInfo.mTmpTransformLocation;
+
+            // Without loss of generality pick a point.
+            final float point = pathMeasure.getLength() * 0.01f;
+            if (!pathMeasure.getPosTan(point, coordinates, null)) {
+                return false;
+            }
+
+            outPoint.set(Math.round(coordinates[0]), Math.round(coordinates[1]));
+        }
+
+        return true;
+    }
+
+    static void offsetRects(List<RectF> rects, float offsetX, float offsetY) {
+        final int rectCount = rects.size();
+        for (int i = 0; i < rectCount; i++) {
+            RectF intersection = rects.get(i);
+            intersection.offset(offsetX, offsetY);
+        }
+    }
+
+    /**
      * Returns the delegate for implementing accessibility support via
      * composition. For more details see {@link AccessibilityDelegate}.
      *
@@ -10224,6 +10356,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
      *
      * @return true if the content in this view might overlap, false otherwise.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public boolean hasOverlappingRendering() {
         return true;
     }
@@ -10926,6 +11059,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         invalidateViewProperty(false, false);
     }
 
+    /**
+     * HierarchyViewer only
+     *
+     * @hide
+     */
+    @ViewDebug.ExportedProperty(category = "drawing")
+    public boolean hasShadow() {
+        return mRenderNode.hasShadow();
+    }
+
+
     /** @hide */
     public void setRevealClip(boolean shouldClip, float x, float y, float radius) {
         mRenderNode.setRevealClip(shouldClip, x, y, radius);
@@ -20142,6 +20286,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         final RectF mTmpTransformRect = new RectF();
 
         /**
+         * Temporary for use in computing hit areas with transformed views
+         */
+        final RectF mTmpTransformRect1 = new RectF();
+
+        /**
+         * Temporary list of rectanges.
+         */
+        final List<RectF> mTmpRectList = new ArrayList<>();
+
+        /**
          * Temporary for use in transforming invalidation rect
          */
         final Matrix mTmpMatrix = new Matrix();
index 6c66eb0..a94f973 100644 (file)
@@ -26,6 +26,7 @@ import android.os.Handler;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
 
 import java.io.BufferedOutputStream;
 import java.io.BufferedWriter;
@@ -315,6 +316,7 @@ public class ViewDebug {
 
     private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
     private static final String REMOTE_COMMAND_DUMP = "DUMP";
+    private static final String REMOTE_COMMAND_DUMP_THEME = "DUMP_THEME";
     private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
     private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
     private static final String REMOTE_PROFILE = "PROFILE";
@@ -430,6 +432,8 @@ public class ViewDebug {
 
         if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
             dump(view, false, true, clientStream);
+        } else if (REMOTE_COMMAND_DUMP_THEME.equalsIgnoreCase(command)) {
+            dumpTheme(view, clientStream);
         } else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
             captureLayers(view, new DataOutputStream(clientStream));
         } else {
@@ -820,6 +824,64 @@ public class ViewDebug {
         }
     }
 
+    /**
+     * Dumps the theme attributes from the given View.
+     * @hide
+     */
+    public static void dumpTheme(View view, OutputStream clientStream) throws IOException {
+        BufferedWriter out = null;
+        try {
+            out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
+            String[] attributes = getStyleAttributesDump(view.getContext().getResources(),
+                    view.getContext().getTheme());
+            if (attributes != null) {
+                for (int i = 0; i < attributes.length; i += 2) {
+                    if (attributes[i] != null) {
+                        out.write(attributes[i] + "\n");
+                        out.write(attributes[i + 1] + "\n");
+                    }
+                }
+            }
+            out.write("DONE.");
+            out.newLine();
+        } catch (Exception e) {
+            android.util.Log.w("View", "Problem dumping View Theme:", e);
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    /**
+     * Gets the style attributes from the {@link Resources.Theme}. For debugging only.
+     *
+     * @param resources Resources to resolve attributes from.
+     * @param theme Theme to dump.
+     * @return a String array containing pairs of adjacent Theme attribute data: name followed by
+     * its value.
+     *
+     * @hide
+     */
+    private static String[] getStyleAttributesDump(Resources resources, Resources.Theme theme) {
+        TypedValue outValue = new TypedValue();
+        String nullString = "null";
+        int i = 0;
+        int[] attributes = theme.getAllAttributes();
+        String[] data = new String[attributes.length * 2];
+        for (int attributeId : attributes) {
+            try {
+                data[i] = resources.getResourceName(attributeId);
+                data[i + 1] = theme.resolveAttribute(attributeId, outValue, true) ?
+                        outValue.coerceToString().toString() :  nullString;
+                i += 2;
+            } catch (Resources.NotFoundException e) {
+                // ignore resources we can't resolve
+            }
+        }
+        return data;
+    }
+
     private static View findView(ViewGroup group, String className, int hashCode) {
         if (isRequestedView(group, className, hashCode)) {
             return group;
index 974fe4e..4e1db90 100644 (file)
@@ -771,6 +771,112 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     }
 
     /**
+     * Translates the given bounds and intersections from child coordinates to
+     * local coordinates. In case any interactive sibling of the calling child
+     * covers the latter, a new intersections is added to the intersection list.
+     * This method is for the exclusive use by the accessibility layer to compute
+     * a point where a sequence of down and up events would click on a view.
+     *
+     * @param child The child making the call.
+     * @param bounds The bounds to translate in child coordinates.
+     * @param intersections The intersections of interactive views covering the child.
+     * @return True if the bounds and intersections were computed, false otherwise.
+     */
+    boolean translateBoundsAndIntersectionsInWindowCoordinates(View child,
+            RectF bounds, List<RectF> intersections) {
+        // Not attached, done.
+        if (mAttachInfo == null) {
+            return false;
+        }
+
+        if (getAlpha() <= 0 || getTransitionAlpha() <= 0 ||
+                getVisibility() != VISIBLE) {
+            // Cannot click on a view with an invisible predecessor.
+            return false;
+        }
+
+        // Compensate for the child transformation.
+        if (!child.hasIdentityMatrix()) {
+            Matrix matrix = child.getMatrix();
+            matrix.mapRect(bounds);
+            final int intersectionCount = intersections.size();
+            for (int i = 0; i < intersectionCount; i++) {
+                RectF intersection = intersections.get(i);
+                matrix.mapRect(intersection);
+            }
+        }
+
+        // Translate the bounds from child to parent coordinates.
+        final int dx = child.mLeft - mScrollX;
+        final int dy = child.mTop - mScrollY;
+        bounds.offset(dx, dy);
+        offsetRects(intersections, dx, dy);
+
+        // If the bounds do not intersect our bounds, done.
+        if (!bounds.intersects(0, 0, getWidth(), getHeight())) {
+            return false;
+        }
+
+        // Check whether any clickable siblings cover the child
+        // view and if so keep track of the intersections. Also
+        // respect Z ordering when iterating over children.
+        ArrayList<View> orderedList = buildOrderedChildList();
+        final boolean useCustomOrder = orderedList == null
+                && isChildrenDrawingOrderEnabled();
+
+        final int childCount = mChildrenCount;
+        for (int i = childCount - 1; i >= 0; i--) {
+            final int childIndex = useCustomOrder
+                    ? getChildDrawingOrder(childCount, i) : i;
+            final View sibling = (orderedList == null)
+                    ? mChildren[childIndex] : orderedList.get(childIndex);
+
+            // We care only about siblings over the child.
+            if (sibling == child) {
+                break;
+            }
+
+            // If sibling is not interactive we do not care.
+            if (!sibling.isClickable() && !sibling.isLongClickable()) {
+                continue;
+            }
+
+            // Compute the sibling bounds in its coordinates.
+            RectF siblingBounds = mAttachInfo.mTmpTransformRect1;
+            siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight());
+
+            // Take into account the sibling transformation matrix.
+            if (!sibling.hasIdentityMatrix()) {
+                sibling.getMatrix().mapRect(siblingBounds);
+            }
+
+            // Offset the sibling to our coordinates.
+            final int siblingDx = sibling.mLeft - mScrollX;
+            final int siblingDy = sibling.mTop - mScrollY;
+            siblingBounds.offset(siblingDx, siblingDy);
+
+            // Compute the intersection between the child and the sibling.
+            if (siblingBounds.intersect(bounds)) {
+                // If an interactive sibling completely covers the child, done.
+                if (siblingBounds.equals(bounds)) {
+                    return false;
+                }
+                // Keep track of the intersection rectangle.
+                RectF intersection = new RectF(siblingBounds);
+                intersections.add(intersection);
+            }
+        }
+
+        if (mParent instanceof ViewGroup) {
+            ViewGroup parentGroup = (ViewGroup) mParent;
+            return parentGroup.translateBoundsAndIntersectionsInWindowCoordinates(
+                    this, bounds, intersections);
+        }
+
+        return true;
+    }
+
+    /**
      * Called when a child view has changed whether or not it is tracking transient state.
      */
     public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
@@ -3301,6 +3407,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
      * @return True if the group's children will be clipped to their bounds,
      * false otherwise.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public boolean getClipChildren() {
         return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
     }
@@ -3349,6 +3456,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
      *
      * @attr ref android.R.styleable#ViewGroup_clipToPadding
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public boolean getClipToPadding() {
         return hasBooleanFlag(FLAG_CLIP_TO_PADDING);
     }
@@ -5722,6 +5830,28 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     }
 
     @Override
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
+
+        if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
+            if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
+                throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
+                        + " child has duplicateParentState set to true");
+            }
+
+            final View[] children = mChildren;
+            final int count = mChildrenCount;
+
+            for (int i = 0; i < count; i++) {
+                final View child = children[i];
+                if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
+                    child.drawableHotspotChanged(x, y);
+                }
+            }
+        }
+    }
+
+    @Override
     protected int[] onCreateDrawableState(int extraSpace) {
         if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
             return super.onCreateDrawableState(extraSpace);
index 4299e2e..80b9ade 100644 (file)
@@ -6679,12 +6679,12 @@ public final class ViewRootImpl implements ViewParent,
         public void performAccessibilityAction(long accessibilityNodeId, int action,
                 Bundle arguments, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interogatingPid, long interrogatingTid) {
+                int interrogatingPid, long interrogatingTid) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .performAccessibilityActionClientThread(accessibilityNodeId, action, arguments,
-                            interactionId, callback, flags, interogatingPid, interrogatingTid);
+                            interactionId, callback, flags, interrogatingPid, interrogatingTid);
             } else {
                 // We cannot make the call and notify the caller so it does not wait.
                 try {
@@ -6696,6 +6696,26 @@ public final class ViewRootImpl implements ViewParent,
         }
 
         @Override
+        public void computeClickPointInScreen(long accessibilityNodeId, Region interactiveRegion,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback,
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+            ViewRootImpl viewRootImpl = mViewRootImpl.get();
+            if (viewRootImpl != null && viewRootImpl.mView != null) {
+                viewRootImpl.getAccessibilityInteractionController()
+                        .computeClickPointInScreenClientThread(accessibilityNodeId,
+                                interactiveRegion, interactionId, callback, interrogatingPid,
+                                interrogatingTid, spec);
+            } else {
+                // We cannot make the call and notify the caller so it does not wait.
+                try {
+                    callback.setComputeClickPointInScreenActionResult(null, interactionId);
+                } catch (RemoteException re) {
+                    /* best effort - ignore */
+                }
+            }
+        }
+
+        @Override
         public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
                 String viewId, Region interactiveRegion, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int flags,
index db78ec5..374f7e0 100644 (file)
@@ -17,6 +17,7 @@
 package android.view.accessibility;
 
 import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.graphics.Point;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -98,6 +99,8 @@ public final class AccessibilityInteractionClient
 
     private boolean mPerformAccessibilityActionResult;
 
+    private Point mComputeClickPointResult;
+
     private Message mSameThreadMessage;
 
     private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
@@ -519,6 +522,43 @@ public final class AccessibilityInteractionClient
         return false;
     }
 
+    /**
+     * Computes a point in screen coordinates where sending a down/up events would
+     * perform a click on an {@link AccessibilityNodeInfo}.
+     *
+     * @param connectionId The id of a connection for interacting with the system.
+     * @param accessibilityWindowId A unique window id. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+     *     to query the currently active window.
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+     *     to start from the root.
+     * @return Point the click point of null if no such point.
+     */
+    public Point computeClickPointInScreen(int connectionId, int accessibilityWindowId,
+            long accessibilityNodeId) {
+        try {
+            IAccessibilityServiceConnection connection = getConnection(connectionId);
+            if (connection != null) {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                final boolean success = connection.computeClickPointInScreen(
+                        accessibilityWindowId, accessibilityNodeId,
+                        interactionId, this, Thread.currentThread().getId());
+                if (success) {
+                    return getComputeClickPointInScreenResultAndClear(interactionId);
+                }
+            } else {
+                if (DEBUG) {
+                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+                }
+            }
+        } catch (RemoteException re) {
+            Log.w(LOG_TAG, "Error while calling remote computeClickPointInScreen", re);
+        }
+        return null;
+    }
+
     public void clearCache() {
         sAccessibilityCache.clear();
     }
@@ -634,6 +674,34 @@ public final class AccessibilityInteractionClient
     }
 
     /**
+     * Gets the result of a request to compute a point in screen for clicking on a node.
+     *
+     * @param interactionId The interaction id to match the result with the request.
+     * @return The point or null if no such point.
+     */
+    private Point getComputeClickPointInScreenResultAndClear(int interactionId) {
+        synchronized (mInstanceLock) {
+            final boolean success = waitForResultTimedLocked(interactionId);
+            Point result = success ? mComputeClickPointResult : null;
+            clearResultLocked();
+            return result;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setComputeClickPointInScreenActionResult(Point point, int interactionId) {
+        synchronized (mInstanceLock) {
+            if (interactionId > mInteractionId) {
+                mComputeClickPointResult = point;
+                mInteractionId = interactionId;
+            }
+            mInstanceLock.notifyAll();
+        }
+    }
+
+    /**
      * Clears the result state.
      */
     private void clearResultLocked() {
@@ -641,6 +709,7 @@ public final class AccessibilityInteractionClient
         mFindAccessibilityNodeInfoResult = null;
         mFindAccessibilityNodeInfosResult = null;
         mPerformAccessibilityActionResult = false;
+        mComputeClickPointResult = null;
     }
 
     /**
index faf7789..66a3f46 100644 (file)
@@ -17,6 +17,7 @@
 package android.view.accessibility;
 
 import android.graphics.Region;
+import android.graphics.Point;
 import android.os.Bundle;
 import android.view.MagnificationSpec;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -53,4 +54,8 @@ oneway interface IAccessibilityInteractionConnection {
     void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments,
         int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
         int interrogatingPid, long interrogatingTid);
+
+    void computeClickPointInScreen(long accessibilityNodeId, in Region bounds, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+        long interrogatingTid, in MagnificationSpec spec);
 }
index c1a3ab7..f480216 100644 (file)
@@ -16,6 +16,7 @@
 
 package android.view.accessibility;
 
+import android.graphics.Point;
 import android.view.accessibility.AccessibilityNodeInfo;
 import java.util.List;
 
@@ -51,4 +52,12 @@ oneway interface IAccessibilityInteractionConnectionCallback {
      * @param interactionId The interaction id to match the result with the request.
      */
     void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
+
+    /**
+     * Sets the result of a request to compute a point for clicking in a view.
+     *
+     * @param point The point of null if no such point.
+     * @param interactionId The interaction id to match the result with the request.
+     */
+    void setComputeClickPointInScreenActionResult(in Point point, int interactionId);
 }
index fe0f5b9..600fffe 100644 (file)
@@ -35,9 +35,21 @@ import java.util.Objects;
  * actually inserted.</p>
  */
 public final class CursorAnchorInfo implements Parcelable {
+    /**
+     * The index of the first character of the selected text (inclusive). {@code -1} when there is
+     * no text selection.
+     */
     private final int mSelectionStart;
+    /**
+     * The index of the first character of the selected text (exclusive). {@code -1} when there is
+     * no text selection.
+     */
     private final int mSelectionEnd;
 
+    /**
+     * The index of the first character of the composing text (inclusive). {@code -1} when there is
+     * no composing text.
+     */
     private final int mComposingTextStart;
     /**
      * The text, tracked as a composing region.
@@ -82,7 +94,7 @@ public final class CursorAnchorInfo implements Parcelable {
      * Java chars, in the local coordinates that will be transformed with the transformation matrix
      * when rendered on the screen.
      */
-    private final SparseRectFArray mCharacterRects;
+    private final SparseRectFArray mCharacterBoundsArray;
 
     /**
      * Transformation matrix that is applied to any positional information of this class to
@@ -91,18 +103,24 @@ public final class CursorAnchorInfo implements Parcelable {
     private final Matrix mMatrix;
 
     /**
-     * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterRectFlags(int)}: the
+     * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
      * insertion marker or character bounds have at least one visible region.
      */
     public static final int FLAG_HAS_VISIBLE_REGION = 0x01;
 
     /**
-     * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterRectFlags(int)}: the
+     * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
      * insertion marker or character bounds have at least one invisible (clipped) region.
      */
     public static final int FLAG_HAS_INVISIBLE_REGION = 0x02;
 
     /**
+     * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
+     * insertion marker or character bounds is placed at right-to-left (RTL) character.
+     */
+    public static final int FLAG_IS_RTL = 0x04;
+
+    /**
      * @removed
      */
     public static final int CHARACTER_RECT_TYPE_MASK = 0x0f;
@@ -144,7 +162,7 @@ public final class CursorAnchorInfo implements Parcelable {
         mInsertionMarkerTop = source.readFloat();
         mInsertionMarkerBaseline = source.readFloat();
         mInsertionMarkerBottom = source.readFloat();
-        mCharacterRects = source.readParcelable(SparseRectFArray.class.getClassLoader());
+        mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader());
         mMatrix = new Matrix();
         mMatrix.setValues(source.createFloatArray());
     }
@@ -166,7 +184,7 @@ public final class CursorAnchorInfo implements Parcelable {
         dest.writeFloat(mInsertionMarkerTop);
         dest.writeFloat(mInsertionMarkerBaseline);
         dest.writeFloat(mInsertionMarkerBottom);
-        dest.writeParcelable(mCharacterRects, flags);
+        dest.writeParcelable(mCharacterBoundsArray, flags);
         final float[] matrixArray = new float[9];
         mMatrix.getValues(matrixArray);
         dest.writeFloatArray(matrixArray);
@@ -174,7 +192,6 @@ public final class CursorAnchorInfo implements Parcelable {
 
     @Override
     public int hashCode(){
-        // TODO: Improve the hash function.
         final float floatHash = mInsertionMarkerHorizontal + mInsertionMarkerTop
                 + mInsertionMarkerBaseline + mInsertionMarkerBottom;
         int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash);
@@ -185,7 +202,7 @@ public final class CursorAnchorInfo implements Parcelable {
         hash *= 31;
         hash += Objects.hashCode(mComposingText);
         hash *= 31;
-        hash += Objects.hashCode(mCharacterRects);
+        hash += Objects.hashCode(mCharacterBoundsArray);
         hash *= 31;
         hash += Objects.hashCode(mMatrix);
         return hash;
@@ -231,7 +248,7 @@ public final class CursorAnchorInfo implements Parcelable {
                 || !areSameFloatImpl(mInsertionMarkerBottom, that.mInsertionMarkerBottom)) {
             return false;
         }
-        if (!Objects.equals(mCharacterRects, that.mCharacterRects)) {
+        if (!Objects.equals(mCharacterBoundsArray, that.mCharacterBoundsArray)) {
             return false;
         }
         if (!Objects.equals(mMatrix, that.mMatrix)) {
@@ -250,7 +267,7 @@ public final class CursorAnchorInfo implements Parcelable {
                 + " mInsertionMarkerTop=" + mInsertionMarkerTop
                 + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
                 + " mInsertionMarkerBottom=" + mInsertionMarkerBottom
-                + " mCharacterRects=" + Objects.toString(mCharacterRects)
+                + " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray)
                 + " mMatrix=" + Objects.toString(mMatrix)
                 + "}";
     }
@@ -259,6 +276,19 @@ public final class CursorAnchorInfo implements Parcelable {
      * Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe.
      */
     public static final class Builder {
+        private int mSelectionStart = -1;
+        private int mSelectionEnd = -1;
+        private int mComposingTextStart = -1;
+        private CharSequence mComposingText = null;
+        private float mInsertionMarkerHorizontal = Float.NaN;
+        private float mInsertionMarkerTop = Float.NaN;
+        private float mInsertionMarkerBaseline = Float.NaN;
+        private float mInsertionMarkerBottom = Float.NaN;
+        private int mInsertionMarkerFlags = 0;
+        private SparseRectFArrayBuilder mCharacterBoundsArrayBuilder = null;
+        private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX);
+        private boolean mMatrixInitialized = false;
+
         /**
          * Sets the text range of the selection. Calling this can be skipped if there is no
          * selection.
@@ -268,8 +298,6 @@ public final class CursorAnchorInfo implements Parcelable {
             mSelectionEnd = newEnd;
             return this;
         }
-        private int mSelectionStart = -1;
-        private int mSelectionEnd = -1;
 
         /**
          * Sets the text range of the composing text. Calling this can be skipped if there is
@@ -288,8 +316,6 @@ public final class CursorAnchorInfo implements Parcelable {
             }
             return this;
         }
-        private int mComposingTextStart = -1;
-        private CharSequence mComposingText = null;
 
         /**
          * @removed
@@ -335,11 +361,33 @@ public final class CursorAnchorInfo implements Parcelable {
             mInsertionMarkerFlags = flags;
             return this;
         }
-        private float mInsertionMarkerHorizontal = Float.NaN;
-        private float mInsertionMarkerTop = Float.NaN;
-        private float mInsertionMarkerBaseline = Float.NaN;
-        private float mInsertionMarkerBottom = Float.NaN;
-        private int mInsertionMarkerFlags = 0;
+
+        /**
+         * Adds the bounding box of the character specified with the index.
+         *
+         * @param index index of the character in Java chars units. Must be specified in
+         * ascending order across successive calls.
+         * @param left x coordinate of the left edge of the character in local coordinates.
+         * @param top y coordinate of the top edge of the character in local coordinates.
+         * @param right x coordinate of the right edge of the character in local coordinates.
+         * @param bottom y coordinate of the bottom edge of the character in local coordinates.
+         * @param flags flags for this character bounds. See {@link #FLAG_HAS_VISIBLE_REGION},
+         * {@link #FLAG_HAS_INVISIBLE_REGION} and {@link #FLAG_IS_RTL}. These flags must be
+         * specified when necessary.
+         * @throws IllegalArgumentException If the index is a negative value, or not greater than
+         * all of the previously called indices.
+         */
+        public Builder addCharacterBounds(final int index, final float left, final float top,
+                final float right, final float bottom, final int flags) {
+            if (index < 0) {
+                throw new IllegalArgumentException("index must not be a negative integer.");
+            }
+            if (mCharacterBoundsArrayBuilder == null) {
+                mCharacterBoundsArrayBuilder = new SparseRectFArrayBuilder();
+            }
+            mCharacterBoundsArrayBuilder.append(index, left, top, right, bottom, flags);
+            return this;
+        }
 
         /**
          * Adds the bounding box of the character specified with the index.
@@ -358,21 +406,25 @@ public final class CursorAnchorInfo implements Parcelable {
          * example.
          * @throws IllegalArgumentException If the index is a negative value, or not greater than
          * all of the previously called indices.
+         * @removed
          */
         public Builder addCharacterRect(final int index, final float leadingEdgeX,
                 final float leadingEdgeY, final float trailingEdgeX, final float trailingEdgeY,
                 final int flags) {
-            if (index < 0) {
-                throw new IllegalArgumentException("index must not be a negative integer.");
-            }
-            if (mCharacterRectBuilder == null) {
-                mCharacterRectBuilder = new SparseRectFArrayBuilder();
+            final int newFlags;
+            final float left;
+            final float right;
+            if (leadingEdgeX <= trailingEdgeX) {
+                newFlags = flags;
+                left = leadingEdgeX;
+                right = trailingEdgeX;
+            } else {
+                newFlags = flags | FLAG_IS_RTL;
+                left = trailingEdgeX;
+                right = leadingEdgeX;
             }
-            mCharacterRectBuilder.append(index, leadingEdgeX, leadingEdgeY, trailingEdgeX,
-                    trailingEdgeY, flags);
-            return this;
+            return addCharacterBounds(index, left, leadingEdgeY, right, trailingEdgeY, newFlags);
         }
-        private SparseRectFArrayBuilder mCharacterRectBuilder = null;
 
         /**
          * Sets the matrix that transforms local coordinates into screen coordinates.
@@ -384,8 +436,6 @@ public final class CursorAnchorInfo implements Parcelable {
             mMatrixInitialized = true;
             return this;
         }
-        private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX);
-        private boolean mMatrixInitialized = false;
 
         /**
          * @return {@link CursorAnchorInfo} using parameters in this {@link Builder}.
@@ -394,13 +444,15 @@ public final class CursorAnchorInfo implements Parcelable {
          */
         public CursorAnchorInfo build() {
             if (!mMatrixInitialized) {
-                // Coordinate transformation matrix is mandatory when positional parameters are
-                // specified.
-                if ((mCharacterRectBuilder != null && !mCharacterRectBuilder.isEmpty()) ||
-                        !Float.isNaN(mInsertionMarkerHorizontal) ||
-                        !Float.isNaN(mInsertionMarkerTop) ||
-                        !Float.isNaN(mInsertionMarkerBaseline) ||
-                        !Float.isNaN(mInsertionMarkerBottom)) {
+                // Coordinate transformation matrix is mandatory when at least one positional
+                // parameter is specified.
+                final boolean hasCharacterBounds = (mCharacterBoundsArrayBuilder != null
+                        && !mCharacterBoundsArrayBuilder.isEmpty());
+                if (hasCharacterBounds
+                        || !Float.isNaN(mInsertionMarkerHorizontal)
+                        || !Float.isNaN(mInsertionMarkerTop)
+                        || !Float.isNaN(mInsertionMarkerBaseline)
+                        || !Float.isNaN(mInsertionMarkerBottom)) {
                     throw new IllegalArgumentException("Coordinate transformation matrix is " +
                             "required when positional parameters are specified.");
                 }
@@ -424,8 +476,8 @@ public final class CursorAnchorInfo implements Parcelable {
             mInsertionMarkerBottom = Float.NaN;
             mMatrix.set(Matrix.IDENTITY_MATRIX);
             mMatrixInitialized = false;
-            if (mCharacterRectBuilder != null) {
-                mCharacterRectBuilder.reset();
+            if (mCharacterBoundsArrayBuilder != null) {
+                mCharacterBoundsArrayBuilder.reset();
             }
         }
     }
@@ -440,8 +492,8 @@ public final class CursorAnchorInfo implements Parcelable {
         mInsertionMarkerTop = builder.mInsertionMarkerTop;
         mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline;
         mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
-        mCharacterRects = builder.mCharacterRectBuilder != null ?
-                builder.mCharacterRectBuilder.build() : null;
+        mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ?
+                builder.mCharacterBoundsArrayBuilder.build() : null;
         mMatrix = new Matrix(builder.mMatrix);
     }
 
@@ -539,6 +591,19 @@ public final class CursorAnchorInfo implements Parcelable {
     /**
      * Returns a new instance of {@link RectF} that indicates the location of the character
      * specified with the index.
+     * @param index index of the character in a Java chars.
+     * @return the character bounds in local coordinates as a new instance of {@link RectF}.
+     */
+    public RectF getCharacterBounds(final int index) {
+        if (mCharacterBoundsArray == null) {
+            return null;
+        }
+        return mCharacterBoundsArray.get(index);
+    }
+
+    /**
+     * Returns a new instance of {@link RectF} that indicates the location of the character
+     * specified with the index.
      * <p>
      * Note that coordinates are not necessarily contiguous or even monotonous, especially when
      * RTL text and LTR text are mixed.
@@ -549,28 +614,32 @@ public final class CursorAnchorInfo implements Parcelable {
      * the location. Note that the {@code left} field can be greater than the {@code right} field
      * if the character is in RTL text. Returns {@code null} if no location information is
      * available.
+     * @removed
      */
-    // TODO: Prepare a document about the expected behavior for surrogate pairs, combining
-    // characters, and non-graphical chars.
     public RectF getCharacterRect(final int index) {
-        if (mCharacterRects == null) {
-            return null;
+        return getCharacterBounds(index);
+    }
+
+    /**
+     * Returns the flags associated with the character bounds specified with the index.
+     * @param index index of the character in a Java chars.
+     * @return {@code 0} if no flag is specified.
+     */
+    public int getCharacterBoundsFlags(final int index) {
+        if (mCharacterBoundsArray == null) {
+            return 0;
         }
-        return mCharacterRects.get(index);
+        return mCharacterBoundsArray.getFlags(index, 0);
     }
 
     /**
      * Returns the flags associated with the character rect specified with the index.
      * @param index index of the character in a Java chars.
      * @return {@code 0} if no flag is specified.
+     * @removed
      */
-    // TODO: Prepare a document about the expected behavior for surrogate pairs, combining
-    // characters, and non-graphical chars.
     public int getCharacterRectFlags(final int index) {
-        if (mCharacterRects == null) {
-            return 0;
-        }
-        return mCharacterRects.getFlags(index, 0);
+        return getCharacterBoundsFlags(index);
     }
 
     /**
index 2729bd0..d77f0b2 100644 (file)
@@ -125,6 +125,7 @@ public class DatePicker extends FrameLayout {
         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
                 defStyleAttr, defStyleRes);
         final int mode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER);
+        final int firstDayOfWeek = a.getInt(R.styleable.DatePicker_firstDayOfWeek, 0);
         a.recycle();
 
         switch (mode) {
@@ -136,6 +137,10 @@ public class DatePicker extends FrameLayout {
                 mDelegate = createSpinnerUIDelegate(context, attrs, defStyleAttr, defStyleRes);
                 break;
         }
+
+        if (firstDayOfWeek != 0) {
+            setFirstDayOfWeek(firstDayOfWeek);
+        }
     }
 
     private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs,
@@ -300,6 +305,47 @@ public class DatePicker extends FrameLayout {
     }
 
     /**
+     * Sets the first day of week.
+     *
+     * @param firstDayOfWeek The first day of the week conforming to the
+     *            {@link CalendarView} APIs.
+     * @see Calendar#SUNDAY
+     * @see Calendar#MONDAY
+     * @see Calendar#TUESDAY
+     * @see Calendar#WEDNESDAY
+     * @see Calendar#THURSDAY
+     * @see Calendar#FRIDAY
+     * @see Calendar#SATURDAY
+     *
+     * @attr ref android.R.styleable#DatePicker_firstDayOfWeek
+     */
+    public void setFirstDayOfWeek(int firstDayOfWeek) {
+        if (firstDayOfWeek < Calendar.SUNDAY || firstDayOfWeek > Calendar.SATURDAY) {
+            throw new IllegalArgumentException("firstDayOfWeek must be between 1 and 7");
+        }
+        mDelegate.setFirstDayOfWeek(firstDayOfWeek);
+    }
+
+    /**
+     * Gets the first day of week.
+     *
+     * @return The first day of the week conforming to the {@link CalendarView}
+     *         APIs.
+     * @see Calendar#SUNDAY
+     * @see Calendar#MONDAY
+     * @see Calendar#TUESDAY
+     * @see Calendar#WEDNESDAY
+     * @see Calendar#THURSDAY
+     * @see Calendar#FRIDAY
+     * @see Calendar#SATURDAY
+     *
+     * @attr ref android.R.styleable#DatePicker_firstDayOfWeek
+     */
+    public int getFirstDayOfWeek() {
+        return mDelegate.getFirstDayOfWeek();
+    }
+
+    /**
      * Gets whether the {@link CalendarView} is shown.
      *
      * @return True if the calendar view is shown.
@@ -315,7 +361,7 @@ public class DatePicker extends FrameLayout {
      * @return The calendar view.
      * @see #getCalendarViewShown()
      */
-    public CalendarView getCalendarView () {
+    public CalendarView getCalendarView() {
         return mDelegate.getCalendarView();
     }
 
@@ -382,6 +428,9 @@ public class DatePicker extends FrameLayout {
         int getMonth();
         int getDayOfMonth();
 
+        void setFirstDayOfWeek(int firstDayOfWeek);
+        int getFirstDayOfWeek();
+
         void setMinDate(long minDate);
         Calendar getMinDate();
 
@@ -699,6 +748,16 @@ public class DatePicker extends FrameLayout {
         }
 
         @Override
+        public void setFirstDayOfWeek(int firstDayOfWeek) {
+            mCalendarView.setFirstDayOfWeek(firstDayOfWeek);
+        }
+
+        @Override
+        public int getFirstDayOfWeek() {
+            return mCalendarView.getFirstDayOfWeek();
+        }
+
+        @Override
         public void setMinDate(long minDate) {
             mTempDate.setTimeInMillis(minDate);
             if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
index eed49bf..b962962 100644 (file)
@@ -49,6 +49,7 @@ import java.util.Locale;
  */
 class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements
         View.OnClickListener, DatePickerController {
+    private static final int USE_LOCALE = 0;
 
     private static final int UNINITIALIZED = -1;
     private static final int MONTH_AND_DAY_VIEW = 0;
@@ -99,6 +100,8 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
     private Calendar mMinDate;
     private Calendar mMaxDate;
 
+    private int mFirstDayOfWeek = USE_LOCALE;
+
     private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
 
     public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs,
@@ -438,7 +441,15 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
     }
 
     @Override
+    public void setFirstDayOfWeek(int firstDayOfWeek) {
+        mFirstDayOfWeek = firstDayOfWeek;
+    }
+
+    @Override
     public int getFirstDayOfWeek() {
+        if (mFirstDayOfWeek != USE_LOCALE) {
+            return mFirstDayOfWeek;
+        }
         return mCurrentDate.getFirstDayOfWeek();
     }
 
index 6a074da..059709d 100644 (file)
@@ -35,6 +35,7 @@ interface DatePickerController {
 
     Calendar getSelectedDay();
 
+    void setFirstDayOfWeek(int firstDayOfWeek);
     int getFirstDayOfWeek();
 
     int getMinYear();
index 22138d0..3f168e8 100644 (file)
@@ -3060,47 +3060,69 @@ public class Editor {
                     final CharSequence composingText = text.subSequence(composingTextStart,
                             composingTextEnd);
                     builder.setComposingText(composingTextStart, composingText);
-                }
-                // TODO: Optimize this loop by caching the result.
-                for (int offset = composingTextStart; offset < composingTextEnd; offset++) {
-                    if (offset < 0) {
-                        continue;
-                    }
-                    final boolean isRtl = layout.isRtlCharAt(offset);
-                    final int line = layout.getLineForOffset(offset);
-                    final int nextCharIndex = offset + 1;
-                    final float localLeadingEdgeX = layout.getPrimaryHorizontal(offset);
-                    final float localTrailingEdgeX;
-                    if (nextCharIndex != layout.getLineEnd(line)) {
-                        localTrailingEdgeX = layout.getPrimaryHorizontal(nextCharIndex);
-                    } else if (isRtl) {
-                        localTrailingEdgeX = layout.getLineLeft(line);
-                    } else {
-                        localTrailingEdgeX = layout.getLineRight(line);
-                    }
-                    final float leadingEdgeX = localLeadingEdgeX
-                            + viewportToContentHorizontalOffset;
-                    final float trailingEdgeX = localTrailingEdgeX
-                            + viewportToContentHorizontalOffset;
-                    final float top = layout.getLineTop(line) + viewportToContentVerticalOffset;
-                    final float bottom = layout.getLineBottom(line)
-                            + viewportToContentVerticalOffset;
-                    // TODO: Check right-top and left-bottom as well.
-                    final boolean isLeadingEdgeTopVisible = isPositionVisible(leadingEdgeX, top);
-                    final boolean isTrailingEdgeBottomVisible =
-                            isPositionVisible(trailingEdgeX, bottom);
-                    int characterRectFlags = 0;
-                    if (isLeadingEdgeTopVisible || isTrailingEdgeBottomVisible) {
-                        characterRectFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
-                    }
-                    if (!isLeadingEdgeTopVisible || !isTrailingEdgeBottomVisible) {
-                        characterRectFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+
+                    final int minLine = layout.getLineForOffset(composingTextStart);
+                    final int maxLine = layout.getLineForOffset(composingTextEnd - 1);
+                    for (int line = minLine; line <= maxLine; ++line) {
+                        final int lineStart = layout.getLineStart(line);
+                        final int lineEnd = layout.getLineEnd(line);
+                        final int offsetStart = Math.max(lineStart, composingTextStart);
+                        final int offsetEnd = Math.min(lineEnd, composingTextEnd);
+                        final boolean ltrLine =
+                                layout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT;
+                        final float[] widths = new float[offsetEnd - offsetStart];
+                        layout.getPaint().getTextWidths(text, offsetStart, offsetEnd, widths);
+                        final float top = layout.getLineTop(line);
+                        final float bottom = layout.getLineBottom(line);
+                        for (int offset = offsetStart; offset < offsetEnd; ++offset) {
+                            final float charWidth = widths[offset - offsetStart];
+                            final boolean isRtl = layout.isRtlCharAt(offset);
+                            final float primary = layout.getPrimaryHorizontal(offset);
+                            final float secondary = layout.getSecondaryHorizontal(offset);
+                            // TODO: This doesn't work perfectly for text with custom styles and
+                            // TAB chars.
+                            final float left;
+                            final float right;
+                            if (ltrLine) {
+                                if (isRtl) {
+                                    left = secondary - charWidth;
+                                    right = secondary;
+                                } else {
+                                    left = primary;
+                                    right = primary + charWidth;
+                                }
+                            } else {
+                                if (!isRtl) {
+                                    left = secondary;
+                                    right = secondary + charWidth;
+                                } else {
+                                    left = primary - charWidth;
+                                    right = primary;
+                                }
+                            }
+                            // TODO: Check top-right and bottom-left as well.
+                            final float localLeft = left + viewportToContentHorizontalOffset;
+                            final float localRight = right + viewportToContentHorizontalOffset;
+                            final float localTop = top + viewportToContentVerticalOffset;
+                            final float localBottom = bottom + viewportToContentVerticalOffset;
+                            final boolean isTopLeftVisible = isPositionVisible(localLeft, localTop);
+                            final boolean isBottomRightVisible =
+                                    isPositionVisible(localRight, localBottom);
+                            int characterBoundsFlags = 0;
+                            if (isTopLeftVisible || isBottomRightVisible) {
+                                characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+                            }
+                            if (!isTopLeftVisible || !isTopLeftVisible) {
+                                characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+                            }
+                            if (isRtl) {
+                                characterBoundsFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+                            }
+                            // Here offset is the index in Java chars.
+                            builder.addCharacterBounds(offset, localLeft, localTop, localRight,
+                                    localBottom, characterBoundsFlags);
+                        }
                     }
-                    // Here offset is the index in Java chars.
-                    // TODO: We must have a well-defined specification. For example, how
-                    // surrogate pairs and composition letters are handled must be documented.
-                    builder.addCharacterRect(offset, leadingEdgeX, top, trailingEdgeX, bottom,
-                            characterRectFlags);
                 }
             }
 
@@ -3127,6 +3149,9 @@ public class Editor {
                 if (!isTopVisible || !isBottomVisible) {
                     insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
                 }
+                if (layout.isRtlCharAt(offset)) {
+                    insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+                }
                 builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
                         insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags);
             }
index c17f4ee..7bdb4be 100644 (file)
@@ -390,7 +390,10 @@ public class PackageHelper {
         if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) {
             final File target = new UserEnvironment(UserHandle.USER_OWNER)
                     .getExternalStorageDirectory();
-            fitsOnExternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
+            // External is only an option when size is known
+            if (sizeBytes > 0) {
+                fitsOnExternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
+            }
         }
 
         if (prefer == RECOMMEND_INSTALL_INTERNAL) {
index 69cdbff..45a9dde 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 = 113 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 114 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -385,10 +385,9 @@ public final class BatteryStatsImpl extends BatteryStats {
 
     String mLastWakeupReason = null;
     long mLastWakeupUptimeMs = 0;
-    private final HashMap<String, LongSamplingCounter> mWakeupReasonStats =
-            new HashMap<String, LongSamplingCounter>();
+    private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
 
-    public Map<String, ? extends LongCounter> getWakeupReasonStats() {
+    public Map<String, ? extends Timer> getWakeupReasonStats() {
         return mWakeupReasonStats;
     }
 
@@ -1131,6 +1130,10 @@ public final class BatteryStatsImpl extends BatteryStats {
             mCurrentReportedCount = count;
         }
 
+        public void addCurrentReportedCount(int delta) {
+            updateCurrentReportedCount(mCurrentReportedCount + delta);
+        }
+
         public void updateCurrentReportedTotalTime(long totalTime) {
             if (mTimeBaseRunning && mUnpluggedReportedTotalTime == 0) {
                 // Updating the reported value for the first time.
@@ -1141,6 +1144,10 @@ public final class BatteryStatsImpl extends BatteryStats {
             mCurrentReportedTotalTime = totalTime;
         }
 
+        public void addCurrentReportedTotalTime(long delta) {
+            updateCurrentReportedTotalTime(mCurrentReportedTotalTime + delta);
+        }
+
         public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
             super.onTimeStarted(elapsedRealtime, baseUptime, baseRealtime);
             if (mTrackingReportedValues) {
@@ -1688,13 +1695,13 @@ public final class BatteryStatsImpl extends BatteryStats {
      * Get the wakeup reason counter, and create a new one if one
      * doesn't already exist.
      */
-    public LongSamplingCounter getWakeupReasonCounterLocked(String name) {
-        LongSamplingCounter counter = mWakeupReasonStats.get(name);
-        if (counter == null) {
-            counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
-            mWakeupReasonStats.put(name, counter);
+    public SamplingTimer getWakeupReasonTimerLocked(String name) {
+        SamplingTimer timer = mWakeupReasonStats.get(name);
+        if (timer == null) {
+            timer = new SamplingTimer(mOnBatteryTimeBase, true);
+            mWakeupReasonStats.put(name, timer);
         }
-        return counter;
+        return timer;
     }
 
     private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
@@ -2753,8 +2760,9 @@ public final class BatteryStatsImpl extends BatteryStats {
     void aggregateLastWakeupUptimeLocked(long uptimeMs) {
         if (mLastWakeupReason != null) {
             long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
-            LongSamplingCounter timer = getWakeupReasonCounterLocked(mLastWakeupReason);
-            timer.addCountLocked(deltaUptime);
+            SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
+            timer.addCurrentReportedCount(1);
+            timer.addCurrentReportedTotalTime(deltaUptime * 1000); // time is in microseconds
             mLastWakeupReason = null;
         }
     }
@@ -2762,7 +2770,7 @@ public final class BatteryStatsImpl extends BatteryStats {
     public void noteWakeupReasonLocked(String reason) {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
-        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason reason \"" + reason +"\": "
+        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason \"" + reason +"\": "
                 + Integer.toHexString(mHistoryCur.states));
         aggregateLastWakeupUptimeLocked(uptime);
         mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
@@ -6193,7 +6201,7 @@ public final class BatteryStatsImpl extends BatteryStats {
         }
 
         public void noteStartJobLocked(String name, long elapsedRealtimeMs) {
-            StopwatchTimer t = mJobStats.stopObject(name);
+            StopwatchTimer t = mJobStats.startObject(name);
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
             }
@@ -6636,8 +6644,8 @@ public final class BatteryStatsImpl extends BatteryStats {
         }
 
         if (mWakeupReasonStats.size() > 0) {
-            for (LongSamplingCounter timer : mWakeupReasonStats.values()) {
-                mOnBatteryScreenOffTimeBase.remove(timer);
+            for (SamplingTimer timer : mWakeupReasonStats.values()) {
+                mOnBatteryTimeBase.remove(timer);
             }
             mWakeupReasonStats.clear();
         }
@@ -7848,7 +7856,7 @@ public final class BatteryStatsImpl extends BatteryStats {
         for (int iwr = 0; iwr < NWR; iwr++) {
             if (in.readInt() != 0) {
                 String reasonName = in.readString();
-                getWakeupReasonCounterLocked(reasonName).readSummaryFromParcelLocked(in);
+                getWakeupReasonTimerLocked(reasonName).readSummaryFromParcelLocked(in);
             }
         }
 
@@ -8122,12 +8130,12 @@ public final class BatteryStatsImpl extends BatteryStats {
         }
 
         out.writeInt(mWakeupReasonStats.size());
-        for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
-            LongSamplingCounter counter = ent.getValue();
-            if (counter != null) {
+        for (Map.Entry<String, SamplingTimer> ent : mWakeupReasonStats.entrySet()) {
+            SamplingTimer timer = ent.getValue();
+            if (timer != null) {
                 out.writeInt(1);
                 out.writeString(ent.getKey());
-                counter.writeSummaryFromParcelLocked(out);
+                timer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
             } else {
                 out.writeInt(0);
             }
@@ -8438,7 +8446,7 @@ public final class BatteryStatsImpl extends BatteryStats {
         for (int ikw = 0; ikw < NKW; ikw++) {
             if (in.readInt() != 0) {
                 String wakelockName = in.readString();
-                SamplingTimer kwlt = new SamplingTimer(mOnBatteryTimeBase, in);
+                SamplingTimer kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase, in);
                 mKernelWakelockStats.put(wakelockName, kwlt);
             }
         }
@@ -8448,9 +8456,8 @@ public final class BatteryStatsImpl extends BatteryStats {
         for (int iwr = 0; iwr < NWR; iwr++) {
             if (in.readInt() != 0) {
                 String reasonName = in.readString();
-                LongSamplingCounter counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase,
-                        in);
-                mWakeupReasonStats.put(reasonName, counter);
+                SamplingTimer timer = new SamplingTimer(mOnBatteryTimeBase, in);
+                mWakeupReasonStats.put(reasonName, timer);
             }
         }
 
@@ -8585,12 +8592,12 @@ public final class BatteryStatsImpl extends BatteryStats {
                 }
             }
             out.writeInt(mWakeupReasonStats.size());
-            for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
-                LongSamplingCounter counter = ent.getValue();
-                if (counter != null) {
+            for (Map.Entry<String, SamplingTimer> ent : mWakeupReasonStats.entrySet()) {
+                SamplingTimer timer = ent.getValue();
+                if (timer != null) {
                     out.writeInt(1);
                     out.writeString(ent.getKey());
-                    counter.writeToParcel(out);
+                    timer.writeToParcel(out, uSecRealtime);
                 } else {
                     out.writeInt(0);
                 }
index 7db70ba..45d790b 100644 (file)
@@ -1440,6 +1440,16 @@ public class XmlUtils {
         return Boolean.parseBoolean(value);
     }
 
+    public static boolean readBooleanAttribute(XmlPullParser in, String name,
+            boolean defaultValue) {
+        final String value = in.getAttributeValue(null, name);
+        if (value == null) {
+            return defaultValue;
+        } else {
+            return Boolean.parseBoolean(value);
+        }
+    }
+
     public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
             throws IOException {
         out.attribute(null, name, Boolean.toString(value));
index f0d7a35..7b33bc2 100644 (file)
@@ -211,7 +211,7 @@ void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognitio
     }
 
     jobject jAudioFormat = NULL;
-    if (event->trigger_in_data) {
+    if (event->trigger_in_data || event->capture_available) {
         jAudioFormat = env->NewObject(gAudioFormatClass,
                                     gAudioFormatCstor,
                                     audioFormatFromNative(event->audio_config.format),
index 5faf03d..396f3ec 100644 (file)
@@ -1888,6 +1888,37 @@ static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, j
     return array;
 }
 
+static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz,
+                                                                 jint styleId)
+{
+    AssetManager* am = assetManagerForJavaObject(env, clazz);
+    if (am == NULL) {
+        return NULL;
+    }
+    const ResTable& res(am->getResources());
+
+    const ResTable::bag_entry* startOfBag;
+    const ssize_t N = res.lockBag(styleId, &startOfBag);
+    if (N < 0) {
+        return NULL;
+    }
+
+    jintArray array = env->NewIntArray(N);
+    if (array == NULL) {
+        res.unlockBag(startOfBag);
+        return NULL;
+    }
+
+    Res_value value;
+    const ResTable::bag_entry* bag = startOfBag;
+    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
+        int resourceId = bag->map.name.ident;
+        env->SetIntArrayRegion(array, i, 1, &resourceId);
+    }
+    res.unlockBag(startOfBag);
+    return array;
+}
+
 static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
 {
     if (isSystem) {
@@ -2038,6 +2069,8 @@ static JNINativeMethod gAssetManagerMethods[] = {
         (void*) android_content_AssetManager_getArrayStringInfo },
     { "getArrayIntResource","(I)[I",
         (void*) android_content_AssetManager_getArrayIntResource },
+    { "getStyleAttributes","(I)[I",
+        (void*) android_content_AssetManager_getStyleAttributes },
 
     // Bookkeeping.
     { "init",           "(Z)V",
index 949f4ff..050037e 100644 (file)
@@ -173,6 +173,12 @@ static jboolean android_view_RenderNode_setOutlineNone(JNIEnv* env,
     return true;
 }
 
+static jboolean android_view_RenderNode_hasShadow(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().hasShadow();
+}
+
 static jboolean android_view_RenderNode_setClipToOutline(JNIEnv* env,
         jobject clazz, jlong renderNodePtr, jboolean clipToOutline) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
@@ -491,6 +497,7 @@ static JNINativeMethod gMethods[] = {
     { "nSetOutlineConvexPath", "(JJF)Z", (void*) android_view_RenderNode_setOutlineConvexPath },
     { "nSetOutlineEmpty",      "(J)Z",   (void*) android_view_RenderNode_setOutlineEmpty },
     { "nSetOutlineNone",       "(J)Z",   (void*) android_view_RenderNode_setOutlineNone },
+    { "nHasShadow",            "(J)Z",   (void*) android_view_RenderNode_hasShadow },
     { "nSetClipToOutline",     "(JZ)Z",  (void*) android_view_RenderNode_setClipToOutline },
     { "nSetRevealClip",        "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
 
index 85c2a09..84b7913 100644 (file)
@@ -159,6 +159,11 @@ static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong
     animator->setInterpolator(interpolator);
 }
 
+static void setAllowRunningAsync(JNIEnv* env, jobject clazz, jlong animatorPtr, jboolean mayRunAsync) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->setAllowRunningAsync(mayRunAsync);
+}
+
 static void start(JNIEnv* env, jobject clazz, jlong animatorPtr, jobject finishListener) {
     BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
     if (finishListener) {
@@ -191,6 +196,7 @@ static JNINativeMethod gMethods[] = {
     { "nGetDuration", "(J)J", (void*) getDuration },
     { "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
     { "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
+    { "nSetAllowRunningAsync", "(JZ)V", (void*) setAllowRunningAsync },
     { "nStart", "(JLandroid/view/RenderNodeAnimator;)V", (void*) start },
     { "nEnd", "(J)V", (void*) end },
 #endif
index 7e6d335..a8edb77 100644 (file)
@@ -158,9 +158,11 @@ public:
 
     // Marks the start of a frame, which will update the frame time and move all
     // next frame animations into the current frame
-    virtual void startFrame() {
-        mRootNode->doAttachAnimatingNodes(this);
-        AnimationContext::startFrame();
+    virtual void startFrame(TreeInfo::TraversalMode mode) {
+        if (mode == TreeInfo::MODE_FULL) {
+            mRootNode->doAttachAnimatingNodes(this);
+        }
+        AnimationContext::startFrame(mode);
     }
 
     // Runs any animations still left in mCurrentFrameAnimations
index 2382d18..8a66c3f 100644 (file)
@@ -44,6 +44,7 @@
             android:layout_width="match_parent"
             android:layout_weight="1"
             android:layout_height="0dp"
+            android:layout_marginEnd="8dp"
             android:orientation="horizontal"
             >
             <TextView android:id="@+id/inbox_text0"
@@ -60,7 +61,6 @@
                 android:layout_height="@dimen/notification_badge_size"
                 android:layout_weight="0"
                 android:layout_marginStart="4dp"
-                android:layout_marginEnd="8dp"
                 android:scaleType="fitCenter"
                 android:visibility="gone"
                 />
index 2f9394a..f8fff3b 100644 (file)
     -->
     <integer name="config_mobile_mtu">1440</integer>
 
+    <!-- Flag specifying whether VoLTE & VT should be available for carrier: independent of
+         carrier provisioning. If false: hard disabled. If true: then depends on carrier
+         provisioning, availability etc -->
+    <bool name="config_carrier_volte_vt_available">true</bool>
+
     <!-- Values for GPS configuration (T-Mobile) -->
     <string-array translatable="false" name="config_gpsParameters">
         <item>CAPABILITEIS=0x33</item>
index cd5d55b..57ecd31 100644 (file)
         be disabled) but individual Features can be disabled using ImsConfig.setFeatureValue() -->
     <bool name="imsServiceAllowTurnOff">false</bool>
 
-    <!-- Flag specifying whether VoLTE & VT should be allowed on device: independent of the
+    <!-- Flag specifying whether VoLTE & VT should be available for carrier: independent of
          carrier provisioning. If false: hard disabled. If true: then depends on carrier
          provisioning, availability etc -->
-    <bool name="config_mobile_allow_volte_vt">false</bool>
+    <bool name="config_carrier_volte_vt_available">false</bool>
 
     <bool name="config_auto_attach_data_on_creation">false</bool>
 
index d1cc1fd..cf4064f 100644 (file)
         <attr name="minDate" format="string" />
         <!-- The maximal date shown by this calendar view in mm/dd/yyyy format. -->
         <attr name="maxDate" format="string" />
+        <!-- The first day of week according to {@link java.util.Calendar}. -->
+        <attr name="firstDayOfWeek" />
         <!-- @hide The layout of the date picker. -->
         <attr name="internalLayout" format="reference"  />
         <!-- @hide The layout of the legacy DatePicker. -->
index e905a3a..10c2518 100644 (file)
          {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
          Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. If the value of
          documentLaunchModes is <code>never</code> then any use of
-.........{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+         {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
          Intent.FLAG_ACTIVITY_NEW_DOCUMENT} to launch this activity will be ignored. -->
     <attr name="documentLaunchMode">
         <!-- The default mode, which will create a new task only when
          TaskDescription to change labels, colors and icons in the recent task list. -->
     <attr name="relinquishTaskIdentity" format="boolean" />
 
+    <!-- Indicate that it is okay for this activity be resumed while the previous
+         activity is in the process of pausing, without waiting for the previous pause
+         to complete.  Use this with caution: your activity can not acquire any exclusive
+         resources (such as opening the camera or recording audio) when it launches, or it
+         may conflict with the previous activity and fail.
+
+         <p>The default value of this attribute is <code>false</code>. -->
+    <attr name="resumeWhilePausing" format="boolean" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
         <attr name="maxRecents" />
         <attr name="autoRemoveFromRecents" />
         <attr name="relinquishTaskIdentity" />
+        <attr name="resumeWhilePausing" />
     </declare-styleable>
     
     <!-- The <code>activity-alias</code> tag declares a new
index d8e14a0..939cbf1 100644 (file)
@@ -19,8 +19,8 @@
     <color name="background_material_dark">#ff212121</color>
     <color name="background_material_light">#fffafafa</color>
 
-    <color name="ripple_material_light">#20444444</color>
-    <color name="ripple_material_dark">#20ffffff</color>
+    <color name="ripple_material_light">#40000000</color>
+    <color name="ripple_material_dark">#40ffffff</color>
 
     <color name="button_material_dark">#ff5a595b</color>
     <color name="button_material_light">#ffd6d7d7</color>
index c3d430b..f3b3077 100644 (file)
          specified -->
     <string name="default_wallpaper_component" translatable="false">@null</string>
 
+    <!-- Component name of the built in wallpaper used to display bitmap wallpapers. This must not be null. -->
+    <string name="image_wallpaper_component" translatable="false">com.android.systemui/com.android.systemui.ImageWallpaper</string>
+
     <!-- True if WallpaperService is enabled -->
     <bool name="config_enableWallpaperService">true</bool>
 
         be disabled) but individual Features can be disabled using ImsConfig.setFeatureValue() -->
     <bool name="imsServiceAllowTurnOff">true</bool>
 
-    <!-- Flag specifying whether VoLTE & VT should be allowed on device: independent of the
+    <!-- Flag specifying whether VoLTE & VT is availasble on device -->
+    <bool name="config_device_volte_vt_available">false</bool>
+
+    <!-- Flag specifying whether VoLTE & VT should be available for carrier: independent of
          carrier provisioning. If false: hard disabled. If true: then depends on carrier
          provisioning, availability etc -->
-    <bool name="config_mobile_allow_volte_vt">true</bool>
+    <bool name="config_carrier_volte_vt_available">false</bool>
 
     <bool name="config_networkSamplingWakesDevice">true</bool>
 
index a4c3474..5b047f7 100644 (file)
   <public type="attr" name="windowReenterTransition" />
   <public type="attr" name="windowSharedElementReturnTransition" />
   <public type="attr" name="windowSharedElementReenterTransition" />
-  <public type="attr" name="__removed1" />
+  <public type="attr" name="resumeWhilePausing" />
   <public type="attr" name="datePickerMode"/>
   <public type="attr" name="timePickerMode"/>
   <public type="attr" name="inset" />
index 1b0f9c4..01e6e76 100644 (file)
   <java-symbol type="string" name="hardware" />
   <java-symbol type="string" name="heavy_weight_notification" />
   <java-symbol type="string" name="heavy_weight_notification_detail" />
+  <java-symbol type="string" name="image_wallpaper_component" />
   <java-symbol type="string" name="input_method_binding_label" />
   <java-symbol type="string" name="launch_warning_original" />
   <java-symbol type="string" name="launch_warning_replace" />
   <java-symbol type="attr" name="preferenceFragmentStyle" />
   <java-symbol type="bool" name="skipHoldBeforeMerge" />
   <java-symbol type="bool" name="imsServiceAllowTurnOff" />
-  <java-symbol type="bool" name="config_mobile_allow_volte_vt" />
+  <java-symbol type="bool" name="config_device_volte_vt_available" />
+  <java-symbol type="bool" name="config_carrier_volte_vt_available" />
   <java-symbol type="bool" name="useImsAlwaysForEmergencyCall" />
   <java-symbol type="attr" name="touchscreenBlocksFocus" />
   <java-symbol type="layout" name="resolver_list_with_default" />
index a3c5351..77e1c53 100644 (file)
@@ -54,7 +54,7 @@ public class ConnectivityManagerTestBase extends InstrumentationTestCase {
     protected static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
     protected static final int WIFI_SCAN_TIMEOUT = 50 * 1000; // 50 seconds
     protected static final int SHORT_TIMEOUT = 5 * 1000; // 5 seconds
-    protected static final long LONG_TIMEOUT = 50 * 1000;  // 50 seconds
+    protected static final long LONG_TIMEOUT = 2 * 60 * 1000;  // 2 minutes
     protected static final long WIFI_CONNECTION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
     // 2 minutes timer between wifi stop and start
     protected static final long  WIFI_STOP_START_INTERVAL = 2 * 60 * 1000; // 2 minutes
index ed8b7f7..a11e49b 100755 (executable)
@@ -15,7 +15,7 @@ fi
 
 if [[ $rebuild == true ]]; then
   make -j4 FrameworksCoreInputMethodTests
-  TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreInputMethodTests.apk
+  TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreInputMethodTests/FrameworksCoreInputMethodTests.apk
   COMMAND="adb install -r $TESTAPP"
   echo $COMMAND
   $COMMAND
index cc4a7c4..b6a03d9 100644 (file)
@@ -26,14 +26,12 @@ import android.view.inputmethod.CursorAnchorInfo.Builder;
 
 import java.util.Objects;
 
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_FULLY_VISIBLE;
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_INVISIBLE;
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_NOT_FEASIBLE;
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_PARTIALLY_VISIBLE;
-import static android.view.inputmethod.CursorAnchorInfo.CHARACTER_RECT_TYPE_UNSPECIFIED;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL;
 
 public class CursorAnchorInfoTest extends InstrumentationTestCase {
-    private static final RectF[] MANY_RECTS = new RectF[] {
+    private static final RectF[] MANY_BOUNDS = new RectF[] {
             new RectF(101.0f, 201.0f, 301.0f, 401.0f),
             new RectF(102.0f, 202.0f, 302.0f, 402.0f),
             new RectF(103.0f, 203.0f, 303.0f, 403.0f),
@@ -55,25 +53,25 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
             new RectF(119.0f, 219.0f, 319.0f, 419.0f),
     };
     private static final int[] MANY_FLAGS_ARRAY = new int[] {
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_INVISIBLE,
-        CHARACTER_RECT_TYPE_PARTIALLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_NOT_FEASIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_FULLY_VISIBLE,
-        CHARACTER_RECT_TYPE_NOT_FEASIBLE,
-        CHARACTER_RECT_TYPE_NOT_FEASIBLE,
+        FLAG_HAS_INVISIBLE_REGION,
+        FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
+        FLAG_HAS_VISIBLE_REGION,
+        FLAG_HAS_INVISIBLE_REGION,
+        FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
     };
 
     @SmallTest
@@ -82,11 +80,13 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         final int SELECTION_END = 40;
         final int COMPOSING_TEXT_START = 32;
         final String COMPOSING_TEXT = "test";
-        final boolean INSERTION_MARKER_CLIPPED = true;
+        final int INSERTION_MARKER_FLAGS =
+                FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
         final float INSERTION_MARKER_HORIZONTAL = 10.5f;
         final float INSERTION_MARKER_TOP = 100.1f;
         final float INSERTION_MARKER_BASELINE = 110.4f;
         final float INSERTION_MARKER_BOTOM = 111.0f;
+
         Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
         TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
 
@@ -94,13 +94,13 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         builder.setSelectionRange(SELECTION_START, SELECTION_END)
                 .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT)
                 .setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
-                        INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM,
-                        INSERTION_MARKER_CLIPPED)
+                        INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS)
                 .setMatrix(TRANSFORM_MATRIX);
-        for (int i = 0; i < MANY_RECTS.length; i++) {
-            final RectF rect = MANY_RECTS[i];
+        for (int i = 0; i < MANY_BOUNDS.length; i++) {
+            final RectF bounds = MANY_BOUNDS[i];
             final int flags = MANY_FLAGS_ARRAY[i];
-            builder.addCharacterRect(i, rect.left, rect.top, rect.right, rect.bottom, flags);
+            builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom,
+                    flags);
         }
 
         final CursorAnchorInfo info = builder.build();
@@ -108,26 +108,24 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         assertEquals(SELECTION_END, info.getSelectionEnd());
         assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart());
         assertTrue(TextUtils.equals(COMPOSING_TEXT, info.getComposingText()));
-        assertTrue(info.isInsertionMarkerClipped());
+        assertEquals(INSERTION_MARKER_FLAGS, info.getInsertionMarkerFlags());
         assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal());
         assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop());
         assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline());
         assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom());
         assertEquals(TRANSFORM_MATRIX, info.getMatrix());
-        for (int i = 0; i < MANY_RECTS.length; i++) {
-            final RectF expectedRect = MANY_RECTS[i];
-            assertEquals(expectedRect, info.getCharacterRect(i));
+        for (int i = 0; i < MANY_BOUNDS.length; i++) {
+            final RectF expectedBounds = MANY_BOUNDS[i];
+            assertEquals(expectedBounds, info.getCharacterRect(i));
         }
         assertNull(info.getCharacterRect(-1));
-        assertNull(info.getCharacterRect(MANY_RECTS.length + 1));
+        assertNull(info.getCharacterRect(MANY_BOUNDS.length + 1));
         for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
             final int expectedFlags = MANY_FLAGS_ARRAY[i];
             assertEquals(expectedFlags, info.getCharacterRectFlags(i));
         }
-        assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED,
-                info.getCharacterRectFlags(-1));
-        assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED,
-                info.getCharacterRectFlags(MANY_RECTS.length + 1));
+        assertEquals(0, info.getCharacterRectFlags(-1));
+        assertEquals(0, info.getCharacterRectFlags(MANY_BOUNDS.length + 1));
 
         // Make sure that the builder can reproduce the same object.
         final CursorAnchorInfo info2 = builder.build();
@@ -135,25 +133,24 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         assertEquals(SELECTION_END, info2.getSelectionEnd());
         assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart());
         assertTrue(TextUtils.equals(COMPOSING_TEXT, info2.getComposingText()));
-        assertTrue(info2.isInsertionMarkerClipped());
+        assertEquals(INSERTION_MARKER_FLAGS, info2.getInsertionMarkerFlags());
         assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal());
         assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop());
         assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline());
         assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom());
         assertEquals(TRANSFORM_MATRIX, info2.getMatrix());
-        for (int i = 0; i < MANY_RECTS.length; i++) {
-            final RectF expectedRect = MANY_RECTS[i];
-            assertEquals(expectedRect, info2.getCharacterRect(i));
+        for (int i = 0; i < MANY_BOUNDS.length; i++) {
+            final RectF expectedBounds = MANY_BOUNDS[i];
+            assertEquals(expectedBounds, info2.getCharacterRect(i));
         }
         assertNull(info2.getCharacterRect(-1));
-        assertNull(info2.getCharacterRect(MANY_RECTS.length + 1));
+        assertNull(info2.getCharacterRect(MANY_BOUNDS.length + 1));
         for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
             final int expectedFlags = MANY_FLAGS_ARRAY[i];
             assertEquals(expectedFlags, info2.getCharacterRectFlags(i));
         }
-        assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED, info2.getCharacterRectFlags(-1));
-        assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED,
-                info2.getCharacterRectFlags(MANY_RECTS.length + 1));
+        assertEquals(0, info2.getCharacterRectFlags(-1));
+        assertEquals(0, info2.getCharacterRectFlags(MANY_BOUNDS.length + 1));
         assertEquals(info, info2);
         assertEquals(info.hashCode(), info2.hashCode());
 
@@ -163,25 +160,24 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         assertEquals(SELECTION_END, info3.getSelectionEnd());
         assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart());
         assertTrue(TextUtils.equals(COMPOSING_TEXT, info3.getComposingText()));
-        assertTrue(info3.isInsertionMarkerClipped());
+        assertEquals(INSERTION_MARKER_FLAGS, info3.getInsertionMarkerFlags());
         assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal());
         assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop());
         assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline());
         assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom());
         assertEquals(TRANSFORM_MATRIX, info3.getMatrix());
-        for (int i = 0; i < MANY_RECTS.length; i++) {
-            final RectF expectedRect = MANY_RECTS[i];
-            assertEquals(expectedRect, info3.getCharacterRect(i));
+        for (int i = 0; i < MANY_BOUNDS.length; i++) {
+            final RectF expectedBounds = MANY_BOUNDS[i];
+            assertEquals(expectedBounds, info3.getCharacterRect(i));
         }
         assertNull(info3.getCharacterRect(-1));
-        assertNull(info3.getCharacterRect(MANY_RECTS.length + 1));
+        assertNull(info3.getCharacterRect(MANY_BOUNDS.length + 1));
         for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
             final int expectedFlags = MANY_FLAGS_ARRAY[i];
             assertEquals(expectedFlags, info3.getCharacterRectFlags(i));
         }
-        assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED, info3.getCharacterRectFlags(-1));
-        assertEquals(CHARACTER_RECT_TYPE_UNSPECIFIED,
-                info3.getCharacterRectFlags(MANY_RECTS.length + 1));
+        assertEquals(0, info3.getCharacterRectFlags(-1));
+        assertEquals(0, info3.getCharacterRectFlags(MANY_BOUNDS.length + 1));
         assertEquals(info.hashCode(), info3.hashCode());
 
         builder.reset();
@@ -190,7 +186,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         assertEquals(-1, uninitializedInfo.getSelectionEnd());
         assertEquals(-1, uninitializedInfo.getComposingTextStart());
         assertNull(uninitializedInfo.getComposingText());
-        assertFalse(uninitializedInfo.isInsertionMarkerClipped());
+        assertEquals(0, uninitializedInfo.getInsertionMarkerFlags());
         assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal());
         assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop());
         assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline());
@@ -218,7 +214,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         final int SELECTION_END1 = 7;
         final String COMPOSING_TEXT1 = "0123456789";
         final int COMPOSING_TEXT_START1 = 0;
-        final boolean INSERTION_MARKER_CLIPPED1 = true;
+        final int INSERTION_MARKER_FLAGS1 = FLAG_HAS_VISIBLE_REGION;
         final float INSERTION_MARKER_HORIZONTAL1 = 10.5f;
         final float INSERTION_MARKER_TOP1 = 100.1f;
         final float INSERTION_MARKER_BASELINE1 = 110.4f;
@@ -227,7 +223,8 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         final int SELECTION_END2 = 8;
         final String COMPOSING_TEXT2 = "9876543210";
         final int COMPOSING_TEXT_START2 = 3;
-        final boolean INSERTION_MARKER_CLIPPED2 = false;
+        final int INSERTION_MARKER_FLAGS2 =
+                FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
         final float INSERTION_MARKER_HORIZONTAL2 = 14.5f;
         final float INSERTION_MARKER_TOP2 = 200.1f;
         final float INSERTION_MARKER_BASELINE2 = 210.4f;
@@ -265,10 +262,10 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         assertEquals(
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         Float.NaN, Float.NaN, Float.NaN, Float.NaN,
-                        INSERTION_MARKER_CLIPPED1).build(),
+                        INSERTION_MARKER_FLAGS1).build(),
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         Float.NaN, Float.NaN, Float.NaN, Float.NaN,
-                        INSERTION_MARKER_CLIPPED1).build());
+                        INSERTION_MARKER_FLAGS1).build());
 
         // Check Matrix.
         assertEquals(
@@ -290,74 +287,74 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build(),
+                        INSERTION_MARKER_FLAGS1).build(),
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build());
+                        INSERTION_MARKER_FLAGS1).build());
         assertNotEquals(
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         Float.NaN, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build(),
+                        INSERTION_MARKER_FLAGS1).build(),
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build());
+                        INSERTION_MARKER_FLAGS1).build());
         assertNotEquals(
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build(),
+                        INSERTION_MARKER_FLAGS1).build(),
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build());
+                        INSERTION_MARKER_FLAGS1).build());
         assertNotEquals(
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build(),
+                        INSERTION_MARKER_FLAGS1).build(),
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP2,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build());
+                        INSERTION_MARKER_FLAGS1).build());
         assertNotEquals(
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build(),
+                        INSERTION_MARKER_FLAGS1).build(),
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE2, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build());
+                        INSERTION_MARKER_FLAGS1).build());
         assertNotEquals(
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build(),
+                        INSERTION_MARKER_FLAGS1).build(),
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build());
+                        INSERTION_MARKER_FLAGS1).build());
         assertNotEquals(
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build(),
+                        INSERTION_MARKER_FLAGS1).build(),
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM2,
-                        INSERTION_MARKER_CLIPPED1).build());
+                        INSERTION_MARKER_FLAGS1).build());
         assertNotEquals(
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED1).build(),
+                        INSERTION_MARKER_FLAGS1).build(),
                 new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
                         INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
                         INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
-                        INSERTION_MARKER_CLIPPED2).build());
+                        INSERTION_MARKER_FLAGS2).build());
     }
 
     @SmallTest
@@ -394,7 +391,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
         final int SELECTION_END = 40;
         final int COMPOSING_TEXT_START = 32;
         final String COMPOSING_TEXT = "test";
-        final boolean INSERTION_MARKER_CLIPPED = true;
+        final int INSERTION_MARKER_FLAGS = FLAG_HAS_VISIBLE_REGION;
         final float INSERTION_MARKER_HORIZONTAL = 10.5f;
         final float INSERTION_MARKER_TOP = 100.1f;
         final float INSERTION_MARKER_BASELINE = 110.4f;
@@ -416,7 +413,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
             }
 
             builder.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
-                    INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_CLIPPED);
+                    INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS);
             try {
                 // Coordinate transformation matrix is required if no positional information is
                 // specified.
@@ -438,19 +435,10 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase {
     }
 
     @SmallTest
-    public void testBuilderAddCharacterRect() throws Exception {
+    public void testBuilderAddCharacterBounds() throws Exception {
         // A negative index should be rejected.
         try {
-            new Builder().addCharacterRect(-1, 0.0f, 0.0f, 0.0f, 0.0f,
-                    CHARACTER_RECT_TYPE_FULLY_VISIBLE);
-            assertTrue(false);
-        } catch (IllegalArgumentException ex) {
-        }
-
-        // CHARACTER_RECT_TYPE_UNSPECIFIED is not allowed.
-        try {
-            new Builder().addCharacterRect(0, 0.0f, 0.0f, 0.0f, 0.0f,
-                    CHARACTER_RECT_TYPE_UNSPECIFIED);
+            new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION);
             assertTrue(false);
         } catch (IllegalArgumentException ex) {
         }
index 71b15d5..e75f451 100644 (file)
@@ -2,59 +2,59 @@ ndk=true
 page.template=sdk
 
 
-ndk.mac64_download=android-ndk32-r10-darwin-x86_64.tar.bz2
-ndk.mac64_bytes=411610468
-ndk.mac64_checksum=3ce1fa3dbe7a188f5d2640fd2f7ca944
+ndk.mac64_download=android-ndk32-r10b-darwin-x86_64.tar.bz2
+ndk.mac64_bytes=413652124
+ndk.mac64_checksum=7ca4a84e9c56c38acdafb007e7cd33c5
 
-ndk.mac32_download=android-ndk32-r10-darwin-x86.tar.bz2
-ndk.mac32_bytes=404768263
-ndk.mac32_checksum=1824eec1f6749b6cb7bb306a3b924c33
+ndk.mac32_download=android-ndk32-r10b-darwin-x86.tar.bz2
+ndk.mac32_bytes=406998070
+ndk.mac32_checksum=db3626b2c5f3245d90e2724f7bcf4c3e
 
-ndk.linux64_download=android-ndk32-r10-linux-x86_64.tar.bz2
-ndk.linux64_bytes=420671390
-ndk.linux64_checksum=e3ff629d212a8106a43415862fa39baf
+ndk.linux64_download=android-ndk32-r10b-linux-x86_64.tar.bz2
+ndk.linux64_bytes=422237011
+ndk.linux64_checksum=5c0f301aa789a1a747d5d2aeb8c69ef3
 
-ndk.linux32_download=android-ndk32-r10-linux-x86.tar.bz2
-ndk.linux32_bytes=420078216
-ndk.linux32_checksum=8d9a5faa6e77b43bfae0f169079b21c4
+ndk.linux32_download=android-ndk32-r10b-linux-x86.tar.bz2
+ndk.linux32_bytes=421052081
+ndk.linux32_checksum=e8f55daa5c9de7ab79aaaf5d7d751b69
 
-ndk.win64_download=android-ndk32-r10-windows-x86_64.zip
-ndk.win64_bytes=529850429
-ndk.win64_checksum=b11f9239344f7c377ed5b627f0fb236e
+ndk.win64_download=android-ndk32-r10b-windows-x86_64.zip
+ndk.win64_bytes=531912027
+ndk.win64_checksum=e4dd2e0c6f38e3ad936c366bdf6b1d4e
 
-ndk.win32_download=android-ndk32-r10-windows-x86.zip
-ndk.win32_bytes=500135685
-ndk.win32_checksum=0a3c01147abba945cc4ef5837519ec97
+ndk.win32_download=android-ndk32-r10b-windows-x86.zip
+ndk.win32_bytes=502720425
+ndk.win32_checksum=9fa4f19bca7edd6eefa63fe788737987
 
 
 
-ndk.mac64_64_download=android-ndk64-r10-darwin-x86_64.tar.bz2
-ndk.mac64_64_bytes=327740247
-ndk.mac64_64_checksum=72561b27acc6192a2e81b345ea128a20
+ndk.mac64_64_download=android-ndk64-r10b-darwin-x86_64.tar.bz2
+ndk.mac64_64_bytes=346423776
+ndk.mac64_64_checksum=5bae7feed20ebf0762c0baefe6b84b6d
 
-ndk.mac32_64_download=android-ndk64-r10-darwin-x86.tar.bz2
-ndk.mac32_64_bytes=323736411
-ndk.mac32_64_checksum=5bbaf9d8051ba5d2c0fff74cfd87c374
+ndk.mac32_64_download=android-ndk64-r10b-darwin-x86.tar.bz2
+ndk.mac32_64_bytes=344052876
+ndk.mac32_64_checksum=4447049ac2b5877176b9b6b1cf3bcdb2
 
-ndk.linux64_64_download=android-ndk64-r10-linux-x86_64.tar.bz2
-ndk.linux64_64_bytes=339708042
-ndk.linux64_64_checksum=737290195583268b7fbff4aa56465ab6
+ndk.linux64_64_download=android-ndk64-r10b-linux-x86_64.tar.bz2
+ndk.linux64_64_bytes=358835298
+ndk.linux64_64_checksum=2aa12a0d9a70bcab83e42d010a685136
 
-ndk.linux32_64_download=android-ndk64-r10-linux-x86.tar.bz2
-ndk.linux32_64_bytes=338544906
-ndk.linux32_64_checksum=bea5d027baeb948cbff6af840d26b80d
+ndk.linux32_64_download=android-ndk64-r10b-linux-x86.tar.bz2
+ndk.linux32_64_bytes=358060577
+ndk.linux32_64_checksum=b77eb583626d8c7f5c11e49181fd5eac
 
-ndk.win64_64_download=android-ndk64-r10-windows-x86_64.zip
-ndk.win64_64_bytes=417411195
-ndk.win64_64_checksum=91879ec85539b45313a21b9526b911a8
+ndk.win64_64_download=android-ndk64-r10b-windows-x86_64.zip
+ndk.win64_64_bytes=437152652
+ndk.win64_64_checksum=df39185e6c5a4d72eb9fca3f9aaabc46
 
-ndk.win32_64_download=android-ndk64-r10-windows-x86.zip
-ndk.win32_64_bytes=396751892
-ndk.win32_64_checksum=f79070ace2cde9ebf6a2e2be4a61ac7a
+ndk.win32_64_download=android-ndk64-r10b-windows-x86.zip
+ndk.win32_64_bytes=417290468
+ndk.win32_64_checksum=0f0324cb11f04e8b2641e5422ee39c81
 
-ndk.debug_info_download=android-ndk-r10-cxx-stl-libs-with-debug-info.zip
-ndk.debug_info_bytes=253198908
-ndk.debug_info_checksum=c2a90c43d17dbb5f0609cc8237491788
+ndk.debug_info_download=android-ndk-r10b-cxx-stl-libs-with-debug-info.zip
+ndk.debug_info_bytes=227302317
+ndk.debug_info_checksum=bed1bb855a41bdb572a804dbf6d45aa6
 
 
 page.title=Android NDK
@@ -357,15 +357,6 @@ injunctive remedies (or an equivalent type of urgent legal relief) in any jurisd
 </div>
 </div>
 
-
-
-
-
-
-
-
-
-
  <div id="qv-wrapper">
     <div id="qv">
       <h2>In this document</h2>
@@ -418,7 +409,7 @@ $('#Downloads').after($('#download-table'));
 
 
 <p>With NDK revision 9 and higher, the release packages have been split to reduce download size.
-  The first download for each platform contains the default NDK toolchain. The second download
+  The first download for each platform contains the default NDK toolchain. The additional download
   contains legacy NDK toolchains for that platform, which is only required if you are not using
   the current, recommended toolchain for your NDK builds.</p>
 
@@ -426,10 +417,91 @@ $('#Downloads').after($('#download-table'));
 
 <p>The following sections provide information about releases of the NDK.</p>
 
+
 <div class="toggle-content opened">
  <p>
    <a href="#" onclick="return toggleContent(this)"> <img
      src="/assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+   >Android NDK, Revision 10b</a> <em>(September 2014)</em>
+ </p>
+ <div class="toggle-content-toggleme">
+   <dl>
+
+        <dt>Important notes:</dt>
+     <dd>
+     <ul>
+      <li>Because of the 512MB size restriction on downloadable packages, the following 32-bit items are not in the 32-bit NDK download packages. Instead, they reside in the 64-bit ones:</li>
+      <ul>
+      <li>Android-L headers</li>
+      <li>GCC 4.9</li>
+      </ul>
+     <li>Currently, the only Renderscript support provided by the NDK is for 32-bit Renderscript with Android 4.4 (API level 19). You cannot build HelloComputeNDK (the only Renderscript sample) with any other combination of Renderscript (32- or 64-bit) and Android version.</li>
+     <li>To compile native-codec, you must use a 64-bit NDK package, which is where all the Android-L headers are located. </li>
+     </ul>
+     </dd>
+
+
+     <dt>Important bug fixes:</dt>
+     <dd>
+     <ul>
+     <li>Fixed gdb 7.6 in GCC 4.8/4.9. (Issues <a href="http://b.android.com/74112">74112</a> and <a href="http://b.android.com/74371">74371</a>.)</li>
+     <li>Fixed GCC 4.8/4.9 for x86, so that they no longer enable <code>-msse4.2</code> and <code>-mpopcnt</code> by default. (Issue <a href="http://b.android.com/73843">73843</a>.)</li>
+     </ul>
+     </dd>
+
+     <dt>Other bug fixes:</dt>
+     <dd>
+     <ul>
+     <li>Removed <code>stdio.h</code> from the <code>include-fixed/</code> directories of all versions of GCC. (Issue <a href="http://b.android.com/73728">73728</a>.)</li>
+     <li>Removed duplicate header files from the Windows packages in the <code>platforms/android-L/arch-*/usr/include/linux/netfilter*/</code> directories. (Issue <a href="https://code.google.com/p/android/issues/detail?id=73704">73704</a>.)</li>
+     <li>Fixed a problem that prevented Clang from building HelloComputeNDK.</li>
+     <li>Fixed atexit. (Issue <a href="http://b.android.com/66595">66595</a>.)</li>
+     <li>Made various fixes to the docs in <code>docs/</code> and <code>sources/third_party/googletest/README.NDK</code>. (Issue <a href="http://b.android.com/74069">74069</a>.)</li>
+     <li>Made the following fixes to the Android-L headers:</li>
+     <ol>
+     <li>Added the following functions to <code>ctype.h</code> and <code>wchar.h</code>: <code>dn_expand()</code>, <code>grantpt()</code>, <code> inet_nsap_addr()</code>, <code>inet_nsap_ntoa()</code>, <code>insque()</code>, <code>nsdispatch()</code>, <code>posix_openpt()</code>, <code>__pthread_cleanup_pop()</code>, <code>__pthread_cleanup_push()</code>, <code>remque()</code>, <code>setfsgid()</code>, <code>setfsuid()</code>, <code>splice()</code>, <code>tee()</code>, <code>twalk()</code> (Issue <a href = "http://b.android.com/73719">73719</a>), and 42 <code>*_l()</code> functions.</li>
+
+    <li>Renamed <code>cmsg_nxthdr</code> to <code>__cmsg_nxthdr</code>.</li>
+
+    <li>Removed <code>__libc_malloc_dispatch</code>.</li>
+
+    <li>Changed the <code>ptrace()</code> prototype to <code>long ptrace(int, ...);</code>.</li>
+
+    <li>Removed <code>sha1.h</code>.</li>
+
+    <li>Extended <code>android_dlextinfo</code> in <code>android/dlext.h</code>.</li>
+
+    <li>Annotated <code>__NDK_FPABI__</code> for functions receiving or returning float- or double-type values in <code>stdlib.h</code>, <code>time.h</code>, <code>wchar.h</code>, and <code>complex.h</code>.</li>
+    </ol>
+     </ul>
+     </dd>
+
+     <dt>Other changes:</dt>
+     <dd>
+     <ul>
+        <li>Updated <code>mipsel-linux-android-4.9</code> and <code>mips64el-linux-android-4.9</code>, implementing a new multilib directory layout, and providing support for gdb-7.7</li>
+        <li>Enhanced <code>cpu-features</code> to detect more arm64 features.  (Change list <a href="https://android-review.googlesource.com/#/c/100339">100339</a>.)</li>
+     </dd>
+     </ul>
+
+
+
+   </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 10</a> <em>(July 2014)</em>
  </p>
  <div class="toggle-content-toggleme">
@@ -442,7 +514,7 @@ $('#Downloads').after($('#download-table'));
            <li>GCC 4.9 is the default compiler for 64-bit ABIs. Clang is currently version 3.4.
 <code>NDK_TOOLCHAIN_VERSION=clang</code>
       may not work for arm64-v8a and mips64.</li>
-           <li>Android API level L is the first level with 64-bit support.  Note that this API
+           <li>Android-L is the first level with 64-bit support.  Note that this API
 level is a temporary one, and only for L-preview. An actual API level number will replace it at
 L-release.</li>
            <li>This release includes now includes <code>all32</code> and <code>all64</code>
@@ -479,7 +551,7 @@ GCC 4.6 is still the default.</li>
            <li>For ndk-build, enable 32-bit, GCC 4.9 building either by adding
 <code>NDK_TOOLCHAIN_VERSION=4.9</code> to <code>Application.mk</code>, or exporting it as an
 environment variable from the command line.</li>
-           <li>For a standalone toolchain, use the <code> --toolchain=</code> option in the
+           <li>For a standalone toolchain, use the <code>--toolchain=</code> option in the
 <code>make-standalone-toolchain.sh</code> script. For example: <code>--toolchain=arm-linux-androideabi-4.9.</code></li>
         </ul>
         <li>Upgraded GDB to version 7.6 in GCC 4.8/4.9 and x86*. Since GDB is still at version GDB-7.3.x in
index 43922b8..6f95f91 100644 (file)
@@ -22,6 +22,7 @@ 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;
@@ -58,7 +59,7 @@ class Ripple {
     private final Rect mBounds;
 
     /** Full-opacity color for drawing this ripple. */
-    private int mColor;
+    private int mColorOpaque;
 
     /** Maximum ripple radius. */
     private float mOuterRadius;
@@ -120,7 +121,7 @@ class Ripple {
     }
 
     public void setup(int maxRadius, int color, float density) {
-        mColor = color | 0xFF000000;
+        mColorOpaque = color | 0xFF000000;
 
         if (maxRadius != RippleDrawable.RADIUS_AUTO) {
             mHasMaxRadius = true;
@@ -236,6 +237,9 @@ class Ripple {
         if (N > 0) {
             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();
@@ -253,9 +257,8 @@ class Ripple {
     private boolean drawSoftware(Canvas c, Paint p) {
         boolean hasContent = false;
 
-        // Cache the paint alpha so we can restore it later.
-        final int paintAlpha = p.getAlpha();
-        final int alpha = (int) (paintAlpha * mOpacity + 0.5f);
+        p.setColor(mColorOpaque);
+        final int alpha = (int) (255 * mOpacity + 0.5f);
         final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
         if (alpha > 0 && radius > 0) {
             final float x = MathUtils.lerp(
@@ -268,8 +271,6 @@ class Ripple {
             hasContent = true;
         }
 
-        p.setAlpha(paintAlpha);
-
         return hasContent;
     }
 
@@ -369,7 +370,7 @@ class Ripple {
         final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
         final Paint paint = getTempPaint();
         paint.setAntiAlias(true);
-        paint.setColor(mColor);
+        paint.setColor(mColorOpaque);
         paint.setAlpha((int) (255 * mOpacity + 0.5f));
         paint.setStyle(Style.FILL);
         mPropPaint = CanvasProperty.createPaint(paint);
@@ -402,6 +403,12 @@ class Ripple {
 
         mHardwareAnimating = true;
 
+        // Set up the software values to match the hardware end values.
+        mOpacity = 0;
+        mTweenX = 1;
+        mTweenY = 1;
+        mTweenRadius = 1;
+
         invalidateSelf();
     }
 
@@ -412,7 +419,7 @@ class Ripple {
     public void jump() {
         mCanceled = true;
         endSoftwareAnimations();
-        endHardwareAnimations();
+        cancelHardwareAnimations(true);
         mCanceled = false;
     }
 
@@ -438,24 +445,6 @@ class Ripple {
         }
     }
 
-    private void endHardwareAnimations() {
-        final ArrayList<RenderNodeAnimator> runningAnimations = mRunningAnimations;
-        final int N = runningAnimations.size();
-        for (int i = 0; i < N; i++) {
-            runningAnimations.get(i).end();
-        }
-        runningAnimations.clear();
-
-        // Abort any pending animations. Since we always have a completion
-        // listener on a pending animation, we also need to remove ourselves.
-        if (!mPendingAnimations.isEmpty()) {
-            mPendingAnimations.clear();
-            removeSelf();
-        }
-
-        mHardwareAnimating = false;
-    }
-
     private Paint getTempPaint() {
         if (mTempPaint == null) {
             mTempPaint = new Paint();
index 80ecea3..bc6f5fb 100644 (file)
@@ -22,6 +22,7 @@ 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;
@@ -46,8 +47,6 @@ class RippleBackground {
     private static final float WAVE_OUTER_SIZE_INFLUENCE_MAX = 200f;
     private static final float WAVE_OUTER_SIZE_INFLUENCE_MIN = 40f;
 
-    private static final long RIPPLE_ENTER_DELAY = 80;
-
     // Hardware animators.
     private final ArrayList<RenderNodeAnimator> mRunningAnimations =
             new ArrayList<RenderNodeAnimator>();
@@ -60,7 +59,10 @@ class RippleBackground {
     private final Rect mBounds;
 
     /** Full-opacity color for drawing this ripple. */
-    private int mColor;
+    private int mColorOpaque;
+
+    /** Maximum alpha value for drawing this ripple. */
+    private int mColorAlpha;
 
     /** Maximum ripple radius. */
     private float mOuterRadius;
@@ -103,7 +105,8 @@ class RippleBackground {
     }
 
     public void setup(int maxRadius, int color, float density) {
-        mColor = color | 0xFF000000;
+        mColorOpaque = color | 0xFF000000;
+        mColorAlpha = Color.alpha(color);
 
         if (maxRadius != RippleDrawable.RADIUS_AUTO) {
             mHasMaxRadius = true;
@@ -159,6 +162,11 @@ class RippleBackground {
         return hasContent;
     }
 
+    public boolean shouldDraw() {
+        final int outerAlpha = (int) (mColorAlpha * mOuterOpacity + 0.5f);
+        return mCanUseHardware && mHardwareAnimating || outerAlpha > 0 && mOuterRadius > 0;
+    }
+
     private boolean drawHardware(HardwareCanvas c) {
         // If we have any pending hardware animations, cancel any running
         // animations and start those now.
@@ -167,6 +175,9 @@ class RippleBackground {
         if (N > 0) {
             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();
@@ -184,10 +195,8 @@ class RippleBackground {
     private boolean drawSoftware(Canvas c, Paint p) {
         boolean hasContent = false;
 
-        // Cache the paint alpha so we can restore it later.
-        final int paintAlpha = p.getAlpha();
-
-        final int outerAlpha = (int) (paintAlpha * mOuterOpacity + 0.5f);
+        p.setColor(mColorOpaque);
+        final int outerAlpha = (int) (mColorAlpha * mOuterOpacity + 0.5f);
         if (outerAlpha > 0 && mOuterRadius > 0) {
             p.setAlpha(outerAlpha);
             p.setStyle(Style.FILL);
@@ -195,8 +204,6 @@ class RippleBackground {
             hasContent = true;
         }
 
-        p.setAlpha(paintAlpha);
-
         return hasContent;
     }
 
@@ -248,25 +255,25 @@ class RippleBackground {
         // Determine at what time the inner and outer opacity intersect.
         // inner(t) = mOpacity - t * WAVE_OPACITY_DECAY_VELOCITY / 1000
         // outer(t) = mOuterOpacity + t * WAVE_OUTER_OPACITY_VELOCITY / 1000
-        final int outerInflection = Math.max(0, (int) (1000 * (1 - mOuterOpacity)
+        final int inflectionDuration = Math.max(0, (int) (1000 * (1 - mOuterOpacity)
                 / (WAVE_OPACITY_DECAY_VELOCITY + outerOpacityVelocity) + 0.5f));
-        final int inflectionOpacity = (int) (255 * (mOuterOpacity + outerInflection
-                * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
+        final int inflectionOpacity = (int) (mColorAlpha * (mOuterOpacity
+                + inflectionDuration * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
 
         if (mCanUseHardware) {
-            exitHardware(opacityDuration, outerInflection, inflectionOpacity);
+            exitHardware(opacityDuration, inflectionDuration, inflectionOpacity);
         } else {
-            exitSoftware(opacityDuration, outerInflection, inflectionOpacity);
+            exitSoftware(opacityDuration, inflectionDuration, inflectionOpacity);
         }
     }
 
-    private void exitHardware(int opacityDuration, int outerInflection, int inflectionOpacity) {
+    private void exitHardware(int opacityDuration, int inflectionDuration, int inflectionOpacity) {
         mPendingAnimations.clear();
 
         final Paint outerPaint = getTempPaint();
         outerPaint.setAntiAlias(true);
-        outerPaint.setColor(mColor);
-        outerPaint.setAlpha((int) (255 * mOuterOpacity + 0.5f));
+        outerPaint.setColor(mColorOpaque);
+        outerPaint.setAlpha((int) (mColorAlpha * mOuterOpacity + 0.5f));
         outerPaint.setStyle(Style.FILL);
         mPropOuterPaint = CanvasProperty.createPaint(outerPaint);
         mPropOuterRadius = CanvasProperty.createFloat(mOuterRadius);
@@ -274,21 +281,21 @@ class RippleBackground {
         mPropOuterY = CanvasProperty.createFloat(mOuterY);
 
         final RenderNodeAnimator outerOpacityAnim;
-        if (outerInflection > 0) {
+        if (inflectionDuration > 0) {
             // Outer opacity continues to increase for a bit.
-            outerOpacityAnim = new RenderNodeAnimator(
-                    mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, inflectionOpacity);
-            outerOpacityAnim.setDuration(outerInflection);
+            outerOpacityAnim = new RenderNodeAnimator(mPropOuterPaint,
+                    RenderNodeAnimator.PAINT_ALPHA, inflectionOpacity);
+            outerOpacityAnim.setDuration(inflectionDuration);
             outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
             // Chain the outer opacity exit animation.
-            final int outerDuration = opacityDuration - outerInflection;
+            final int outerDuration = opacityDuration - inflectionDuration;
             if (outerDuration > 0) {
                 final RenderNodeAnimator outerFadeOutAnim = new RenderNodeAnimator(
                         mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
                 outerFadeOutAnim.setDuration(outerDuration);
                 outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
-                outerFadeOutAnim.setStartDelay(outerInflection);
+                outerFadeOutAnim.setStartDelay(inflectionDuration);
                 outerFadeOutAnim.setStartValue(inflectionOpacity);
                 outerFadeOutAnim.addListener(mAnimationListener);
 
@@ -320,7 +327,7 @@ class RippleBackground {
      */
     public void jump() {
         endSoftwareAnimations();
-        endHardwareAnimations();
+        cancelHardwareAnimations(true);
     }
 
     private void endSoftwareAnimations() {
@@ -330,23 +337,6 @@ class RippleBackground {
         }
     }
 
-    private void endHardwareAnimations() {
-        final ArrayList<RenderNodeAnimator> runningAnimations = mRunningAnimations;
-        final int N = runningAnimations.size();
-        for (int i = 0; i < N; i++) {
-            runningAnimations.get(i).end();
-        }
-        runningAnimations.clear();
-
-        // Abort any pending animations. Since we always have a completion
-        // listener on a pending animation, we also need to remove ourselves.
-        if (!mPendingAnimations.isEmpty()) {
-            mPendingAnimations.clear();
-        }
-
-        mHardwareAnimating = false;
-    }
-
     private Paint getTempPaint() {
         if (mTempPaint == null) {
             mTempPaint = new Paint();
@@ -354,18 +344,18 @@ class RippleBackground {
         return mTempPaint;
     }
 
-    private void exitSoftware(int opacityDuration, int outerInflection, int inflectionOpacity) {
+    private void exitSoftware(int opacityDuration, int inflectionDuration, int inflectionOpacity) {
         final ObjectAnimator outerOpacityAnim;
-        if (outerInflection > 0) {
+        if (inflectionDuration > 0) {
             // Outer opacity continues to increase for a bit.
             outerOpacityAnim = ObjectAnimator.ofFloat(this,
                     "outerOpacity", inflectionOpacity / 255.0f);
             outerOpacityAnim.setAutoCancel(true);
-            outerOpacityAnim.setDuration(outerInflection);
+            outerOpacityAnim.setDuration(inflectionDuration);
             outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
             // Chain the outer opacity exit animation.
-            final int outerDuration = opacityDuration - outerInflection;
+            final int outerDuration = opacityDuration - inflectionDuration;
             if (outerDuration > 0) {
                 outerOpacityAnim.addListener(new AnimatorListenerAdapter() {
                     @Override
@@ -446,14 +436,4 @@ class RippleBackground {
             mHardwareAnimating = false;
         }
     };
-
-    /**
-    * Interpolator with a smooth log deceleration
-    */
-    private static final class LogInterpolator implements TimeInterpolator {
-        @Override
-        public float getInterpolation(float input) {
-            return 1 - (float) Math.pow(400, -input * 1.4);
-        }
-    }
 }
index fa762b7..b05fb61 100644 (file)
@@ -242,7 +242,7 @@ public class RippleDrawable extends LayerDrawable {
 
     @Override
     protected boolean onStateChange(int[] stateSet) {
-        super.onStateChange(stateSet);
+        final boolean changed = super.onStateChange(stateSet);
 
         boolean enabled = false;
         boolean pressed = false;
@@ -263,19 +263,7 @@ public class RippleDrawable extends LayerDrawable {
         setRippleActive(enabled && pressed);
         setBackgroundActive(focused || (enabled && pressed));
 
-        // Update the paint color. Only applicable when animated in software.
-        if (mRipplePaint != null && mState.mColor != null) {
-            final ColorStateList stateList = mState.mColor;
-            final int newColor = stateList.getColorForState(stateSet, 0);
-            final int oldColor = mRipplePaint.getColor();
-            if (oldColor != newColor) {
-                mRipplePaint.setColor(newColor);
-                invalidateSelf();
-                return true;
-            }
-        }
-
-        return false;
+        return changed;
     }
 
     private void setRippleActive(boolean active) {
@@ -587,6 +575,10 @@ public class RippleDrawable extends LayerDrawable {
             ripples[i].onHotspotBoundsChanged();
         }
 
+        if (mRipple != null) {
+            mRipple.onHotspotBoundsChanged();
+        }
+
         if (mBackground != null) {
             mBackground.onHotspotBoundsChanged();
         }
@@ -617,17 +609,23 @@ public class RippleDrawable extends LayerDrawable {
         final boolean drawNonMaskContent = mLayerState.mNum > (hasMask ? 1 : 0);
         final boolean drawMask = hasMask && mMask.getOpacity() != PixelFormat.OPAQUE;
         final Rect bounds = getDirtyBounds();
+        final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+        canvas.clipRect(bounds);
 
         // If we have content, draw it into a layer first.
-        final int contentLayer = drawNonMaskContent ?
-                drawContentLayer(canvas, bounds, SRC_OVER) : -1;
+        final int contentLayer;
+        if (drawNonMaskContent) {
+            contentLayer = drawContentLayer(canvas, bounds, SRC_OVER);
+        } else {
+            contentLayer = -1;
+        }
 
         // Next, try to draw the ripples (into a layer if necessary). If we need
         // to mask against the underlying content, set the xfermode to SRC_ATOP.
         final PorterDuffXfermode xfermode = (hasMask || !drawNonMaskContent) ? SRC_OVER : SRC_ATOP;
 
         // If we have a background and a non-opaque mask, draw the masking layer.
-        final int backgroundLayer = drawBackgroundLayer(canvas, bounds, xfermode);
+        final int backgroundLayer = drawBackgroundLayer(canvas, bounds, xfermode, drawMask);
         if (backgroundLayer >= 0) {
             if (drawMask) {
                 drawMaskingLayer(canvas, bounds, DST_IN);
@@ -644,10 +642,13 @@ public class RippleDrawable extends LayerDrawable {
             canvas.restoreToCount(rippleLayer);
         }
 
-        // Composite the layers if needed.
-        if (contentLayer >= 0) {
-            canvas.restoreToCount(contentLayer);
+        // If we failed to draw anything, at least draw a color so that
+        // invalidation works correctly.
+        if (contentLayer < 0 && backgroundLayer < 0 && rippleLayer < 0) {
+            canvas.drawColor(Color.TRANSPARENT);
         }
+
+        canvas.restoreToCount(saveCount);
     }
 
     /**
@@ -711,81 +712,29 @@ public class RippleDrawable extends LayerDrawable {
         return restoreToCount;
     }
 
-    private int drawBackgroundLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
-        // Separate the ripple color and alpha channel. The alpha will be
-        // applied when we merge the ripples down to the canvas.
-        final int rippleARGB;
-        if (mState.mColor != null) {
-            rippleARGB = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
-        } else {
-            rippleARGB = Color.TRANSPARENT;
-        }
-
-        if (mRipplePaint == null) {
-            mRipplePaint = new Paint();
-            mRipplePaint.setAntiAlias(true);
-        }
-
-        final int rippleAlpha = Color.alpha(rippleARGB);
-        final Paint ripplePaint = mRipplePaint;
-        ripplePaint.setColor(rippleARGB);
-        ripplePaint.setAlpha(0xFF);
-
-        boolean drewRipples = false;
-        int restoreToCount = -1;
-        int restoreTranslate = -1;
-
-        // Draw background.
-        final RippleBackground background = mBackground;
-        if (background != null) {
-            // If we're masking the ripple layer, make sure we have a layer
-            // first. This will merge SRC_OVER (directly) onto the canvas.
-            final Paint maskingPaint = getMaskingPaint(mode);
-            maskingPaint.setAlpha(rippleAlpha);
-            restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
-                    bounds.right, bounds.bottom, maskingPaint);
-
-            restoreTranslate = canvas.save();
-            // Translate the canvas to the current hotspot bounds.
-            canvas.translate(mHotspotBounds.exactCenterX(), mHotspotBounds.exactCenterY());
-
-            drewRipples = background.draw(canvas, ripplePaint);
-        }
+    private int drawBackgroundLayer(
+            Canvas canvas, Rect bounds, PorterDuffXfermode mode, boolean drawMask) {
+        int saveCount = -1;
 
-        // Always restore the translation.
-        if (restoreTranslate >= 0) {
-            canvas.restoreToCount(restoreTranslate);
-        }
+        if (mBackground != null && mBackground.shouldDraw()) {
+            // TODO: We can avoid saveLayer here if we push the xfermode into
+            // the background's render thread animator at exit() time.
+            if (drawMask || mode != SRC_OVER) {
+                saveCount = canvas.saveLayer(bounds.left, bounds.top, bounds.right,
+                        bounds.bottom, getMaskingPaint(mode));
+            }
 
-        // If we created a layer with no content, merge it immediately.
-        if (restoreToCount >= 0 && !drewRipples) {
-            canvas.restoreToCount(restoreToCount);
-            restoreToCount = -1;
+            final float x = mHotspotBounds.exactCenterX();
+            final float y = mHotspotBounds.exactCenterY();
+            canvas.translate(x, y);
+            mBackground.draw(canvas, getRipplePaint());
+            canvas.translate(-x, -y);
         }
 
-        return restoreToCount;
+        return saveCount;
     }
 
     private int drawRippleLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
-        // Separate the ripple color and alpha channel. The alpha will be
-        // applied when we merge the ripples down to the canvas.
-        final int rippleARGB;
-        if (mState.mColor != null) {
-            rippleARGB = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
-        } else {
-            rippleARGB = Color.TRANSPARENT;
-        }
-
-        if (mRipplePaint == null) {
-            mRipplePaint = new Paint();
-            mRipplePaint.setAntiAlias(true);
-        }
-
-        final int rippleAlpha = Color.alpha(rippleARGB);
-        final Paint ripplePaint = mRipplePaint;
-        ripplePaint.setColor(rippleARGB);
-        ripplePaint.setAlpha(0xFF);
-
         boolean drewRipples = false;
         int restoreToCount = -1;
         int restoreTranslate = -1;
@@ -807,16 +756,21 @@ public class RippleDrawable extends LayerDrawable {
             // first. This will merge SRC_OVER (directly) onto the canvas.
             if (restoreToCount < 0) {
                 final Paint maskingPaint = getMaskingPaint(mode);
-                maskingPaint.setAlpha(rippleAlpha);
+                final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
+                final int alpha = Color.alpha(color);
+                maskingPaint.setAlpha(alpha / 2);
+
+                // TODO: We can avoid saveLayer here if we're only drawing one
+                // ripple and we don't have content or a translucent mask.
                 restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
                         bounds.right, bounds.bottom, maskingPaint);
 
-                restoreTranslate = canvas.save();
                 // Translate the canvas to the current hotspot bounds.
+                restoreTranslate = canvas.save();
                 canvas.translate(mHotspotBounds.exactCenterX(), mHotspotBounds.exactCenterY());
             }
 
-            drewRipples |= ripple.draw(canvas, ripplePaint);
+            drewRipples |= ripple.draw(canvas, getRipplePaint());
         }
 
         // Always restore the translation.
@@ -845,6 +799,14 @@ public class RippleDrawable extends LayerDrawable {
         return restoreToCount;
     }
 
+    private Paint getRipplePaint() {
+        if (mRipplePaint == null) {
+            mRipplePaint = new Paint();
+            mRipplePaint.setAntiAlias(true);
+        }
+        return mRipplePaint;
+    }
+
     private Paint getMaskingPaint(PorterDuffXfermode xfermode) {
         if (mMaskingPaint == null) {
             mMaskingPaint = new Paint();
index 716dcf5..a20bdae 100644 (file)
@@ -17,7 +17,6 @@
 
 #include "Animator.h"
 #include "RenderNode.h"
-#include "TreeInfo.h"
 #include "renderthread/TimeLord.h"
 
 namespace android {
@@ -34,7 +33,7 @@ AnimationContext::~AnimationContext() {
 }
 
 void AnimationContext::destroy() {
-    startFrame();
+    startFrame(TreeInfo::MODE_RT_ONLY);
     while (mCurrentFrameAnimations.mNextHandle) {
         AnimationHandle* current = mCurrentFrameAnimations.mNextHandle;
         AnimatorManager& animators = current->mRenderNode->animators();
@@ -55,7 +54,7 @@ void AnimationContext::addAnimationHandle(AnimationHandle* handle) {
     handle->insertAfter(&mNextFrameAnimations);
 }
 
-void AnimationContext::startFrame() {
+void AnimationContext::startFrame(TreeInfo::TraversalMode mode) {
     LOG_ALWAYS_FATAL_IF(mCurrentFrameAnimations.mNextHandle,
             "Missed running animations last frame!");
     AnimationHandle* head = mNextFrameAnimations.mNextHandle;
index 9b3d5f4..909ed36 100644 (file)
@@ -20,6 +20,7 @@
 #include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 
+#include "TreeInfo.h"
 #include "renderthread/TimeLord.h"
 #include "utils/Macros.h"
 
@@ -30,7 +31,6 @@ class AnimationContext;
 class AnimationListener;
 class BaseRenderNodeAnimator;
 class RenderNode;
-class TreeInfo;
 
 /*
  * AnimationHandle is several classes merged into one.
@@ -90,7 +90,7 @@ public:
 
     // Marks the start of a frame, which will update the frame time and move all
     // next frame animations into the current frame
-    ANDROID_API virtual void startFrame();
+    ANDROID_API virtual void startFrame(TreeInfo::TraversalMode mode);
 
     // Runs any animations still left in mCurrentFrameAnimations that were not run
     // as part of the standard RenderNode:prepareTree pass.
index 1ab6235..35a4a09 100644 (file)
@@ -53,6 +53,10 @@ public:
         mListener = listener;
     }
     AnimationListener* listener() { return mListener.get(); }
+    ANDROID_API void setAllowRunningAsync(bool mayRunAsync) {
+        mMayRunAsync = mayRunAsync;
+    }
+    bool mayRunAsync() { return mMayRunAsync; }
     ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); }
     ANDROID_API void end() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); }
 
@@ -101,6 +105,7 @@ protected:
     nsecs_t mStartTime;
     nsecs_t mDuration;
     nsecs_t mStartDelay;
+    bool mMayRunAsync;
 
     sp<AnimationListener> mListener;
 
index e06d800..c28fb88 100644 (file)
@@ -90,6 +90,9 @@ public:
             if (animator->isRunning()) {
                 mInfo.out.hasAnimations = true;
             }
+            if (CC_UNLIKELY(!animator->mayRunAsync())) {
+                mInfo.out.requiresUiRedraw = true;
+            }
         }
         return remove;
     }
index a10e70f..254492f 100644 (file)
@@ -872,6 +872,8 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
             handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
                     renderer.getSaveCount() - 1, properties().getClipToBounds());
         } else {
+            const int saveCountOffset = renderer.getSaveCount() - 1;
+            const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
             DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
             for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) {
                 const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex];
@@ -882,8 +884,6 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
                 issueOperationsOf3dChildren(kNegativeZChildren,
                         initialTransform, zTranslatedNodes, renderer, handler);
 
-                const int saveCountOffset = renderer.getSaveCount() - 1;
-                const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
 
                 for (int opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
                     DisplayListOp *op = mDisplayListData->displayListOps[opIndex];
index 0c8d07f..46eeb6a 100644 (file)
@@ -569,6 +569,10 @@ public:
         return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty());
     }
 
+    bool hasShadow() const {
+        return getZ() >= 0.0f && getOutline().getPath() != NULL;
+    }
+
 private:
     // Rendering properties
     struct PrimitiveFields {
index a7c5e85..ec8307f 100644 (file)
@@ -38,6 +38,7 @@ void RenderState::onGLContextCreated() {
 }
 
 void RenderState::onGLContextDestroyed() {
+    AutoMutex _lock(mLayerLock);
     if (CC_UNLIKELY(!mActiveLayers.empty())) {
         mCaches->dumpMemoryUsage();
         for (std::set<renderthread::CanvasContext*>::iterator cit = mRegisteredContexts.begin();
@@ -51,6 +52,13 @@ void RenderState::onGLContextDestroyed() {
             }
             context->mRootRenderNode->debugDumpLayers("  ");
         }
+
+        for (std::set<const Layer*>::iterator lit = mActiveLayers.begin();
+             lit != mActiveLayers.end(); lit++) {
+            const Layer* layer = *(lit);
+            ALOGD("Layer %p, fbo %d, buildlayered %d",
+                    layer, layer->getFbo(), layer->wasBuildLayered);
+        }
         LOG_ALWAYS_FATAL("layers have survived gl context destruction");
     }
 }
index c7ab197..74bafca 100644 (file)
@@ -19,6 +19,7 @@
 #include <set>
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
+#include <utils/Mutex.h>
 
 #include <private/hwui/DrawGlInfo.h>
 
@@ -52,9 +53,11 @@ public:
     void debugOverdraw(bool enable, bool clear);
 
     void registerLayer(const Layer* layer) {
+        AutoMutex _lock(mLayerLock);
         mActiveLayers.insert(layer);
     }
     void unregisterLayer(const Layer* layer) {
+        AutoMutex _lock(mLayerLock);
         mActiveLayers.erase(layer);
     }
 
@@ -83,6 +86,7 @@ private:
     GLsizei mViewportWidth;
     GLsizei mViewportHeight;
     GLuint mFramebuffer;
+    Mutex mLayerLock;
 };
 
 } /* namespace uirenderer */
index 4129a89..5e6796c 100644 (file)
@@ -150,7 +150,7 @@ void CanvasContext::prepareTree(TreeInfo& info) {
     if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) {
         info.canvasContext = this;
     }
-    mAnimationContext->startFrame();
+    mAnimationContext->startFrame(info.mode);
     mRootRenderNode->prepareTree(info);
     mAnimationContext->runRemainingAnimations(info);
 
index 082a158..2c805bb 100644 (file)
@@ -1109,9 +1109,9 @@ public class LocationManager {
      * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
      *
      * <p>
-     * Before API version 20, this method would throw {@link SecurityException}
-     * if the location permissions were not sufficient to use the specified
-     * provider.
+     * Before API version {@link android.os.Build.VERSION_CODES#L}, this
+     * method would throw {@link SecurityException} if the location permissions
+     * were not sufficient to use the specified provider.
      *
      * @param provider the name of the provider
      * @return true if the provider exists and is enabled
@@ -1119,7 +1119,6 @@ public class LocationManager {
      * @throws IllegalArgumentException if provider is null
      */
     public boolean isProviderEnabled(String provider) {
-        // STOPSHIP: finalize API version number in javadoc
         checkProvider(provider);
 
         try {
index 5a286ee..e0901d0 100644 (file)
@@ -21,15 +21,24 @@ import java.io.UnsupportedEncodingException;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.location.LocationManager;
+import android.location.INetInitiatedListener;
+import android.telephony.TelephonyManager;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.PhoneStateListener;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.SystemProperties;
 import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.TelephonyProperties;
 
 /**
  * A GPS Network-initiated Handler class used by LocationManager.
@@ -45,55 +54,67 @@ public class GpsNetInitiatedHandler {
 
     // NI verify activity for bringing up UI (not used yet)
     public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
-    
+
     // string constants for defining data fields in NI Intent
     public static final String NI_INTENT_KEY_NOTIF_ID = "notif_id";
     public static final String NI_INTENT_KEY_TITLE = "title";
     public static final String NI_INTENT_KEY_MESSAGE = "message";
     public static final String NI_INTENT_KEY_TIMEOUT = "timeout";
     public static final String NI_INTENT_KEY_DEFAULT_RESPONSE = "default_resp";
-    
+
     // the extra command to send NI response to GpsLocationProvider
     public static final String NI_RESPONSE_EXTRA_CMD = "send_ni_response";
-    
+
     // the extra command parameter names in the Bundle
     public static final String NI_EXTRA_CMD_NOTIF_ID = "notif_id";
     public static final String NI_EXTRA_CMD_RESPONSE = "response";
-    
+
     // these need to match GpsNiType constants in gps_ni.h
     public static final int GPS_NI_TYPE_VOICE = 1;
     public static final int GPS_NI_TYPE_UMTS_SUPL = 2;
     public static final int GPS_NI_TYPE_UMTS_CTRL_PLANE = 3;
-    
-    // these need to match GpsUserResponseType constants in gps_ni.h    
+    public static final int GPS_NI_TYPE_EMERGENCY_SUPL = 4;
+
+    // these need to match GpsUserResponseType constants in gps_ni.h
     public static final int GPS_NI_RESPONSE_ACCEPT = 1;
     public static final int GPS_NI_RESPONSE_DENY = 2;
-    public static final int GPS_NI_RESPONSE_NORESP = 3;    
-    
+    public static final int GPS_NI_RESPONSE_NORESP = 3;
+    public static final int GPS_NI_RESPONSE_IGNORE = 4;
+
     // these need to match GpsNiNotifyFlags constants in gps_ni.h
     public static final int GPS_NI_NEED_NOTIFY = 0x0001;
     public static final int GPS_NI_NEED_VERIFY = 0x0002;
     public static final int GPS_NI_PRIVACY_OVERRIDE = 0x0004;
-    
+
     // these need to match GpsNiEncodingType in gps_ni.h
     public static final int GPS_ENC_NONE = 0;
     public static final int GPS_ENC_SUPL_GSM_DEFAULT = 1;
     public static final int GPS_ENC_SUPL_UTF8 = 2;
     public static final int GPS_ENC_SUPL_UCS2 = 3;
     public static final int GPS_ENC_UNKNOWN = -1;
-    
+
     private final Context mContext;
-    
+    private final TelephonyManager mTelephonyManager;
+    private final PhoneStateListener mPhoneStateListener;
+
     // parent gps location provider
     private final LocationManager mLocationManager;
-    
+
     // configuration of notificaiton behavior
     private boolean mPlaySounds = false;
     private boolean mPopupImmediately = true;
-    
-    // Set to true if string from HAL is encoded as Hex, e.g., "3F0039"    
+
+    // read the SUPL_ES form gps.conf
+    private volatile boolean mIsSuplEsEnabled;
+
+    // Set to true if the phone is having emergency call.
+    private volatile boolean mIsInEmergency;
+
+    private final INetInitiatedListener mNetInitiatedListener;
+
+    // Set to true if string from HAL is encoded as Hex, e.g., "3F0039"
     static private boolean mIsHexInput = true;
-        
+
     public static class GpsNiNotification
     {
         public int notificationId;
@@ -109,58 +130,95 @@ public class GpsNetInitiatedHandler {
         public int textEncoding;
         public Bundle extras;
     };
-    
+
     public static class GpsNiResponse {
         /* User reponse, one of the values in GpsUserResponseType */
         int userResponse;
         /* Optional extra data to pass with the user response */
         Bundle extras;
     };
-    
+
+    private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
+
+        @Override public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
+                String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
+                /*
+                   Emergency Mode is when during emergency call or in emergency call back mode.
+                   For checking if it is during emergency call:
+                       mIsInEmergency records if the phone is in emergency call or not. It will
+                       be set to true when the phone is having emergency call, and then will
+                       be set to false by mPhoneStateListener when the emergency call ends.
+                   For checking if it is in emergency call back mode:
+                       Emergency call back mode will be checked by reading system properties
+                       when necessary: SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)
+                */
+                mIsInEmergency |= PhoneNumberUtils.isEmergencyNumber(phoneNumber);
+                if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + mIsInEmergency);
+            }
+        }
+    };
+
     /**
      * The notification that is shown when a network-initiated notification
-     * (and verification) event is received. 
+     * (and verification) event is received.
      * <p>
      * This is lazily created, so use {@link #setNINotification()}.
      */
     private Notification mNiNotification;
-    
-    public GpsNetInitiatedHandler(Context context) {
+
+    public GpsNetInitiatedHandler(Context context,
+                                  INetInitiatedListener netInitiatedListener,
+                                  boolean isSuplEsEnabled) {
         mContext = context;
-        mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
-    }
-    
-    // Handles NI events from HAL
-    public void handleNiNotification(GpsNiNotification notif)
-    {
-        if (DEBUG) Log.d(TAG, "handleNiNotification" + " notificationId: " + notif.notificationId
-                + " requestorId: " + notif.requestorId + " text: " + notif.text);
 
-        // Notify and verify with immediate pop-up
-        if (notif.needNotify && notif.needVerify && mPopupImmediately)
-        {
-            // Popup the dialog box now
-            openNiDialog(notif);
+        if (netInitiatedListener == null) {
+            throw new IllegalArgumentException("netInitiatedListener is null");
+        } else {
+            mNetInitiatedListener = netInitiatedListener;
         }
 
-        // Notify only, or delayed pop-up (change mPopupImmediately to FALSE)
-        if (notif.needNotify && !notif.needVerify ||
-            notif.needNotify && notif.needVerify && !mPopupImmediately)
-        {
-            // Show the notification
+        mIsSuplEsEnabled = isSuplEsEnabled;
+        mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
+        mTelephonyManager =
+            (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+
+        mPhoneStateListener = new PhoneStateListener() {
+            @Override
+            public void onCallStateChanged(int state, String incomingNumber) {
+                if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is "+ state);
+                // listening for emergency call ends
+                if (state == TelephonyManager.CALL_STATE_IDLE) {
+                    mIsInEmergency = false;
+                }
+            }
+        };
+        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
 
-            // if mPopupImmediately == FALSE and needVerify == TRUE, a dialog will be opened
-            // when the user opens the notification message
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
+        mContext.registerReceiver(mBroadcastReciever, intentFilter);
+    }
 
-            setNiNotification(notif);
-        }
+    public void setSuplEsEnablement(boolean isEnabled)
+    {
+        mIsSuplEsEnabled = isEnabled;
+    }
 
-        // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify; 3. privacy override.
-        if ( notif.needNotify && !notif.needVerify ||
-            !notif.needNotify && !notif.needVerify ||
-             notif.privacyOverride)
-        {
-            mLocationManager.sendNiResponse(notif.notificationId, GPS_NI_RESPONSE_ACCEPT);
+    // Handles NI events from HAL
+    public void handleNiNotification(GpsNiNotification notif)
+    {
+        if (DEBUG) Log.d(TAG, "in handleNiNotification () :"
+                        + " notificationId: " + notif.notificationId
+                        + " requestorId: " + notif.requestorId
+                        + " text: " + notif.text
+                        + " mIsSuplEsEnabled" + mIsSuplEsEnabled);
+
+        if (mIsSuplEsEnabled == false) {
+            handleNi(notif);
+        } else {
+            handleNiInEs(notif);
         }
 
         //////////////////////////////////////////////////////////////////////////
@@ -176,6 +234,72 @@ public class GpsNetInitiatedHandler {
         //
     }
 
+    // handle NI form HAL when SUPL_ES is disabled.
+    private void handleNi(GpsNiNotification notif) {
+        if (DEBUG) Log.d(TAG, "in handleNi () :"
+                        + " needNotify: " + notif.needNotify
+                        + " needVerify: " + notif.needVerify
+                        + " privacyOverride: " + notif.privacyOverride
+                        + " mPopupImmediately: " + mPopupImmediately);
+
+        // legacy behaviour
+        if (notif.needNotify) {
+        // If NI does not need verify or the dialog is not requested
+        // to pop up immediately, the dialog box will not pop up.
+            if (notif.needVerify && mPopupImmediately) {
+                // Popup the dialog box now
+                openNiDialog(notif);
+            } else {
+                // Show the notification
+                setNiNotification(notif);
+            }
+        }
+        // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify;
+        // 3. privacy override.
+        if (!notif.needVerify || notif.privacyOverride) {
+            try {
+                mNetInitiatedListener.sendNiResponse(notif.notificationId,
+                                                     GPS_NI_RESPONSE_ACCEPT);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException in sendNiResponse");
+            }
+        }
+    }
+
+    // handle NI from HAL when the SUPL_ES is enabled
+    private void handleNiInEs(GpsNiNotification notif) {
+
+        if (DEBUG) Log.d(TAG, "in handleNiInEs () :"
+                    + " niType: " + notif.niType
+                    + " notificationId: " + notif.notificationId);
+
+        // UE is in emergency mode when in emergency call mode or in emergency call back mode
+        boolean isUEInEmergencyMode = mIsInEmergency ||
+            Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE));
+
+        /*
+           1. When SUPL ES bit is off and UE is not in emergency mode:
+                  Call handleNi() to do legacy behaviour.
+           2. When SUPL ES bit is on and UE is in emergency mode:
+                  Call handleNi() to do acceptance behaviour.
+           3. When SUPL ES bit is off but UE is in emergency mode:
+                  Ignore the emergency SUPL INIT.
+           4. When SUPL ES bit is on but UE is not in emergency mode:
+                  Ignore the emergency SUPL INIT.
+        */
+        boolean isNiTypeES = (notif.niType == GPS_NI_TYPE_EMERGENCY_SUPL);
+        if (isNiTypeES != isUEInEmergencyMode) {
+            try {
+                mNetInitiatedListener.sendNiResponse(notif.notificationId,
+                                                     GPS_NI_RESPONSE_IGNORE);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException in sendNiResponse");
+            }
+        } else {
+            handleNi(notif);
+        }
+    }
+
     // Sets the NI notification.
     private synchronized void setNiNotification(GpsNiNotification notif) {
         NotificationManager notificationManager = (NotificationManager) mContext
@@ -202,7 +326,7 @@ public class GpsNetInitiatedHandler {
             mNiNotification.defaults |= Notification.DEFAULT_SOUND;
         } else {
             mNiNotification.defaults &= ~Notification.DEFAULT_SOUND;
-        }        
+        }
 
         mNiNotification.flags = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_AUTO_CANCEL;
         mNiNotification.tickerText = getNotifTicker(notif, mContext);
@@ -230,7 +354,7 @@ public class GpsNetInitiatedHandler {
         mContext.startActivity(intent);
     }
 
-    // Construct the intent for bringing up the dialog activity, which shows the 
+    // Construct the intent for bringing up the dialog activity, which shows the
     // notification and takes user input
     private Intent getDlgIntent(GpsNiNotification notif)
     {
@@ -239,7 +363,7 @@ public class GpsNetInitiatedHandler {
         String message = getDialogMessage(notif, mContext);
 
         // directly bring up the NI activity
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         intent.setClass(mContext, com.android.internal.app.NetInitiatedActivity.class);
 
         // put data in the intent
@@ -409,7 +533,7 @@ public class GpsNetInitiatedHandler {
                 decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
                 decodeString(notif.text, mIsHexInput, notif.textEncoding));
         return message;
-    }       
+    }
 
     // change this to configure dialog display (for verification)
     static public String getDialogTitle(GpsNiNotification notif, Context context)
index 308f783..9b644f4 100644 (file)
@@ -530,7 +530,6 @@ public final class AudioAttributes implements Parcelable {
         }
     }
 
-    /** @hide */
     public static final Parcelable.Creator<AudioAttributes> CREATOR
             = new Parcelable.Creator<AudioAttributes>() {
         /**
index 9ee7027..e3b8985 100644 (file)
@@ -243,7 +243,7 @@ public class AudioManager {
      * or unplugged.
      * An integer value of 1 indicates a plugged-in state, 0 is unplugged.
      */
-    public static final String EXTRA_AUDIO_PLUG_STATE = "android.media.extra.audio_plug_state";
+    public static final String EXTRA_AUDIO_PLUG_STATE = "android.media.extra.AUDIO_PLUG_STATE";
 
     /**
      * Extra used in {@link #ACTION_HDMI_AUDIO_PLUG} to define the maximum number of channels
@@ -251,7 +251,7 @@ public class AudioManager {
      * The corresponding integer value is only available when the device is plugged in (as expressed
      * by {@link #EXTRA_AUDIO_PLUG_STATE}).
      */
-    public static final String EXTRA_MAX_CHANNEL_COUNT = "android.media.extra.max_channel_count";
+    public static final String EXTRA_MAX_CHANNEL_COUNT = "android.media.extra.MAX_CHANNEL_COUNT";
 
     /**
      * Extra used in {@link #ACTION_HDMI_AUDIO_PLUG} to define the audio encodings supported by
@@ -261,7 +261,7 @@ public class AudioManager {
      * {@link AudioFormat} (for instance see {@link AudioFormat#ENCODING_PCM_16BIT}). Use
      * {@link android.content.Intent#getIntArrayExtra(String)} to retrieve the encoding values.
      */
-    public static final String EXTRA_ENCODINGS = "android.media.extra.encodings";
+    public static final String EXTRA_ENCODINGS = "android.media.extra.ENCODINGS";
 
     /**
      * Broadcast Action: An analog audio speaker/headset plugged in or unplugged.
index 3e8ee93..675916b 100644 (file)
@@ -1622,8 +1622,7 @@ final public class MediaCodec {
      * @throws IllegalStateException if in the Uninitialized state.
      */
     public MediaCodecInfo getCodecInfo() {
-        return MediaCodecList.getCodecInfoAt(
-                   MediaCodecList.findCodecByName(getName()));
+        return MediaCodecList.getInfoFor(getName());
     }
 
     private native final ByteBuffer[] getBuffers(boolean input);
index 323a3e3..01f8193 100644 (file)
@@ -321,6 +321,9 @@ public final class MediaCodecInfo {
             // check feature support
             for (Feature feat: getValidFeatures()) {
                 Integer yesNo = (Integer)map.get(MediaFormat.KEY_FEATURE_ + feat.mName);
+                if (yesNo == null) {
+                    continue;
+                }
                 if ((yesNo == 1 && !isFeatureSupported(feat.mName)) ||
                         (yesNo == 0 && isFeatureRequired(feat.mName))) {
                     return false;
@@ -414,7 +417,7 @@ public final class MediaCodecInfo {
          * profile} and {@code level}.  If the type, or profile-level combination
          * is not understood by the framework, it returns null.
          */
-        public static CodecCapabilities CreateFromProfileLevel(
+        public static CodecCapabilities createFromProfileLevel(
                 String mime, int profile, int level) {
             CodecProfileLevel pl = new CodecProfileLevel();
             pl.profile = profile;
@@ -470,13 +473,12 @@ public final class MediaCodecInfo {
                 Integer yesNo = (Integer)map.get(key);
                 if (yesNo == null) {
                     continue;
-                } else if (yesNo > 0) {
+                }
+                if (yesNo > 0) {
                     mFlagsRequired |= feat.mValue;
-                    mDefaultFormat.setInteger(key, 1);
-                } else {
-                    mFlagsSupported |= feat.mValue;
-                    mDefaultFormat.setInteger(key, 1);
                 }
+                mFlagsSupported |= feat.mValue;
+                mDefaultFormat.setInteger(key, 1);
                 // TODO restrict features by mFlagsVerified once all codecs reliably verify them
             }
         }
index 5084c5c..85e9b16 100644 (file)
@@ -116,6 +116,11 @@ final public class MediaCodecList {
 
     /* package private */ static native final int findCodecByName(String codec);
 
+    /** @hide */
+    public static MediaCodecInfo getInfoFor(String codec) {
+        return sAllCodecInfos[findCodecByName(codec)];
+    }
+
     private static native final void native_init();
 
     /**
index 8cb039a..2c38697 100644 (file)
@@ -191,11 +191,8 @@ public final class MediaSession {
                 return;
             }
             if (mCallback != null) {
-                if (mCallback.mCallback == callback) {
-                    Log.w(TAG, "Tried to set same callback, ignoring");
-                    return;
-                }
-                // We're changing callbacks, clear the session from the old one.
+                // We're updating the callback, clear the session from the old
+                // one.
                 mCallback.mCallback.mSession = null;
             }
             if (handler == null) {
index aa196a9..06e40c5 100644 (file)
@@ -467,10 +467,7 @@ public class MediaSessionLegacyHelper {
                 mSessions.remove(mPi);
             } else if (mCb == null) {
                 mCb = new SessionCallback();
-                Handler handler = null;
-                if (Looper.myLooper() == null) {
-                    handler = new Handler(Looper.getMainLooper());
-                }
+                Handler handler = new Handler(Looper.getMainLooper());
                 mSession.setCallback(mCb, handler);
             }
         }
index 72a6f88..317d472 100644 (file)
@@ -389,8 +389,11 @@ public abstract class TvInputService extends Service {
         }
 
         /**
-         * Informs the application that video is available and the playback of the TV stream has
-         * been started.
+         * Informs the application that the video is now available for watching. This is primarily
+         * used to signal the application to unblock the screen. The TV input service must call this
+         * method as soon as the content rendered onto its surface gets ready for viewing.
+         *
+         * @see #notifyVideoUnavailable
          */
         public void notifyVideoAvailable() {
             runOnMainThread(new Runnable() {
@@ -407,16 +410,18 @@ public abstract class TvInputService extends Service {
         }
 
         /**
-         * Informs the application that video is not available, so the TV input cannot continue
-         * playing the TV stream.
+         * Informs the application that the video became unavailable for some reason. This is
+         * primarily used to signal the application to block the screen not to show any intermittent
+         * video artifacts.
          *
-         * @param reason The reason that the TV input stopped the playback:
-         * <ul>
-         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
-         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
-         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL}
-         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING}
-         * </ul>
+         * @param reason The reason why the video became unavailable:
+         *            <ul>
+         *            <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
+         *            <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
+         *            <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL}
+         *            <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING}
+         *            </ul>
+         * @see #notifyVideoAvailable
          */
         public void notifyVideoUnavailable(final int reason) {
             if (reason < TvInputManager.VIDEO_UNAVAILABLE_REASON_START
diff --git a/packages/PrintSpooler/res/layout/preview_page_loading.xml b/packages/PrintSpooler/res/layout/preview_page_loading.xml
new file mode 100644 (file)
index 0000000..1af3a17
--- /dev/null
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="36dip"
+        android:layout_gravity="center"
+        android:src="@drawable/ic_grayedout_printer"
+        android:contentDescription="@null"
+        android:scaleType="centerInside"
+        android:adjustViewBounds="true">
+    </ImageView>
+
+</FrameLayout>
index 6cf9754..84fc050 100644 (file)
@@ -16,7 +16,7 @@
 
 <resources>
 
-    <integer name="preview_page_per_row_count">2</integer>
+    <integer name="preview_page_per_row_count">4</integer>
 
     <integer name="print_option_column_count">3</integer>
 
index 5b7fda3..27e1d51 100644 (file)
@@ -76,7 +76,6 @@
     <!-- Title for the print dialog announced to the user for accessibility. Not shown in the UI. [CHAR LIMIT=none] -->
     <string name="print_dialog">Print dialog</string>
 
-
     <!-- Template for the message that shows the current page out of the total number of pages -->
     <string name="current_page_template"><xliff:g id="current_page">%1$d</xliff:g>
         /<xliff:g id="page_count">%2$d</xliff:g></string>
index 09e8b39..1e7a011 100644 (file)
@@ -317,6 +317,11 @@ public final class RemotePrintDocument {
         return mState == STATE_FAILED;
     }
 
+    public boolean hasLaidOutPages() {
+        return mDocumentInfo.info != null
+                && mDocumentInfo.info.getPageCount() > 0;
+    }
+
     public void clearUpdateError() {
         if (!hasUpdateError()) {
             throw new IllegalStateException("No update error to clear");
index d949673..ce0b9b6 100644 (file)
@@ -17,6 +17,9 @@
 package com.android.printspooler.ui;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
 import android.os.ParcelFileDescriptor;
 import android.print.PageRange;
 import android.print.PrintAttributes.MediaSize;
@@ -26,12 +29,12 @@ import android.support.v7.widget.RecyclerView.Adapter;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.View.MeasureSpec;
 import android.widget.TextView;
 import com.android.printspooler.R;
 import com.android.printspooler.model.PageContentRepository;
@@ -48,7 +51,7 @@ import java.util.List;
  * This class represents the adapter for the pages in the print preview list.
  */
 public final class PageAdapter extends Adapter implements
-        PageContentRepository.OnMalformedPdfFileListener{
+        PageContentRepository.OnMalformedPdfFileListener {
     private static final String LOG_TAG = "PageAdapter";
 
     private static final int MAX_PREVIEW_PAGES_BATCH = 50;
@@ -86,6 +89,8 @@ public final class PageAdapter extends Adapter implements
     // Pages the user selected in the UI.
     private PageRange[] mSelectedPages;
 
+    private BitmapDrawable mEmptyState;
+
     private int mDocumentPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
     private int mSelectedPageCount;
 
@@ -257,7 +262,7 @@ public final class PageAdapter extends Adapter implements
         }
 
         if (updatePreviewAreaAndPageSize) {
-            updatePreviewAreaAndPageSize();
+            updatePreviewAreaPageSizeAndEmptyState();
         }
 
         if (documentChanged) {
@@ -318,12 +323,12 @@ public final class PageAdapter extends Adapter implements
             }
 
             // OK, there are bugs in recycler view which tries to bind views
-            // without recycling them which would give us a chane to clean up.
+            // without recycling them which would give us a chance to clean up.
             PageContentProvider boundProvider = mPageContentRepository
-                   .peekPageContentProvider(pageIndexInFile);
+                    .peekPageContentProvider(pageIndexInFile);
             if (boundProvider != null) {
                 PageContentView owner = (PageContentView) boundProvider.getOwner();
-                owner.init(null, mMediaSize, mMinMargins);
+                owner.init(null, mEmptyState, mMediaSize, mMinMargins);
                 mPageContentRepository.releasePageContentProvider(boundProvider);
             }
 
@@ -333,7 +338,7 @@ public final class PageAdapter extends Adapter implements
         } else {
             onSelectedPageNotInFile(pageInDocument);
         }
-        content.init(provider, mMediaSize, mMinMargins);
+        content.init(provider, mEmptyState, mMediaSize, mMinMargins);
 
         View pageSelector = page.findViewById(R.id.page_selector);
         pageSelector.setTag(myHolder);
@@ -384,7 +389,7 @@ public final class PageAdapter extends Adapter implements
             mSelectedPages = selectedPages;
             mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
                     mSelectedPages, mDocumentPageCount);
-            updatePreviewAreaAndPageSize();
+            updatePreviewAreaPageSizeAndEmptyState();
             notifyDataSetChanged();
         }
         return mSelectedPages;
@@ -392,12 +397,12 @@ public final class PageAdapter extends Adapter implements
 
     public void onPreviewAreaSizeChanged() {
         if (mMediaSize != null) {
-            updatePreviewAreaAndPageSize();
+            updatePreviewAreaPageSizeAndEmptyState();
             notifyDataSetChanged();
         }
     }
 
-    private void updatePreviewAreaAndPageSize() {
+    private void updatePreviewAreaPageSizeAndEmptyState() {
         final int availableWidth = mPreviewArea.getWidth();
         final int availableHeight = mPreviewArea.getHeight();
 
@@ -419,7 +424,7 @@ public final class PageAdapter extends Adapter implements
         final int pageContentDesiredHeight = (int) (((float) pageContentDesiredWidth
                 / pageAspectRatio) + 0.5f);
 
-        // If the page does not fit entirely in a vertial direction,
+        // If the page does not fit entirely in a vertical direction,
         // we shirk it but not less than the minimal page width.
         final int pageContentMinHeight = (int) (mPreviewPageMinWidth / pageAspectRatio + 0.5f);
         final int pageContentMaxHeight = Math.max(pageContentMinHeight,
@@ -448,6 +453,23 @@ public final class PageAdapter extends Adapter implements
 
         mPreviewArea.setPadding(horizontalPadding, verticalPadding,
                 horizontalPadding, verticalPadding);
+
+        // Now update the empty state drawable, as it depends on the page
+        // size and is reused for all views for better performance.
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        View content = inflater.inflate(R.layout.preview_page_loading, null, false);
+        content.measure(MeasureSpec.makeMeasureSpec(mPageContentWidth, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mPageContentHeight, MeasureSpec.EXACTLY));
+        content.layout(0, 0, content.getMeasuredWidth(), content.getMeasuredHeight());
+
+        Bitmap bitmap = Bitmap.createBitmap(mPageContentWidth, mPageContentHeight,
+                Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        content.draw(canvas);
+
+        // Do not recycle the old bitmap if such as it may be set as an empty
+        // state to any of the page views. Just let the GC take care of it.
+        mEmptyState = new BitmapDrawable(mContext.getResources(), bitmap);
     }
 
     private PageRange[] computeSelectedPages() {
@@ -718,7 +740,7 @@ public final class PageAdapter extends Adapter implements
     private void recyclePageView(PageContentView page, int pageIndexInAdapter) {
         PageContentProvider provider = page.getPageContentProvider();
         if (provider != null) {
-            page.init(null, null, null);
+            page.init(null, null, null, null);
             mPageContentRepository.releasePageContentProvider(provider);
             mBoundPagesInAdapter.remove(pageIndexInAdapter);
         }
index 022e0d0..535081f 100644 (file)
@@ -114,14 +114,15 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
     private static final int DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF = Integer.MAX_VALUE;
     private static final int DEST_ADAPTER_ITEM_ID_ALL_PRINTERS = Integer.MAX_VALUE - 1;
 
-    private static final int STATE_CONFIGURING = 0;
-    private static final int STATE_PRINT_CONFIRMED = 1;
-    private static final int STATE_PRINT_CANCELED = 2;
-    private static final int STATE_UPDATE_FAILED = 3;
-    private static final int STATE_CREATE_FILE_FAILED = 4;
-    private static final int STATE_PRINTER_UNAVAILABLE = 5;
-    private static final int STATE_UPDATE_SLOW = 6;
-    private static final int STATE_PRINT_COMPLETED = 7;
+    private static final int STATE_INITIALIZING = 0;
+    private static final int STATE_CONFIGURING = 1;
+    private static final int STATE_PRINT_CONFIRMED = 2;
+    private static final int STATE_PRINT_CANCELED = 3;
+    private static final int STATE_UPDATE_FAILED = 4;
+    private static final int STATE_CREATE_FILE_FAILED = 5;
+    private static final int STATE_PRINTER_UNAVAILABLE = 6;
+    private static final int STATE_UPDATE_SLOW = 7;
+    private static final int STATE_PRINT_COMPLETED = 8;
 
     private static final int UI_STATE_PREVIEW = 0;
     private static final int UI_STATE_ERROR = 1;
@@ -196,7 +197,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
 
     private int mCurrentPageCount;
 
-    private int mState;
+    private int mState = STATE_INITIALIZING;
 
     private int mUiState = UI_STATE_PREVIEW;
 
@@ -290,10 +291,17 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
         mPrintedDocument.start();
 
         ensurePreviewUiShown();
+
+        setState(STATE_CONFIGURING);
     }
 
     @Override
     public void onPause() {
+        if (mState == STATE_INITIALIZING) {
+            super.onPause();
+            return;
+        }
+
         if (isFinishing()) {
             PrintSpoolerService spooler = mSpoolerProvider.getSpooler();
             spooler.updatePrintJobUserConfigurableOptionsNoPersistence(mPrintJob);
@@ -341,9 +349,13 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (mState == STATE_INITIALIZING) {
+            return super.onKeyUp(keyCode, event);
+        }
+
         if (keyCode == KeyEvent.KEYCODE_BACK
                 && event.isTracking() && !event.isCanceled()) {
-            if (mPrintPreviewController != null&&mPrintPreviewController.isOptionsOpened()
+            if (mPrintPreviewController != null && mPrintPreviewController.isOptionsOpened()
                     && !hasErrors()) {
                 mPrintPreviewController.closeOptions();
             } else {
@@ -900,7 +912,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
         final boolean willUpdate = mPrintedDocument.update(mPrintJob.getAttributes(),
                 pages, preview);
 
-        if (willUpdate) {
+        if (willUpdate && !mPrintedDocument.hasLaidOutPages()) {
             // When the update is done we update the print preview.
             mProgressMessageController.post();
             return true;
index 2b5b41b..a25e05e 100644 (file)
@@ -195,6 +195,7 @@ class PrintPreviewController implements MutexFileProvider.OnReleaseRequestCallba
         if (mPageAdapter.isOpened()) {
             mPageAdapter.close(null);
         }
+        mRecyclerView.setAdapter(null);
         mPageAdapter.destroy();
     }
 
index 23a01bd..e2ae758 100644 (file)
 package com.android.printspooler.widget;
 
 import android.content.Context;
-import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Margins;
 import android.util.AttributeSet;
-import android.util.TypedValue;
 import android.view.View;
 import com.android.printspooler.model.PageContentRepository;
-import com.android.printspooler.model.PageContentRepository.PageContentProvider;
 import com.android.printspooler.model.PageContentRepository.RenderSpec;
+import com.android.printspooler.model.PageContentRepository.PageContentProvider;
 
 /**
  * This class represents a page in the print preview list. The width of the page
@@ -37,35 +35,20 @@ import com.android.printspooler.model.PageContentRepository.RenderSpec;
  */
 public class PageContentView extends View
         implements PageContentRepository.OnPageContentAvailableCallback {
-
-    private final ColorDrawable mEmptyState;
-
     private PageContentProvider mProvider;
 
     private MediaSize mMediaSize;
 
     private Margins mMinMargins;
 
+    private Drawable mEmptyState;
+
     private boolean mContentRequested;
 
     private boolean mNeedsLayout;
 
     public PageContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
-
-        TypedValue typedValue = new TypedValue();
-        context.getTheme().resolveAttribute(com.android.internal.R.attr.textColorPrimary,
-                typedValue, true);
-
-        mEmptyState = new ColorDrawable(typedValue.data);
-
-        setBackground(mEmptyState);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        requestPageContentIfNeeded();
     }
 
     @Override
@@ -85,15 +68,19 @@ public class PageContentView extends View
         return mProvider;
     }
 
-    public void init(PageContentProvider provider, MediaSize mediaSize, Margins minMargins) {
+    public void init(PageContentProvider provider, Drawable emptyState,
+            MediaSize mediaSize, Margins minMargins) {
         final boolean providerChanged = (mProvider == null)
                 ? provider != null : !mProvider.equals(provider);
+        final boolean loadingDrawableChanged = (mEmptyState == null)
+                ? mEmptyState != null : !mEmptyState.equals(emptyState);
         final boolean mediaSizeChanged = (mMediaSize == null)
                 ? mediaSize != null : !mMediaSize.equals(mediaSize);
         final boolean marginsChanged = (mMinMargins == null)
                 ? minMargins != null : !mMinMargins.equals(minMargins);
 
-        if (!providerChanged && !mediaSizeChanged && !marginsChanged) {
+        if (!providerChanged && !mediaSizeChanged
+                && !marginsChanged && !loadingDrawableChanged) {
             return;
         }
 
@@ -101,6 +88,7 @@ public class PageContentView extends View
         mMediaSize = mediaSize;
         mMinMargins = minMargins;
 
+        mEmptyState = emptyState;
         mContentRequested = false;
         mNeedsLayout = mNeedsLayout || mediaSizeChanged || marginsChanged;
 
index d4ebb01..b94a258 100644 (file)
                 android:theme="@style/RecentsStyle"
                 android:excludeFromRecents="true"
                 android:launchMode="singleInstance"
+                android:resumeWhilePausing="true"
                 android:exported="true">
           <intent-filter>
             <action android:name="com.android.systemui.TOGGLE_RECENTS" />
                   android:label="@string/accessibility_desc_recent_apps"
                   android:launchMode="singleInstance"
                   android:excludeFromRecents="true"
+                  android:stateNotNeeded="true"
+                  android:resumeWhilePausing="true"
                   android:theme="@style/RecentsTheme">
             <intent-filter>
                 <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
diff --git a/packages/SystemUI/res/drawable/stat_sys_vpn_ic.xml b/packages/SystemUI/res/drawable/stat_sys_vpn_ic.xml
new file mode 100644 (file)
index 0000000..7ca8c40
--- /dev/null
@@ -0,0 +1,24 @@
+<!--
+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="17.0dp"
+        android:height="17.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.700000,10.000000c-0.800000,-2.300000 -3.000000,-4.000000 -5.700000,-4.000000c-3.300000,0.000000 -6.000000,2.700000 -6.000000,6.000000s2.700000,6.000000 6.000000,6.000000c2.600000,0.000000 4.800000,-1.700000 5.700000,-4.000000L17.000000,14.000000l0.000000,4.000000l4.000000,0.000000l0.000000,-4.000000l2.000000,0.000000l0.000000,-4.000000L12.700000,10.000000zM7.000000,14.000000c-1.100000,0.000000 -2.000000,-0.900000 -2.000000,-2.000000c0.000000,-1.100000 0.900000,-2.000000 2.000000,-2.000000s2.000000,0.900000 2.000000,2.000000C9.000000,13.100000 8.100000,14.000000 7.000000,14.000000z"/>
+</vector>
index 1ef9cad..4b68e77 100644 (file)
@@ -23,6 +23,6 @@
     android:textSize="16sp"
     android:textColor="#ffffffff"
     android:text="@string/recents_empty_message"
-    android:fontFamily="sans-serif-light"
+    android:fontFamily="sans-serif"
     android:background="#80000000"
     android:visibility="gone" />
\ No newline at end of file
index 347c8a9..3a8a17d 100644 (file)
     android:gravity="center_vertical"
     android:orientation="horizontal"
     >
+    <ImageView
+        android:id="@+id/vpn"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:paddingEnd="6dp"
+        android:src="@drawable/stat_sys_vpn_ic"
+        />
     <FrameLayout
         android:id="@+id/wifi_combo"
         android:layout_height="wrap_content"
index 5399a39..c0e2b10 100644 (file)
     <integer name="recents_animate_task_exit_to_home_duration">225</integer>
     <!-- The min animation duration for animating the task bar out. -->
     <integer name="recents_animate_task_bar_exit_duration">125</integer>
+    <!-- The animation delay for animating the first task in. This should roughly be the animation
+     duration of the transition in to recents from home. -->
+    <integer name="recents_animate_task_enter_from_home_delay">150</integer>
     <!-- The min animation duration for animating the task in when transitioning from home. -->
     <integer name="recents_animate_task_enter_from_home_duration">275</integer>
     <!-- The animation stagger to apply to each task animation when transitioning from home. -->
-    <integer name="recents_animate_task_enter_from_home_delay">150</integer>
+    <integer name="recents_animate_task_enter_from_home_stagger_delay">10</integer>
     <!-- The short duration when animating in/out the lock to app button. -->
     <integer name="recents_animate_lock_to_app_button_short_duration">150</integer>
     <!-- The long duration when animating in/out the lock to app button. -->
index b7434fd..34430d9 100644 (file)
@@ -203,8 +203,7 @@ public class RecentTasksLoader implements View.OnTouchListener {
         Drawable icon = getFullResIcon(td.resolveInfo, pm);
         if (td.userId != UserHandle.myUserId()) {
             // Need to badge the icon
-            icon = mContext.getPackageManager().getUserBadgedDrawableForDensity(icon,
-                    new UserHandle(td.userId), null, 0);
+            icon = mContext.getPackageManager().getUserBadgedIcon(icon, new UserHandle(td.userId));
         }
         if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
                 + td + ": " + thumbnail);
index 2d114c0..5fa9fa4 100644 (file)
 
 package com.android.systemui.recents;
 
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -385,9 +387,9 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
                 toTask);
         if (toTransform != null && toTask.key != null) {
             Rect toTaskRect = toTransform.rect;
-
-            // XXX: Reduce the memory usage the to the task bar height
-            Bitmap thumbnail = Bitmap.createBitmap(toTaskRect.width(), toTaskRect.height(),
+            int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
+            int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+            Bitmap thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
                     Bitmap.Config.ARGB_8888);
             if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
                 thumbnail.eraseColor(0xFFff0000);
@@ -401,7 +403,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
 
             mStartAnimationTriggered = false;
             return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mStatusBarView,
-                    thumbnail, toTaskRect.left, toTaskRect.top, this);
+                    thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
+                    toTaskRect.height(), this);
         }
 
         // If both the screenshot and thumbnail fails, then just fall back to the default transition
@@ -562,11 +565,34 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
     public void onAnimationStarted() {
         // Notify recents to start the enter animation
         if (!mStartAnimationTriggered) {
+            // There can be a race condition between the start animation callback and
+            // the start of the new activity (where we register the receiver that listens
+            // to this broadcast, so we add our own receiver and if that gets called, then
+            // we know the activity has not yet started and we can retry sending the broadcast.
+            BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (getResultCode() == Activity.RESULT_OK) {
+                        mStartAnimationTriggered = true;
+                        return;
+                    }
+
+                    // Schedule for the broadcast to be sent again after some time
+                    mHandler.postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            onAnimationStarted();
+                        }
+                    }, 75);
+                }
+            };
+
+            // Send the broadcast to notify Recents that the animation has started
             Intent intent = new Intent(ACTION_START_ENTER_ANIMATION);
             intent.setPackage(mContext.getPackageName());
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
-            mStartAnimationTriggered = true;
+            mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
+                    fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
         }
     }
 }
index ec7799a..8f92027 100644 (file)
@@ -146,6 +146,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
                 ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
                 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
                 onEnterAnimationTriggered();
+                // Notify the fallback receiver that we have successfully got the broadcast
+                // See AlternateRecentsComponent.onAnimationStarted()
+                setResultCode(Activity.RESULT_OK);
             }
         }
     };
index 5d8181c..396be3b 100644 (file)
@@ -74,8 +74,9 @@ public class RecentsConfiguration {
     public float taskStackOverscrollPct;
 
     /** Task view animation and styles */
-    public int taskViewEnterFromHomeDuration;
     public int taskViewEnterFromHomeDelay;
+    public int taskViewEnterFromHomeDuration;
+    public int taskViewEnterFromHomeStaggerDelay;
     public int taskViewExitToHomeDuration;
     public int taskViewRemoveAnimDuration;
     public int taskViewRemoveAnimTranslationXPx;
@@ -209,10 +210,12 @@ public class RecentsConfiguration {
         taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
 
         // Task view animation and styles
-        taskViewEnterFromHomeDuration =
-                res.getInteger(R.integer.recents_animate_task_enter_from_home_duration);
         taskViewEnterFromHomeDelay =
                 res.getInteger(R.integer.recents_animate_task_enter_from_home_delay);
+        taskViewEnterFromHomeDuration =
+                res.getInteger(R.integer.recents_animate_task_enter_from_home_duration);
+        taskViewEnterFromHomeStaggerDelay =
+                res.getInteger(R.integer.recents_animate_task_enter_from_home_stagger_delay);
         taskViewExitToHomeDuration =
                 res.getInteger(R.integer.recents_animate_task_exit_to_home_duration);
         taskViewRemoveAnimDuration =
index 07a42bd..887cbac 100644 (file)
@@ -356,7 +356,7 @@ public class SystemServicesProxy {
      */
     public Drawable getBadgedIcon(Drawable icon, int userId) {
         if (userId != UserHandle.myUserId()) {
-            icon = mPm.getUserBadgedDrawableForDensity(icon, new UserHandle(userId), null, 0);
+            icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
         }
         return icon;
     }
index 0c6e7b6..1bfb41f 100644 (file)
@@ -413,10 +413,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
         final SystemServicesProxy ssp =
                 RecentsTaskLoader.getInstance().getSystemServicesProxy();
         ActivityOptions opts = null;
-        int thumbnailWidth = transform.rect.width();
-        int thumbnailHeight = transform.rect.height();
-        if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
-                task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
+        if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
+                task.thumbnail.getHeight() > 0) {
             Bitmap b;
             if (tv != null) {
                 // Disable any focused state before we draw the header
@@ -424,7 +422,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
                     tv.unsetFocusedTask();
                 }
 
-                b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888);
+                float scale = tv.getScaleX();
+                int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale);
+                int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale);
+                b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
+                        Bitmap.Config.ARGB_8888);
                 if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
                     b.eraseColor(0xFFff0000);
                 } else {
@@ -435,7 +437,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
                 }
             } else {
                 // Notify the system to skip the thumbnail layer by using an ALPHA_8 bitmap
-                b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ALPHA_8);
+                b = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
             }
             ActivityOptions.OnAnimationStartedListener animStartedListener = null;
             if (lockToTask) {
@@ -456,7 +458,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
                 };
             }
             opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
-                    b, offsetX, offsetY, animStartedListener);
+                    b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
+                    animStartedListener);
         }
 
         final ActivityOptions launchOpts = opts;
index eecc170..6fe86be 100644 (file)
@@ -284,7 +284,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
                 float scaledWindowInsetTop = (int) (taskScale * windowInsetTop);
                 float scaledTranslationY = taskRect.top + transform.translationY -
                         (scaledWindowInsetTop + scaledYOffset);
-                startDelay = mConfig.taskViewEnterFromHomeDelay;
+                startDelay = mConfig.taskViewEnterFromHomeStaggerDelay;
 
                 // Animate the top clip
                 mViewBounds.animateClipTop(windowInsetTop, duration,
@@ -410,8 +410,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
         } else if (mConfig.launchedFromHome) {
             // Animate the tasks up
             int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
-            int delay = mConfig.taskBarEnterAnimDelay +
-                    frontIndex * mConfig.taskViewEnterFromHomeDelay;
+            int delay = mConfig.taskViewEnterFromHomeDelay +
+                    frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay;
             if (Constants.DebugFlags.App.EnableShadows) {
                 animate().translationZ(transform.translationZ);
             }
@@ -839,25 +839,28 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
 
     @Override
      public void onClick(final View v) {
-        // We purposely post the handler delayed to allow for the touch feedback to draw
         final TaskView tv = this;
-        postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                if (Constants.DebugFlags.App.EnableTaskFiltering && v == mHeaderView.mApplicationIcon) {
-                    mCb.onTaskViewAppIconClicked(tv);
-                } else if (v == mHeaderView.mDismissButton) {
-                    dismissTask();
-                } else {
-                    if (v == mActionButtonView) {
-                        // Reset the translation of the action button before we animate it out
-                        mActionButtonView.setTranslationZ(0f);
+        final boolean delayViewClick = (v != this);
+        if (delayViewClick) {
+            // We purposely post the handler delayed to allow for the touch feedback to draw
+            postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    if (Constants.DebugFlags.App.EnableTaskFiltering && v == mHeaderView.mApplicationIcon) {
+                        mCb.onTaskViewAppIconClicked(tv);
+                    } else if (v == mHeaderView.mDismissButton) {
+                        dismissTask();
                     }
-                    mCb.onTaskViewClicked(tv, tv.getTask(),
-                            (v == mFooterView || v == mActionButtonView));
                 }
+            }, 125);
+        } else {
+            if (v == mActionButtonView) {
+                // Reset the translation of the action button before we animate it out
+                mActionButtonView.setTranslationZ(0f);
             }
-        }, 125);
+            mCb.onTaskViewClicked(tv, tv.getTask(),
+                    (v == mFooterView || v == mActionButtonView));
+        }
     }
 
     /**** View.OnLongClickListener Implementation ****/
index 1c4556f..f4587db 100644 (file)
@@ -91,6 +91,7 @@ import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.PreviewInflater;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
@@ -260,10 +261,12 @@ public abstract class BaseStatusBar extends SystemUI implements
             final boolean isActivity = pendingIntent.isActivity();
             if (isActivity) {
                 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
+                final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
+                        mContext, pendingIntent.getIntent(), mCurrentUserId);
                 dismissKeyguardThenExecute(new OnDismissAction() {
                     @Override
                     public boolean onDismiss() {
-                        if (keyguardShowing) {
+                        if (keyguardShowing && !afterKeyguardGone) {
                             try {
                                 ActivityManagerNative.getDefault()
                                         .keyguardWaitingForActivityDrawn();
@@ -277,7 +280,7 @@ public abstract class BaseStatusBar extends SystemUI implements
                         }
 
                         boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
-                        overrideActivityPendingAppTransition(keyguardShowing);
+                        overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
 
                         // close the shade if it was open
                         if (handled) {
@@ -287,7 +290,7 @@ public abstract class BaseStatusBar extends SystemUI implements
                         // Wait for activity start.
                         return handled;
                     }
-                }, false /* afterKeyguardGone */);
+                }, afterKeyguardGone);
                 return true;
             } else {
                 return super.onClickHandler(view, pendingIntent, fillInIntent);
@@ -1440,6 +1443,9 @@ public abstract class BaseStatusBar extends SystemUI implements
 
         public void onClick(final View v) {
             final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
+            final boolean afterKeyguardGone = mIntent.isActivity()
+                    && PreviewInflater.wouldLaunchResolverActivity(mContext, mIntent.getIntent(),
+                            mCurrentUserId);
             dismissKeyguardThenExecute(new OnDismissAction() {
                 public boolean onDismiss() {
                     if (mIsHeadsUp) {
@@ -1448,7 +1454,7 @@ public abstract class BaseStatusBar extends SystemUI implements
                     AsyncTask.execute(new Runnable() {
                         @Override
                         public void run() {
-                            if (keyguardShowing) {
+                            if (keyguardShowing && !afterKeyguardGone) {
                                 try {
                                     ActivityManagerNative.getDefault()
                                             .keyguardWaitingForActivityDrawn();
@@ -1472,7 +1478,8 @@ public abstract class BaseStatusBar extends SystemUI implements
                                     // TODO: Dismiss Keyguard.
                                 }
                                 if (mIntent.isActivity()) {
-                                    overrideActivityPendingAppTransition(keyguardShowing);
+                                    overrideActivityPendingAppTransition(keyguardShowing
+                                            && !afterKeyguardGone);
                                 }
                             }
 
@@ -1490,7 +1497,7 @@ public abstract class BaseStatusBar extends SystemUI implements
 
                     return mIntent != null && mIntent.isActivity();
                 }
-            }, false /* afterKeyguardGone */);
+            }, afterKeyguardGone);
         }
     }
 
@@ -1625,7 +1632,7 @@ public abstract class BaseStatusBar extends SystemUI implements
                 }
             }
             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
-            if ((isLockscreenPublicMode() && !showOnKeyguard) ||
+            if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
                     (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
                             || !showOnKeyguard))) {
                 entry.row.setVisibility(View.GONE);
@@ -1776,7 +1783,7 @@ public abstract class BaseStatusBar extends SystemUI implements
                 oldEntry.notification.getNotification().tickerText);
 
         final boolean shouldInterrupt = shouldInterrupt(notification);
-        final boolean alertAgain = alertAgain(oldEntry);
+        final boolean alertAgain = alertAgain(oldEntry, n);
         boolean updateSuccessful = false;
         if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
                 && publicUnchanged) {
@@ -1940,10 +1947,9 @@ public abstract class BaseStatusBar extends SystemUI implements
         }
     }
 
-    private boolean alertAgain(Entry entry) {
-        final StatusBarNotification sbn = entry.notification;
-        return entry == null || !entry.hasInterrupted()
-                || (sbn.getNotification().flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
+    private boolean alertAgain(Entry oldEntry, Notification newNotification) {
+        return oldEntry == null || !oldEntry.hasInterrupted()
+                || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
     }
 
     protected boolean shouldInterrupt(StatusBarNotification sbn) {
index 5883c26..740211f 100644 (file)
@@ -27,17 +27,21 @@ import android.widget.LinearLayout;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
 
 // Intimately tied to the design of res/layout/signal_cluster_view.xml
 public class SignalClusterView
         extends LinearLayout
-        implements NetworkControllerImpl.SignalCluster {
+        implements NetworkControllerImpl.SignalCluster,
+        SecurityController.SecurityControllerCallback {
 
     static final String TAG = "SignalClusterView";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     NetworkControllerImpl mNC;
+    SecurityController mSC;
 
+    private boolean mVpnVisible = false;
     private boolean mWifiVisible = false;
     private int mWifiStrengthId = 0;
     private boolean mMobileVisible = false;
@@ -48,7 +52,7 @@ public class SignalClusterView
     private boolean mRoaming;
 
     ViewGroup mWifiGroup, mMobileGroup;
-    ImageView mWifi, mMobile, mMobileType, mAirplane;
+    ImageView mVpn, mWifi, mMobile, mMobileType, mAirplane;
     View mWifiAirplaneSpacer;
 
     public SignalClusterView(Context context) {
@@ -68,10 +72,18 @@ public class SignalClusterView
         mNC = nc;
     }
 
+    public void setSecurityController(SecurityController sc) {
+        if (DEBUG) Log.d(TAG, "SecurityController=" + sc);
+        mSC = sc;
+        mSC.addCallback(this);
+        mVpnVisible = mSC.isVpnEnabled();
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
+        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);
@@ -85,6 +97,7 @@ public class SignalClusterView
 
     @Override
     protected void onDetachedFromWindow() {
+        mVpn            = null;
         mWifiGroup      = null;
         mWifi           = null;
         mMobileGroup    = null;
@@ -95,6 +108,18 @@ public class SignalClusterView
         super.onDetachedFromWindow();
     }
 
+    // From SecurityController.
+    @Override
+    public void onStateChanged() {
+        post(new Runnable() {
+            @Override
+            public void run() {
+                mVpnVisible = mSC.isVpnEnabled();
+                apply();
+            }
+        });
+    }
+
     @Override
     public void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription) {
         mWifiVisible = visible;
@@ -168,6 +193,8 @@ public class SignalClusterView
     private void apply() {
         if (mWifiGroup == null) return;
 
+        mVpn.setVisibility(mVpnVisible ? View.VISIBLE : View.GONE);
+        if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE"));
         if (mWifiVisible) {
             mWifi.setImageResource(mWifiStrengthId);
             mWifiGroup.setContentDescription(mWifiDescription);
index 9dfbb27..23810f9 100644 (file)
@@ -24,5 +24,5 @@ import android.content.Intent;
  * Keyguard.
  */
 public interface ActivityStarter {
-    public void startActivity(Intent intent, boolean dismissShade, boolean afterKeyguardGone);
+    public void startActivity(Intent intent, boolean dismissShade);
 }
index f9da30f..e84ca52 100644 (file)
@@ -315,15 +315,15 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
     public void launchCamera() {
         mFlashlightController.killFlashlight();
         Intent intent = getCameraIntent();
-        boolean wouldLaunchResolverActivity = mPreviewInflater.wouldLaunchResolverActivity(intent);
+        boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
+                mContext, intent, mLockPatternUtils.getCurrentUser());
         if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) {
             mContext.startActivityAsUser(intent, UserHandle.CURRENT);
         } else {
 
             // We need to delay starting the activity because ResolverActivity finishes itself if
             // launched behind lockscreen.
-            mActivityStarter.startActivity(intent, false /* dismissShade */,
-                    wouldLaunchResolverActivity /* afterKeyguardGone */);
+            mActivityStarter.startActivity(intent, false /* dismissShade */);
         }
     }
 
@@ -337,8 +337,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
                 }
             });
         } else {
-            mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */,
-                    mPreviewInflater.wouldLaunchResolverActivity(PHONE_INTENT));
+            mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */);
         }
     }
 
index 861bf4a..128c687 100644 (file)
@@ -149,6 +149,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.LocationControllerImpl;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.PreviewInflater;
 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityControllerImpl;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -786,8 +787,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         mNetworkController.addSignalCluster(signalCluster);
         mNetworkController.addSignalCluster(signalClusterKeyguard);
         mNetworkController.addSignalCluster(signalClusterQs);
+        signalCluster.setSecurityController(mSecurityController);
         signalCluster.setNetworkController(mNetworkController);
+        signalClusterKeyguard.setSecurityController(mSecurityController);
         signalClusterKeyguard.setNetworkController(mNetworkController);
+        signalClusterQs.setSecurityController(mSecurityController);
         signalClusterQs.setNetworkController(mNetworkController);
         final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
         if (isAPhone) {
@@ -2067,8 +2071,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
     }
 
     @Override
-    public void startActivity(Intent intent, boolean dismissShade, boolean afterKeyguardGone) {
-        startActivityDismissingKeyguard(intent, false, dismissShade, afterKeyguardGone);
+    public void startActivity(Intent intent, boolean dismissShade) {
+        startActivityDismissingKeyguard(intent, false, dismissShade);
     }
 
     public ScrimController getScrimController() {
@@ -2957,9 +2961,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
     }
 
     public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
-            final boolean dismissShade, final boolean afterKeyguardGone) {
+            final boolean dismissShade) {
         if (onlyProvisioned && !isDeviceProvisioned()) return;
 
+        final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
+                mContext, intent, mCurrentUserId);
         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
         dismissKeyguardThenExecute(new OnDismissAction() {
             @Override
@@ -3308,8 +3314,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
     }
 
     private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) {
-        startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */,
-                false /* afterKeyguardGone */);
+        startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
     }
 
     private static class FastColorDrawable extends Drawable {
index eeb97e3..15a7229 100644 (file)
@@ -497,20 +497,19 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
         } else if (v == mAlarmStatus && mNextAlarm != null) {
             PendingIntent showIntent = mNextAlarm.getShowIntent();
             if (showIntent != null && showIntent.isActivity()) {
-                mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */,
-                        false /* afterKeyguardGone */);
+                mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */);
             }
         }
     }
 
     private void startSettingsActivity() {
         mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
-                true /* dismissShade */, false /* afterKeyguardGone */);
+                true /* dismissShade */);
     }
 
     private void startBatteryActivity() {
         mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY),
-                true /* dismissShade */, false /* afterKeyguardGone */);
+                true /* dismissShade */);
     }
 
     public void setQSPanel(QSPanel qsp) {
index 4a6f1a8..f04d6a5 100644 (file)
@@ -928,8 +928,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
                     intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
             boolean wasConnected = mWifiConnected;
             mWifiConnected = networkInfo != null && networkInfo.isConnected();
-            // If we just connected, grab the inintial signal strength and ssid
-            if (mWifiConnected && !wasConnected) {
+            // If Connected grab the signal strength and ssid
+            if (mWifiConnected) {
                 // try getting it out of the intent first
                 WifiInfo info = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
                 if (info == null) {
index cdbe494..030cd6d 100644 (file)
@@ -20,14 +20,12 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardActivityLauncher;
 import com.android.systemui.statusbar.phone.KeyguardPreviewContainer;
 
 import java.util.List;
@@ -107,20 +105,21 @@ public class PreviewInflater {
         return info;
     }
 
-    public boolean wouldLaunchResolverActivity(Intent intent) {
-        PackageManager packageManager = mContext.getPackageManager();
+    public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent,
+            int currentUserId) {
+        PackageManager packageManager = ctx.getPackageManager();
         final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
-                intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
+                intent, PackageManager.MATCH_DEFAULT_ONLY, currentUserId);
         if (appList.size() == 0) {
             return false;
         }
         ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
-                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
-                mLockPatternUtils.getCurrentUser());
+                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, currentUserId);
         return wouldLaunchResolverActivity(resolved, appList);
     }
 
-    private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
+    private static boolean wouldLaunchResolverActivity(
+            ResolveInfo resolved, List<ResolveInfo> appList) {
         // If the list contains the above resolved activity, then it can't be
         // ResolverActivity itself.
         for (int i = 0; i < appList.size(); i++) {
index a15ddaf..2fbb812 100644 (file)
@@ -139,14 +139,14 @@ public class SecurityControllerImpl implements SecurityController {
     }
 
     @Override
-    public void addCallback(SecurityControllerCallback callback) {
+    public void removeCallback(SecurityControllerCallback callback) {
         if (callback == null) return;
         if (DEBUG) Log.d(TAG, "removeCallback " + callback);
         mCallbacks.remove(callback);
     }
 
     @Override
-    public void removeCallback(SecurityControllerCallback callback) {
+    public void addCallback(SecurityControllerCallback callback) {
         if (callback == null || mCallbacks.contains(callback)) return;
         if (DEBUG) Log.d(TAG, "addCallback " + callback);
         mCallbacks.add(callback);
index acb4827..0586a83 100644 (file)
@@ -140,8 +140,7 @@ public class VolumeUI extends SystemUI {
         @Override
         public void run() {
             getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
-                    ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */,
-                    false /* afterKeyguardGone */);
+                    ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
             mPanel.postDismiss(mDismissDelay);
         }
     };
index cd3eab5..ab311c0 100644 (file)
@@ -4240,14 +4240,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
         int result;
         boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
                 || event.isWakeKey();
-        if (interactive
-                || (isInjected && !isWakeKey)
-                || (!interactive && shouldDispatchInputWhenNonInteractive())) {
-            // When the device is interactive, the key is injected, or we're currently dozing in a
-            // non-interactive state with the screen on and the keyguard showing,  pass the key to
-            // the application.
+        if (interactive || (isInjected && !isWakeKey)) {
+            // When the device is interactive or the key is injected pass the key to the
+            // application.
             result = ACTION_PASS_TO_USER;
             isWakeKey = false;
+        } else if (!interactive && shouldDispatchInputWhenNonInteractive()) {
+            // If we're currently dozing with the screen on and the keyguard showing, pass the key
+            // to the application but preserve its wake key status to make sure we still move
+            // from dozing to fully interactive if we would normally go from off to fully
+            // interactive.
+            result = ACTION_PASS_TO_USER;
         } else {
             // When the screen is off and the key is not injected, determine whether
             // to wake the device but don't pass the key to the application.
index a43a2a6..ebe21ff 100644 (file)
@@ -746,14 +746,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
     }
 
     /**
-     * Gets the bounds of the accessibility focus in the active window.
+     * Gets a point within the accessibility focused node where we can send down
+     * and up events to perform a click.
      *
-     * @param outBounds The output to which to write the focus bounds.
-     * @return Whether accessibility focus was found and the bounds are populated.
+     * @param outPoint The click point to populate.
+     * @return Whether accessibility a click point was found and set.
      */
     // TODO: (multi-display) Make sure this works for multiple displays.
-    boolean getAccessibilityFocusBounds(Rect outBounds) {
-        return getInteractionBridgeLocked().getAccessibilityFocusBoundsNotLocked(outBounds);
+    boolean getAccessibilityFocusClickPointInScreen(Point outPoint) {
+        return getInteractionBridgeLocked()
+                .getAccessibilityFocusClickPointInScreenNotLocked(outPoint);
     }
 
     /**
@@ -2196,8 +2198,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
             MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
             try {
                 connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId, viewIdResName,
-                        partialInteractiveRegion, interactionId, callback, mFetchFlags, interrogatingPid,
-                        interrogatingTid, spec);
+                        partialInteractiveRegion, interactionId, callback, mFetchFlags,
+                        interrogatingPid, interrogatingTid, spec);
                 return true;
             } catch (RemoteException re) {
                 if (DEBUG) {
@@ -2248,8 +2250,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
             MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
             try {
                 connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
-                        partialInteractiveRegion, interactionId, callback, mFetchFlags, interrogatingPid,
-                        interrogatingTid, spec);
+                        partialInteractiveRegion, interactionId, callback, mFetchFlags,
+                        interrogatingPid, interrogatingTid, spec);
                 return true;
             } catch (RemoteException re) {
                 if (DEBUG) {
@@ -2352,8 +2354,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
             final long identityToken = Binder.clearCallingIdentity();
             MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
             try {
-                connection.findFocus(accessibilityNodeId, focusType, partialInteractiveRegion, interactionId,
-                        callback, mFetchFlags, interrogatingPid, interrogatingTid, spec);
+                connection.findFocus(accessibilityNodeId, focusType, partialInteractiveRegion,
+                        interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
+                        spec);
                 return true;
             } catch (RemoteException re) {
                 if (DEBUG) {
@@ -2403,8 +2406,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
             final long identityToken = Binder.clearCallingIdentity();
             MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
             try {
-                connection.focusSearch(accessibilityNodeId, direction, partialInteractiveRegion, interactionId,
-                        callback, mFetchFlags, interrogatingPid, interrogatingTid, spec);
+                connection.focusSearch(accessibilityNodeId, direction, partialInteractiveRegion,
+                        interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
+                        spec);
                 return true;
             } catch (RemoteException re) {
                 if (DEBUG) {
@@ -2460,6 +2464,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
             return true;
         }
 
+        @Override
         public boolean performGlobalAction(int action) {
             synchronized (mLock) {
                 // We treat calls from a profile as if made by its parent as profiles
@@ -2501,6 +2506,57 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
         }
 
         @Override
+        public  boolean computeClickPointInScreen(int accessibilityWindowId,
+                long accessibilityNodeId, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
+                throws RemoteException {
+            final int resolvedWindowId;
+            IAccessibilityInteractionConnection connection = null;
+            Region partialInteractiveRegion = mTempRegion;
+            synchronized (mLock) {
+                // We treat calls from a profile as if made by its parent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
+                final int resolvedUserId = mSecurityPolicy
+                        .resolveCallingUserIdEnforcingPermissionsLocked(
+                                UserHandle.getCallingUserId());
+                if (resolvedUserId != mCurrentUserId) {
+                    return false;
+                }
+                resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
+                final boolean permissionGranted =
+                        mSecurityPolicy.canRetrieveWindowContentLocked(this);
+                if (!permissionGranted) {
+                    return false;
+                } else {
+                    connection = getConnectionLocked(resolvedWindowId);
+                    if (connection == null) {
+                        return false;
+                    }
+                }
+                if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
+                        resolvedWindowId, partialInteractiveRegion)) {
+                    partialInteractiveRegion = null;
+                }
+            }
+            final int interrogatingPid = Binder.getCallingPid();
+            final long identityToken = Binder.clearCallingIdentity();
+            MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
+            try {
+                connection.computeClickPointInScreen(accessibilityNodeId, partialInteractiveRegion,
+                        interactionId, callback, interrogatingPid, interrogatingTid, spec);
+                return true;
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error computeClickPointInScreen().");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return false;
+        }
+
+        @Override
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
             synchronized (mLock) {
@@ -3119,30 +3175,43 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
             }
         }
 
-        public boolean getAccessibilityFocusBoundsNotLocked(Rect outBounds) {
+        public boolean getAccessibilityFocusClickPointInScreenNotLocked(Point outPoint) {
             AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked();
             if (focus == null) {
                 return false;
             }
 
             synchronized (mLock) {
-                focus.getBoundsInScreen(outBounds);
+                Point point = mClient.computeClickPointInScreen(mConnectionId,
+                        focus.getWindowId(), focus.getSourceNodeId());
+
+                if (point == null) {
+                    return false;
+                }
 
                 MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
                 if (spec != null && !spec.isNop()) {
-                    outBounds.offset((int) -spec.offsetX, (int) -spec.offsetY);
-                    outBounds.scale(1 / spec.scale);
+                    point.offset((int) -spec.offsetX, (int) -spec.offsetY);
+                    point.x = (int) (point.x * (1 / spec.scale));
+                    point.y = (int) (point.y * (1 / spec.scale));
                 }
 
-                // Clip to the window rectangle.
+                // Make sure the point is within the window.
                 Rect windowBounds = mTempRect;
                 getActiveWindowBounds(windowBounds);
-                outBounds.intersect(windowBounds);
+                if (!windowBounds.contains(point.x, point.y)) {
+                    return false;
+                }
 
-                // Clip to the screen rectangle.
-                mDefaultDisplay.getRealSize(mTempPoint);
-                outBounds.intersect(0, 0, mTempPoint.x, mTempPoint.y);
+                // Make sure the point is within the screen.
+                Point screenSize = mTempPoint;
+                mDefaultDisplay.getRealSize(screenSize);
+                if (point.x < 0 || point.x > screenSize.x
+                        || point.y < 0 || point.y > screenSize.y) {
+                    return false;
+                }
 
+                outPoint.set(point.x, point.y);
                 return true;
             }
         }
index ac0ca0a..9e63433 100644 (file)
@@ -24,6 +24,7 @@ import android.gesture.GesturePoint;
 import android.gesture.GestureStore;
 import android.gesture.GestureStroke;
 import android.gesture.Prediction;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -171,6 +172,9 @@ class TouchExplorer implements EventStreamTransformation {
     // Temporary rectangle to avoid instantiation.
     private final Rect mTempRect = new Rect();
 
+    // Temporary point to avoid instantiation.
+    private final Point mTempPoint = new Point();
+
     // Context in which this explorer operates.
     private final Context mContext;
 
@@ -1157,18 +1161,18 @@ class TouchExplorer implements EventStreamTransformation {
                 mInjectedPointerTracker.getLastInjectedHoverEventForClick();
             if (lastExploreEvent == null) {
                 // No last touch explored event but there is accessibility focus in
-                // the active window. We click in the middle of the focus bounds.
-                Rect focusBounds = mTempRect;
-                if (mAms.getAccessibilityFocusBounds(focusBounds)) {
-                    clickLocationX = focusBounds.centerX();
-                    clickLocationY = focusBounds.centerY();
+                // the active window. We click in the focus bounds.
+                Point point = mTempPoint;
+                if (mAms.getAccessibilityFocusClickPointInScreen(point)) {
+                    clickLocationX = point.x;
+                    clickLocationY = point.y;
                 } else {
                     // Out of luck - do nothing.
                     return;
                 }
             } else {
                 // If the click is within the active window but not within the
-                // accessibility focus bounds we click in the focus center.
+                // accessibility focus bounds we click in the focus bounds.
                 final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
                 clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex);
                 clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex);
@@ -1176,12 +1180,10 @@ class TouchExplorer implements EventStreamTransformation {
                 if (mLastTouchedWindowId == mAms.getActiveWindowId()) {
                     mAms.getActiveWindowBounds(activeWindowBounds);
                     if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
-                        Rect focusBounds = mTempRect;
-                        if (mAms.getAccessibilityFocusBounds(focusBounds)) {
-                            if (!focusBounds.contains(clickLocationX, clickLocationY)) {
-                                clickLocationX = focusBounds.centerX();
-                                clickLocationY = focusBounds.centerY();
-                            }
+                        Point point = mTempPoint;
+                        if (mAms.getAccessibilityFocusClickPointInScreen(point)) {
+                            clickLocationX = point.x;
+                            clickLocationY = point.y;
                         }
                     }
                 }
@@ -1330,18 +1332,18 @@ class TouchExplorer implements EventStreamTransformation {
                 mInjectedPointerTracker.getLastInjectedHoverEventForClick();
             if (lastExploreEvent == null) {
                 // No last touch explored event but there is accessibility focus in
-                // the active window. We click in the middle of the focus bounds.
-                Rect focusBounds = mTempRect;
-                if (mAms.getAccessibilityFocusBounds(focusBounds)) {
-                    clickLocationX = focusBounds.centerX();
-                    clickLocationY = focusBounds.centerY();
+                // the active window. We click in the focus bounds.
+                Point point = mTempPoint;
+                if (mAms.getAccessibilityFocusClickPointInScreen(point)) {
+                    clickLocationX = point.x;
+                    clickLocationY = point.y;
                 } else {
                     // Out of luck - do nothing.
                     return;
                 }
             } else {
                 // If the click is within the active window but not within the
-                // accessibility focus bounds we click in the focus center.
+                // accessibility focus bounds we click in the focus bounds.
                 final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
                 clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex);
                 clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex);
@@ -1349,12 +1351,10 @@ class TouchExplorer implements EventStreamTransformation {
                 if (mLastTouchedWindowId == mAms.getActiveWindowId()) {
                     mAms.getActiveWindowBounds(activeWindowBounds);
                     if (activeWindowBounds.contains(clickLocationX, clickLocationY)) {
-                        Rect focusBounds = mTempRect;
-                        if (mAms.getAccessibilityFocusBounds(focusBounds)) {
-                            if (!focusBounds.contains(clickLocationX, clickLocationY)) {
-                                clickLocationX = focusBounds.centerX();
-                                clickLocationY = focusBounds.centerY();
-                            }
+                        Point point = mTempPoint;
+                        if (mAms.getAccessibilityFocusClickPointInScreen(point)) {
+                            clickLocationX = point.x;
+                            clickLocationY = point.y;
                         }
                     }
                 }
index 2095152..59d50bd 100644 (file)
@@ -201,20 +201,16 @@ public class NativeDaemonEvent {
         }
         while (current < length) {
             // find the end of the word
-            if (quoted) {
-                wordEnd = current;
-                while ((wordEnd = rawEvent.indexOf('\"', wordEnd)) != -1) {
-                    if (rawEvent.charAt(wordEnd - 1) != '\\') {
-                        break;
-                    } else {
-                        wordEnd++; // skip this escaped quote and keep looking
-                    }
+            char terminator = quoted ? '\"' : ' ';
+            wordEnd = current;
+            while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
+                if (rawEvent.charAt(wordEnd) == '\\') {
+                    // skip the escaped char
+                    ++wordEnd;
                 }
-            } else {
-                wordEnd = rawEvent.indexOf(' ', current);
+                ++wordEnd;
             }
-            // if we didn't find the end-o-word token, take the rest of the string
-            if (wordEnd == -1) wordEnd = length;
+            if (wordEnd > length) wordEnd = length;
             String word = rawEvent.substring(current, wordEnd);
             current += word.length();
             if (!quoted) {
index cf7e65c..cb1748d 100644 (file)
@@ -397,8 +397,7 @@ public class NsdService extends INsdManager.Stub {
                         break;
                     case NsdManager.NATIVE_DAEMON_EVENT:
                         NativeEvent event = (NativeEvent) msg.obj;
-                        if (!handleNativeEvent(event.code, event.raw,
-                                NativeDaemonEvent.unescapeArgs(event.raw))) {
+                        if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
                             result = NOT_HANDLED;
                         }
                         break;
@@ -474,8 +473,14 @@ public class NsdService extends INsdManager.Stub {
                     case NativeResponseCode.SERVICE_RESOLVED:
                         /* NNN resolveId fullName hostName port txtlen txtdata */
                         if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
-                        int index = cooked[2].indexOf(".");
-                        if (index == -1) {
+                        int index = 0;
+                        while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
+                            if (cooked[2].charAt(index) == '\\') {
+                                ++index;
+                            }
+                            ++index;
+                        }
+                        if (index >= cooked[2].length()) {
                             Slog.e(TAG, "Invalid service found " + raw);
                             break;
                         }
@@ -483,6 +488,8 @@ public class NsdService extends INsdManager.Stub {
                         String rest = cooked[2].substring(index);
                         String type = rest.replace(".local.", "");
 
+                        name = unescape(name);
+
                         clientInfo.mResolvedService.setServiceName(name);
                         clientInfo.mResolvedService.setServiceType(type);
                         clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
@@ -541,6 +548,30 @@ public class NsdService extends INsdManager.Stub {
        }
     }
 
+    private String unescape(String s) {
+        StringBuilder sb = new StringBuilder(s.length());
+        for (int i = 0; i < s.length(); ++i) {
+            char c = s.charAt(i);
+            if (c == '\\') {
+                if (++i >= s.length()) {
+                    Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
+                    break;
+                }
+                c = s.charAt(i);
+                if (c != '.' && c != '\\') {
+                    if (i + 2 >= s.length()) {
+                        Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
+                        break;
+                    }
+                    c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
+                    i += 2;
+                }
+            }
+            sb.append(c);
+        }
+        return sb.toString();
+    }
+
     private NativeDaemonConnector mNativeConnector;
     private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
 
@@ -625,10 +656,12 @@ public class NsdService extends INsdManager.Stub {
     private class NativeEvent {
         final int code;
         final String raw;
+        final String[] cooked;
 
-        NativeEvent(int code, String raw) {
+        NativeEvent(int code, String raw, String[] cooked) {
             this.code = code;
             this.raw = raw;
+            this.cooked = cooked;
         }
     }
 
@@ -644,7 +677,7 @@ public class NsdService extends INsdManager.Stub {
         public boolean onEvent(int code, String raw, String[] cooked) {
             // TODO: NDC translates a message to a callback, we could enhance NDC to
             // directly interact with a state machine through messages
-            NativeEvent event = new NativeEvent(code, raw);
+            NativeEvent event = new NativeEvent(code, raw, cooked);
             mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
             return true;
         }
index 6ddad41..1397ea4 100755 (executable)
@@ -6278,12 +6278,12 @@ public final class ActivityManagerService extends ActivityManagerNative
     }
 
     @Override
-    public final void activityPaused(IBinder token, PersistableBundle persistentState) {
+    public final void activityPaused(IBinder token) {
         final long origId = Binder.clearCallingIdentity();
         synchronized(this) {
             ActivityStack stack = ActivityRecord.getStackLocked(token);
             if (stack != null) {
-                stack.activityPausedLocked(token, false, persistentState);
+                stack.activityPausedLocked(token, false);
             }
         }
         Binder.restoreCallingIdentity(origId);
index 77b4cc9..e043f03 100755 (executable)
@@ -698,12 +698,12 @@ final class ActivityRecord {
                 case ActivityOptions.ANIM_SCALE_UP:
                     service.mWindowManager.overridePendingAppTransitionScaleUp(
                             pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getStartWidth(), pendingOptions.getStartHeight());
+                            pendingOptions.getWidth(), pendingOptions.getHeight());
                     if (intent.getSourceBounds() == null) {
                         intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
                                 pendingOptions.getStartY(),
-                                pendingOptions.getStartX()+pendingOptions.getStartWidth(),
-                                pendingOptions.getStartY()+pendingOptions.getStartHeight()));
+                                pendingOptions.getStartX()+pendingOptions.getWidth(),
+                                pendingOptions.getStartY()+pendingOptions.getHeight()));
                     }
                     break;
                 case ActivityOptions.ANIM_THUMBNAIL_SCALE_UP:
@@ -728,15 +728,14 @@ final class ActivityRecord {
                     service.mWindowManager.overridePendingAppTransitionAspectScaledThumb(
                             pendingOptions.getThumbnail(),
                             pendingOptions.getStartX(), pendingOptions.getStartY(),
+                            pendingOptions.getWidth(), pendingOptions.getHeight(),
                             pendingOptions.getOnAnimationStartListener(),
                             (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP));
                     if (intent.getSourceBounds() == null) {
                         intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
                                 pendingOptions.getStartY(),
-                                pendingOptions.getStartX()
-                                        + pendingOptions.getThumbnail().getWidth(),
-                                pendingOptions.getStartY()
-                                        + pendingOptions.getThumbnail().getHeight()));
+                                pendingOptions.getStartX() + pendingOptions.getWidth(),
+                                pendingOptions.getStartY() + pendingOptions.getHeight()));
                     }
                     break;
                 default:
index 81c379a..bcf3b17 100755 (executable)
@@ -284,7 +284,7 @@ final class ActivityStack {
                         if (r.app != null) {
                             mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
                         }
-                        activityPausedLocked(r.appToken, true, r.persistentState);
+                        activityPausedLocked(r.appToken, true);
                     }
                 } break;
                 case LAUNCH_TICK_MSG: {
@@ -425,13 +425,13 @@ final class ActivityStack {
     }
 
     final ActivityRecord topActivity() {
-        // Iterate to find the first non-empty task stack. Note that this code can
-        // be simplified once we stop storing tasks with empty mActivities lists.
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            final int topActivityNdx = activities.size() - 1;
-            if (topActivityNdx >= 0) {
-                return activities.get(topActivityNdx);
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (!r.finishing) {
+                    return r;
+                }
             }
         }
         return null;
@@ -712,7 +712,7 @@ final class ActivityStack {
             // Still have something resumed; can't sleep until it is paused.
             if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause " + mResumedActivity);
             if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
-            startPausingLocked(false, true);
+            startPausingLocked(false, true, false, false);
             return true;
         }
         if (mPausingActivity != null) {
@@ -790,22 +790,38 @@ final class ActivityStack {
         return null;
     }
 
-    final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+    /**
+     * Start pausing the currently resumed activity.  It is an error to call this if there
+     * is already an activity being paused or there is no resumed activity.
+     *
+     * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+     * @param uiSleeping True if this is happening with the user interface going to sleep (the
+     * screen turning off).
+     * @param resuming True if this is being called as part of resuming the top activity, so
+     * we shouldn't try to instigate a resume here.
+     * @param dontWait True if the caller does not want to wait for the pause to complete.  If
+     * set to true, we will immediately complete the pause here before returning.
+     * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+     * it to tell us when it is done.
+     */
+    final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
+            boolean dontWait) {
         if (mPausingActivity != null) {
-            Slog.e(TAG, "Trying to pause when pause is already pending for "
-                  + mPausingActivity, new RuntimeException("here").fillInStackTrace());
+            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity);
+            completePauseLocked(false);
         }
         ActivityRecord prev = mResumedActivity;
         if (prev == null) {
-            Slog.e(TAG, "Trying to pause when nothing is resumed",
-                    new RuntimeException("here").fillInStackTrace());
-            mStackSupervisor.resumeTopActivitiesLocked();
-            return;
+            if (!resuming) {
+                Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+                mStackSupervisor.resumeTopActivitiesLocked();
+            }
+            return false;
         }
 
         if (mActivityContainer.mParentActivity == null) {
             // Top level stack, not a child. Look for child stacks.
-            mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping);
+            mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);
         }
 
         if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
@@ -834,7 +850,7 @@ final class ActivityStack {
                         prev.shortComponentName);
                 mService.updateUsageStats(prev, false);
                 prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
-                        userLeaving, prev.configChangeFlags);
+                        userLeaving, prev.configChangeFlags, dontWait);
             } catch (Exception e) {
                 // Ignore exception, if process died other code will cleanup.
                 Slog.w(TAG, "Exception thrown during pause", e);
@@ -865,39 +881,46 @@ final class ActivityStack {
                 if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
             }
 
-            // Schedule a pause timeout in case the app doesn't respond.
-            // We don't give it much time because this directly impacts the
-            // responsiveness seen by the user.
-            Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
-            msg.obj = prev;
-            prev.pauseTime = SystemClock.uptimeMillis();
-            mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
-            if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+            if (dontWait) {
+                // If the caller said they don't want to wait for the pause, then complete
+                // the pause now.
+                completePauseLocked(false);
+                return false;
+
+            } else {
+                // Schedule a pause timeout in case the app doesn't respond.
+                // We don't give it much time because this directly impacts the
+                // responsiveness seen by the user.
+                Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
+                msg.obj = prev;
+                prev.pauseTime = SystemClock.uptimeMillis();
+                mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+                if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+                return true;
+            }
+
         } else {
             // This activity failed to schedule the
             // pause, so just treat it as being paused now.
             if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
-            mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+            if (!resuming) {
+                mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+            }
+            return false;
         }
     }
 
-    final void activityPausedLocked(IBinder token, boolean timeout,
-            PersistableBundle persistentState) {
+    final void activityPausedLocked(IBinder token, boolean timeout) {
         if (DEBUG_PAUSE) Slog.v(
             TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
 
         final ActivityRecord r = isInStackLocked(token);
         if (r != null) {
             mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-            if (persistentState != null) {
-                r.persistentState = persistentState;
-                mService.notifyTaskPersisterLocked(r.task, false);
-            }
             if (mPausingActivity == r) {
                 if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
                         + (timeout ? " (due to timeout)" : " (pause complete)"));
-                r.state = ActivityState.PAUSED;
-                completePauseLocked();
+                completePauseLocked(true);
             } else {
                 EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
                         r.userId, System.identityHashCode(r), r.shortComponentName,
@@ -948,11 +971,12 @@ final class ActivityStack {
         }
     }
 
-    private void completePauseLocked() {
+    private void completePauseLocked(boolean resumeNext) {
         ActivityRecord prev = mPausingActivity;
         if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
 
         if (prev != null) {
+            prev.state = ActivityState.PAUSED;
             if (prev.finishing) {
                 if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
                 prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
@@ -995,19 +1019,21 @@ final class ActivityStack {
             mPausingActivity = null;
         }
 
-        final ActivityStack topStack = mStackSupervisor.getFocusedStack();
-        if (!mService.isSleepingOrShuttingDown()) {
-            mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
-        } else {
-            mStackSupervisor.checkReadyForSleepLocked();
-            ActivityRecord top = topStack.topRunningActivityLocked(null);
-            if (top == null || (prev != null && top != prev)) {
-                // If there are no more activities available to run,
-                // do resume anyway to start something.  Also if the top
-                // activity on the stack is not the just paused activity,
-                // we need to go ahead and resume it to ensure we complete
-                // an in-flight app switch.
-                mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+        if (resumeNext) {
+            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
+            if (!mService.isSleepingOrShuttingDown()) {
+                mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
+            } else {
+                mStackSupervisor.checkReadyForSleepLocked();
+                ActivityRecord top = topStack.topRunningActivityLocked(null);
+                if (top == null || (prev != null && top != prev)) {
+                    // If there are no more activities available to run,
+                    // do resume anyway to start something.  Also if the top
+                    // activity on the stack is not the just paused activity,
+                    // we need to go ahead and resume it to ensure we complete
+                    // an in-flight app switch.
+                    mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+                }
             }
         }
 
@@ -1607,10 +1633,10 @@ final class ActivityStack {
 
         // We need to start pausing the current activity so the top one
         // can be resumed...
-        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving);
+        boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;
+        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
         if (mResumedActivity != null) {
-            pausing = true;
-            startPausingLocked(userLeaving, false);
+            pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
             if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity);
         }
         if (pausing) {
@@ -2720,7 +2746,7 @@ final class ActivityStack {
             if (mPausingActivity == null) {
                 if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
                 if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
-                startPausingLocked(false, false);
+                startPausingLocked(false, false, false, false);
             }
 
             if (endTask) {
index 780efa1..b8261a4 100644 (file)
@@ -592,7 +592,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
      * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
      * @return true if any activity was paused as a result of this call.
      */
-    boolean pauseBackStacks(boolean userLeaving) {
+    boolean pauseBackStacks(boolean userLeaving, boolean resuming, boolean dontWait) {
         boolean someActivityPaused = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -601,8 +601,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 if (!isFrontStack(stack) && stack.mResumedActivity != null) {
                     if (DEBUG_STATES) Slog.d(TAG, "pauseBackStacks: stack=" + stack +
                             " mResumedActivity=" + stack.mResumedActivity);
-                    stack.startPausingLocked(userLeaving, false);
-                    someActivityPaused = true;
+                    someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
+                            dontWait);
                 }
             }
         }
@@ -631,7 +631,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
         return pausing;
     }
 
-    void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping) {
+    void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping,
+            boolean resuming, boolean dontWait) {
         // TODO: Put all stacks in supervisor and iterate through them instead.
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -639,7 +640,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 final ActivityStack stack = stacks.get(stackNdx);
                 if (stack.mResumedActivity != null &&
                         stack.mActivityContainer.mParentActivity == parent) {
-                    stack.startPausingLocked(userLeaving, uiSleeping);
+                    stack.startPausingLocked(userLeaving, uiSleeping, resuming, dontWait);
                 }
             }
         }
@@ -1640,57 +1641,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
             }
         }
 
-        if (sourceRecord == null) {
-            // This activity is not being started from another...  in this
-            // case we -always- start a new task.
-            if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
-                Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
-                        "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
-                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
-            }
-        } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-            // The original activity who is starting us is running as a single
-            // instance...  this new activity it is starting must go on its
-            // own task.
-            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
-        } else if (launchSingleInstance || launchSingleTask) {
-            // The activity being started is a single instance...  it always
-            // gets launched into its own task.
-            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
-        }
-
-        ActivityInfo newTaskInfo = null;
-        Intent newTaskIntent = null;
-        ActivityStack sourceStack;
-        if (sourceRecord != null) {
-            if (sourceRecord.finishing) {
-                // If the source is finishing, we can't further count it as our source.  This
-                // is because the task it is associated with may now be empty and on its way out,
-                // so we don't want to blindly throw it in to that task.  Instead we will take
-                // the NEW_TASK flow and try to find a task for it. But save the task information
-                // so it can be used when creating the new task.
-                if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
-                    Slog.w(TAG, "startActivity called from finishing " + sourceRecord
-                            + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
-                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
-                    newTaskInfo = sourceRecord.info;
-                    newTaskIntent = sourceRecord.task.intent;
-                }
-                sourceRecord = null;
-                sourceStack = null;
-            } else {
-                sourceStack = sourceRecord.task.stack;
-            }
-        } else {
-            sourceStack = null;
-        }
-
         boolean addingToTask = false;
-        boolean movedHome = false;
         TaskRecord reuseTask = null;
-        ActivityStack targetStack;
-
-        intent.setFlags(launchFlags);
 
         // If the caller is not coming from another activity, but has given us an
         // explicit task into which they would like us to launch the new activity,
@@ -1746,6 +1698,58 @@ public final class ActivityStackSupervisor implements DisplayListener {
             inTask = null;
         }
 
+        if (inTask == null) {
+            if (sourceRecord == null) {
+                // This activity is not being started from another...  in this
+                // case we -always- start a new task.
+                if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
+                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
+                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+                }
+            } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+                // The original activity who is starting us is running as a single
+                // instance...  this new activity it is starting must go on its
+                // own task.
+                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+            } else if (launchSingleInstance || launchSingleTask) {
+                // The activity being started is a single instance...  it always
+                // gets launched into its own task.
+                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+            }
+        }
+
+        ActivityInfo newTaskInfo = null;
+        Intent newTaskIntent = null;
+        ActivityStack sourceStack;
+        if (sourceRecord != null) {
+            if (sourceRecord.finishing) {
+                // If the source is finishing, we can't further count it as our source.  This
+                // is because the task it is associated with may now be empty and on its way out,
+                // so we don't want to blindly throw it in to that task.  Instead we will take
+                // the NEW_TASK flow and try to find a task for it. But save the task information
+                // so it can be used when creating the new task.
+                if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+                    Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+                            + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+                    newTaskInfo = sourceRecord.info;
+                    newTaskIntent = sourceRecord.task.intent;
+                }
+                sourceRecord = null;
+                sourceStack = null;
+            } else {
+                sourceStack = sourceRecord.task.stack;
+            }
+        } else {
+            sourceStack = null;
+        }
+
+        boolean movedHome = false;
+        ActivityStack targetStack;
+
+        intent.setFlags(launchFlags);
+
         // We may want to try to place the new activity in to an existing task.  We always
         // do this if the target activity is singleTask or singleInstance; we will also do
         // this if NEW_TASK has been requested, and there is not an additional qualifier telling
@@ -1768,7 +1772,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 if (intentActivity != null) {
                     if (isLockTaskModeViolation(intentActivity.task)) {
                         showLockTaskToast();
-                        Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
+                        Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
                         return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
                     }
                     if (r.task == null) {
@@ -2779,9 +2783,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
         }
 
         // A non-top activity is reporting a visibility change.
-        if ((visible && (top.fullscreen || top.state != ActivityState.RESUMED)) ||
-                top.app == null || top.app.thread == null) {
-            // Can't carry out this request.
+        if (visible && top.fullscreen) {
+            // Let the caller know that it can't be seen.
             if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG, "requestVisibleBehind: returning top.fullscreen="
                     + top.fullscreen + " top.state=" + top.state + " top.app=" + top.app +
                     " top.app.thread=" + top.app.thread);
@@ -2803,9 +2806,12 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 mService.convertFromTranslucent(next.appToken);
             }
         }
-        try {
-            top.app.thread.scheduleBackgroundVisibleBehindChanged(top.appToken, visible);
-        } catch (RemoteException e) {
+        if (top.app != null && top.app.thread != null) {
+            // Notify the top app of the change.
+            try {
+                top.app.thread.scheduleBackgroundVisibleBehindChanged(top.appToken, visible);
+            } catch (RemoteException e) {
+            }
         }
         return true;
     }
@@ -3840,7 +3846,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 mContainerState = CONTAINER_STATE_NO_SURFACE;
                 ((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
                 if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
-                    mStack.startPausingLocked(false, true);
+                    mStack.startPausingLocked(false, true, false, false);
                 }
             }
 
index 2ef9828..11818d8 100644 (file)
@@ -331,6 +331,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
     private int mSuplServerPort;
     private String mC2KServerHost;
     private int mC2KServerPort;
+    private boolean mSuplEsEnabled = false;
 
     private final Context mContext;
     private final NtpTrustedTime mNtpTime;
@@ -453,11 +454,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
         @Override public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
+            if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
             if (action.equals(ALARM_WAKEUP)) {
-                if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
                 startNavigating(false);
             } else if (action.equals(ALARM_TIMEOUT)) {
-                if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
                 hibernate();
             } else if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) {
                 checkSmsSuplInit(intent);
@@ -494,6 +494,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
                     Log.d(TAG, "SIM STATE is ready, SIM MCC/MNC is " + mccMnc);
                     synchronized (mLock) {
                         reloadGpsProperties(context, mProperties);
+                        mNIHandler.setSuplEsEnablement(mSuplEsEnabled);
                     }
                 }
             }
@@ -571,6 +572,16 @@ public class GpsLocationProvider implements LocationProviderInterface {
         } catch (IOException ex) {
             Log.w(TAG, "failed to dump properties contents");
         }
+
+        // SUPL_ES configuration.
+        String suplESProperty = mProperties.getProperty("SUPL_ES");
+        if (suplESProperty != null) {
+            try {
+                mSuplEsEnabled = (Integer.parseInt(suplESProperty) == 1);
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
+            }
+        }
     }
 
     private void loadPropertiesFromResource(Context context,
@@ -612,7 +623,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
         mContext = context;
         mNtpTime = NtpTrustedTime.getInstance(context);
         mILocationManager = ilocationManager;
-        mNIHandler = new GpsNetInitiatedHandler(context);
 
         mLocation.setExtras(mLocationExtras);
 
@@ -639,6 +649,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
         mProperties = new Properties();
         reloadGpsProperties(mContext, mProperties);
 
+        // Create a GPS net-initiated handler.
+        mNIHandler = new GpsNetInitiatedHandler(context,
+                                                mNetInitiatedListener,
+                                                mSuplEsEnabled);
+
         // construct handler, listen for events
         mHandler = new ProviderHandler(looper);
         listenForBroadcasts();
@@ -691,7 +706,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
         intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-        intentFilter = new IntentFilter();
         intentFilter.addAction(SIM_STATE_CHANGED);
         mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
     }
index 317ebef..b71bad8 100644 (file)
@@ -43,6 +43,7 @@ import java.util.Calendar;
 import java.util.Date;
 import java.util.Locale;
 import java.util.Objects;
+import java.util.TimeZone;
 
 /** Built-in zen condition provider for managing downtime */
 public class DowntimeConditionProvider extends ConditionProviderService {
@@ -275,6 +276,9 @@ public class DowntimeConditionProvider extends ConditionProviderService {
                 final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
                 if (DEBUG) Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
                         action, ts(schTime), ts(now), now - schTime));
+            } else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
+                if (DEBUG) Slog.d(TAG, "timezone changed to " + TimeZone.getDefault());
+                mCalendar.setTimeZone(TimeZone.getDefault());
             } else {
                 if (DEBUG) Slog.d(TAG, action + " fired at " + now);
             }
index bc538ed..a56f783 100644 (file)
@@ -69,6 +69,7 @@ import android.service.notification.Condition;
 import android.service.notification.IConditionListener;
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
+import android.service.notification.IStatusBarNotificationHolder;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.StatusBarNotification;
@@ -2647,6 +2648,11 @@ public class NotificationManagerService extends SystemService {
             if (speedBumpIndex == -1 &&
                     // Intrusiveness trumps priority, hence ignore intrusives.
                     !record.isRecentlyIntrusive() &&
+                    // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
+                    // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
+                    // (or lower as a safeguard) is sufficient to find the speedbump index.
+                    // We'll have to revisit this when more package priority buckets are introduced.
+                    record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
                     record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
                 speedBumpIndex = keys.size() - 1;
             }
@@ -2848,8 +2854,9 @@ public class NotificationManagerService extends SystemService {
         private void notifyPosted(final ManagedServiceInfo info,
                 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
             final INotificationListener listener = (INotificationListener)info.service;
+            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
             try {
-                listener.onNotificationPosted(sbn, rankingUpdate);
+                listener.onNotificationPosted(sbnHolder, rankingUpdate);
             } catch (RemoteException ex) {
                 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
             }
@@ -2861,8 +2868,9 @@ public class NotificationManagerService extends SystemService {
                 return;
             }
             final INotificationListener listener = (INotificationListener) info.service;
+            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
             try {
-                listener.onNotificationRemoved(sbn, rankingUpdate);
+                listener.onNotificationRemoved(sbnHolder, rankingUpdate);
             } catch (RemoteException ex) {
                 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
             }
@@ -2950,4 +2958,22 @@ public class NotificationManagerService extends SystemService {
             return zen ? "zen" : ('\'' + pkgFilter + '\'');
         }
     }
+
+    /**
+     * Wrapper for a StatusBarNotification object that allows transfer across a oneway
+     * binder without sending large amounts of data over a oneway transaction.
+     */
+    private static final class StatusBarNotificationHolder
+            extends IStatusBarNotificationHolder.Stub {
+        private final StatusBarNotification mValue;
+
+        public StatusBarNotificationHolder(StatusBarNotification value) {
+            mValue = value;
+        }
+
+        @Override
+        public StatusBarNotification get() {
+            return mValue;
+        }
+    }
 }
index 168328f..5639dad 100644 (file)
@@ -18,7 +18,6 @@ package com.android.server.notification;
 
 import static android.media.AudioAttributes.USAGE_ALARM;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
-import static android.media.AudioAttributes.USAGE_UNKNOWN;
 
 import android.app.AppOpsManager;
 import android.app.Notification;
@@ -225,11 +224,6 @@ public class ZenModeHelper {
                 muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
                 exceptionPackages);
 
-        // restrict vibrations with no hints
-        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, USAGE_UNKNOWN,
-                zen ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
-                exceptionPackages);
-
         // alarm restrictions
         final boolean muteAlarms = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
         mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, USAGE_ALARM,
index 9a00923..496c136 100644 (file)
@@ -42,7 +42,6 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageInstallerCallback;
 import android.content.pm.IPackageInstallerSession;
@@ -50,15 +49,11 @@ import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageLite;
-import android.content.pm.PackageParser.PackageParserException;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.Environment.UserEnvironment;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -70,7 +65,6 @@ import android.os.RemoteException;
 import android.os.SELinux;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.storage.StorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
@@ -126,6 +120,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
     private static final String ATTR_CREATED_MILLIS = "createdMillis";
     private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
     private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
+    private static final String ATTR_PREPARED = "prepared";
     private static final String ATTR_SEALED = "sealed";
     private static final String ATTR_MODE = "mode";
     private static final String ATTR_INSTALL_FLAGS = "installFlags";
@@ -148,7 +143,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
     private final Context mContext;
     private final PackageManagerService mPm;
     private final AppOpsManager mAppOps;
-    private final StorageManager mStorage;
 
     private final File mStagingDir;
     private final HandlerThread mInstallThread;
@@ -190,7 +184,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
         mContext = context;
         mPm = pm;
         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
-        mStorage = StorageManager.from(mContext);
 
         mStagingDir = stagingDir;
 
@@ -266,7 +259,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
             try {
                 final int sessionId = allocateSessionIdLocked();
                 mLegacySessions.put(sessionId, true);
-                return prepareInternalStageDir(sessionId);
+                final File stageDir = buildInternalStageDir(sessionId);
+                prepareInternalStageDir(stageDir);
+                return stageDir;
             } catch (IllegalStateException e) {
                 throw new IOException(e);
             }
@@ -345,6 +340,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
         final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
         final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
         final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
+        final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
         final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
 
         final SessionParams params = new SessionParams(
@@ -362,7 +358,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
 
         return new PackageInstallerSession(mInternalCallback, mContext, mPm,
                 mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
-                createdMillis, stageDir, stageCid, sealed);
+                createdMillis, stageDir, stageCid, prepared, sealed);
     }
 
     private void writeSessionsLocked() {
@@ -410,6 +406,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
         if (session.stageCid != null) {
             writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
         }
+        writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared());
         writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
 
         writeIntAttribute(out, ATTR_MODE, params.mode);
@@ -483,14 +480,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
             }
         }
 
-        // TODO: treat INHERIT_EXISTING as install for user
-
-        // Figure out where we're going to be staging session data
-        final boolean stageInternal;
-
-        if (params.mode == SessionParams.MODE_FULL_INSTALL) {
-            // Brand new install, use best resolved location. This also verifies
-            // that target has enough free space for the install.
+        if (params.mode == SessionParams.MODE_FULL_INSTALL
+                || params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+            // Resolve best location for install, based on combination of
+            // requested install flags, delta size, and manifest settings.
             final long ident = Binder.clearCallingIdentity();
             try {
                 final int resolved = PackageHelper.resolveInstallLocation(mContext,
@@ -498,46 +491,15 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
                         params.installFlags);
 
                 if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) {
-                    stageInternal = true;
+                    params.setInstallFlagsInternal();
                 } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
-                    stageInternal = false;
+                    params.setInstallFlagsExternal();
                 } else {
                     throw new IOException("No storage with enough free space; res=" + resolved);
                 }
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
-        } else if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
-            // Inheriting existing install, so stay on the same storage medium.
-            final ApplicationInfo existingApp = mPm.getApplicationInfo(params.appPackageName, 0,
-                    userId);
-            if (existingApp == null) {
-                throw new IllegalStateException(
-                        "Missing existing app " + params.appPackageName);
-            }
-
-            final long existingSize;
-            try {
-                final PackageLite existingPkg = PackageParser.parsePackageLite(
-                        new File(existingApp.getCodePath()), 0);
-                existingSize = PackageHelper.calculateInstalledSize(existingPkg, false,
-                        params.abiOverride);
-            } catch (PackageParserException e) {
-                throw new IllegalStateException(
-                        "Failed to calculate size of " + params.appPackageName);
-            }
-
-            if ((existingApp.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0) {
-                // Internal we can link existing install into place, so we only
-                // need enough space for the new data.
-                checkInternalStorage(params.sizeBytes);
-                stageInternal = true;
-            } else {
-                // External we're going to copy existing install into our
-                // container, so we need footprint of both.
-                checkExternalStorage(params.sizeBytes + existingSize);
-                stageInternal = false;
-            }
         } else {
             throw new IllegalArgumentException("Invalid install mode: " + params.mode);
         }
@@ -563,15 +525,15 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
             // We're staging to exactly one location
             File stageDir = null;
             String stageCid = null;
-            if (stageInternal) {
-                stageDir = prepareInternalStageDir(sessionId);
+            if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+                stageDir = buildInternalStageDir(sessionId);
             } else {
-                stageCid = prepareExternalStageCid(sessionId, params.sizeBytes);
+                stageCid = buildExternalStageCid(sessionId);
             }
 
             session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
                     mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
-                    createdMillis, stageDir, stageCid, false);
+                    createdMillis, stageDir, stageCid, false, false);
             mSessions.put(sessionId, session);
         }
 
@@ -615,32 +577,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
         }
     }
 
-    private void checkInternalStorage(long sizeBytes) throws IOException {
-        if (sizeBytes <= 0) return;
-
-        final File target = Environment.getDataDirectory();
-        final long targetBytes = sizeBytes + mStorage.getStorageLowBytes(target);
-
-        mPm.freeStorage(targetBytes);
-        if (target.getUsableSpace() < targetBytes) {
-            throw new IOException("Not enough internal space to write " + sizeBytes + " bytes");
-        }
-    }
-
-    private void checkExternalStorage(long sizeBytes) throws IOException {
-        if (sizeBytes <= 0) return;
-
-        final File target = new UserEnvironment(UserHandle.USER_OWNER)
-                .getExternalStorageDirectory();
-        final long targetBytes = sizeBytes + mStorage.getStorageLowBytes(target);
-
-        if (target.getUsableSpace() < targetBytes) {
-            throw new IOException("Not enough external space to write " + sizeBytes + " bytes");
+    @Override
+    public IPackageInstallerSession openSession(int sessionId) {
+        try {
+            return openSessionInternal(sessionId);
+        } catch (IOException e) {
+            throw ExceptionUtils.wrap(e);
         }
     }
 
-    @Override
-    public IPackageInstallerSession openSession(int sessionId) {
+    private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
         synchronized (mSessions) {
             final PackageInstallerSession session = mSessions.get(sessionId);
             if (session == null || !isCallingUidOwner(session)) {
@@ -665,40 +611,37 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
         throw new IllegalStateException("Failed to allocate session ID");
     }
 
-    private File prepareInternalStageDir(int sessionId) throws IOException {
-        final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp");
+    private File buildInternalStageDir(int sessionId) {
+        return new File(mStagingDir, "vmdl" + sessionId + ".tmp");
+    }
 
-        if (file.exists()) {
-            throw new IOException("Session dir already exists: " + file);
+    static void prepareInternalStageDir(File stageDir) throws IOException {
+        if (stageDir.exists()) {
+            throw new IOException("Session dir already exists: " + stageDir);
         }
 
         try {
-            Os.mkdir(file.getAbsolutePath(), 0755);
-            Os.chmod(file.getAbsolutePath(), 0755);
+            Os.mkdir(stageDir.getAbsolutePath(), 0755);
+            Os.chmod(stageDir.getAbsolutePath(), 0755);
         } catch (ErrnoException e) {
             // This purposefully throws if directory already exists
-            throw new IOException("Failed to prepare session dir", e);
+            throw new IOException("Failed to prepare session dir: " + stageDir, e);
         }
 
-        if (!SELinux.restorecon(file)) {
-            throw new IOException("Failed to restorecon session dir");
+        if (!SELinux.restorecon(stageDir)) {
+            throw new IOException("Failed to restorecon session dir: " + stageDir);
         }
-
-        return file;
     }
 
-    private String prepareExternalStageCid(int sessionId, long sizeBytes) throws IOException {
-        if (sizeBytes <= 0) {
-            throw new IOException("Session must provide valid size for ASEC");
-        }
+    private String buildExternalStageCid(int sessionId) {
+        return "smdl" + sessionId + ".tmp";
+    }
 
-        final String cid = "smdl" + sessionId + ".tmp";
-        if (PackageHelper.createSdDir(sizeBytes, cid, PackageManagerService.getEncryptKey(),
+    static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
+        if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
                 Process.SYSTEM_UID, true) == null) {
-            throw new IOException("Failed to create ASEC");
+            throw new IOException("Failed to create session cid: " + stageCid);
         }
-
-        return cid;
     }
 
     @Override
@@ -897,10 +840,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
     private static class Callbacks extends Handler {
         private static final int MSG_SESSION_CREATED = 1;
         private static final int MSG_SESSION_BADGING_CHANGED = 2;
-        private static final int MSG_SESSION_OPENED = 3;
+        private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
         private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
-        private static final int MSG_SESSION_CLOSED = 5;
-        private static final int MSG_SESSION_FINISHED = 6;
+        private static final int MSG_SESSION_FINISHED = 5;
 
         private final RemoteCallbackList<IPackageInstallerCallback>
                 mCallbacks = new RemoteCallbackList<>();
@@ -945,15 +887,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
                 case MSG_SESSION_BADGING_CHANGED:
                     callback.onSessionBadgingChanged(sessionId);
                     break;
-                case MSG_SESSION_OPENED:
-                    callback.onSessionOpened(sessionId);
+                case MSG_SESSION_ACTIVE_CHANGED:
+                    callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
                     break;
                 case MSG_SESSION_PROGRESS_CHANGED:
                     callback.onSessionProgressChanged(sessionId, (float) msg.obj);
                     break;
-                case MSG_SESSION_CLOSED:
-                    callback.onSessionClosed(sessionId);
-                    break;
                 case MSG_SESSION_FINISHED:
                     callback.onSessionFinished(sessionId, (boolean) msg.obj);
                     break;
@@ -968,18 +907,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
             obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
         }
 
-        private void notifySessionOpened(int sessionId, int userId) {
-            obtainMessage(MSG_SESSION_OPENED, sessionId, userId).sendToTarget();
+        private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
+            obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
         }
 
         private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
             obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
         }
 
-        private void notifySessionClosed(int sessionId, int userId) {
-            obtainMessage(MSG_SESSION_CLOSED, sessionId, userId).sendToTarget();
-        }
-
         public void notifySessionFinished(int sessionId, int userId, boolean success) {
             obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
         }
@@ -1022,18 +957,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
             writeSessionsAsync();
         }
 
-        public void onSessionOpened(PackageInstallerSession session) {
-            mCallbacks.notifySessionOpened(session.sessionId, session.userId);
+        public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
+            mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
         }
 
         public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
             mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
         }
 
-        public void onSessionClosed(PackageInstallerSession session) {
-            mCallbacks.notifySessionClosed(session.sessionId, session.userId);
-        }
-
         public void onSessionFinished(PackageInstallerSession session, boolean success) {
             mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
             synchronized (mSessions) {
@@ -1043,6 +974,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
             writeSessionsAsync();
         }
 
+        public void onSessionPrepared(PackageInstallerSession session) {
+            // We prepared the destination to write into; we want to persist
+            // this, but it's not critical enough to block for.
+            writeSessionsAsync();
+        }
+
         public void onSessionSealed(PackageInstallerSession session) {
             // It's very important that we block until we've recorded the
             // session as being sealed, since we never want to allow mutation
index 85ff54e..adca46a 100644 (file)
 package com.android.server.pm;
 
 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_WRONLY;
+import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
+import static com.android.server.pm.PackageInstallerService.prepareInternalStageDir;
 
 import android.content.Context;
 import android.content.Intent;
@@ -88,8 +88,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     // TODO: enforce INSTALL_ALLOW_TEST
     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
 
-    // TODO: treat INHERIT_EXISTING as installExistingPackage()
-
     private final PackageInstallerService.InternalCallback mCallback;
     private final Context mContext;
     private final PackageManagerService mPm;
@@ -108,18 +106,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     /** Note that UID is not persisted; it's always derived at runtime. */
     final int installerUid;
 
-    private final AtomicInteger mOpenCount = new AtomicInteger();
+    private final AtomicInteger mActiveCount = new AtomicInteger();
 
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
     private float mClientProgress = 0;
     @GuardedBy("mLock")
+    private float mInternalProgress = 0;
+
+    @GuardedBy("mLock")
     private float mProgress = 0;
     @GuardedBy("mLock")
     private float mReportedProgress = -1;
 
     @GuardedBy("mLock")
+    private boolean mPrepared = false;
+    @GuardedBy("mLock")
     private boolean mSealed = false;
     @GuardedBy("mLock")
     private boolean mPermissionsAccepted = false;
@@ -184,7 +187,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
             Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
             String installerPackageName, SessionParams params, long createdMillis,
-            File stageDir, String stageCid, boolean sealed) {
+            File stageDir, String stageCid, boolean prepared, boolean sealed) {
         mCallback = callback;
         mContext = context;
         mPm = pm;
@@ -203,6 +206,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                     "Exactly one of stageDir or stageCid stage must be set");
         }
 
+        mPrepared = prepared;
         mSealed = sealed;
 
         // Always derived at runtime
@@ -214,8 +218,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
         } else {
             mPermissionsAccepted = false;
         }
-
-        computeProgressLocked();
     }
 
     public SessionInfo generateInfo() {
@@ -227,7 +229,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                     mResolvedBaseFile.getAbsolutePath() : null;
             info.progress = mProgress;
             info.sealed = mSealed;
-            info.open = mOpenCount.get() > 0;
+            info.active = mActiveCount.get() > 0;
 
             info.mode = params.mode;
             info.sizeBytes = params.sizeBytes;
@@ -238,14 +240,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
         return info;
     }
 
+    public boolean isPrepared() {
+        synchronized (mLock) {
+            return mPrepared;
+        }
+    }
+
     public boolean isSealed() {
         synchronized (mLock) {
             return mSealed;
         }
     }
 
-    private void assertNotSealed(String cookie) {
+    private void assertPreparedAndNotSealed(String cookie) {
         synchronized (mLock) {
+            if (!mPrepared) {
+                throw new IllegalStateException(cookie + " before prepared");
+            }
             if (mSealed) {
                 throw new SecurityException(cookie + " not allowed after commit");
             }
@@ -278,30 +289,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     @Override
     public void setClientProgress(float progress) {
         synchronized (mLock) {
+            // Always publish first staging movement
+            final boolean forcePublish = (mClientProgress == 0);
             mClientProgress = progress;
-            computeProgressLocked();
+            computeProgressLocked(forcePublish);
         }
-        maybePublishProgress();
     }
 
     @Override
     public void addClientProgress(float progress) {
         synchronized (mLock) {
-            mClientProgress += progress;
-            computeProgressLocked();
+            setClientProgress(mClientProgress + progress);
         }
-        maybePublishProgress();
     }
 
-    private void computeProgressLocked() {
-        if (mProgress <= 0.8f) {
-            mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f);
-        }
-    }
+    private void computeProgressLocked(boolean forcePublish) {
+        mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
+                + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
 
-    private void maybePublishProgress() {
         // Only publish when meaningful change
-        if (Math.abs(mProgress - mReportedProgress) > 0.01) {
+        if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
             mReportedProgress = mProgress;
             mCallback.onSessionProgressChanged(this, mProgress);
         }
@@ -309,7 +316,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
 
     @Override
     public String[] getNames() {
-        assertNotSealed("getNames");
+        assertPreparedAndNotSealed("getNames");
         try {
             return resolveStageDir().list();
         } catch (IOException e) {
@@ -333,7 +340,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
         // will block any attempted install transitions.
         final FileBridge bridge;
         synchronized (mLock) {
-            assertNotSealed("openWrite");
+            assertPreparedAndNotSealed("openWrite");
 
             bridge = new FileBridge();
             mBridges.add(bridge);
@@ -387,7 +394,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     }
 
     private ParcelFileDescriptor openReadInternal(String name) throws IOException {
-        assertNotSealed("openRead");
+        assertPreparedAndNotSealed("openRead");
 
         try {
             if (!FileUtils.isValidExtFilename(name)) {
@@ -407,6 +414,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     public void commit(IntentSender statusReceiver) {
         Preconditions.checkNotNull(statusReceiver);
 
+        synchronized (mLock) {
+            if (!mSealed) {
+                // Verify that all writers are hands-off
+                for (FileBridge bridge : mBridges) {
+                    if (!bridge.isClosed()) {
+                        throw new SecurityException("Files still open");
+                    }
+                }
+
+                // Persist the fact that we've sealed ourselves to prevent
+                // mutations of any hard links we create.
+                mSealed = true;
+                mCallback.onSessionSealed(this);
+            }
+        }
+
+        // Client staging is fully done at this point
+        mClientProgress = 1f;
+        computeProgressLocked(true);
+
+        // This ongoing commit should keep session active, even though client
+        // will probably close their end.
+        mActiveCount.incrementAndGet();
+
         final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
                 statusReceiver, sessionId);
         mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
@@ -414,22 +445,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
 
     private void commitLocked() throws PackageManagerException {
         if (mDestroyed) {
-            throw new PackageManagerException(INSTALL_FAILED_ALREADY_EXISTS, "Invalid session");
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
         }
-
-        // Verify that all writers are hands-off
         if (!mSealed) {
-            for (FileBridge bridge : mBridges) {
-                if (!bridge.isClosed()) {
-                    throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
-                            "Files still open");
-                }
-            }
-            mSealed = true;
-
-            // Persist the fact that we've sealed ourselves to prevent mutations
-            // of any hard links we create below.
-            mCallback.onSessionSealed(this);
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
         }
 
         try {
@@ -458,6 +477,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                 mRemoteObserver.onUserActionRequired(intent);
             } catch (RemoteException ignored) {
             }
+
+            // Commit was keeping session marked as active until now; release
+            // that extra refcount so session appears idle.
+            close();
             return;
         }
 
@@ -487,8 +510,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
         }
 
         // TODO: surface more granular state from dexopt
-        mProgress = 0.9f;
-        maybePublishProgress();
+        mInternalProgress = 0.5f;
+        computeProgressLocked(true);
 
         // Unpack native libraries
         extractNativeLibraries(mResolvedStageDir, params.abiOverride);
@@ -831,16 +854,36 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
         }
     }
 
-    public void open() {
-        if (mOpenCount.getAndIncrement() == 0) {
-            mCallback.onSessionOpened(this);
+    public void open() throws IOException {
+        if (mActiveCount.getAndIncrement() == 0) {
+            mCallback.onSessionActiveChanged(this, true);
+        }
+
+        synchronized (mLock) {
+            if (!mPrepared) {
+                if (stageDir != null) {
+                    prepareInternalStageDir(stageDir);
+                } else if (stageCid != null) {
+                    prepareExternalStageCid(stageCid, params.sizeBytes);
+
+                    // TODO: deliver more granular progress for ASEC allocation
+                    mInternalProgress = 0.25f;
+                    computeProgressLocked(true);
+                } else {
+                    throw new IllegalArgumentException(
+                            "Exactly one of stageDir or stageCid stage must be set");
+                }
+
+                mPrepared = true;
+                mCallback.onSessionPrepared(this);
+            }
         }
     }
 
     @Override
     public void close() {
-        if (mOpenCount.decrementAndGet() == 0) {
-            mCallback.onSessionClosed(this);
+        if (mActiveCount.decrementAndGet() == 0) {
+            mCallback.onSessionActiveChanged(this, false);
         }
     }
 
@@ -869,6 +912,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
         synchronized (mLock) {
             mSealed = true;
             mDestroyed = true;
+
+            // Force shut down all bridges
+            for (FileBridge bridge : mBridges) {
+                bridge.forceClose();
+            }
         }
         if (stageDir != null) {
             FileUtils.deleteContents(stageDir);
index d2a627e..7b4270b 100644 (file)
@@ -3783,21 +3783,25 @@ public class PackageManagerService extends IPackageManager.Stub {
         } else {
             pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
         }
-        if ((flags&PackageManager.GET_PERMISSIONS) == 0) {
-            if (numMatch == permissions.length) {
-                pi.requestedPermissions = permissions;
-            } else {
-                pi.requestedPermissions = new String[numMatch];
-                numMatch = 0;
-                for (int i=0; i<permissions.length; i++) {
-                    if (tmp[i]) {
-                        pi.requestedPermissions[numMatch] = permissions[i];
-                        numMatch++;
+        // The above might return null in cases of uninstalled apps or install-state
+        // skew across users/profiles.
+        if (pi != null) {
+            if ((flags&PackageManager.GET_PERMISSIONS) == 0) {
+                if (numMatch == permissions.length) {
+                    pi.requestedPermissions = permissions;
+                } else {
+                    pi.requestedPermissions = new String[numMatch];
+                    numMatch = 0;
+                    for (int i=0; i<permissions.length; i++) {
+                        if (tmp[i]) {
+                            pi.requestedPermissions[numMatch] = permissions[i];
+                            numMatch++;
+                        }
                     }
                 }
             }
+            list.add(pi);
         }
-        list.add(pi);
     }
 
     @Override
@@ -10260,7 +10264,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                             || !bp.packageSetting.keySetData.isUsingUpgradeKeySets()
                             || ((PackageSetting) bp.packageSetting).sharedUser != null) {
                         sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
-                                pkg.mSignatures) != PackageManager.SIGNATURE_MATCH;
+                                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
                     } else {
                         sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
                     }
index 3eb2d5f..802df95 100644 (file)
@@ -85,6 +85,7 @@ import org.xmlpull.v1.XmlSerializer;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
+import com.android.internal.R;
 
 public class WallpaperManagerService extends IWallpaperManager.Stub {
     static final String TAG = "WallpaperManagerService";
@@ -99,12 +100,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
     static final long MIN_WALLPAPER_CRASH_TIME = 10000;
     static final String WALLPAPER = "wallpaper";
     static final String WALLPAPER_INFO = "wallpaper_info.xml";
-    /**
-     * Name of the component used to display bitmap wallpapers from either the gallery or
-     * built-in wallpapers.
-     */
-    static final ComponentName IMAGE_WALLPAPER = new ComponentName("com.android.systemui",
-            "com.android.systemui.ImageWallpaper");
 
     /**
      * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
@@ -146,7 +141,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
                         if (event == CLOSE_WRITE) {
                             mWallpaper.imageWallpaperPending = false;
                         }
-                        bindWallpaperComponentLocked(IMAGE_WALLPAPER, true,
+                        bindWallpaperComponentLocked(mImageWallpaper, true,
                                 false, mWallpaper, null);
                         saveSettingsLocked(mWallpaper);
                     }
@@ -161,6 +156,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
     final MyPackageMonitor mMonitor;
     WallpaperData mLastWallpaper;
 
+    /**
+     * Name of the component used to display bitmap wallpapers from either the gallery or
+     * built-in wallpapers.
+     */
+    final ComponentName mImageWallpaper;
+
     SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
 
     int mCurrentUserId;
@@ -447,6 +448,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
     public WallpaperManagerService(Context context) {
         if (DEBUG) Slog.v(TAG, "WallpaperService startup");
         mContext = context;
+        mImageWallpaper = ComponentName.unflattenFromString(
+                context.getResources().getString(R.string.image_wallpaper_component));
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mIPackageManager = AppGlobals.getPackageManager();
@@ -599,33 +602,35 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
             f.delete();
         }
         final long ident = Binder.clearCallingIdentity();
-        RuntimeException e = null;
         try {
-            wallpaper.imageWallpaperPending = false;
-            if (userId != mCurrentUserId) return;
-            if (bindWallpaperComponentLocked(defaultFailed
-                    ? IMAGE_WALLPAPER
-                    : null, true, false, wallpaper, reply)) {
-                return;
+            RuntimeException e = null;
+            try {
+                wallpaper.imageWallpaperPending = false;
+                if (userId != mCurrentUserId) return;
+                if (bindWallpaperComponentLocked(defaultFailed
+                        ? mImageWallpaper
+                                : null, true, false, wallpaper, reply)) {
+                    return;
+                }
+            } catch (IllegalArgumentException e1) {
+                e = e1;
+            }
+
+            // This can happen if the default wallpaper component doesn't
+            // exist.  This should be a system configuration problem, but
+            // let's not let it crash the system and just live with no
+            // wallpaper.
+            Slog.e(TAG, "Default wallpaper component not found!", e);
+            clearWallpaperComponentLocked(wallpaper);
+            if (reply != null) {
+                try {
+                    reply.sendResult(null);
+                } catch (RemoteException e1) {
+                }
             }
-        } catch (IllegalArgumentException e1) {
-            e = e1;
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        
-        // This can happen if the default wallpaper component doesn't
-        // exist.  This should be a system configuration problem, but
-        // let's not let it crash the system and just live with no
-        // wallpaper.
-        Slog.e(TAG, "Default wallpaper component not found!", e);
-        clearWallpaperComponentLocked(wallpaper);
-        if (reply != null) {
-            try {
-                reply.sendResult(null);
-            } catch (RemoteException e1) {
-            }
-        }
     }
 
     public boolean hasNamedWallpaper(String name) {
@@ -848,7 +853,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
                 componentName = WallpaperManager.getDefaultWallpaperComponent(mContext);
                 if (componentName == null) {
                     // Fall back to static image wallpaper
-                    componentName = IMAGE_WALLPAPER;
+                    componentName = mImageWallpaper;
                     //clearWallpaperComponentLocked();
                     //return;
                     if (DEBUG) Slog.v(TAG, "Using image wallpaper");
@@ -876,7 +881,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
             WallpaperInfo wi = null;
             
             Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
-            if (componentName != null && !componentName.equals(IMAGE_WALLPAPER)) {
+            if (componentName != null && !componentName.equals(mImageWallpaper)) {
                 // Make sure the selected service is actually a wallpaper service.
                 List<ResolveInfo> ris =
                         mIPackageManager.queryIntentServices(intent,
@@ -1052,7 +1057,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
             out.attribute(null, "height", Integer.toString(wallpaper.height));
             out.attribute(null, "name", wallpaper.name);
             if (wallpaper.wallpaperComponent != null
-                    && !wallpaper.wallpaperComponent.equals(IMAGE_WALLPAPER)) {
+                    && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
                 out.attribute(null, "component",
                         wallpaper.wallpaperComponent.flattenToShortString());
             }
@@ -1124,7 +1129,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
                         if (wallpaper.nextWallpaperComponent == null
                                 || "android".equals(wallpaper.nextWallpaperComponent
                                         .getPackageName())) {
-                            wallpaper.nextWallpaperComponent = IMAGE_WALLPAPER;
+                            wallpaper.nextWallpaperComponent = mImageWallpaper;
                         }
                           
                         if (DEBUG) {
@@ -1196,7 +1201,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
             loadSettingsLocked(0);
             wallpaper = mWallpaperMap.get(0);
             if (wallpaper.nextWallpaperComponent != null
-                    && !wallpaper.nextWallpaperComponent.equals(IMAGE_WALLPAPER)) {
+                    && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
                 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
                         wallpaper, null)) {
                     // No such live wallpaper or other failure; fall back to the default
index aabb8f7..bfc7659 100644 (file)
@@ -569,9 +569,9 @@ public class AppTransition implements Dump {
             int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
             Rect contentInsets, boolean isFullScreen) {
         Animation a;
-        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+        final int thumbWidthI = mNextAppTransitionStartWidth;
         final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
-        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+        final int thumbHeightI = mNextAppTransitionStartHeight;
         final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
 
         // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
@@ -993,7 +993,7 @@ public class AppTransition implements Dump {
     }
 
     void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
-            IRemoteCallback startedCallback, boolean scaleUp) {
+            int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
         if (isTransitionSet()) {
             mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
                     : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1002,6 +1002,8 @@ public class AppTransition implements Dump {
             mNextAppTransitionScaleUp = scaleUp;
             mNextAppTransitionStartX = startX;
             mNextAppTransitionStartY = startY;
+            mNextAppTransitionStartWidth = targetWidth;
+            mNextAppTransitionStartHeight = targetHeight;
             postAnimationCallback();
             mNextAppTransitionCallback = startedCallback;
         } else {
@@ -1138,6 +1140,10 @@ public class AppTransition implements Dump {
                         pw.print(mNextAppTransitionStartX);
                         pw.print(" mNextAppTransitionStartY=");
                         pw.println(mNextAppTransitionStartY);
+                        pw.print(" mNextAppTransitionStartWidth=");
+                        pw.print(mNextAppTransitionStartWidth);
+                        pw.print(" mNextAppTransitionStartHeight=");
+                        pw.println(mNextAppTransitionStartHeight);
                 pw.print("  mNextAppTransitionScaleUp="); pw.println(mNextAppTransitionScaleUp);
                 break;
         }
index a1afe29..55c6d81 100644 (file)
@@ -4031,10 +4031,11 @@ public class WindowManagerService extends IWindowManager.Stub
 
     @Override
     public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX,
-            int startY, IRemoteCallback startedCallback, boolean scaleUp) {
+            int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
+            boolean scaleUp) {
         synchronized(mWindowMap) {
             mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX,
-                    startY, startedCallback, scaleUp);
+                    startY, targetWidth, targetHeight, startedCallback, scaleUp);
         }
     }
 
@@ -4303,20 +4304,28 @@ public class WindowManagerService extends IWindowManager.Stub
     }
 
     public void setAppFullscreen(IBinder token, boolean toOpaque) {
-        AppWindowToken atoken = findAppWindowToken(token);
-        if (atoken != null) {
-            atoken.appFullscreen = toOpaque;
-            setWindowOpaque(token, toOpaque);
-            requestTraversal();
+        synchronized (mWindowMap) {
+            AppWindowToken atoken = findAppWindowToken(token);
+            if (atoken != null) {
+                atoken.appFullscreen = toOpaque;
+                setWindowOpaqueLocked(token, toOpaque);
+                requestTraversalLocked();
+            }
         }
     }
 
     public void setWindowOpaque(IBinder token, boolean isOpaque) {
+        synchronized (mWindowMap) {
+            setWindowOpaqueLocked(token, isOpaque);
+        }
+    }
+
+    public void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
         AppWindowToken wtoken = findAppWindowToken(token);
         if (wtoken != null) {
             WindowState win = wtoken.findMainWindow();
             if (win != null) {
-                win.mWinAnimator.setOpaque(isOpaque);
+                win.mWinAnimator.setOpaqueLocked(isOpaque);
             }
         }
     }
index 3d4be12..a871522 100644 (file)
@@ -1556,11 +1556,11 @@ class WindowStateAnimator {
         }
     }
 
-    void setOpaque(boolean isOpaque) {
+    void setOpaqueLocked(boolean isOpaque) {
         if (mSurfaceControl == null) {
             return;
         }
-        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaque");
+        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaqueLocked");
         SurfaceControl.openTransaction();
         try {
             if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "isOpaque=" + isOpaque,
@@ -1568,7 +1568,7 @@ class WindowStateAnimator {
             mSurfaceControl.setOpaque(isOpaque);
         } finally {
             SurfaceControl.closeTransaction();
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaque");
+            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
         }
     }
 
index bc866d3..65a28c0 100644 (file)
@@ -21,7 +21,6 @@
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
-#include "utils/Vector.h"
 
 #include <usbhost/usbhost.h>
 
@@ -68,8 +67,6 @@ static int usb_device_added(const char *devname, void* client_data) {
 
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     jobject thiz = (jobject)client_data;
-    Vector<int> interfaceValues;
-    Vector<int> endpointValues;
     const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
 
     char *manufacturer = usb_device_get_manufacturer_name(device);
index 6c80a65..5f639ab 100644 (file)
@@ -23,13 +23,11 @@ import android.content.res.Configuration;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
-import java.util.ArrayList;
-
 class IntervalStats {
     public long beginTime;
     public long endTime;
     public long lastTimeSaved;
-    public final ArrayMap<String, UsageStats> stats = new ArrayMap<>();
+    public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
     public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
     public Configuration activeConfiguration;
     public TimeSparseArray<UsageEvents.Event> events;
@@ -44,13 +42,13 @@ class IntervalStats {
      * Gets the UsageStats object for the given package, or creates one and adds it internally.
      */
     UsageStats getOrCreateUsageStats(String packageName) {
-        UsageStats usageStats = stats.get(packageName);
+        UsageStats usageStats = packageStats.get(packageName);
         if (usageStats == null) {
             usageStats = new UsageStats();
             usageStats.mPackageName = getCachedStringRef(packageName);
             usageStats.mBeginTimeStamp = beginTime;
             usageStats.mEndTimeStamp = endTime;
-            stats.put(usageStats.mPackageName, usageStats);
+            packageStats.put(usageStats.mPackageName, usageStats);
         }
         return usageStats;
     }
diff --git a/services/usage/java/com/android/server/usage/UnixCalendar.java b/services/usage/java/com/android/server/usage/UnixCalendar.java
new file mode 100644 (file)
index 0000000..ce06a91
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.android.server.usage;
+
+import android.app.usage.UsageStatsManager;
+
+/**
+ * A handy calendar object that knows nothing of Locale's or TimeZones. This simplifies
+ * interval book-keeping. It is *NOT* meant to be used as a user-facing calendar, as it has
+ * no concept of Locale or TimeZone.
+ */
+public class UnixCalendar {
+    private static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
+    private static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
+    private static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
+    private static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
+    private long mTime;
+
+    public UnixCalendar(long time) {
+        mTime = time;
+    }
+
+    public void truncateToDay() {
+        mTime -= mTime % DAY_IN_MILLIS;
+    }
+
+    public void truncateToWeek() {
+        mTime -= mTime % WEEK_IN_MILLIS;
+    }
+
+    public void truncateToMonth() {
+        mTime -= mTime % MONTH_IN_MILLIS;
+    }
+
+    public void truncateToYear() {
+        mTime -= mTime % YEAR_IN_MILLIS;
+    }
+
+    public void addDays(int val) {
+        mTime += val * DAY_IN_MILLIS;
+    }
+
+    public void addWeeks(int val) {
+        mTime += val * WEEK_IN_MILLIS;
+    }
+
+    public void addMonths(int val) {
+        mTime += val * MONTH_IN_MILLIS;
+    }
+
+    public void addYears(int val) {
+        mTime += val * YEAR_IN_MILLIS;
+    }
+
+    public void setTimeInMillis(long time) {
+        mTime = time;
+    }
+
+    public long getTimeInMillis() {
+        return mTime;
+    }
+
+    public static void truncateTo(UnixCalendar calendar, int intervalType) {
+        switch (intervalType) {
+            case UsageStatsManager.INTERVAL_YEARLY:
+                calendar.truncateToYear();
+                break;
+
+            case UsageStatsManager.INTERVAL_MONTHLY:
+                calendar.truncateToMonth();
+                break;
+
+            case UsageStatsManager.INTERVAL_WEEKLY:
+                calendar.truncateToWeek();
+                break;
+
+            case UsageStatsManager.INTERVAL_DAILY:
+                calendar.truncateToDay();
+                break;
+
+            default:
+                throw new UnsupportedOperationException("Can't truncate date to interval " +
+                        intervalType);
+        }
+    }
+}
index 37340a4..62a7ec0 100644 (file)
@@ -21,24 +21,30 @@ import android.app.usage.UsageStatsManager;
 import android.util.AtomicFile;
 import android.util.Slog;
 
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Calendar;
 import java.util.List;
 
 /**
  * Provides an interface to query for UsageStat data from an XML database.
  */
 class UsageStatsDatabase {
+    private static final int CURRENT_VERSION = 2;
+
     private static final String TAG = "UsageStatsDatabase";
     private static final boolean DEBUG = UsageStatsService.DEBUG;
 
     private final Object mLock = new Object();
     private final File[] mIntervalDirs;
     private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
-    private final Calendar mCal;
+    private final UnixCalendar mCal;
+    private final File mVersionFile;
 
     public UsageStatsDatabase(File dir) {
         mIntervalDirs = new File[] {
@@ -47,8 +53,9 @@ class UsageStatsDatabase {
                 new File(dir, "monthly"),
                 new File(dir, "yearly"),
         };
+        mVersionFile = new File(dir, "version");
         mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
-        mCal = Calendar.getInstance();
+        mCal = new UnixCalendar(0);
     }
 
     /**
@@ -64,6 +71,8 @@ class UsageStatsDatabase {
                 }
             }
 
+            checkVersionLocked();
+
             final FilenameFilter backupFileFilter = new FilenameFilter() {
                 @Override
                 public boolean accept(File dir, String name) {
@@ -81,7 +90,45 @@ class UsageStatsDatabase {
                     }
 
                     for (File f : files) {
-                        mSortedStatFiles[i].put(Long.parseLong(f.getName()), new AtomicFile(f));
+                        final AtomicFile af = new AtomicFile(f);
+                        mSortedStatFiles[i].put(UsageStatsXml.parseBeginTime(af), af);
+                    }
+                }
+            }
+        }
+    }
+
+    private void checkVersionLocked() {
+        int version;
+        try (BufferedReader reader = new BufferedReader(new FileReader(mVersionFile))) {
+            version = Integer.parseInt(reader.readLine());
+        } catch (NumberFormatException | IOException e) {
+            version = 0;
+        }
+
+        if (version != CURRENT_VERSION) {
+            Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION);
+            doUpgradeLocked(version);
+
+            try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) {
+                writer.write(Integer.toString(CURRENT_VERSION));
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to write new version");
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private void doUpgradeLocked(int thisVersion) {
+        if (thisVersion < 2) {
+            // Delete all files if we are version 0. This is a pre-release version,
+            // so this is fine.
+            Slog.i(TAG, "Deleting all usage stats files");
+            for (int i = 0; i < mIntervalDirs.length; i++) {
+                File[] files = mIntervalDirs[i].listFiles();
+                if (files != null) {
+                    for (File f : files) {
+                        f.delete();
                     }
                 }
             }
@@ -161,25 +208,48 @@ class UsageStatsDatabase {
                 throw new IllegalArgumentException("Bad interval type " + intervalType);
             }
 
-            if (endTime < beginTime) {
+            final TimeSparseArray<AtomicFile> intervalStats = mSortedStatFiles[intervalType];
+
+            if (endTime <= beginTime) {
+                if (DEBUG) {
+                    Slog.d(TAG, "endTime(" + endTime + ") <= beginTime(" + beginTime + ")");
+                }
                 return null;
             }
 
-            final int startIndex = mSortedStatFiles[intervalType].closestIndexOnOrBefore(beginTime);
+            int startIndex = intervalStats.closestIndexOnOrBefore(beginTime);
             if (startIndex < 0) {
-                return null;
+                // All the stats available have timestamps after beginTime, which means they all
+                // match.
+                startIndex = 0;
             }
 
-            int endIndex = mSortedStatFiles[intervalType].closestIndexOnOrAfter(endTime);
+            int endIndex = intervalStats.closestIndexOnOrBefore(endTime);
             if (endIndex < 0) {
-                endIndex = mSortedStatFiles[intervalType].size() - 1;
+                // All the stats start after this range ends, so nothing matches.
+                if (DEBUG) {
+                    Slog.d(TAG, "No results for this range. All stats start after.");
+                }
+                return null;
+            }
+
+            if (intervalStats.keyAt(endIndex) == endTime) {
+                // The endTime is exclusive, so if we matched exactly take the one before.
+                endIndex--;
+                if (endIndex < 0) {
+                    // All the stats start after this range ends, so nothing matches.
+                    if (DEBUG) {
+                        Slog.d(TAG, "No results for this range. All stats start after.");
+                    }
+                    return null;
+                }
             }
 
             try {
                 IntervalStats stats = new IntervalStats();
                 ArrayList<T> results = new ArrayList<>();
                 for (int i = startIndex; i <= endIndex; i++) {
-                    final AtomicFile f = mSortedStatFiles[intervalType].valueAt(i);
+                    final AtomicFile f = intervalStats.valueAt(i);
 
                     if (DEBUG) {
                         Slog.d(TAG, "Reading stat file " + f.getBaseFile().getAbsolutePath());
@@ -230,22 +300,22 @@ class UsageStatsDatabase {
         synchronized (mLock) {
             long timeNow = System.currentTimeMillis();
             mCal.setTimeInMillis(timeNow);
-            mCal.add(Calendar.YEAR, -3);
+            mCal.addYears(-3);
             pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_YEARLY],
                     mCal.getTimeInMillis());
 
             mCal.setTimeInMillis(timeNow);
-            mCal.add(Calendar.MONTH, -6);
+            mCal.addMonths(-6);
             pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_MONTHLY],
                     mCal.getTimeInMillis());
 
             mCal.setTimeInMillis(timeNow);
-            mCal.add(Calendar.WEEK_OF_YEAR, -4);
+            mCal.addWeeks(-4);
             pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_WEEKLY],
                     mCal.getTimeInMillis());
 
             mCal.setTimeInMillis(timeNow);
-            mCal.add(Calendar.DAY_OF_YEAR, -7);
+            mCal.addDays(-7);
             pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_DAILY],
                     mCal.getTimeInMillis());
         }
index e77bf86..2dcdcc4 100644 (file)
@@ -38,6 +38,7 @@ import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArraySet;
@@ -62,9 +63,7 @@ public class UsageStatsService extends SystemService implements
     static final boolean DEBUG = false;
     private static final long TEN_SECONDS = 10 * 1000;
     private static final long TWENTY_MINUTES = 20 * 60 * 1000;
-    private static final long TWO_MINUTES = 2 * 60 * 1000;
     private static final long FLUSH_INTERVAL = DEBUG ? TEN_SECONDS : TWENTY_MINUTES;
-    private static final long END_TIME_DELAY = DEBUG ? 0 : TWO_MINUTES;
 
     // Handler message types.
     static final int MSG_REPORT_EVENT = 0;
@@ -78,6 +77,8 @@ public class UsageStatsService extends SystemService implements
 
     private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
     private File mUsageStatsDir;
+    long mRealTimeSnapshot;
+    long mSystemTimeSnapshot;
 
     public UsageStatsService(Context context) {
         super(context);
@@ -104,6 +105,9 @@ public class UsageStatsService extends SystemService implements
             cleanUpRemovedUsersLocked();
         }
 
+        mRealTimeSnapshot = SystemClock.elapsedRealtime();
+        mSystemTimeSnapshot = System.currentTimeMillis();
+
         publishLocalService(UsageStatsManagerInternal.class, new LocalService());
         publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
     }
@@ -178,7 +182,7 @@ public class UsageStatsService extends SystemService implements
     }
 
     /**
-     * Called by the Bunder stub
+     * Called by the Binder stub
      */
     void shutdown() {
         synchronized (mLock) {
@@ -382,6 +386,14 @@ public class UsageStatsService extends SystemService implements
      */
     private class LocalService extends UsageStatsManagerInternal {
 
+        /**
+         * The system may have its time change, so at least make sure the events
+         * are monotonic in order.
+         */
+        private long computeMonotonicSystemTime(long realTime) {
+            return (realTime - mRealTimeSnapshot) + mSystemTimeSnapshot;
+        }
+
         @Override
         public void reportEvent(ComponentName component, int userId, int eventType) {
             if (component == null) {
@@ -392,7 +404,7 @@ public class UsageStatsService extends SystemService implements
             UsageEvents.Event event = new UsageEvents.Event();
             event.mPackage = component.getPackageName();
             event.mClass = component.getClassName();
-            event.mTimeStamp = System.currentTimeMillis();
+            event.mTimeStamp = computeMonotonicSystemTime(SystemClock.elapsedRealtime());
             event.mEventType = eventType;
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
         }
@@ -406,7 +418,7 @@ public class UsageStatsService extends SystemService implements
 
             UsageEvents.Event event = new UsageEvents.Event();
             event.mPackage = "android";
-            event.mTimeStamp = System.currentTimeMillis();
+            event.mTimeStamp = computeMonotonicSystemTime(SystemClock.elapsedRealtime());
             event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
             event.mConfiguration = new Configuration(config);
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsUtils.java b/services/usage/java/com/android/server/usage/UsageStatsUtils.java
deleted file mode 100644 (file)
index dd5f3b9..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.server.usage;
-
-import android.app.usage.UsageStatsManager;
-
-import java.util.Calendar;
-
-/**
- * A collection of utility methods used by the UsageStatsService and accompanying classes.
- */
-final class UsageStatsUtils {
-    private UsageStatsUtils() {}
-
-    /**
-     * Truncates the date to the given UsageStats bucket. For example, if the bucket is
-     * {@link UsageStatsManager#INTERVAL_YEARLY}, the date is truncated to the 1st day of the year,
-     * with the time set to 00:00:00.
-     *
-     * @param bucket The UsageStats bucket to truncate to.
-     * @param cal The date to truncate.
-     */
-    public static void truncateDateTo(int bucket, Calendar cal) {
-        cal.set(Calendar.HOUR_OF_DAY, 0);
-        cal.set(Calendar.MINUTE, 0);
-        cal.set(Calendar.SECOND, 0);
-        cal.set(Calendar.MILLISECOND, 0);
-
-        switch (bucket) {
-            case UsageStatsManager.INTERVAL_YEARLY:
-                cal.set(Calendar.DAY_OF_YEAR, 0);
-                break;
-
-            case UsageStatsManager.INTERVAL_MONTHLY:
-                cal.set(Calendar.DAY_OF_MONTH, 0);
-                break;
-
-            case UsageStatsManager.INTERVAL_WEEKLY:
-                cal.set(Calendar.DAY_OF_WEEK, 0);
-                break;
-
-            case UsageStatsManager.INTERVAL_DAILY:
-                break;
-
-            default:
-                throw new UnsupportedOperationException("Can't truncate date to bucket " + bucket);
-        }
-    }
-}
index 48881d0..9ce6d63 100644 (file)
@@ -37,10 +37,15 @@ public class UsageStatsXml {
     private static final String USAGESTATS_TAG = "usagestats";
     private static final String VERSION_ATTR = "version";
 
+    public static long parseBeginTime(AtomicFile file) {
+        return Long.parseLong(file.getBaseFile().getName());
+    }
+
     public static void read(AtomicFile file, IntervalStats statsOut) throws IOException {
         try {
             FileInputStream in = file.openRead();
             try {
+                statsOut.beginTime = parseBeginTime(file);
                 read(in, statsOut);
                 statsOut.lastTimeSaved = file.getLastModifiedTime();
             } finally {
index 6529950..ef95a7b 100644 (file)
@@ -15,7 +15,6 @@
  */
 package com.android.server.usage;
 
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -26,44 +25,52 @@ import android.app.usage.ConfigurationStats;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
-import android.content.ComponentName;
 import android.content.res.Configuration;
 
 import java.io.IOException;
 import java.net.ProtocolException;
-import java.util.Locale;
 
 /**
  * UsageStats reader/writer for version 1 of the XML format.
  */
 final class UsageStatsXmlV1 {
+    private static final String PACKAGES_TAG = "packages";
     private static final String PACKAGE_TAG = "package";
-    private static final String CONFIGURATION_TAG = "config";
+
+    private static final String CONFIGURATIONS_TAG = "configurations";
+    private static final String CONFIG_TAG = "config";
+
     private static final String EVENT_LOG_TAG = "event-log";
+    private static final String EVENT_TAG = "event";
 
-    private static final String BEGIN_TIME_ATTR = "beginTime";
-    private static final String END_TIME_ATTR = "endTime";
-    private static final String NAME_ATTR = "name";
+    // Attributes
     private static final String PACKAGE_ATTR = "package";
     private static final String CLASS_ATTR = "class";
-    private static final String TOTAL_TIME_ACTIVE_ATTR = "totalTimeActive";
-    private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+    private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive";
     private static final String COUNT_ATTR = "count";
     private static final String ACTIVE_ATTR = "active";
     private static final String LAST_EVENT_ATTR = "lastEvent";
     private static final String TYPE_ATTR = "type";
+
+    // Time attributes stored as an offset of the beginTime.
+    private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+    private static final String END_TIME_ATTR = "endTime";
     private static final String TIME_ATTR = "time";
 
     private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
             throws XmlPullParserException, IOException {
-        final String name = parser.getAttributeValue(null, NAME_ATTR);
-        if (name == null) {
-            throw new ProtocolException("no " + NAME_ATTR + " attribute present");
+        final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
+        if (pkg == null) {
+            throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
         }
 
-        UsageStats stats = statsOut.getOrCreateUsageStats(name);
+        final UsageStats stats = statsOut.getOrCreateUsageStats(pkg);
+
+        // Apply the offset to the beginTime to find the absolute time.
+        stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
+                parser, LAST_TIME_ACTIVE_ATTR);
+
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
-        stats.mLastTimeUsed = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
     }
 
@@ -72,8 +79,12 @@ final class UsageStatsXmlV1 {
         final Configuration config = new Configuration();
         Configuration.readXmlAttrs(parser, config);
 
-        ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
-        configStats.mLastTimeActive = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
+        final ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
+
+        // Apply the offset to the beginTime to find the absolute time.
+        configStats.mLastTimeActive = statsOut.beginTime + XmlUtils.readLongAttribute(
+                parser, LAST_TIME_ACTIVE_ATTR);
+
         configStats.mTotalTimeActive = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
         configStats.mActivationCount = XmlUtils.readIntAttribute(parser, COUNT_ATTR);
         if (XmlUtils.readBooleanAttribute(parser, ACTIVE_ATTR)) {
@@ -83,30 +94,19 @@ final class UsageStatsXmlV1 {
 
     private static void loadEvent(XmlPullParser parser, IntervalStats statsOut)
             throws XmlPullParserException, IOException {
-        String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
-        String className;
+        final String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
         if (packageName == null) {
-            // Try getting the component name if it exists.
-            final String componentName = XmlUtils.readStringAttribute(parser, NAME_ATTR);
-            if (componentName == null) {
-                throw new ProtocolException("no " + NAME_ATTR + " or " + PACKAGE_ATTR +
-                        " attribute present");
-            }
-            ComponentName component = ComponentName.unflattenFromString(componentName);
-            if (component == null) {
-                throw new ProtocolException("ComponentName " + componentName + " is invalid");
-            }
-
-            packageName = component.getPackageName();
-            className = component.getClassName();
-        } else {
-            className = XmlUtils.readStringAttribute(parser, CLASS_ATTR);
+            throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
         }
 
-        UsageEvents.Event event = statsOut.buildEvent(packageName, className);
-        event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
-        event.mTimeStamp = XmlUtils.readLongAttribute(parser, TIME_ATTR);
+        final String className = XmlUtils.readStringAttribute(parser, CLASS_ATTR);
 
+        final UsageEvents.Event event = statsOut.buildEvent(packageName, className);
+
+        // Apply the offset to the beginTime to find the absolute time of this event.
+        event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR);
+
+        event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
         if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
             event.mConfiguration = new Configuration();
             Configuration.readXmlAttrs(parser, event.mConfiguration);
@@ -118,48 +118,60 @@ final class UsageStatsXmlV1 {
         statsOut.events.put(event.mTimeStamp, event);
     }
 
-    private static void writeUsageStats(XmlSerializer xml, final UsageStats stats)
-            throws IOException {
+    private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
+            final UsageStats usageStats) throws IOException {
         xml.startTag(null, PACKAGE_TAG);
-        XmlUtils.writeStringAttribute(xml, NAME_ATTR, stats.mPackageName);
-        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeInForeground);
-        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeUsed);
-        XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, stats.mLastEvent);
+
+        // Write the time offset.
+        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
+                usageStats.mLastTimeUsed - stats.beginTime);
+
+        XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
+        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
+        XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
+
         xml.endTag(null, PACKAGE_TAG);
     }
 
-    private static void writeConfigStats(XmlSerializer xml, final ConfigurationStats stats,
-            boolean isActive) throws IOException {
-        xml.startTag(null, CONFIGURATION_TAG);
-        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeActive);
-        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeActive);
-        XmlUtils.writeIntAttribute(xml, COUNT_ATTR, stats.mActivationCount);
+    private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats,
+            final ConfigurationStats configStats, boolean isActive) throws IOException {
+        xml.startTag(null, CONFIG_TAG);
+
+        // Write the time offset.
+        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
+                configStats.mLastTimeActive - stats.beginTime);
+
+        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, configStats.mTotalTimeActive);
+        XmlUtils.writeIntAttribute(xml, COUNT_ATTR, configStats.mActivationCount);
         if (isActive) {
             XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true);
         }
 
         // Now write the attributes representing the configuration object.
-        Configuration.writeXmlAttrs(xml, stats.mConfiguration);
+        Configuration.writeXmlAttrs(xml, configStats.mConfiguration);
 
-        xml.endTag(null, CONFIGURATION_TAG);
+        xml.endTag(null, CONFIG_TAG);
     }
 
-    private static void writeEvent(XmlSerializer xml, final UsageEvents.Event event)
-            throws IOException {
-        xml.startTag(null, EVENT_LOG_TAG);
+    private static void writeEvent(XmlSerializer xml, final IntervalStats stats,
+            final UsageEvents.Event event) throws IOException {
+        xml.startTag(null, EVENT_TAG);
+
+        // Store the time offset.
+        XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp - stats.beginTime);
+
         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage);
         if (event.mClass != null) {
             XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass);
         }
         XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
-        XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp);
 
         if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE
                 && event.mConfiguration != null) {
             Configuration.writeXmlAttrs(xml, event.mConfiguration);
         }
 
-        xml.endTag(null, EVENT_LOG_TAG);
+        xml.endTag(null, EVENT_TAG);
     }
 
     /**
@@ -171,7 +183,7 @@ final class UsageStatsXmlV1 {
      */
     public static void read(XmlPullParser parser, IntervalStats statsOut)
             throws XmlPullParserException, IOException {
-        statsOut.stats.clear();
+        statsOut.packageStats.clear();
         statsOut.configurations.clear();
         statsOut.activeConfiguration = null;
 
@@ -179,7 +191,6 @@ final class UsageStatsXmlV1 {
             statsOut.events.clear();
         }
 
-        statsOut.beginTime = XmlUtils.readLongAttribute(parser, BEGIN_TIME_ATTR);
         statsOut.endTime = XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
 
         int eventCode;
@@ -196,11 +207,11 @@ final class UsageStatsXmlV1 {
                     loadUsageStats(parser, statsOut);
                     break;
 
-                case CONFIGURATION_TAG:
+                case CONFIG_TAG:
                     loadConfigStats(parser, statsOut);
                     break;
 
-                case EVENT_LOG_TAG:
+                case EVENT_TAG:
                     loadEvent(parser, statsOut);
                     break;
             }
@@ -208,32 +219,38 @@ final class UsageStatsXmlV1 {
     }
 
     /**
-     * Writes the stats object to an XML file. The {@link FastXmlSerializer}
+     * Writes the stats object to an XML file. The {@link XmlSerializer}
      * has already written the <code><usagestats></code> tag, but attributes may still
      * be added.
      *
-     * @param serializer The serializer to which to write the stats data.
+     * @param xml The serializer to which to write the packageStats data.
      * @param stats The stats object to write to the XML file.
      */
-    public static void write(FastXmlSerializer serializer, IntervalStats stats) throws IOException {
-        serializer.attribute(null, BEGIN_TIME_ATTR, Long.toString(stats.beginTime));
-        serializer.attribute(null, END_TIME_ATTR, Long.toString(stats.endTime));
+    public static void write(XmlSerializer xml, IntervalStats stats) throws IOException {
+        XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime);
 
-        final int statsCount = stats.stats.size();
+        xml.startTag(null, PACKAGES_TAG);
+        final int statsCount = stats.packageStats.size();
         for (int i = 0; i < statsCount; i++) {
-            writeUsageStats(serializer, stats.stats.valueAt(i));
+            writeUsageStats(xml, stats, stats.packageStats.valueAt(i));
         }
+        xml.endTag(null, PACKAGES_TAG);
 
+
+        xml.startTag(null, CONFIGURATIONS_TAG);
         final int configCount = stats.configurations.size();
         for (int i = 0; i < configCount; i++) {
             boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
-            writeConfigStats(serializer, stats.configurations.valueAt(i), active);
+            writeConfigStats(xml, stats, stats.configurations.valueAt(i), active);
         }
+        xml.endTag(null, CONFIGURATIONS_TAG);
 
+        xml.startTag(null, EVENT_LOG_TAG);
         final int eventCount = stats.events != null ? stats.events.size() : 0;
         for (int i = 0; i < eventCount; i++) {
-            writeEvent(serializer, stats.events.valueAt(i));
+            writeEvent(xml, stats, stats.events.valueAt(i));
         }
+        xml.endTag(null, EVENT_LOG_TAG);
     }
 
     private UsageStatsXmlV1() {
index 7142a99..2769666 100644 (file)
@@ -32,7 +32,6 @@ import java.io.IOException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Calendar;
 import java.util.List;
 
 /**
@@ -47,7 +46,7 @@ class UserUsageStatsService {
     private final UsageStatsDatabase mDatabase;
     private final IntervalStats[] mCurrentStats;
     private boolean mStatsChanged = false;
-    private final Calendar mDailyExpiryDate;
+    private final UnixCalendar mDailyExpiryDate;
     private final StatsUpdatedListener mListener;
     private final String mLogPrefix;
 
@@ -56,7 +55,7 @@ class UserUsageStatsService {
     }
 
     UserUsageStatsService(int userId, File usageStatsDir, StatsUpdatedListener listener) {
-        mDailyExpiryDate = Calendar.getInstance();
+        mDailyExpiryDate = new UnixCalendar(0);
         mDatabase = new UsageStatsDatabase(usageStatsDir);
         mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
         mListener = listener;
@@ -66,6 +65,7 @@ class UserUsageStatsService {
     void init() {
         mDatabase.init();
 
+        final long timeNow = System.currentTimeMillis();
         int nullCount = 0;
         for (int i = 0; i < mCurrentStats.length; i++) {
             mCurrentStats[i] = mDatabase.getLatestUsageStats(i);
@@ -73,6 +73,11 @@ class UserUsageStatsService {
                 // Find out how many intervals we don't have data for.
                 // Ideally it should be all or none.
                 nullCount++;
+            } else if (mCurrentStats[i].beginTime > timeNow) {
+                Slog.e(TAG, mLogPrefix + "Interval " + i + " has stat in the future " +
+                        mCurrentStats[i].beginTime);
+                mCurrentStats[i] = null;
+                nullCount++;
             }
         }
 
@@ -94,17 +99,18 @@ class UserUsageStatsService {
             // that is reported.
             mDailyExpiryDate.setTimeInMillis(
                     mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
-            mDailyExpiryDate.add(Calendar.DAY_OF_YEAR, 1);
-            UsageStatsUtils.truncateDateTo(UsageStatsManager.INTERVAL_DAILY, mDailyExpiryDate);
-            Slog.i(TAG, mLogPrefix + "Rollover scheduled for "
-                    + sDateFormat.format(mDailyExpiryDate.getTime()));
+            mDailyExpiryDate.addDays(1);
+            mDailyExpiryDate.truncateToDay();
+            Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
+                    sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) +
+                    "(" + mDailyExpiryDate.getTimeInMillis() + ")");
         }
 
         // Now close off any events that were open at the time this was saved.
         for (IntervalStats stat : mCurrentStats) {
-            final int pkgCount = stat.stats.size();
+            final int pkgCount = stat.packageStats.size();
             for (int i = 0; i < pkgCount; i++) {
-                UsageStats pkgStats = stat.stats.valueAt(i);
+                UsageStats pkgStats = stat.packageStats.valueAt(i);
                 if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
                         pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
                     stat.update(pkgStats.mPackageName, stat.lastTimeSaved,
@@ -162,13 +168,13 @@ class UserUsageStatsService {
                 public void combine(IntervalStats stats, boolean mutable,
                         List<UsageStats> accResult) {
                     if (!mutable) {
-                        accResult.addAll(stats.stats.values());
+                        accResult.addAll(stats.packageStats.values());
                         return;
                     }
 
-                    final int statCount = stats.stats.size();
+                    final int statCount = stats.packageStats.size();
                     for (int i = 0; i < statCount; i++) {
-                        accResult.add(new UsageStats(stats.stats.valueAt(i)));
+                        accResult.add(new UsageStats(stats.packageStats.valueAt(i)));
                     }
                 }
             };
@@ -195,49 +201,68 @@ class UserUsageStatsService {
      * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
      * provided to select the stats to use from the IntervalStats object.
      */
-    private <T> List<T> queryStats(int bucketType, long beginTime, long endTime,
+    private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
             StatCombiner<T> combiner) {
-        if (bucketType == UsageStatsManager.INTERVAL_BEST) {
-            bucketType = mDatabase.findBestFitBucket(beginTime, endTime);
+        if (intervalType == UsageStatsManager.INTERVAL_BEST) {
+            intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
+            if (intervalType < 0) {
+                // Nothing saved to disk yet, so every stat is just as equal (no rollover has
+                // occurred.
+                intervalType = UsageStatsManager.INTERVAL_DAILY;
+            }
         }
 
-        if (bucketType < 0 || bucketType >= mCurrentStats.length) {
+        if (intervalType < 0 || intervalType >= mCurrentStats.length) {
             if (DEBUG) {
-                Slog.d(TAG, mLogPrefix + "Bad bucketType used " + bucketType);
+                Slog.d(TAG, mLogPrefix + "Bad intervalType used " + intervalType);
             }
             return null;
         }
 
-        if (beginTime >= mCurrentStats[bucketType].endTime) {
+        final IntervalStats currentStats = mCurrentStats[intervalType];
+
+        if (DEBUG) {
+            Slog.d(TAG, mLogPrefix + "SELECT * FROM " + intervalType + " WHERE beginTime >= "
+                    + beginTime + " AND endTime < " + endTime);
+        }
+
+        if (beginTime >= currentStats.endTime) {
             if (DEBUG) {
                 Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
-                        + mCurrentStats[bucketType].endTime);
+                        + currentStats.endTime);
             }
             // Nothing newer available.
             return null;
-
-        } else if (beginTime >= mCurrentStats[bucketType].beginTime) {
-            if (DEBUG) {
-                Slog.d(TAG, mLogPrefix + "Returning in-memory stats for bucket " + bucketType);
-            }
-            // Fast path for retrieving in-memory state.
-            ArrayList<T> results = new ArrayList<>();
-            combiner.combine(mCurrentStats[bucketType], true, results);
-            return results;
         }
 
-        // Flush any changes that were made to disk before we do a disk query.
-        // If we're not grabbing the ongoing stats, no need to persist.
-        persistActiveStats();
+        // Truncate the endTime to just before the in-memory stats. Then, we'll append the
+        // in-memory stats to the results (if necessary) so as to avoid writing to disk too
+        // often.
+        final long truncatedEndTime = Math.min(currentStats.beginTime, endTime);
 
+        // Get the stats from disk.
+        List<T> results = mDatabase.queryUsageStats(intervalType, beginTime,
+                truncatedEndTime, combiner);
         if (DEBUG) {
-            Slog.d(TAG, mLogPrefix + "SELECT * FROM " + bucketType + " WHERE beginTime >= "
-                    + beginTime + " AND endTime < " + endTime);
+            Slog.d(TAG, "Got " + (results != null ? results.size() : 0) + " results from disk");
+            Slog.d(TAG, "Current stats beginTime=" + currentStats.beginTime +
+                    " endTime=" + currentStats.endTime);
+        }
+
+        // Now check if the in-memory stats match the range and add them if they do.
+        if (beginTime < currentStats.endTime && endTime > currentStats.beginTime) {
+            if (DEBUG) {
+                Slog.d(TAG, mLogPrefix + "Returning in-memory stats");
+            }
+
+            if (results == null) {
+                results = new ArrayList<>();
+            }
+            combiner.combine(currentStats, true, results);
         }
 
-        final List<T> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime, combiner);
         if (DEBUG) {
-            Slog.d(TAG, mLogPrefix + "Results: " + (results == null ? 0 : results.size()));
+            Slog.d(TAG, mLogPrefix + "Results: " + (results != null ? results.size() : 0));
         }
         return results;
     }
@@ -250,44 +275,45 @@ class UserUsageStatsService {
         return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
     }
 
-    UsageEvents queryEvents(long beginTime, long endTime) {
-        if (endTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime) {
-            if (beginTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].endTime) {
-                return null;
-            }
-
-            TimeSparseArray<UsageEvents.Event> events =
-                    mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events;
-            if (events == null) {
-                return null;
-            }
-
-            final int startIndex = events.closestIndexOnOrAfter(beginTime);
-            if (startIndex < 0) {
-                return null;
-            }
+    UsageEvents queryEvents(final long beginTime, final long endTime) {
+        final ArraySet<String> names = new ArraySet<>();
+        List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
+                beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
+                    @Override
+                    public void combine(IntervalStats stats, boolean mutable,
+                            List<UsageEvents.Event> accumulatedResult) {
+                        if (stats.events == null) {
+                            return;
+                        }
+
+                        final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
+                        if (startIndex < 0) {
+                            return;
+                        }
+
+                        final int size = stats.events.size();
+                        for (int i = startIndex; i < size; i++) {
+                            if (stats.events.keyAt(i) >= endTime) {
+                                return;
+                            }
+
+                            final UsageEvents.Event event = stats.events.valueAt(i);
+                            names.add(event.mPackage);
+                            if (event.mClass != null) {
+                                names.add(event.mClass);
+                            }
+                            accumulatedResult.add(event);
+                        }
+                    }
+                });
 
-            ArraySet<String> names = new ArraySet<>();
-            ArrayList<UsageEvents.Event> results = new ArrayList<>();
-            final int size = events.size();
-            for (int i = startIndex; i < size; i++) {
-                if (events.keyAt(i) >= endTime) {
-                    break;
-                }
-                final UsageEvents.Event event = events.valueAt(i);
-                names.add(event.mPackage);
-                if (event.mClass != null) {
-                    names.add(event.mClass);
-                }
-                results.add(event);
-            }
-            String[] table = names.toArray(new String[names.size()]);
-            Arrays.sort(table);
-            return new UsageEvents(results, table);
+        if (results == null || results.isEmpty()) {
+            return null;
         }
 
-        // TODO(adamlesinski): Query the previous days.
-        return null;
+        String[] table = names.toArray(new String[names.size()]);
+        Arrays.sort(table);
+        return new UsageEvents(results, table);
     }
 
     void persistActiveStats() {
@@ -314,9 +340,9 @@ class UserUsageStatsService {
                 mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
         ArraySet<String> continuePreviousDay = new ArraySet<>();
         for (IntervalStats stat : mCurrentStats) {
-            final int pkgCount = stat.stats.size();
+            final int pkgCount = stat.packageStats.size();
             for (int i = 0; i < pkgCount; i++) {
-                UsageStats pkgStats = stat.stats.valueAt(i);
+                UsageStats pkgStats = stat.packageStats.valueAt(i);
                 if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
                         pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
                     continuePreviousDay.add(pkgStats.mPackageName);
@@ -360,54 +386,53 @@ class UserUsageStatsService {
     private void loadActiveStats() {
         final long timeNow = System.currentTimeMillis();
 
-        Calendar tempCal = mDailyExpiryDate;
-        for (int bucketType = 0; bucketType < mCurrentStats.length; bucketType++) {
+        final UnixCalendar tempCal = mDailyExpiryDate;
+        for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
             tempCal.setTimeInMillis(timeNow);
-            UsageStatsUtils.truncateDateTo(bucketType, tempCal);
+            UnixCalendar.truncateTo(tempCal, intervalType);
 
-            if (mCurrentStats[bucketType] != null &&
-                    mCurrentStats[bucketType].beginTime == tempCal.getTimeInMillis()) {
+            if (mCurrentStats[intervalType] != null &&
+                    mCurrentStats[intervalType].beginTime == tempCal.getTimeInMillis()) {
                 // These are the same, no need to load them (in memory stats are always newer
                 // than persisted stats).
                 continue;
             }
 
-            final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(bucketType);
-            if (lastBeginTime >= tempCal.getTimeInMillis()) {
-                if (DEBUG) {
-                    Slog.d(TAG, mLogPrefix + "Loading existing stats (" + lastBeginTime +
-                            ") for bucket " + bucketType);
-                }
-                mCurrentStats[bucketType] = mDatabase.getLatestUsageStats(bucketType);
+            final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(intervalType);
+            if (lastBeginTime > timeNow) {
+                Slog.e(TAG, mLogPrefix + "Latest usage stats for interval " +
+                        intervalType + " begins in the future");
+                mCurrentStats[intervalType] = null;
+            } else if (lastBeginTime >= tempCal.getTimeInMillis()) {
                 if (DEBUG) {
-                    if (mCurrentStats[bucketType] != null) {
-                        Slog.d(TAG, mLogPrefix + "Found " +
-                                (mCurrentStats[bucketType].events == null ?
-                                        0 : mCurrentStats[bucketType].events.size()) +
-                                " events");
-                    }
+                    Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
+                            sDateFormat.format(lastBeginTime) + "(" + lastBeginTime +
+                            ") for interval " + intervalType);
                 }
+                mCurrentStats[intervalType] = mDatabase.getLatestUsageStats(intervalType);
             } else {
-                mCurrentStats[bucketType] = null;
+                mCurrentStats[intervalType] = null;
             }
 
-            if (mCurrentStats[bucketType] == null) {
+            if (mCurrentStats[intervalType] == null) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Creating new stats (" + tempCal.getTimeInMillis() +
-                            ") for bucket " + bucketType);
+                    Slog.d(TAG, "Creating new stats @ " +
+                            sDateFormat.format(tempCal.getTimeInMillis()) + "(" +
+                            tempCal.getTimeInMillis() + ") for interval " + intervalType);
 
                 }
-                mCurrentStats[bucketType] = new IntervalStats();
-                mCurrentStats[bucketType].beginTime = tempCal.getTimeInMillis();
-                mCurrentStats[bucketType].endTime = timeNow;
+                mCurrentStats[intervalType] = new IntervalStats();
+                mCurrentStats[intervalType].beginTime = tempCal.getTimeInMillis();
+                mCurrentStats[intervalType].endTime = timeNow;
             }
         }
         mStatsChanged = false;
         mDailyExpiryDate.setTimeInMillis(timeNow);
-        mDailyExpiryDate.add(Calendar.DAY_OF_YEAR, 1);
-        UsageStatsUtils.truncateDateTo(UsageStatsManager.INTERVAL_DAILY, mDailyExpiryDate);
-        Slog.i(TAG, mLogPrefix + "Rollover scheduled for "
-                + sDateFormat.format(mDailyExpiryDate.getTime()));
+        mDailyExpiryDate.addDays(1);
+        mDailyExpiryDate.truncateToDay();
+        Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
+                sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
+                tempCal.getTimeInMillis() + ")");
     }
 
 
index a5fda79..314704b 100644 (file)
@@ -34,7 +34,7 @@ public final class AudioState implements Parcelable {
     /** Direct the audio stream through a wired headset. */
     public static final int ROUTE_WIRED_HEADSET = 0x00000004;
 
-    /** Direct the audio stream through the device's spakerphone. */
+    /** Direct the audio stream through the device's speakerphone. */
     public static final int ROUTE_SPEAKER       = 0x00000008;
 
     /**
@@ -43,7 +43,10 @@ public final class AudioState implements Parcelable {
      */
     public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
 
-    /** Bit mask of all possible audio routes. */
+    /** Bit mask of all possible audio routes.
+     *
+     * @hide
+     */
     public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
             ROUTE_SPEAKER;
 
index 5bf0855..c3aa2a0 100644 (file)
@@ -517,6 +517,20 @@ public final class Call {
     }
 
     /**
+     * Merges the calls within this conference. See {@link PhoneCapabilities#MERGE_CONFERENCE}.
+     */
+    public void mergeConference() {
+        mInCallAdapter.mergeConference(mTelecommCallId);
+    }
+
+    /**
+     * Swaps the calls within this conference. See {@link PhoneCapabilities#SWAP_CONFERENCE}.
+     */
+    public void swapConference() {
+        mInCallAdapter.swapConference(mTelecommCallId);
+    }
+
+    /**
      * Obtains the parent of this {@code Call} in a conference, if any.
      *
      * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
index 879ff66..f9c3ac3 100644 (file)
@@ -54,7 +54,7 @@ public abstract class Conference {
         mPhoneAccount = phoneAccount;
     }
 
-    public final PhoneAccountHandle getPhoneAccount() {
+    public final PhoneAccountHandle getPhoneAccountHandle() {
         return mPhoneAccount;
     }
 
@@ -93,6 +93,18 @@ public abstract class Conference {
     public void onUnhold() {}
 
     /**
+     * Invoked when the child calls should be merged. Only invoked if the conference contains the
+     * capability {@link PhoneCapabilities#MERGE_CONFERENCE}.
+     */
+    public void onMerge() {}
+
+    /**
+     * Invoked when the child calls should be swapped. Only invoked if the conference contains the
+     * capability {@link PhoneCapabilities#SWAP_CONFERENCE}.
+     */
+    public void onSwap() {}
+
+    /**
      * Sets state to be on hold.
      */
     public final void setOnHold() {
@@ -141,7 +153,7 @@ public abstract class Conference {
      * @param connection The connection to add.
      * @return True if the connection was successfully added.
      */
-    public boolean addConnection(Connection connection) {
+    public final boolean addConnection(Connection connection) {
         if (connection != null && !mChildConnections.contains(connection)) {
             if (connection.setConference(this)) {
                 mChildConnections.add(connection);
@@ -160,7 +172,7 @@ public abstract class Conference {
      * @param connection The connection to remove.
      * @return True if the connection was successfully removed.
      */
-    public void removeConnection(Connection connection) {
+    public final void removeConnection(Connection connection) {
         Log.d(this, "removing %s from %s", connection, mChildConnections);
         if (connection != null && mChildConnections.remove(connection)) {
             connection.resetConference();
@@ -171,9 +183,9 @@ public abstract class Conference {
     }
 
     /**
-     * Tears down the conference object and any of it's current connections.
+     * Tears down the conference object and any of its current connections.
      */
-    public void destroy() {
+    public final void destroy() {
         Log.d(this, "destroying conference : %s", this);
         // Tear down the children.
         for (Connection connection : mChildConnections) {
index 4d6e267..03db1c3 100644 (file)
@@ -139,7 +139,7 @@ public abstract class Connection {
          */
         public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
 
-        private static final int MSG_SET_VIDEO_LISTENER = 1;
+        private static final int MSG_SET_VIDEO_CALLBACK = 1;
         private static final int MSG_SET_CAMERA = 2;
         private static final int MSG_SET_PREVIEW_SURFACE = 3;
         private static final int MSG_SET_DISPLAY_SURFACE = 4;
@@ -154,7 +154,7 @@ public abstract class Connection {
         private final VideoProvider.VideoProviderHandler
                 mMessageHandler = new VideoProvider.VideoProviderHandler();
         private final VideoProvider.VideoProviderBinder mBinder;
-        private IVideoCallback mVideoListener;
+        private IVideoCallback mVideoCallback;
 
         /**
          * Default handler used to consolidate binder method calls onto a single thread.
@@ -163,8 +163,8 @@ public abstract class Connection {
             @Override
             public void handleMessage(Message msg) {
                 switch (msg.what) {
-                    case MSG_SET_VIDEO_LISTENER:
-                        mVideoListener = IVideoCallback.Stub.asInterface((IBinder) msg.obj);
+                    case MSG_SET_VIDEO_CALLBACK:
+                        mVideoCallback = IVideoCallback.Stub.asInterface((IBinder) msg.obj);
                         break;
                     case MSG_SET_CAMERA:
                         onSetCamera((String) msg.obj);
@@ -206,9 +206,9 @@ public abstract class Connection {
          * IVideoProvider stub implementation.
          */
         private final class VideoProviderBinder extends IVideoProvider.Stub {
-            public void setVideoListener(IBinder videoListenerBinder) {
+            public void setVideoCallback(IBinder videoCallbackBinder) {
                 mMessageHandler.obtainMessage(
-                        MSG_SET_VIDEO_LISTENER, videoListenerBinder).sendToTarget();
+                        MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
             }
 
             public void setCamera(String cameraId) {
@@ -350,9 +350,9 @@ public abstract class Connection {
          * @param videoProfile The requested video call profile.
          */
         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
-            if (mVideoListener != null) {
+            if (mVideoCallback != null) {
                 try {
-                    mVideoListener.receiveSessionModifyRequest(videoProfile);
+                    mVideoCallback.receiveSessionModifyRequest(videoProfile);
                 } catch (RemoteException ignored) {
                 }
             }
@@ -370,9 +370,9 @@ public abstract class Connection {
          */
         public void receiveSessionModifyResponse(int status,
                 VideoProfile requestedProfile, VideoProfile responseProfile) {
-            if (mVideoListener != null) {
+            if (mVideoCallback != null) {
                 try {
-                    mVideoListener.receiveSessionModifyResponse(
+                    mVideoCallback.receiveSessionModifyResponse(
                             status, requestedProfile, responseProfile);
                 } catch (RemoteException ignored) {
                 }
@@ -390,9 +390,9 @@ public abstract class Connection {
          * @param event The event.
          */
         public void handleCallSessionEvent(int event) {
-            if (mVideoListener != null) {
+            if (mVideoCallback != null) {
                 try {
-                    mVideoListener.handleCallSessionEvent(event);
+                    mVideoCallback.handleCallSessionEvent(event);
                 } catch (RemoteException ignored) {
                 }
             }
@@ -405,9 +405,9 @@ public abstract class Connection {
          * @param height The updated peer video height.
          */
         public void changePeerDimensions(int width, int height) {
-            if (mVideoListener != null) {
+            if (mVideoCallback != null) {
                 try {
-                    mVideoListener.changePeerDimensions(width, height);
+                    mVideoCallback.changePeerDimensions(width, height);
                 } catch (RemoteException ignored) {
                 }
             }
@@ -419,9 +419,9 @@ public abstract class Connection {
          * @param dataUsage The updated data usage.
          */
         public void changeCallDataUsage(int dataUsage) {
-            if (mVideoListener != null) {
+            if (mVideoCallback != null) {
                 try {
-                    mVideoListener.changeCallDataUsage(dataUsage);
+                    mVideoCallback.changeCallDataUsage(dataUsage);
                 } catch (RemoteException ignored) {
                 }
             }
@@ -433,9 +433,9 @@ public abstract class Connection {
          * @param cameraCapabilities The changed camera capabilities.
          */
         public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
-            if (mVideoListener != null) {
+            if (mVideoCallback != null) {
                 try {
-                    mVideoListener.changeCameraCapabilities(cameraCapabilities);
+                    mVideoCallback.changeCameraCapabilities(cameraCapabilities);
                 } catch (RemoteException ignored) {
                 }
             }
@@ -1068,10 +1068,10 @@ public abstract class Connection {
         if (mState != state) {
             Log.d(this, "setState: %s", stateToString(state));
             mState = state;
+            onSetState(state);
             for (Listener l : mListeners) {
                 l.onStateChanged(this, state);
             }
-            onSetState(state);
         }
     }
 
index 39ae59a..d5a6aa5 100644 (file)
@@ -29,31 +29,25 @@ public final class ConnectionRequest implements Parcelable {
 
     // TODO: Token to limit recursive invocations
     private final PhoneAccountHandle mAccountHandle;
-    private final Uri mHandle;
-    private final int mHandlePresentation;
+    private final Uri mAddress;
     private final Bundle mExtras;
     private final int mVideoState;
 
     /**
      * @param accountHandle The accountHandle which should be used to place the call.
      * @param handle The handle (e.g., phone number) to which the {@link Connection} is to connect.
-     * @param handlePresentation The {@link PropertyPresentation} which controls how the handle
-     *         is shown.
      * @param extras Application-specific extra data.
      */
     public ConnectionRequest(
             PhoneAccountHandle accountHandle,
             Uri handle,
-            int handlePresentation,
             Bundle extras) {
-        this(accountHandle, handle, handlePresentation, extras, VideoProfile.VideoState.AUDIO_ONLY);
+        this(accountHandle, handle, extras, VideoProfile.VideoState.AUDIO_ONLY);
     }
 
     /**
      * @param accountHandle The accountHandle which should be used to place the call.
      * @param handle The handle (e.g., phone number) to which the {@link Connection} is to connect.
-     * @param handlePresentation The {@link PropertyPresentation} which controls how the handle
-     *         is shown.
      * @param extras Application-specific extra data.
      * @param videoState Determines the video state for the connection.
      * @hide
@@ -61,20 +55,17 @@ public final class ConnectionRequest implements Parcelable {
     public ConnectionRequest(
             PhoneAccountHandle accountHandle,
             Uri handle,
-            int handlePresentation,
             Bundle extras,
             int videoState) {
         mAccountHandle = accountHandle;
-        mHandle = handle;
-        mHandlePresentation = handlePresentation;
+        mAddress = handle;
         mExtras = extras;
         mVideoState = videoState;
     }
 
     private ConnectionRequest(Parcel in) {
         mAccountHandle = in.readParcelable(getClass().getClassLoader());
-        mHandle = in.readParcelable(getClass().getClassLoader());
-        mHandlePresentation = in.readInt();
+        mAddress = in.readParcelable(getClass().getClassLoader());
         mExtras = in.readParcelable(getClass().getClassLoader());
         mVideoState = in.readInt();
     }
@@ -87,12 +78,7 @@ public final class ConnectionRequest implements Parcelable {
     /**
      * The handle (e.g., phone number) to which the {@link Connection} is to connect.
      */
-    public Uri getHandle() { return mHandle; }
-
-    /**
-     * The {@link PropertyPresentation} which controls how the handle is shown.
-     */
-    public int getHandlePresentation() { return mHandlePresentation; }
+    public Uri getAddress() { return mAddress; }
 
     /**
      * Application-specific extra data. Used for passing back information from an incoming
@@ -118,9 +104,9 @@ public final class ConnectionRequest implements Parcelable {
     @Override
     public String toString() {
         return String.format("ConnectionRequest %s %s",
-                mHandle == null
+                mAddress == null
                         ? Uri.EMPTY
-                        : Connection.toLogSafePhoneNumber(mHandle.toString()),
+                        : Connection.toLogSafePhoneNumber(mAddress.toString()),
                 mExtras == null ? "" : mExtras);
     }
 
@@ -147,8 +133,7 @@ public final class ConnectionRequest implements Parcelable {
     @Override
     public void writeToParcel(Parcel destination, int flags) {
         destination.writeParcelable(mAccountHandle, 0);
-        destination.writeParcelable(mHandle, 0);
-        destination.writeInt(mHandlePresentation);
+        destination.writeParcelable(mAddress, 0);
         destination.writeParcelable(mExtras, 0);
         destination.writeInt(mVideoState);
     }
index 2dc6910..39365b6 100644 (file)
@@ -72,6 +72,8 @@ public abstract class ConnectionService extends Service {
     private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 15;
     private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16;
     private static final int MSG_ANSWER_VIDEO = 17;
+    private static final int MSG_MERGE_CONFERENCE = 18;
+    private static final int MSG_SWAP_CONFERENCE = 19;
 
     private static Connection sNullConnection;
 
@@ -182,6 +184,16 @@ public abstract class ConnectionService extends Service {
         }
 
         @Override
+        public void mergeConference(String callId) {
+            mHandler.obtainMessage(MSG_MERGE_CONFERENCE, callId).sendToTarget();
+        }
+
+        @Override
+        public void swapConference(String callId) {
+            mHandler.obtainMessage(MSG_SWAP_CONFERENCE, callId).sendToTarget();
+        }
+
+        @Override
         public void onPostDialContinue(String callId, boolean proceed) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = callId;
@@ -298,6 +310,12 @@ public abstract class ConnectionService extends Service {
                 case MSG_SPLIT_FROM_CONFERENCE:
                     splitFromConference((String) msg.obj);
                     break;
+                case MSG_MERGE_CONFERENCE:
+                    mergeConference((String) msg.obj);
+                    break;
+                case MSG_SWAP_CONFERENCE:
+                    swapConference((String) msg.obj);
+                    break;
                 case MSG_ON_POST_DIAL_CONTINUE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
@@ -639,6 +657,22 @@ public abstract class ConnectionService extends Service {
         }
     }
 
+    private void mergeConference(String callId) {
+        Log.d(this, "mergeConference(%s)", callId);
+        Conference conference = findConferenceForAction(callId, "mergeConference");
+        if (conference != null) {
+            conference.onMerge();
+        }
+    }
+
+    private void swapConference(String callId) {
+        Log.d(this, "swapConference(%s)", callId);
+        Conference conference = findConferenceForAction(callId, "swapConference");
+        if (conference != null) {
+            conference.onSwap();
+        }
+    }
+
     private void onPostDialContinue(String callId, boolean proceed) {
         Log.d(this, "onPostDialContinue(%s)", callId);
         findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
@@ -747,7 +781,7 @@ public abstract class ConnectionService extends Service {
                 }
             }
             ParcelableConference parcelableConference = new ParcelableConference(
-                    conference.getPhoneAccount(),
+                    conference.getPhoneAccountHandle(),
                     conference.getState(),
                     conference.getCapabilities(),
                     connectionIds);
index 80f7b57..96ea5a6 100644 (file)
@@ -241,6 +241,26 @@ public final class InCallAdapter {
     }
 
     /**
+     * Instructs Telecomm to merge child calls of the specified conference call.
+     */
+    public void mergeConference(String callId) {
+        try {
+            mAdapter.mergeConference(callId);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    /**
+     * Instructs Telecomm to swap the child calls of the specified conference call.
+     */
+    public void swapConference(String callId) {
+        try {
+            mAdapter.swapConference(callId);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    /**
      * Instructs Telecomm to turn the proximity sensor on.
      */
     public void turnProximitySensorOn() {
index 1d61a6e..d3da2ec 100644 (file)
@@ -98,8 +98,8 @@ public class PhoneAccount implements Parcelable {
     public static final String SCHEME_SIP = "sip";
 
     private final PhoneAccountHandle mAccountHandle;
-    private final Uri mHandle;
-    private final String mSubscriptionNumber;
+    private final Uri mAddress;
+    private final Uri mSubscriptionAddress;
     private final int mCapabilities;
     private final int mIconResId;
     private final CharSequence mLabel;
@@ -108,47 +108,40 @@ public class PhoneAccount implements Parcelable {
 
     public static class Builder {
         private PhoneAccountHandle mAccountHandle;
-        private Uri mHandle;
-        private String mSubscriptionNumber;
+        private Uri mAddress;
+        private Uri mSubscriptionAddress;
         private int mCapabilities;
         private int mIconResId;
         private CharSequence mLabel;
         private CharSequence mShortDescription;
         private List<String> mSupportedUriSchemes = new ArrayList<String>();
 
-        public Builder() {}
-
-        public Builder withAccountHandle(PhoneAccountHandle value) {
-            this.mAccountHandle = value;
-            return this;
+        public Builder(PhoneAccountHandle accountHandle, CharSequence label) {
+            this.mAccountHandle = accountHandle;
+            this.mLabel = label;
         }
 
-        public Builder withHandle(Uri value) {
-            this.mHandle = value;
+        public Builder setAddress(Uri value) {
+            this.mAddress = value;
             return this;
         }
 
-        public Builder withSubscriptionNumber(String value) {
-            this.mSubscriptionNumber = value;
+        public Builder setSubscriptionAddress(Uri value) {
+            this.mSubscriptionAddress = value;
             return this;
         }
 
-        public Builder withCapabilities(int value) {
+        public Builder setCapabilities(int value) {
             this.mCapabilities = value;
             return this;
         }
 
-        public Builder withIconResId(int value) {
+        public Builder setIconResId(int value) {
             this.mIconResId = value;
             return this;
         }
 
-        public Builder withLabel(CharSequence value) {
-            this.mLabel = value;
-            return this;
-        }
-
-        public Builder withShortDescription(CharSequence value) {
+        public Builder setShortDescription(CharSequence value) {
             this.mShortDescription = value;
             return this;
         }
@@ -158,8 +151,9 @@ public class PhoneAccount implements Parcelable {
          *
          * @param uriScheme The URI scheme.
          * @return The Builder.
+         * @hide
          */
-        public Builder withSupportedUriScheme(String uriScheme) {
+        public Builder addSupportedUriScheme(String uriScheme) {
             if (!TextUtils.isEmpty(uriScheme) && !mSupportedUriSchemes.contains(uriScheme)) {
                 this.mSupportedUriSchemes.add(uriScheme);
             }
@@ -167,15 +161,17 @@ public class PhoneAccount implements Parcelable {
         }
 
         /**
-         * Specifies additional URI schemes supported by the {@link PhoneAccount}.
+         * Specifies the URI schemes supported by the {@link PhoneAccount}.
          *
          * @param uriSchemes The URI schemes.
          * @return The Builder.
          */
-        public Builder withSupportedUriSchemes(List<String> uriSchemes) {
+        public Builder setSupportedUriSchemes(List<String> uriSchemes) {
+            mSupportedUriSchemes.clear();
+
             if (uriSchemes != null && !uriSchemes.isEmpty()) {
                 for (String uriScheme : uriSchemes) {
-                    withSupportedUriScheme(uriScheme);
+                    addSupportedUriScheme(uriScheme);
                 }
             }
             return this;
@@ -184,13 +180,13 @@ public class PhoneAccount implements Parcelable {
         public PhoneAccount build() {
             // If no supported URI schemes were defined, assume "tel" is supported.
             if (mSupportedUriSchemes.isEmpty()) {
-                withSupportedUriScheme(SCHEME_TEL);
+                addSupportedUriScheme(SCHEME_TEL);
             }
 
             return new PhoneAccount(
                     mAccountHandle,
-                    mHandle,
-                    mSubscriptionNumber,
+                    mAddress,
+                    mSubscriptionAddress,
                     mCapabilities,
                     mIconResId,
                     mLabel,
@@ -201,16 +197,16 @@ public class PhoneAccount implements Parcelable {
 
     private PhoneAccount(
             PhoneAccountHandle account,
-            Uri handle,
-            String subscriptionNumber,
+            Uri address,
+            Uri subscriptionAddress,
             int capabilities,
             int iconResId,
             CharSequence label,
             CharSequence shortDescription,
             List<String> supportedUriSchemes) {
         mAccountHandle = account;
-        mHandle = handle;
-        mSubscriptionNumber = subscriptionNumber;
+        mAddress = address;
+        mSubscriptionAddress = subscriptionAddress;
         mCapabilities = capabilities;
         mIconResId = iconResId;
         mLabel = label;
@@ -218,7 +214,11 @@ public class PhoneAccount implements Parcelable {
         mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
     }
 
-    public static Builder builder() { return new Builder(); }
+    public static Builder builder(
+            PhoneAccountHandle accountHandle,
+            CharSequence label) {
+        return new Builder(accountHandle, label);
+    }
 
     /**
      * The unique identifier of this {@code PhoneAccount}.
@@ -230,32 +230,30 @@ public class PhoneAccount implements Parcelable {
     }
 
     /**
-     * The handle (e.g., a phone number) associated with this {@code PhoneAccount}. This
+     * The address (e.g., a phone number) associated with this {@code PhoneAccount}. This
      * represents the destination from which outgoing calls using this {@code PhoneAccount}
      * will appear to come, if applicable, and the destination to which incoming calls using this
      * {@code PhoneAccount} may be addressed.
      *
-     * @return A handle expressed as a {@code Uri}, for example, a phone number.
+     * @return A address expressed as a {@code Uri}, for example, a phone number.
      */
-    public Uri getHandle() {
-        return mHandle;
+    public Uri getAddress() {
+        return mAddress;
     }
 
     /**
      * The raw callback number used for this {@code PhoneAccount}, as distinct from
-     * {@link #getHandle()}. For the majority of {@code PhoneAccount}s this should be registered
+     * {@link #getAddress()}. For the majority of {@code PhoneAccount}s this should be registered
      * as {@code null}.  It is used by the system for SIM-based {@code PhoneAccount} registration
      * where {@link android.telephony.TelephonyManager#setLine1NumberForDisplay(String, String)}
      * or {@link android.telephony.TelephonyManager#setLine1NumberForDisplay(long, String, String)}
      * has been used to alter the callback number.
      * <p>
-     * TODO: Should this also be a URI, for consistency? Should it be called the
-     * "subscription handle"?
      *
      * @return The subscription number, suitable for display to the user.
      */
-    public String getSubscriptionNumber() {
-        return mSubscriptionNumber;
+    public Uri getSubscriptionAddress() {
+        return mSubscriptionAddress;
     }
 
     /**
@@ -295,11 +293,11 @@ public class PhoneAccount implements Parcelable {
     }
 
     /**
-     * Determines if the {@link PhoneAccount} supports calls to/from handles with a specified URI
+     * Determines if the {@link PhoneAccount} supports calls to/from addresses with a specified URI
      * scheme.
      *
      * @param uriScheme The URI scheme to check.
-     * @return {@code True} if the {@code PhoneAccount} supports calls to/from handles with the
+     * @return {@code True} if the {@code PhoneAccount} supports calls to/from addresses with the
      * specified URI scheme.
      */
     public boolean supportsUriScheme(String uriScheme) {
@@ -363,8 +361,8 @@ public class PhoneAccount implements Parcelable {
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeParcelable(mAccountHandle, 0);
-        out.writeParcelable(mHandle, 0);
-        out.writeString(mSubscriptionNumber);
+        out.writeParcelable(mAddress, 0);
+        out.writeParcelable(mSubscriptionAddress, 0);
         out.writeInt(mCapabilities);
         out.writeInt(mIconResId);
         out.writeCharSequence(mLabel);
@@ -389,8 +387,8 @@ public class PhoneAccount implements Parcelable {
         ClassLoader classLoader = PhoneAccount.class.getClassLoader();
 
         mAccountHandle = in.readParcelable(getClass().getClassLoader());
-        mHandle = in.readParcelable(getClass().getClassLoader());
-        mSubscriptionNumber = in.readString();
+        mAddress = in.readParcelable(getClass().getClassLoader());
+        mSubscriptionAddress = in.readParcelable(getClass().getClassLoader());
         mCapabilities = in.readInt();
         mIconResId = in.readInt();
         mLabel = in.readCharSequence();
index 3d76608..7a338b4 100644 (file)
@@ -27,11 +27,17 @@ public final class PhoneCapabilities {
     /** Call supports the hold feature. */
     public static final int SUPPORT_HOLD       = 0x00000002;
 
-    /** Call can currently be merged. */
-    public static final int MERGE_CALLS        = 0x00000004;
+    /**
+     * Calls within a conference can be merged. Some connection services create a conference call
+     * only after two calls have been merged.  However, a conference call can also be added the
+     * moment there are more than one call. CDMA calls are implemented in this way because the call
+     * actions are more limited when more than one call exists. This flag allows merge to be exposed
+     * as a capability on the conference call instead of individual calls.
+     */
+    public static final int MERGE_CONFERENCE   = 0x00000004;
 
-    /** Call can currently be swapped with another call. */
-    public static final int SWAP_CALLS         = 0x00000008;
+    /** Calls withing a conference can be swapped between foreground and background. */
+    public static final int SWAP_CONFERENCE    = 0x00000008;
 
     /** Call currently supports adding another call to this one. */
     public static final int ADD_CALL           = 0x00000010;
@@ -42,8 +48,11 @@ public final class PhoneCapabilities {
     /** Call can be muted. */
     public static final int MUTE               = 0x00000040;
 
-    /** Call supports generic conference mode. */
-    public static final int GENERIC_CONFERENCE = 0x00000080;
+    /**
+     * Call supports conference call management. This capability only applies to conference calls
+     * which can have other calls as children.
+     */
+    public static final int MANAGE_CONFERENCE = 0x00000080;
 
     /**
      * Local device supports video telephony.
@@ -69,8 +78,8 @@ public final class PhoneCapabilities {
      */
     public static final int VoWIFI = 0x00000800;
 
-    public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL
-            | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE;
+    public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CONFERENCE | SWAP_CONFERENCE | ADD_CALL
+            | RESPOND_VIA_TEXT | MUTE | MANAGE_CONFERENCE;
 
     public static String toString(int capabilities) {
         StringBuilder builder = new StringBuilder();
@@ -81,11 +90,11 @@ public final class PhoneCapabilities {
         if ((capabilities & SUPPORT_HOLD) != 0) {
             builder.append(" SUPPORT_HOLD");
         }
-        if ((capabilities & MERGE_CALLS) != 0) {
-            builder.append(" MERGE_CALLS");
+        if ((capabilities & MERGE_CONFERENCE) != 0) {
+            builder.append(" MERGE_CONFERENCE");
         }
-        if ((capabilities & SWAP_CALLS) != 0) {
-            builder.append(" SWAP_CALLS");
+        if ((capabilities & SWAP_CONFERENCE) != 0) {
+            builder.append(" SWAP_CONFERENCE");
         }
         if ((capabilities & ADD_CALL) != 0) {
             builder.append(" ADD_CALL");
@@ -96,8 +105,8 @@ public final class PhoneCapabilities {
         if ((capabilities & MUTE) != 0) {
             builder.append(" MUTE");
         }
-        if ((capabilities & GENERIC_CONFERENCE) != 0) {
-            builder.append(" GENERIC_CONFERENCE");
+        if ((capabilities & MANAGE_CONFERENCE) != 0) {
+            builder.append(" MANAGE_CONFERENCE");
         }
         if ((capabilities & SUPPORTS_VT_LOCAL) != 0) {
             builder.append(" SUPPORTS_VT_LOCAL");
index 02f6de6..b073827 100644 (file)
@@ -32,7 +32,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
  */
 public final class RemoteConference {
 
-    public abstract static class Listener {
+    public abstract static class Callback {
         public void onStateChanged(RemoteConference conference, int oldState, int newState) {}
         public void onDisconnected(RemoteConference conference, int cause, String message) {}
         public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {}
@@ -44,7 +44,7 @@ public final class RemoteConference {
     private final String mId;
     private final IConnectionService mConnectionService;
 
-    private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
+    private final Set<Callback> mCallbacks = new CopyOnWriteArraySet<>();
     private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>();
     private final List<RemoteConnection> mUnmodifiableChildConnections =
             Collections.unmodifiableList(mChildConnections);
@@ -70,8 +70,8 @@ public final class RemoteConference {
         for (RemoteConnection connection : mChildConnections) {
             connection.setConference(null);
         }
-        for (Listener l : mListeners) {
-            l.onDestroyed(this);
+        for (Callback c : mCallbacks) {
+            c.onDestroyed(this);
         }
     }
 
@@ -88,8 +88,8 @@ public final class RemoteConference {
         if (mState != newState) {
             int oldState = mState;
             mState = newState;
-            for (Listener l : mListeners) {
-                l.onStateChanged(this, oldState, newState);
+            for (Callback c : mCallbacks) {
+                c.onStateChanged(this, oldState, newState);
             }
         }
     }
@@ -99,8 +99,8 @@ public final class RemoteConference {
         if (!mChildConnections.contains(connection)) {
             mChildConnections.add(connection);
             connection.setConference(this);
-            for (Listener l : mListeners) {
-                l.onConnectionAdded(this, connection);
+            for (Callback c : mCallbacks) {
+                c.onConnectionAdded(this, connection);
             }
         }
     }
@@ -110,8 +110,8 @@ public final class RemoteConference {
         if (mChildConnections.contains(connection)) {
             mChildConnections.remove(connection);
             connection.setConference(null);
-            for (Listener l : mListeners) {
-                l.onConnectionRemoved(this, connection);
+            for (Callback c : mCallbacks) {
+                c.onConnectionRemoved(this, connection);
             }
         }
     }
@@ -120,8 +120,8 @@ public final class RemoteConference {
     void setCallCapabilities(int capabilities) {
         if (mCallCapabilities != capabilities) {
             mCallCapabilities = capabilities;
-            for (Listener l : mListeners) {
-                l.onCapabilitiesChanged(this, mCallCapabilities);
+            for (Callback c : mCallbacks) {
+                c.onCapabilitiesChanged(this, mCallCapabilities);
             }
         }
     }
@@ -132,8 +132,8 @@ public final class RemoteConference {
             mDisconnectCause = cause;
             mDisconnectMessage = message;
             setState(Connection.STATE_DISCONNECTED);
-            for (Listener l : mListeners) {
-                l.onDisconnected(this, cause, message);
+            for (Callback c : mCallbacks) {
+                c.onDisconnected(this, cause, message);
             }
         }
     }
@@ -188,11 +188,11 @@ public final class RemoteConference {
         return mDisconnectMessage;
     }
 
-    public final void addListener(Listener listener) {
-        mListeners.add(listener);
+    public final void addCallback(Callback callback) {
+        mCallbacks.add(callback);
     }
 
-    public final void removeListener(Listener listener) {
-        mListeners.remove(listener);
+    public final void removeCallback(Callback callback) {
+        mCallbacks.remove(callback);
     }
 }
index 31afb4b..8ad8d19 100644 (file)
 package android.telecomm;
 
 import com.android.internal.telecomm.IConnectionService;
+import com.android.internal.telecomm.IVideoCallback;
+import com.android.internal.telecomm.IVideoProvider;
 
 import android.app.PendingIntent;
 import android.net.Uri;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.DisconnectCause;
+import android.view.Surface;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -183,6 +186,18 @@ public final class RemoteConnection {
                 List<RemoteConnection> conferenceableConnections) {}
 
         /**
+         * Indicates that the {@code VideoProvider} associated with this {@code RemoteConnection}
+         * has changed.
+         *
+         * @param connection The {@code RemoteConnection} invoking this method.
+         * @param videoProvider The new {@code VideoProvider} associated with this
+         *         {@code RemoteConnection}.
+         * @hide
+         */
+        public void onVideoProviderChanged(
+                RemoteConnection connection, VideoProvider videoProvider) {}
+
+        /**
          * Indicates that the {@code RemoteConference} that this {@code RemoteConnection} is a part
          * of has changed.
          *
@@ -195,6 +210,185 @@ public final class RemoteConnection {
                 RemoteConference conference) {}
     }
 
+    /** {@hide} */
+    public static class VideoProvider {
+
+        public abstract static class Listener {
+            public void onReceiveSessionModifyRequest(
+                    VideoProvider videoProvider,
+                    VideoProfile videoProfile) {}
+
+            public void onReceiveSessionModifyResponse(
+                    VideoProvider videoProvider,
+                    int status,
+                    VideoProfile requestedProfile,
+                    VideoProfile responseProfile) {}
+
+            public void onHandleCallSessionEvent(VideoProvider videoProvider, int event) {}
+
+            public void onPeerDimensionsChanged(VideoProvider videoProvider, int width, int height) {}
+
+            public void onCallDataUsageChanged(VideoProvider videoProvider, int dataUsage) {}
+
+            public void onCameraCapabilitiesChanged(
+                    VideoProvider videoProvider,
+                    CameraCapabilities cameraCapabilities) {}
+        }
+
+        private final IVideoCallback mVideoCallbackDelegate = new IVideoCallback() {
+            @Override
+            public void receiveSessionModifyRequest(VideoProfile videoProfile) {
+                for (Listener l : mListeners) {
+                    l.onReceiveSessionModifyRequest(VideoProvider.this, videoProfile);
+                }
+            }
+
+            @Override
+            public void receiveSessionModifyResponse(int status, VideoProfile requestedProfile,
+                    VideoProfile responseProfile) {
+                for (Listener l : mListeners) {
+                    l.onReceiveSessionModifyResponse(
+                            VideoProvider.this,
+                            status,
+                            requestedProfile,
+                            responseProfile);
+                }
+            }
+
+            @Override
+            public void handleCallSessionEvent(int event) {
+                for (Listener l : mListeners) {
+                    l.onHandleCallSessionEvent(VideoProvider.this, event);
+                }
+            }
+
+            @Override
+            public void changePeerDimensions(int width, int height) {
+                for (Listener l : mListeners) {
+                    l.onPeerDimensionsChanged(VideoProvider.this, width, height);
+                }
+            }
+
+            @Override
+            public void changeCallDataUsage(int dataUsage) {
+                for (Listener l : mListeners) {
+                    l.onCallDataUsageChanged(VideoProvider.this, dataUsage);
+                }
+            }
+
+            @Override
+            public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
+                for (Listener l : mListeners) {
+                    l.onCameraCapabilitiesChanged(VideoProvider.this, cameraCapabilities);
+                }
+            }
+
+            @Override
+            public IBinder asBinder() {
+                return null;
+            }
+        };
+
+        private final VideoCallbackServant mVideoCallbackServant =
+                new VideoCallbackServant(mVideoCallbackDelegate);
+
+        private final IVideoProvider mVideoProviderBinder;
+
+        /**
+         * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+         * load factor before resizing, 1 means we only expect a single thread to
+         * access the map so make only a single shard
+         */
+        private final Set<Listener> mListeners = Collections.newSetFromMap(
+                new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
+
+        public VideoProvider(IVideoProvider videoProviderBinder) {
+            mVideoProviderBinder = videoProviderBinder;
+            try {
+                mVideoProviderBinder.setVideoCallback(mVideoCallbackServant.getStub().asBinder());
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void addListener(Listener l) {
+            mListeners.add(l);
+        }
+
+        public void removeListener(Listener l) {
+            mListeners.remove(l);
+        }
+
+        public void setCamera(String cameraId) {
+            try {
+                mVideoProviderBinder.setCamera(cameraId);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void setPreviewSurface(Surface surface) {
+            try {
+                mVideoProviderBinder.setPreviewSurface(surface);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void setDisplaySurface(Surface surface) {
+            try {
+                mVideoProviderBinder.setDisplaySurface(surface);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void setDeviceOrientation(int rotation) {
+            try {
+                mVideoProviderBinder.setDeviceOrientation(rotation);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void setZoom(float value) {
+            try {
+                mVideoProviderBinder.setZoom(value);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void sendSessionModifyRequest(VideoProfile reqProfile) {
+            try {
+                mVideoProviderBinder.sendSessionModifyRequest(reqProfile);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void sendSessionModifyResponse(VideoProfile responseProfile) {
+            try {
+                mVideoProviderBinder.sendSessionModifyResponse(responseProfile);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void requestCameraCapabilities() {
+            try {
+                mVideoProviderBinder.requestCameraCapabilities();
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void requestCallDataUsage() {
+            try {
+                mVideoProviderBinder.requestCallDataUsage();
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void setPauseImage(String uri) {
+            try {
+                mVideoProviderBinder.setPauseImage(uri);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     private IConnectionService mConnectionService;
     private final String mConnectionId;
     /**
@@ -215,6 +409,7 @@ public final class RemoteConnection {
     private boolean mConnected;
     private int mCallCapabilities;
     private int mVideoState;
+    private VideoProvider mVideoProvider;
     private boolean mAudioModeIsVoip;
     private StatusHints mStatusHints;
     private Uri mHandle;
@@ -380,6 +575,14 @@ public final class RemoteConnection {
     }
 
     /**
+     * @return The video provider associated with this {@code RemoteConnection}.
+     * @hide
+     */
+    public final VideoProvider getVideoProvider() {
+        return mVideoProvider;
+    }
+
+    /**
      * @return The failure code ({@see DisconnectCause}) associated with this failed
      * {@code RemoteConnection}.
      */
@@ -684,6 +887,16 @@ public final class RemoteConnection {
         }
     }
 
+    /**
+     * @hide
+     */
+    void setVideoProvider(VideoProvider videoProvider) {
+        mVideoProvider = videoProvider;
+        for (Listener l : mListeners) {
+            l.onVideoProviderChanged(this, videoProvider);
+        }
+    }
+
     /** @hide */
     void setAudioModeIsVoip(boolean isVoip) {
         mAudioModeIsVoip = isVoip;
index 0b6badb..348c36c 100644 (file)
@@ -74,7 +74,7 @@ final class RemoteConnectionService {
                     }
                 }
                 connection.setConferenceableConnections(conferenceable);
-                // TODO: Do we need to support video providers for remote connections?
+                connection.setVideoState(parcel.getVideoState());
                 if (connection.getState() == Connection.STATE_DISCONNECTED) {
                     // ... then, if it was created in a disconnected state, that indicates
                     // failure on the providing end, so immediately mark it destroyed
@@ -191,7 +191,7 @@ final class RemoteConnectionService {
             conference.setState(parcel.getState());
             conference.setCallCapabilities(parcel.getCapabilities());
             mConferenceById.put(callId, conference);
-            conference.addListener(new RemoteConference.Listener() {
+            conference.addCallback(new RemoteConference.Callback() {
                 @Override
                 public void onDestroyed(RemoteConference c) {
                     mConferenceById.remove(callId);
@@ -226,7 +226,8 @@ final class RemoteConnectionService {
 
         @Override
         public void setVideoProvider(String callId, IVideoProvider videoProvider) {
-            // not supported for remote connections.
+            findConnectionForAction(callId, "setVideoProvider")
+                    .setVideoProvider(new RemoteConnection.VideoProvider(videoProvider));
         }
 
         @Override
@@ -325,8 +326,7 @@ final class RemoteConnectionService {
         final String id = UUID.randomUUID().toString();
         final ConnectionRequest newRequest = new ConnectionRequest(
                 request.getAccountHandle(),
-                request.getHandle(),
-                request.getHandlePresentation(),
+                request.getAddress(),
                 request.getExtras(),
                 request.getVideoState());
         try {
index f7c4f2f..ff96a5b 100644 (file)
@@ -32,23 +32,24 @@ import java.util.Objects;
  */
 public final class StatusHints implements Parcelable {
 
-    private final ComponentName mComponentName;
+    private final ComponentName mPackageName;
     private final CharSequence mLabel;
     private final int mIconResId;
     private final Bundle mExtras;
 
-    public StatusHints(ComponentName componentName, CharSequence label, int iconResId, Bundle extras) {
-        mComponentName = componentName;
+    public StatusHints(ComponentName packageName, CharSequence label, int iconResId,
+            Bundle extras) {
+        mPackageName = packageName;
         mLabel = label;
         mIconResId = iconResId;
         mExtras = extras;
     }
 
     /**
-     * @return A component used to load the icon.
+     * @return A package used to load the icon.
      */
-    public ComponentName getComponentName() {
-        return mComponentName;
+    public ComponentName getPackageName() {
+        return mPackageName;
     }
 
     /**
@@ -88,7 +89,7 @@ public final class StatusHints implements Parcelable {
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(mComponentName, flags);
+        out.writeParcelable(mPackageName, flags);
         out.writeCharSequence(mLabel);
         out.writeInt(mIconResId);
         out.writeParcelable(mExtras, 0);
@@ -106,7 +107,7 @@ public final class StatusHints implements Parcelable {
     };
 
     private StatusHints(Parcel in) {
-        mComponentName = in.readParcelable(getClass().getClassLoader());
+        mPackageName = in.readParcelable(getClass().getClassLoader());
         mLabel = in.readCharSequence();
         mIconResId = in.readInt();
         mExtras = in.readParcelable(getClass().getClassLoader());
@@ -115,16 +116,16 @@ public final class StatusHints implements Parcelable {
     private Drawable getIcon(Context context, int resId) {
         Context packageContext;
         try {
-            packageContext = context.createPackageContext(mComponentName.getPackageName(), 0);
+            packageContext = context.createPackageContext(mPackageName.getPackageName(), 0);
         } catch (PackageManager.NameNotFoundException e) {
-            Log.e(this, e, "Cannot find package %s", mComponentName.getPackageName());
+            Log.e(this, e, "Cannot find package %s", mPackageName.getPackageName());
             return null;
         }
         try {
             return packageContext.getDrawable(resId);
         } catch (MissingResourceException e) {
             Log.e(this, e, "Cannot find icon %d in package %s",
-                    resId, mComponentName.getPackageName());
+                    resId, mPackageName.getPackageName());
             return null;
         }
     }
@@ -133,7 +134,7 @@ public final class StatusHints implements Parcelable {
     public boolean equals(Object other) {
         if (other != null && other instanceof StatusHints) {
             StatusHints otherHints = (StatusHints) other;
-            return Objects.equals(otherHints.getComponentName(), getComponentName()) &&
+            return Objects.equals(otherHints.getPackageName(), getPackageName()) &&
                     Objects.equals(otherHints.getLabel(), getLabel()) &&
                     otherHints.getIconResId() == getIconResId() &&
                     Objects.equals(otherHints.getExtras(), getExtras());
@@ -143,7 +144,7 @@ public final class StatusHints implements Parcelable {
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(mComponentName) + Objects.hashCode(mLabel) + mIconResId +
+        return Objects.hashCode(mPackageName) + Objects.hashCode(mLabel) + mIconResId +
                 Objects.hashCode(mExtras);
     }
 }
index b8f6964..5e9e6d0 100644 (file)
@@ -46,34 +46,34 @@ public class TelecommManager {
      *
      * @hide
      */
-    public static final String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
+    public static final String ACTION_INCOMING_CALL = "android.telecomm.action.INCOMING_CALL";
 
     /**
      * The {@link android.content.Intent} action used to configure a
      * {@link android.telecomm.ConnectionService}.
      */
     public static final String ACTION_CONNECTION_SERVICE_CONFIGURE =
-            "android.intent.action.CONNECTION_SERVICE_CONFIGURE";
+            "android.telecomm.action.CONNECTION_SERVICE_CONFIGURE";
 
     /**
      * The {@link android.content.Intent} action used to show the call settings page.
      */
     public static final String ACTION_SHOW_CALL_SETTINGS =
-            "android.telecomm.intent.action.SHOW_CALL_SETTINGS";
+            "android.telecomm.action.SHOW_CALL_SETTINGS";
 
     /**
      * The {@link android.content.Intent} action used to show the settings page used to configure
      * {@link PhoneAccount} preferences.
      */
     public static final String ACTION_CHANGE_PHONE_ACCOUNTS =
-            "android.telecomm.intent.action.CHANGE_PHONE_ACCOUNTS";
+            "android.telecomm.action.CHANGE_PHONE_ACCOUNTS";
 
     /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
      * determines whether the speakerphone should be automatically turned on for an outgoing call.
      */
     public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE =
-            "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
+            "android.telecomm.extra.START_CALL_WITH_SPEAKERPHONE";
 
     /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} containing an integer that
@@ -86,7 +86,7 @@ public class TelecommManager {
      * @hide
      */
     public static final String EXTRA_START_CALL_WITH_VIDEO_STATE =
-            "android.intent.extra.START_CALL_WITH_VIDEO_STATE";
+            "android.telecomm.extra.START_CALL_WITH_VIDEO_STATE";
 
     /**
      * The extra used with an {@link android.content.Intent#ACTION_CALL} and
@@ -96,7 +96,7 @@ public class TelecommManager {
      * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
      */
     public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
-            "android.intent.extra.PHONE_ACCOUNT_HANDLE";
+            "android.telecomm.extra.PHONE_ACCOUNT_HANDLE";
 
     /**
      * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
@@ -106,7 +106,7 @@ public class TelecommManager {
      * @hide
      */
     public static final String EXTRA_INCOMING_CALL_EXTRAS =
-            "android.intent.extra.INCOMING_CALL_EXTRAS";
+            "android.telecomm.extra.INCOMING_CALL_EXTRAS";
 
     /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} and
@@ -117,7 +117,7 @@ public class TelecommManager {
      * @hide
      */
     public static final String EXTRA_OUTGOING_CALL_EXTRAS =
-            "android.intent.extra.OUTGOING_CALL_EXTRAS";
+            "android.telecomm.extra.OUTGOING_CALL_EXTRAS";
 
     /**
      * Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
@@ -203,7 +203,7 @@ public class TelecommManager {
      * @hide
      */
     public static final String ACTION_CURRENT_TTY_MODE_CHANGED =
-            "android.telecomm.intent.action.CURRENT_TTY_MODE_CHANGED";
+            "android.telecomm.action.CURRENT_TTY_MODE_CHANGED";
 
     /**
      * The lookup key for an int that indicates the current TTY mode.
@@ -226,7 +226,7 @@ public class TelecommManager {
      * @hide
      */
     public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
-            "android.telecomm.intent.action.TTY_PREFERRED_MODE_CHANGED";
+            "android.telecomm.action.TTY_PREFERRED_MODE_CHANGED";
 
     /**
      * The lookup key for an int that indicates preferred TTY mode. Valid modes are: -
index c10865f..d33a351 100644 (file)
@@ -157,7 +157,7 @@ public class VideoCallImpl extends VideoCall {
         mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
 
         mBinder = new VideoCallListenerBinder();
-        mVideoProvider.setVideoListener(mBinder);
+        mVideoProvider.setVideoCallback(mBinder);
     }
 
     /** {@inheritDoc} */
diff --git a/telecomm/java/android/telecomm/VideoCallbackServant.java b/telecomm/java/android/telecomm/VideoCallbackServant.java
new file mode 100644 (file)
index 0000000..060b8a9
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * 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
+ R* limitations under the License.
+ */
+
+package android.telecomm;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecomm.IVideoCallback;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+
+/**
+ * A component that provides an RPC servant implementation of {@link IVideoCallback},
+ * posting incoming messages on the main thread on a client-supplied delegate object.
+ *
+ * TODO: Generate this and similar classes using a compiler starting from AIDL interfaces.
+ *
+ * @hide
+ */
+final class VideoCallbackServant {
+    private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 0;
+    private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 1;
+    private static final int MSG_HANDLE_CALL_SESSION_EVENT = 2;
+    private static final int MSG_CHANGE_PEER_DIMENSIONS = 3;
+    private static final int MSG_CHANGE_CALL_DATA_USAGE = 4;
+    private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 5;
+
+    private final IVideoCallback mDelegate;
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            try {
+                internalHandleMessage(msg);
+            } catch (RemoteException e) {
+            }
+        }
+
+        // Internal method defined to centralize handling of RemoteException
+        private void internalHandleMessage(Message msg) throws RemoteException {
+            switch (msg.what) {
+                case MSG_RECEIVE_SESSION_MODIFY_REQUEST: {
+                    mDelegate.receiveSessionModifyRequest((VideoProfile) msg.obj);
+                    break;
+                }
+                case MSG_RECEIVE_SESSION_MODIFY_RESPONSE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        mDelegate.receiveSessionModifyResponse(
+                                args.argi1,
+                                (VideoProfile) args.arg1,
+                                (VideoProfile) args.arg2);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_HANDLE_CALL_SESSION_EVENT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        mDelegate.handleCallSessionEvent(args.argi1);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_CHANGE_PEER_DIMENSIONS: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        mDelegate.changePeerDimensions(args.argi1, args.argi2);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_CHANGE_CALL_DATA_USAGE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        mDelegate.changeCallDataUsage(args.argi1);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_CHANGE_CAMERA_CAPABILITIES: {
+                    mDelegate.changeCameraCapabilities((CameraCapabilities) msg.obj);
+                    break;
+                }
+            }
+        }
+    };
+
+    private final IVideoCallback mStub = new IVideoCallback.Stub() {
+        @Override
+        public void receiveSessionModifyRequest(VideoProfile videoProfile) throws RemoteException {
+            mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_REQUEST, videoProfile).sendToTarget();
+        }
+
+        @Override
+        public void receiveSessionModifyResponse(int status, VideoProfile requestedProfile,
+                VideoProfile responseProfile) throws RemoteException {
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = status;
+            args.arg1 = requestedProfile;
+            args.arg2 = responseProfile;
+            mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
+        }
+
+        @Override
+        public void handleCallSessionEvent(int event) throws RemoteException {
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = event;
+            mHandler.obtainMessage(MSG_HANDLE_CALL_SESSION_EVENT, args).sendToTarget();
+        }
+
+        @Override
+        public void changePeerDimensions(int width, int height) throws RemoteException {
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = width;
+            args.argi2 = height;
+            mHandler.obtainMessage(MSG_CHANGE_PEER_DIMENSIONS, args).sendToTarget();
+        }
+
+        @Override
+        public void changeCallDataUsage(int dataUsage) throws RemoteException {
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = dataUsage;
+            mHandler.obtainMessage(MSG_CHANGE_CALL_DATA_USAGE, args).sendToTarget();
+        }
+
+        @Override
+        public void changeCameraCapabilities(CameraCapabilities cameraCapabilities)
+                throws RemoteException {
+            mHandler.obtainMessage(MSG_CHANGE_CAMERA_CAPABILITIES, cameraCapabilities)
+                    .sendToTarget();
+        }
+    };
+
+    public VideoCallbackServant(IVideoCallback delegate) {
+        mDelegate = delegate;
+    }
+
+    public IVideoCallback getStub() {
+        return mStub;
+    }
+}
index 32b877d..a673733 100644 (file)
@@ -65,6 +65,10 @@ oneway interface IConnectionService {
 
     void splitFromConference(String callId);
 
+    void mergeConference(String conferenceCallId);
+
+    void swapConference(String conferenceCallId);
+
     void onPostDialContinue(String callId, boolean proceed);
 
     void onPhoneAccountClicked(String callId);
index 2ce5c6b..626bb91 100644 (file)
@@ -54,6 +54,10 @@ oneway interface IInCallAdapter {
 
     void splitFromConference(String callId);
 
+    void mergeConference(String callId);
+
+    void swapConference(String callId);
+
     void turnOnProximitySensor();
 
     void turnOffProximitySensor(boolean screenOnImmediately);
index 9d3ad7f..b0aa988 100644 (file)
@@ -25,7 +25,7 @@ import android.telecomm.VideoProfile;
  * @hide
  */
 oneway interface IVideoProvider {
-    void setVideoListener(IBinder videoListenerBinder);
+    void setVideoCallback(IBinder videoCallbackBinder);
 
     void setCamera(String cameraId);
 
index 8af5e98..1d9e6a9 100644 (file)
@@ -38,8 +38,8 @@ import android.util.SparseIntArray;
 
 import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_IDP_STRING;
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
 
 import java.util.Locale;
 import java.util.regex.Matcher;
@@ -2215,81 +2215,79 @@ public class PhoneNumberUtils
     cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
         String retStr = dialStr;
 
+        boolean useNanp = (currFormat == defaultFormat) && (currFormat == FORMAT_NANP);
+
         // Checks if the plus sign character is in the passed-in dial string
         if (dialStr != null &&
             dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
-            // Format the string based on the rules for the country the number is from,
-            // and the current country the phone is camped on.
-            if ((currFormat == defaultFormat) && (currFormat == FORMAT_NANP)) {
-                // Handle case where default and current telephone numbering plans are NANP.
-                String postDialStr = null;
-                String tempDialStr = dialStr;
-
-                // Sets the retStr to null since the conversion will be performed below.
-                retStr = null;
-                if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
-                // This routine is to process the plus sign in the dial string by loop through
-                // the network portion, post dial portion 1, post dial portion 2... etc. if
-                // applied
-                do {
-                    String networkDialStr;
+
+            // Handle case where default and current telephone numbering plans are NANP.
+            String postDialStr = null;
+            String tempDialStr = dialStr;
+
+            // Sets the retStr to null since the conversion will be performed below.
+            retStr = null;
+            if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
+            // This routine is to process the plus sign in the dial string by loop through
+            // the network portion, post dial portion 1, post dial portion 2... etc. if
+            // applied
+            do {
+                String networkDialStr;
+                // Format the string based on the rules for the country the number is from,
+                // and the current country the phone is camped
+                if (useNanp) {
                     networkDialStr = extractNetworkPortion(tempDialStr);
-                    // Handles the conversion within NANP
-                    networkDialStr = processPlusCodeWithinNanp(networkDialStr);
+                } else  {
+                    networkDialStr = extractNetworkPortionAlt(tempDialStr);
 
-                    // Concatenates the string that is converted from network portion
-                    if (!TextUtils.isEmpty(networkDialStr)) {
-                        if (retStr == null) {
-                            retStr = networkDialStr;
-                        } else {
-                            retStr = retStr.concat(networkDialStr);
-                        }
+                }
+
+                networkDialStr = processPlusCode(networkDialStr, useNanp);
+
+                // Concatenates the string that is converted from network portion
+                if (!TextUtils.isEmpty(networkDialStr)) {
+                    if (retStr == null) {
+                        retStr = networkDialStr;
                     } else {
-                        // This should never happen since we checked the if dialStr is null
-                        // and if it contains the plus sign in the beginning of this function.
-                        // The plus sign is part of the network portion.
-                        Rlog.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
-                        return dialStr;
+                        retStr = retStr.concat(networkDialStr);
                     }
-                    postDialStr = extractPostDialPortion(tempDialStr);
-                    if (!TextUtils.isEmpty(postDialStr)) {
-                        int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
-
-                        // dialableIndex should always be greater than 0
-                        if (dialableIndex >= 1) {
-                            retStr = appendPwCharBackToOrigDialStr(dialableIndex,
-                                     retStr,postDialStr);
-                            // Skips the P/W character, extracts the dialable portion
-                            tempDialStr = postDialStr.substring(dialableIndex);
-                        } else {
-                            // Non-dialable character such as P/W should not be at the end of
-                            // the dial string after P/W processing in CdmaConnection.java
-                            // Set the postDialStr to "" to break out of the loop
-                            if (dialableIndex < 0) {
-                                postDialStr = "";
-                            }
-                            Rlog.e("wrong postDialStr=", postDialStr);
+                } else {
+                    // This should never happen since we checked the if dialStr is null
+                    // and if it contains the plus sign in the beginning of this function.
+                    // The plus sign is part of the network portion.
+                    Rlog.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
+                    return dialStr;
+                }
+                postDialStr = extractPostDialPortion(tempDialStr);
+                if (!TextUtils.isEmpty(postDialStr)) {
+                    int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
+
+                    // dialableIndex should always be greater than 0
+                    if (dialableIndex >= 1) {
+                        retStr = appendPwCharBackToOrigDialStr(dialableIndex,
+                                 retStr,postDialStr);
+                        // Skips the P/W character, extracts the dialable portion
+                        tempDialStr = postDialStr.substring(dialableIndex);
+                    } else {
+                        // Non-dialable character such as P/W should not be at the end of
+                        // the dial string after P/W processing in CdmaConnection.java
+                        // Set the postDialStr to "" to break out of the loop
+                        if (dialableIndex < 0) {
+                            postDialStr = "";
                         }
+                        Rlog.e("wrong postDialStr=", postDialStr);
                     }
-                    if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
-                } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
-            } else {
-                // TODO: Support NANP international conversion and other telephone numbering plans.
-                // Currently the phone is never used in non-NANP system, so return the original
-                // dial string.
-                Rlog.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
-            }
+                }
+                if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
+            } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
         }
         return retStr;
-     }
+    }
 
-    // This function gets the default international dialing prefix
-    private static String getDefaultIdp( ) {
-        String ps = null;
-        SystemProperties.get(PROPERTY_IDP_STRING, ps);
-        if (TextUtils.isEmpty(ps)) {
-            ps = NANP_IDP_STRING;
-        }
+    private static String getCurrentIdp(boolean useNanp) {
+        // in case, there is no IDD is found, we shouldn't convert it.
+        String ps = SystemProperties.get(
+                PROPERTY_OPERATOR_IDP_STRING, useNanp ? NANP_IDP_STRING : PLUS_SIGN_STRING);
         return ps;
     }
 
@@ -2399,31 +2397,32 @@ public class PhoneNumberUtils
     }
 
     /**
-     * This function handles the plus code conversion within NANP CDMA network
+     * This function handles the plus code conversion
      * If the number format is
      * 1)+1NANP,remove +,
      * 2)other than +1NANP, any + numbers,replace + with the current IDP
      */
-    private static String processPlusCodeWithinNanp(String networkDialStr) {
+    private static String processPlusCode(String networkDialStr, boolean useNanp) {
         String retStr = networkDialStr;
 
-        if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
+        if (DBG) log("processPlusCode, networkDialStr = " + networkDialStr
+                + "for NANP = " + useNanp);
         // If there is a plus sign at the beginning of the dial string,
         // Convert the plus sign to the default IDP since it's an international number
         if (networkDialStr != null &&
             networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
             networkDialStr.length() > 1) {
             String newStr = networkDialStr.substring(1);
-            if (isOneNanp(newStr)) {
+            // TODO: for nonNanp, should the '+' be removed if following number is country code
+            if (useNanp && isOneNanp(newStr)) {
                 // Remove the leading plus sign
                 retStr = newStr;
-             } else {
-                 String idpStr = getDefaultIdp();
-                 // Replaces the plus sign with the default IDP
-                 retStr = networkDialStr.replaceFirst("[+]", idpStr);
+            } else {
+                // Replaces the plus sign with the default IDP
+                retStr = networkDialStr.replaceFirst("[+]", getCurrentIdp(useNanp));
             }
         }
-        if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
+        if (DBG) log("processPlusCoderetStr=" + retStr);
         return retStr;
     }
 
index cdee3de..93c49ba 100644 (file)
@@ -2511,9 +2511,9 @@ public class TelephonyManager {
      * @param content String containing SAT/USAT response in hexadecimal
      *                format starting with command tag. See TS 102 223 for
      *                details.
-     * @return The APDU response from the ICC card, with the last 4 bytes
-     *         being the status word. If the command fails, returns an empty
-     *         string.
+     * @return The APDU response from the ICC card in hexadecimal format
+     *         with the last 4 bytes being the status word. If the command fails,
+     *         returns an empty string.
      */
     public String sendEnvelopeWithStatus(String content) {
         try {
@@ -2903,27 +2903,6 @@ public class TelephonyManager {
     }
 
     /**
-     * Get the calculated preferred network type.
-     * Used for debugging incorrect network type.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
-     *
-     * @return the preferred network type, defined in RILConstants.java or -1 if
-     *         none available.
-     */
-    public int getCalculatedPreferredNetworkType() {
-        try {
-            return getITelephony().getCalculatedPreferredNetworkType();
-        } catch (RemoteException ex) {
-            Rlog.e(TAG, "getCalculatedPreferredNetworkType RemoteException", ex);
-        } catch (NullPointerException ex) {
-            Rlog.e(TAG, "getCalculatedPreferredNetworkType NPE", ex);
-        }
-        return -1;
-    }
-
-    /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
      * <p>
@@ -2932,6 +2911,7 @@ public class TelephonyManager {
      * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
      *
      * @return the preferred network type, defined in RILConstants.java.
+     * @hide
      */
     public int getPreferredNetworkType() {
         try {
@@ -2954,6 +2934,7 @@ public class TelephonyManager {
      *
      * @param networkType the preferred network type, defined in RILConstants.java.
      * @return true on success; false on any failure.
+     * @hide
      */
     public boolean setPreferredNetworkType(int networkType) {
         try {
@@ -2967,25 +2948,17 @@ public class TelephonyManager {
     }
 
     /**
-     * Set the CDMA subscription source.
-     * Used for device supporting both NV and RUIM for CDMA.
+     * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
+     *
      * <p>
      * Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
      *
-     * @param subscriptionType the subscription type, 0 for RUIM, 1 for NV.
      * @return true on success; false on any failure.
      */
-    public boolean setCdmaSubscription(int subscriptionType) {
-        try {
-            return getITelephony().setCdmaSubscription(subscriptionType);
-        } catch (RemoteException ex) {
-            Rlog.e(TAG, "setCdmaSubscription RemoteException", ex);
-        } catch (NullPointerException ex) {
-            Rlog.e(TAG, "setCdmaSubscription NPE", ex);
-        }
-        return false;
+    public boolean setGlobalPreferredNetworkType() {
+        return setPreferredNetworkType(RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
     }
 
     /**
@@ -3023,9 +2996,9 @@ public class TelephonyManager {
     }
 
     /**
-     * Override the branding for the input ICCID.
+     * Override the branding for the current ICCID.
      *
-     * Once set, whenever the ICCID is inserted into the device, the service
+     * Once set, whenever the SIM is present in the device, the service
      * provider name (SPN) and the operator name will both be replaced by the
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
@@ -3034,14 +3007,12 @@ public class TelephonyManager {
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *  or has to be carrier app - see #hasCarrierPrivileges.
      *
-     * @param iccId The ICCID of that the branding applies to.
      * @param brand The brand name to display/set.
      * @return true if the operation was executed correctly.
      */
-    public boolean setOperatorBrandOverride(String iccId, String brand) {
-        // TODO: Validate ICCID format.
+    public boolean setOperatorBrandOverride(String brand) {
         try {
-            return getITelephony().setOperatorBrandOverride(iccId, brand);
+            return getITelephony().setOperatorBrandOverride(brand);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "setOperatorBrandOverride RemoteException", ex);
         } catch (NullPointerException ex) {
@@ -3393,6 +3364,7 @@ public class TelephonyManager {
      * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
      *
      * @param enable true means enabling the simplified UI.
+     * @hide
      */
     public void enableSimplifiedNetworkSettings(boolean enable) {
         enableSimplifiedNetworkSettings(getDefaultSubscription(), enable);
@@ -3408,6 +3380,7 @@ public class TelephonyManager {
      *
      * @param subId for which the simplified UI should be enabled or disabled.
      * @param enable true means enabling the simplified UI.
+     * @hide
      */
     public void enableSimplifiedNetworkSettings(long subId, boolean enable) {
         try {
@@ -3424,6 +3397,7 @@ public class TelephonyManager {
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      *
      * @return true if the simplified UI is enabled.
+     * @hide
      */
     public boolean getSimplifiedNetworkSettingsEnabled() {
         return getSimplifiedNetworkSettingsEnabled(getDefaultSubscription());
@@ -3437,6 +3411,7 @@ public class TelephonyManager {
      *
      * @param subId for which the simplified UI should be enabled or disabled.
      * @return true if the simplified UI is enabled.
+     * @hide
      */
     public boolean getSimplifiedNetworkSettingsEnabled(long subId) {
         try {
index 5c3dcdb..79c72b8 100644 (file)
@@ -643,15 +643,6 @@ interface ITelephony {
     boolean setPreferredNetworkType(int networkType);
 
     /**
-     * Set the CDMA subscription source.
-     * Used for device supporting both NV and RUIM for CDMA.
-     *
-     * @param subscriptionType the subscription type, 0 for RUIM, 1 for NV.
-     * @return true on success; false on any failure.
-     */
-    boolean setCdmaSubscription(int subscriptionType);
-
-    /**
      * User enable/disable Mobile Data.
      *
      * @param enable true to turn on, else false
@@ -766,9 +757,9 @@ interface ITelephony {
     String getLine1AlphaTagForDisplay(long subId);
 
     /**
-     * Override the operator branding for the input ICCID.
+     * Override the operator branding for the current ICCID.
      *
-     * Once set, whenever the ICCID is inserted into the device, the service
+     * Once set, whenever the SIM is present in the device, the service
      * provider name (SPN) and the operator name will both be replaced by the
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
@@ -777,11 +768,10 @@ interface ITelephony {
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *  or has to be carrier app - see #hasCarrierPrivileges.
      *
-     * @param iccid The ICCID of that the branding applies to.
      * @param brand The brand name to display/set.
      * @return true if the operation was executed correctly.
      */
-    boolean setOperatorBrandOverride(String iccId, String brand);
+    boolean setOperatorBrandOverride(String brand);
 
     /**
      * Returns the result and response from RIL for oem request
index 5ec4247..34992b8 100644 (file)
@@ -123,8 +123,8 @@ public interface TelephonyProperties
     /** Indicate the timer value for exiting emergency callback mode */
     static final String PROPERTY_ECM_EXIT_TIMER = "ro.cdma.ecmexittimer";
 
-    /** The international dialing prefix conversion string */
-    static final String PROPERTY_IDP_STRING = "ro.cdma.idpstring";
+    /** the international dialing prefix of current operator network */
+    static final String PROPERTY_OPERATOR_IDP_STRING = "telephony.operator.idpstring";
 
     /**
      * Defines the schema for the carrier specified OTASP number
index e03b9c8..44787e7 100644 (file)
@@ -30,7 +30,6 @@ import android.content.ContentProviderClient;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.graphics.BitmapFactory;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -391,6 +390,17 @@ public class ActivityTestMain extends Activity {
     }
 
     @Override
+    protected void onPause() {
+        super.onPause();
+        Log.i(TAG, "I'm such a slooow poor loser");
+        try {
+            Thread.sleep(500);
+        } catch (InterruptedException e) {
+        }
+        Log.i(TAG, "See?");
+    }
+
+    @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         if (mOverrideConfig != null) {
index 3360e30..256a1d4 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.test.hwui;
 
 import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -34,6 +35,7 @@ public class RevealActivity extends Activity implements OnClickListener {
     private static final int DURATION = 800;
 
     private boolean mShouldBlock;
+    private int mIteration = 0;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -57,10 +59,17 @@ public class RevealActivity extends Activity implements OnClickListener {
         Animator animator = ViewAnimationUtils.createCircularReveal(view,
                 view.getWidth() / 2, view.getHeight() / 2,
                 0, Math.max(view.getWidth(), view.getHeight()));
-        animator.setDuration(DURATION);
-        animator.setAllowRunningAsynchronously(true);
-        animator.start();
+        if (mIteration < 2) {
+            animator.setDuration(DURATION);
+            animator.start();
+        } else {
+            AnimatorSet set = new AnimatorSet();
+            set.playTogether(animator);
+            set.setDuration(DURATION);
+            set.start();
+        }
 
+        mIteration = (mIteration + 1) % 4;
         mShouldBlock = !mShouldBlock;
         if (mShouldBlock) {
             view.post(sBlockThread);
index 2604e97..c403ce6 100644 (file)
@@ -222,7 +222,8 @@ public class IWindowManagerImpl implements IWindowManager {
 
     @Override
     public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX,
-            int startY, IRemoteCallback startedCallback, boolean scaleUp) {
+            int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
+            boolean scaleUp) {
         // TODO Auto-generated method stub
     }
 
index ac2a176..21f200f 100644 (file)
@@ -341,6 +341,12 @@ public class WifiConfiguration implements Parcelable {
 
     /**
      * @hide
+     * last time we connected, this configuration had no internet access
+     */
+    public boolean noInternetAccess;
+
+    /**
+     * @hide
      * Uid of app creating the configuration
      */
     @SystemApi
@@ -673,6 +679,49 @@ public class WifiConfiguration implements Parcelable {
     @SystemApi
     public int numAssociation;
 
+    /**
+     * @hide
+     * Number of time user disabled WiFi while associated to this configuration with Low RSSI.
+     */
+    public int numUserTriggeredWifiDisableLowRSSI;
+
+    /**
+     * @hide
+     * Number of time user disabled WiFi while associated to this configuration with Bad RSSI.
+     */
+    public int numUserTriggeredWifiDisableBadRSSI;
+
+    /**
+     * @hide
+     * Number of time user disabled WiFi while associated to this configuration
+     * and RSSI was not HIGH.
+     */
+    public int numUserTriggeredWifiDisableNotHighRSSI;
+
+    /**
+     * @hide
+     * Number of ticks associated to this configuration with Low RSSI.
+     */
+    public int numTicksAtLowRSSI;
+
+    /**
+     * @hide
+     * Number of ticks associated to this configuration with Bad RSSI.
+     */
+    public int numTicksAtBadRSSI;
+
+    /**
+     * @hide
+     * Number of ticks associated to this configuration
+     * and RSSI was not HIGH.
+     */
+    public int numTicksAtNotHighRSSI;
+    /**
+     * @hide
+     * Number of time user (WifiManager) triggered association to this configuration.
+     * TODO: count this only for Wifi Settings uuid, so as to not count 3rd party apps
+     */
+    public int numUserTriggeredJoinAttempts;
 
     /**
      * @hide
@@ -725,6 +774,7 @@ public class WifiConfiguration implements Parcelable {
         selfAdded = false;
         didSelfAdd = false;
         ephemeral = false;
+        noInternetAccess = false;
         mIpConfiguration = new IpConfiguration();
     }
 
@@ -824,8 +874,10 @@ public class WifiConfiguration implements Parcelable {
             sbuf.append(" autoJoinStatus ").append(this.numConnectionFailures).append("\n");
         }
         if (this.didSelfAdd || this.selfAdded) {
-            if (this.didSelfAdd) sbuf.append(" didSelfAdd ");
-            if (this.selfAdded) sbuf.append(" selfAdded ");
+            if (this.didSelfAdd) sbuf.append(" didSelfAdd");
+            if (this.selfAdded) sbuf.append(" selfAdded");
+            if (this.noInternetAccess) sbuf.append(" noInternetAccess");
+
             sbuf.append("\n");
         }
         sbuf.append(" KeyMgmt:");
@@ -896,18 +948,44 @@ public class WifiConfiguration implements Parcelable {
 
         sbuf.append(mIpConfiguration.toString());
 
-        if (selfAdded)  sbuf.append("selfAdded");
-        if (creatorUid != 0)  sbuf.append("uid=" + Integer.toString(creatorUid));
+        if (this.creatorUid != 0)  sbuf.append("uid=" + Integer.toString(creatorUid));
 
-        if (blackListTimestamp != 0) {
+        if (this.blackListTimestamp != 0) {
             long now_ms = System.currentTimeMillis();
-            long diff = now_ms - blackListTimestamp;
+            long diff = now_ms - this.blackListTimestamp;
             if (diff <= 0) {
                 sbuf.append("blackListed since <incorrect>");
             } else {
                 sbuf.append("blackListed since ").append(Long.toString(diff/1000)).append( "sec");
             }
         }
+        sbuf.append('\n');
+        if (this.linkedConfigurations != null) {
+            for(String key : this.linkedConfigurations.keySet()) {
+                sbuf.append(" linked: ").append(key);
+                sbuf.append('\n');
+            }
+        }
+        if (this.connectChoices != null) {
+            for(String key : this.connectChoices.keySet()) {
+                Integer choice = this.connectChoices.get(key);
+                if (choice != null) {
+                    sbuf.append(" choice: ").append(key);
+                    sbuf.append(" = ").append(choice);
+                    sbuf.append('\n');
+                }
+            }
+        }
+        sbuf.append(" triggeredLow: ").append(numUserTriggeredWifiDisableLowRSSI);
+        sbuf.append(" triggeredBad: ").append(numUserTriggeredWifiDisableBadRSSI);
+        sbuf.append(" triggeredNotHigh: ").append(numUserTriggeredWifiDisableNotHighRSSI);
+        sbuf.append('\n');
+        sbuf.append(" ticksLow: ").append(numTicksAtLowRSSI);
+        sbuf.append(" ticksBad: ").append(numTicksAtBadRSSI);
+        sbuf.append(" ticksNotHigh: ").append(numTicksAtNotHighRSSI);
+        sbuf.append('\n');
+        sbuf.append(" triggeredJoin: ").append(numUserTriggeredJoinAttempts);
+        sbuf.append('\n');
 
         return sbuf.toString();
     }
@@ -1197,7 +1275,7 @@ public class WifiConfiguration implements Parcelable {
             mCachedConfigKey = null; //force null configKey
             autoJoinStatus = source.autoJoinStatus;
             selfAdded = source.selfAdded;
-
+            noInternetAccess = source.noInternetAccess;
             if (source.visibility != null) {
                 visibility = new Visibility(source.visibility);
             }
@@ -1217,6 +1295,13 @@ public class WifiConfiguration implements Parcelable {
             numScorerOverride = source.numScorerOverride;
             numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
             numAssociation = source.numAssociation;
+            numUserTriggeredWifiDisableLowRSSI = source.numUserTriggeredWifiDisableLowRSSI;
+            numUserTriggeredWifiDisableBadRSSI = source.numUserTriggeredWifiDisableBadRSSI;
+            numUserTriggeredWifiDisableNotHighRSSI = source.numUserTriggeredWifiDisableNotHighRSSI;
+            numTicksAtLowRSSI = source.numTicksAtLowRSSI;
+            numTicksAtBadRSSI = source.numTicksAtBadRSSI;
+            numTicksAtNotHighRSSI = source.numTicksAtNotHighRSSI;
+            numUserTriggeredJoinAttempts = source.numUserTriggeredJoinAttempts;
         }
     }
 
@@ -1259,6 +1344,7 @@ public class WifiConfiguration implements Parcelable {
         dest.writeInt(autoJoinStatus);
         dest.writeInt(selfAdded ? 1 : 0);
         dest.writeInt(didSelfAdd ? 1 : 0);
+        dest.writeInt(noInternetAccess ? 1 : 0);
         dest.writeInt(creatorUid);
         dest.writeInt(lastConnectUid);
         dest.writeInt(lastUpdateUid);
@@ -1269,6 +1355,14 @@ public class WifiConfiguration implements Parcelable {
         dest.writeInt(numScorerOverride);
         dest.writeInt(numScorerOverrideAndSwitchedNetwork);
         dest.writeInt(numAssociation);
+        dest.writeInt(numUserTriggeredWifiDisableLowRSSI);
+        dest.writeInt(numUserTriggeredWifiDisableBadRSSI);
+        dest.writeInt(numUserTriggeredWifiDisableNotHighRSSI);
+        dest.writeInt(numTicksAtLowRSSI);
+        dest.writeInt(numTicksAtBadRSSI);
+        dest.writeInt(numTicksAtNotHighRSSI);
+        dest.writeInt(numUserTriggeredJoinAttempts);
+
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -1307,6 +1401,7 @@ public class WifiConfiguration implements Parcelable {
                 config.autoJoinStatus = in.readInt();
                 config.selfAdded = in.readInt() != 0;
                 config.didSelfAdd = in.readInt() != 0;
+                config.noInternetAccess = in.readInt() != 0;
                 config.creatorUid = in.readInt();
                 config.lastConnectUid = in.readInt();
                 config.lastUpdateUid = in.readInt();
@@ -1317,6 +1412,13 @@ public class WifiConfiguration implements Parcelable {
                 config.numScorerOverride = in.readInt();
                 config.numScorerOverrideAndSwitchedNetwork = in.readInt();
                 config.numAssociation = in.readInt();
+                config.numUserTriggeredWifiDisableLowRSSI = in.readInt();
+                config.numUserTriggeredWifiDisableBadRSSI = in.readInt();
+                config.numUserTriggeredWifiDisableNotHighRSSI = in.readInt();
+                config.numTicksAtLowRSSI = in.readInt();
+                config.numTicksAtBadRSSI = in.readInt();
+                config.numTicksAtNotHighRSSI = in.readInt();
+                config.numUserTriggeredJoinAttempts = in.readInt();
                 return config;
             }