summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk6
-rw-r--r--api/current.txt107
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java2
-rw-r--r--core/java/android/app/ActivityManager.java25
-rw-r--r--core/java/android/app/AppOpsManager.java65
-rw-r--r--core/java/android/app/UiAutomation.java112
-rw-r--r--core/java/android/net/NetworkStats.java12
-rw-r--r--core/java/android/preference/PreferenceActivity.java11
-rw-r--r--core/java/android/security/IKeystoreService.java35
-rw-r--r--core/java/android/text/GraphicsOperations.java13
-rw-r--r--core/java/android/text/MeasuredText.java7
-rw-r--r--core/java/android/text/SpannableStringBuilder.java63
-rw-r--r--core/java/android/text/TextDirectionHeuristic.java28
-rw-r--r--core/java/android/text/TextDirectionHeuristics.java147
-rw-r--r--core/java/android/text/TextLine.java24
-rw-r--r--core/java/android/text/bidi/BidiFormatter.java1123
-rw-r--r--core/java/android/util/DisplayMetrics.java9
-rw-r--r--core/java/android/view/GLES20Canvas.java53
-rw-r--r--core/java/android/view/GLES20RecordingCanvas.java8
-rw-r--r--core/java/android/view/HardwareRenderer.java2
-rw-r--r--core/java/android/view/Surface.java1
-rw-r--r--core/java/android/view/View.java3
-rw-r--r--core/java/android/widget/TextView.java22
-rw-r--r--core/java/com/android/internal/app/IAppOpsCallback.aidl2
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl10
-rw-r--r--core/java/com/android/internal/net/NetworkStatsFactory.java84
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java4
-rw-r--r--core/jni/Android.mk8
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp2
-rw-r--r--core/jni/android/graphics/Canvas.cpp120
-rw-r--r--core/jni/android/graphics/NinePatchImpl.cpp2
-rw-r--r--core/jni/android/graphics/Paint.cpp148
-rw-r--r--core/jni/android/graphics/Region.cpp6
-rw-r--r--core/jni/android/graphics/TextLayout.cpp114
-rw-r--r--core/jni/android/graphics/TextLayout.h29
-rw-r--r--core/jni/android/graphics/TextLayoutCache.cpp202
-rw-r--r--core/jni/android/graphics/TextLayoutCache.h11
-rw-r--r--core/jni/android/graphics/Typeface.cpp20
-rw-r--r--core/jni/android_net_TrafficStats.cpp4
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp63
-rw-r--r--core/jni/android_view_Surface.cpp39
-rw-r--r--core/jni/android_view_TextureView.cpp37
-rw-r--r--core/jni/com_android_internal_net_NetworkStatsFactory.cpp187
-rw-r--r--core/res/Android.mk4
-rw-r--r--core/res/res/values-ko/strings.xml2
-rw-r--r--core/res/res/values-sw/strings.xml10
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java8
-rw-r--r--core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java54
-rw-r--r--docs/html/google/google_toc.cs3
-rw-r--r--docs/html/google/play/billing/api.jd49
-rw-r--r--docs/html/google/play/billing/billing_integrate.jd54
-rw-r--r--docs/html/google/play/billing/billing_overview.jd120
-rw-r--r--docs/html/google/play/billing/billing_reference.jd6
-rw-r--r--docs/html/google/play/billing/billing_subscriptions.jd414
-rw-r--r--docs/html/google/play/billing/index.jd6
-rw-r--r--docs/html/google/play/billing/v2/api.jd9
-rw-r--r--docs/html/google/play/billing/v2/billing_subscriptions.jd539
-rw-r--r--docs/html/google/play/billing/versions.jd8
-rw-r--r--docs/html/images/in-app-billing/v3/billing_subscription_v3.pngbin0 -> 27860 bytes
-rw-r--r--docs/html/reference/gcm-lists.js16
-rw-r--r--docs/html/reference/gms-lists.js93
-rw-r--r--docs/html/sdk/index.jd53
-rw-r--r--docs/html/sdk/installing/installing-adt.jd6
-rw-r--r--docs/html/tools/device.jd12
-rw-r--r--docs/html/tools/help/adb.jd32
-rw-r--r--docs/html/tools/publishing/app-signing.jd24
-rw-r--r--docs/html/tools/sdk/eclipse-adt.jd57
-rw-r--r--docs/html/tools/sdk/tools-notes.jd37
-rw-r--r--docs/html/tools/tools_toc.cs26
-rw-r--r--graphics/java/android/graphics/Canvas.java117
-rw-r--r--graphics/java/android/graphics/Paint.java290
-rw-r--r--graphics/java/android/graphics/Path.java4
-rw-r--r--graphics/java/android/graphics/Typeface.java12
-rw-r--r--graphics/java/android/renderscript/RenderScript.java8
-rw-r--r--graphics/java/android/renderscript/Script.java5
-rw-r--r--graphics/jni/android_renderscript_RenderScript.cpp25
-rw-r--r--include/androidfw/ResourceTypes.h1
-rw-r--r--keystore/java/android/security/KeyChain.java7
-rw-r--r--keystore/java/android/security/KeyStore.java14
-rw-r--r--libs/hwui/Android.mk13
-rw-r--r--libs/hwui/Caches.cpp4
-rw-r--r--libs/hwui/Caches.h2
-rw-r--r--libs/hwui/Debug.h3
-rw-r--r--libs/hwui/DisplayListRenderer.h3
-rw-r--r--libs/hwui/FontRenderer.cpp61
-rw-r--r--libs/hwui/FontRenderer.h16
-rw-r--r--libs/hwui/Layer.cpp3
-rw-r--r--libs/hwui/Layer.h1
-rw-r--r--libs/hwui/LayerCache.cpp1
-rw-r--r--libs/hwui/LayerCache.h22
-rw-r--r--libs/hwui/OpenGLRenderer.cpp9
-rw-r--r--libs/hwui/Properties.h6
-rw-r--r--libs/hwui/RenderBuffer.h25
-rw-r--r--libs/hwui/RenderBufferCache.cpp166
-rw-r--r--libs/hwui/RenderBufferCache.h130
-rw-r--r--libs/hwui/TextDropShadowCache.cpp2
-rw-r--r--libs/hwui/font/CacheTexture.cpp1
-rw-r--r--libs/hwui/font/Font.cpp1
-rw-r--r--libs/hwui/utils/Timing.h42
-rw-r--r--media/tests/omxjpegdecoder/SkOmxPixelRef.h1
-rw-r--r--nfc-extras/java/com/android/nfc_extras/EeAlreadyOpenException.java27
-rw-r--r--nfc-extras/java/com/android/nfc_extras/EeExternalFieldException.java27
-rw-r--r--nfc-extras/java/com/android/nfc_extras/EeIOException.java29
-rw-r--r--nfc-extras/java/com/android/nfc_extras/EeInitializationException.java27
-rw-r--r--nfc-extras/java/com/android/nfc_extras/EeListenModeException.java27
-rw-r--r--nfc-extras/java/com/android/nfc_extras/EeNfcDisabledException.java27
-rw-r--r--nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java63
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java42
-rw-r--r--packages/VpnDialogs/res/layout/confirm.xml1
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java1
-rw-r--r--services/input/SpriteController.cpp3
-rw-r--r--services/java/com/android/server/BackupManagerService.java2
-rw-r--r--services/java/com/android/server/BootReceiver.java29
-rw-r--r--services/java/com/android/server/NetworkManagementService.java32
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java5
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java118
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java5
-rw-r--r--services/java/com/android/server/am/ActivityStack.java230
-rw-r--r--services/java/com/android/server/pm/UserManagerService.java8
-rw-r--r--services/java/com/android/server/wm/DisplayContent.java172
-rw-r--r--services/java/com/android/server/wm/TaskGroup.java (renamed from services/java/com/android/server/am/TaskGroup.java)2
-rw-r--r--services/java/com/android/server/wm/TaskList.java12
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java280
-rw-r--r--services/java/com/android/server/wm/WindowState.java6
-rw-r--r--tests/BiDiTests/res/layout/canvas.xml40
-rw-r--r--tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java1
-rw-r--r--tests/BiDiTests/src/com/android/bidi/BiDiTestCanvas.java67
-rw-r--r--tests/BiDiTests/src/com/android/bidi/BiDiTestView.java212
-rw-r--r--tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java18
-rw-r--r--tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java12
-rw-r--r--tests/RenderScriptTests/LivePreview/res/layout/cf_main.xml2
-rw-r--r--tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/CameraPreviewActivity.java39
-rw-r--r--tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/RsYuv.java98
-rw-r--r--tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/yuv.rs2
-rw-r--r--tests/RenderScriptTests/tests/src/com/android/rs/test/UT_foreach_bounds.java17
-rw-r--r--tests/RenderScriptTests/tests/src/com/android/rs/test/foreach_bounds.rs20
-rw-r--r--tools/aapt/AaptAssets.cpp5
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java5
-rw-r--r--tools/layoutlib/bridge/src/android/text/format/DateFormat_Delegate.java37
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java1
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java1
-rw-r--r--wifi/java/android/net/wifi/WifiEnterpriseConfig.java30
146 files changed, 4885 insertions, 2579 deletions
diff --git a/Android.mk b/Android.mk
index 1b63a91b08d5..c8576a04b511 100644
--- a/Android.mk
+++ b/Android.mk
@@ -700,9 +700,6 @@ LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
include $(BUILD_DROIDDOC)
-# explicitly specify that online-sdk depends on framework-res and any generated docs
-$(full_target): framework-res-package-target
-
# ==== docs for the web (on the devsite app engine server) =======================
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
@@ -731,9 +728,6 @@ LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
include $(BUILD_DROIDDOC)
-# explicitly specify that ds depends on framework-res and any generated docs
-$(full_target): framework-res-package-target
-
# ==== docs that have all of the stuff that's @hidden =======================
include $(CLEAR_VARS)
diff --git a/api/current.txt b/api/current.txt
index d77770790e5f..2615e0bcc877 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4153,11 +4153,14 @@ package android.app {
}
public final class UiAutomation {
- method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, com.android.internal.util.Predicate<android.view.accessibility.AccessibilityEvent>, long) throws java.util.concurrent.TimeoutException;
+ method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
+ method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public boolean injectInputEvent(android.view.InputEvent, boolean);
+ method public final boolean performGlobalAction(int);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
method public boolean setRotation(int);
+ method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public android.graphics.Bitmap takeScreenshot();
method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
field public static final int ROTATION_FREEZE_0 = 0; // 0x0
@@ -4168,6 +4171,10 @@ package android.app {
field public static final int ROTATION_UNFREEZE = -2; // 0xfffffffe
}
+ public static abstract interface UiAutomation.AccessibilityEventFilter {
+ method public abstract boolean accept(android.view.accessibility.AccessibilityEvent);
+ }
+
public static abstract interface UiAutomation.OnAccessibilityEventListener {
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
}
@@ -13410,6 +13417,7 @@ package android.net.wifi {
field public java.util.BitSet allowedKeyManagement;
field public java.util.BitSet allowedPairwiseCiphers;
field public java.util.BitSet allowedProtocols;
+ field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig;
field public boolean hiddenSSID;
field public int networkId;
field public java.lang.String preSharedKey;
@@ -13467,6 +13475,45 @@ package android.net.wifi {
field public static final java.lang.String[] strings;
}
+ public class WifiEnterpriseConfig implements android.os.Parcelable {
+ ctor public WifiEnterpriseConfig();
+ ctor public WifiEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public int describeContents();
+ method public java.lang.String getAnonymousIdentity();
+ method public int getEapMethod();
+ method public java.lang.String getIdentity();
+ method public int getPhase2Method();
+ method public java.lang.String getSubjectMatch();
+ method public void setAnonymousIdentity(java.lang.String);
+ method public void setCaCertificate(java.security.cert.X509Certificate);
+ method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
+ method public void setEapMethod(int);
+ method public void setIdentity(java.lang.String);
+ method public void setPassword(java.lang.String);
+ method public void setPhase2Method(int);
+ method public void setSubjectMatch(java.lang.String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static final class WifiEnterpriseConfig.Eap {
+ ctor public WifiEnterpriseConfig.Eap();
+ field public static final int NONE = -1; // 0xffffffff
+ field public static final int PEAP = 0; // 0x0
+ field public static final int PWD = 3; // 0x3
+ field public static final int TLS = 1; // 0x1
+ field public static final int TTLS = 2; // 0x2
+ }
+
+ public static final class WifiEnterpriseConfig.Phase2 {
+ ctor public WifiEnterpriseConfig.Phase2();
+ field public static final int GTC = 4; // 0x4
+ field public static final int MSCHAP = 2; // 0x2
+ field public static final int MSCHAPV2 = 3; // 0x3
+ field public static final int NONE = 0; // 0x0
+ field public static final int PAP = 1; // 0x1
+ }
+
public class WifiInfo implements android.os.Parcelable {
method public int describeContents();
method public java.lang.String getBSSID();
@@ -22266,6 +22313,21 @@ package android.text {
method public int getTopPadding();
}
+ public abstract interface TextDirectionHeuristic {
+ method public abstract boolean isRtl(char[], int, int);
+ method public abstract boolean isRtl(java.lang.CharSequence, int, int);
+ }
+
+ public class TextDirectionHeuristics {
+ ctor public TextDirectionHeuristics();
+ field public static final android.text.TextDirectionHeuristic ANYRTL_LTR;
+ field public static final android.text.TextDirectionHeuristic FIRSTSTRONG_LTR;
+ field public static final android.text.TextDirectionHeuristic FIRSTSTRONG_RTL;
+ field public static final android.text.TextDirectionHeuristic LOCALE;
+ field public static final android.text.TextDirectionHeuristic LTR;
+ field public static final android.text.TextDirectionHeuristic RTL;
+ }
+
public class TextPaint extends android.graphics.Paint {
ctor public TextPaint();
ctor public TextPaint(int);
@@ -22357,6 +22419,48 @@ package android.text {
}
+package android.text.bidi {
+
+ public final class BidiFormatter {
+ method public java.lang.String dirAttr(java.lang.String);
+ method public java.lang.String dirAttr(java.lang.String, android.text.TextDirectionHeuristic);
+ method public java.lang.String dirAttr(boolean);
+ method public java.lang.String dirAttrValue(java.lang.String);
+ method public java.lang.String dirAttrValue(java.lang.String, android.text.TextDirectionHeuristic);
+ method public java.lang.String dirAttrValue(boolean);
+ method public java.lang.String endEdge();
+ method public static android.text.bidi.BidiFormatter getInstance(boolean);
+ method public static android.text.bidi.BidiFormatter getInstance(java.util.Locale);
+ method public boolean getStereoReset();
+ method public boolean isRtl(java.lang.String);
+ method public boolean isRtlContext();
+ method public java.lang.String mark();
+ method public java.lang.String markAfter(java.lang.String);
+ method public java.lang.String markAfter(java.lang.String, android.text.TextDirectionHeuristic);
+ method public java.lang.String markBefore(java.lang.String);
+ method public java.lang.String markBefore(java.lang.String, android.text.TextDirectionHeuristic);
+ method public java.lang.String spanWrap(java.lang.String, android.text.TextDirectionHeuristic, boolean);
+ method public java.lang.String spanWrap(java.lang.String, android.text.TextDirectionHeuristic);
+ method public java.lang.String spanWrap(java.lang.String, boolean);
+ method public java.lang.String spanWrap(java.lang.String);
+ method public java.lang.String startEdge();
+ method public java.lang.String unicodeWrap(java.lang.String, android.text.TextDirectionHeuristic, boolean);
+ method public java.lang.String unicodeWrap(java.lang.String, android.text.TextDirectionHeuristic);
+ method public java.lang.String unicodeWrap(java.lang.String, boolean);
+ method public java.lang.String unicodeWrap(java.lang.String);
+ }
+
+ public static final class BidiFormatter.Builder {
+ ctor public BidiFormatter.Builder();
+ ctor public BidiFormatter.Builder(boolean);
+ ctor public BidiFormatter.Builder(java.util.Locale);
+ method public android.text.bidi.BidiFormatter build();
+ method public android.text.bidi.BidiFormatter.Builder setTextDirectionHeuristic(android.text.TextDirectionHeuristic);
+ method public android.text.bidi.BidiFormatter.Builder stereoReset(boolean);
+ }
+
+}
+
package android.text.format {
public class DateFormat {
@@ -23279,6 +23383,7 @@ package android.util {
field public static final int DENSITY_TV = 213; // 0xd5
field public static final int DENSITY_XHIGH = 320; // 0x140
field public static final int DENSITY_XXHIGH = 480; // 0x1e0
+ field public static final int DENSITY_XXXHIGH = 640; // 0x280
field public float density;
field public int densityDpi;
field public int heightPixels;
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 8dddbc10242a..811b92a5e102 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -457,7 +457,7 @@ public abstract class AccessibilityService extends Service {
*
* @return The accessibility service info.
*
- * @see AccessibilityNodeInfo
+ * @see AccessibilityServiceInfo
*/
public final AccessibilityServiceInfo getServiceInfo() {
IAccessibilityServiceConnection connection =
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 594be68c3591..944a53310979 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1915,7 +1915,30 @@ public class ActivityManager {
return PackageManager.PERMISSION_DENIED;
}
- /** @hide */
+ /**
+ * @hide
+ * Helper for dealing with incoming user arguments to system service calls.
+ * Takes care of checking permissions and converting USER_CURRENT to the
+ * actual current user.
+ *
+ * @param callingPid The pid of the incoming call, as per Binder.getCallingPid().
+ * @param callingUid The uid of the incoming call, as per Binder.getCallingUid().
+ * @param userId The user id argument supplied by the caller -- this is the user
+ * they want to run as.
+ * @param allowAll If true, we will allow USER_ALL. This means you must be prepared
+ * to get a USER_ALL returned and deal with it correctly. If false,
+ * an exception will be thrown if USER_ALL is supplied.
+ * @param requireFull If true, the caller must hold
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} to be able to run as a
+ * different user than their current process; otherwise they must hold
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS}.
+ * @param name Optional textual name of the incoming call; only for generating error messages.
+ * @param callerPackage Optional package name of caller; only for error messages.
+ *
+ * @return Returns the user ID that the call should run as. Will always be a concrete
+ * user number, unless <var>allowAll</var> is true in which case it could also be
+ * USER_ALL.
+ */
public static int handleIncomingUser(int callingPid, int callingUid, int userId,
boolean allowAll, boolean requireFull, String name, String callerPackage) {
if (UserHandle.getUserId(callingUid) == userId) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 34708ab84f9d..de698678c4a6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.Manifest;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsCallback;
@@ -29,7 +30,25 @@ import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
-/** @hide */
+/**
+ * API for interacting with "application operation" tracking. Allows you to:
+ *
+ * - Note when operations are happening, and find out if they are allowed for the current caller.
+ * - Disallow specific apps from doing specific operations.
+ * - Collect all of the current information about operations that have been executed or are not
+ * being allowed.
+ * - Monitor for changes in whether an operation is allowed.
+ *
+ * Each operation is identified by a single integer; these integers are a fixed set of
+ * operations, enumerated by the OP_* constants.
+ *
+ * When checking operations, the result is a "mode" integer indicating the current setting
+ * for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute the operation but
+ * fake its behavior enough so that the caller doesn't crash), MODE_ERRORED (through a
+ * SecurityException back to the caller; the normal operation calls will do this for you).
+ *
+ * @hide
+ */
public class AppOpsManager {
final Context mContext;
final IAppOpsService mService;
@@ -71,8 +90,11 @@ public class AppOpsManager {
public static final int OP_WRITE_SETTINGS = 23;
public static final int OP_SYSTEM_ALERT_WINDOW = 24;
public static final int OP_ACCESS_NOTIFICATIONS = 25;
+ public static final int OP_CAMERA = 26;
+ public static final int OP_RECORD_AUDIO = 27;
+ public static final int OP_PLAY_AUDIO = 28;
/** @hide */
- public static final int _NUM_OP = 26;
+ public static final int _NUM_OP = 29;
/**
* This maps each operation to the operation that serves as the
@@ -109,6 +131,9 @@ public class AppOpsManager {
OP_WRITE_SETTINGS,
OP_SYSTEM_ALERT_WINDOW,
OP_ACCESS_NOTIFICATIONS,
+ OP_CAMERA,
+ OP_RECORD_AUDIO,
+ OP_PLAY_AUDIO,
};
/**
@@ -142,6 +167,9 @@ public class AppOpsManager {
"WRITE_SETTINGS",
"SYSTEM_ALERT_WINDOW",
"ACCESS_NOTIFICATIONS",
+ "CAMERA",
+ "RECORD_AUDIO",
+ "PLAY_AUDIO",
};
/**
@@ -175,21 +203,36 @@ public class AppOpsManager {
android.Manifest.permission.WRITE_SETTINGS,
android.Manifest.permission.SYSTEM_ALERT_WINDOW,
android.Manifest.permission.ACCESS_NOTIFICATIONS,
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.RECORD_AUDIO,
+ null, // no permission for playing audio
};
+ /**
+ * Retrieve the op switch that controls the given operation.
+ */
public static int opToSwitch(int op) {
return sOpToSwitch[op];
}
+ /**
+ * Retrieve a non-localized name for the operation, for debugging output.
+ */
public static String opToName(int op) {
if (op == OP_NONE) return "NONE";
return op < sOpNames.length ? sOpNames[op] : ("Unknown(" + op + ")");
}
+ /**
+ * Retrieve the permission associated with an operation, or null if there is not one.
+ */
public static String opToPermission(int op) {
return sOpPerms[op];
}
+ /**
+ * Class holding all of the operation information associated with an app.
+ */
public static class PackageOps implements Parcelable {
private final String mPackageName;
private final int mUid;
@@ -249,6 +292,9 @@ public class AppOpsManager {
};
}
+ /**
+ * Class holding the information about one unique operation of an application.
+ */
public static class OpEntry implements Parcelable {
private final int mOp;
private final int mMode;
@@ -321,6 +367,9 @@ public class AppOpsManager {
};
}
+ /**
+ * Callback for notification of changes to operation state.
+ */
public interface Callback {
public void opChanged(int op, String packageName);
}
@@ -330,6 +379,11 @@ public class AppOpsManager {
mService = service;
}
+ /**
+ * Retrieve current operation state for all applications.
+ *
+ * @param ops The set of operations you are interested in, or null if you want all of them.
+ */
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
try {
return mService.getPackagesForOps(ops);
@@ -338,6 +392,13 @@ public class AppOpsManager {
return null;
}
+ /**
+ * Retrieve current operation state for one application.
+ *
+ * @param uid The uid of the application of interest.
+ * @param packageName The name of the application of interest.
+ * @param ops The set of operations you are interested in, or null if you want all of them.
+ */
public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) {
try {
return mService.getOpsForPackage(uid, packageName, ops);
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index e611f6da95df..7d02342ca59c 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -18,7 +18,9 @@ package android.app;
import android.accessibilityservice.AccessibilityService.Callbacks;
import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.IAccessibilityServiceConnection;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
@@ -35,8 +37,6 @@ import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
-import com.android.internal.util.Predicate;
-
import java.util.ArrayList;
import java.util.concurrent.TimeoutException;
@@ -45,7 +45,10 @@ import java.util.concurrent.TimeoutException;
* introspection of the screen content. It relies on the platform accessibility
* APIs to introspect the screen and to perform some actions on the remote view
* tree. It also allows injecting of arbitrary raw input events simulating user
- * interaction with keyboards and touch devices.
+ * interaction with keyboards and touch devices. One can think of a UiAutomation
+ * as a special type of {@link android.accessibilityservice.AccessibilityService}
+ * which does not provide hooks for the service life cycle and exposes other
+ * APIs that are useful for UI test automation.
* <p>
* The APIs exposed by this class are low-level to maximize flexibility when
* developing UI test automation tools and libraries. Generally, a UiAutomation
@@ -130,6 +133,21 @@ public final class UiAutomation {
}
/**
+ * Listener for filtering accessibility events.
+ */
+ public static interface AccessibilityEventFilter {
+
+ /**
+ * Callback for determining whether an event is accepted or
+ * it is filtered out.
+ *
+ * @param event The event to process.
+ * @return True if the event is accepted, false to filter it out.
+ */
+ public boolean accept(AccessibilityEvent event);
+ }
+
+ /**
* Creates a new instance that will handle callbacks from the accessibility
* layer on the thread of the provided looper and perform requests for privileged
* operations on the provided connection.
@@ -243,6 +261,90 @@ public final class UiAutomation {
}
/**
+ * Performs a global action. Such an action can be performed at any moment
+ * regardless of the current application or user location in that application.
+ * For example going back, going home, opening recents, etc.
+ *
+ * @param action The action to perform.
+ * @return Whether the action was successfully performed.
+ *
+ * @see AccessibilityService#GLOBAL_ACTION_BACK
+ * @see AccessibilityService#GLOBAL_ACTION_HOME
+ * @see AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
+ * @see AccessibilityService#GLOBAL_ACTION_RECENTS
+ */
+ public final boolean performGlobalAction(int action) {
+ final IAccessibilityServiceConnection connection;
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ connection = AccessibilityInteractionClient.getInstance()
+ .getConnection(mConnectionId);
+ }
+ // Calling out without a lock held.
+ if (connection != null) {
+ try {
+ return connection.performGlobalAction(action);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the an {@link AccessibilityServiceInfo} describing this UiAutomation.
+ * This method is useful if one wants to change some of the dynamically
+ * configurable properties at runtime.
+ *
+ * @return The accessibility service info.
+ *
+ * @see AccessibilityServiceInfo
+ */
+ public final AccessibilityServiceInfo getServiceInfo() {
+ final IAccessibilityServiceConnection connection;
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ connection = AccessibilityInteractionClient.getInstance()
+ .getConnection(mConnectionId);
+ }
+ // Calling out without a lock held.
+ if (connection != null) {
+ try {
+ return connection.getServiceInfo();
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the {@link AccessibilityServiceInfo} that describes how this
+ * UiAutomation will be handled by the platform accessibility layer.
+ *
+ * @param info The info.
+ *
+ * @see AccessibilityServiceInfo
+ */
+ public final void setServiceInfo(AccessibilityServiceInfo info) {
+ final IAccessibilityServiceConnection connection;
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ AccessibilityInteractionClient.getInstance().clearCache();
+ connection = AccessibilityInteractionClient.getInstance()
+ .getConnection(mConnectionId);
+ }
+ // Calling out without a lock held.
+ if (connection != null) {
+ try {
+ connection.setServiceInfo(info);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
+ }
+ }
+ }
+
+ /**
* Gets the root {@link AccessibilityNodeInfo} in the active window.
*
* @return The root info.
@@ -339,7 +441,7 @@ public final class UiAutomation {
* @throws TimeoutException If the expected event is not received within the timeout.
*/
public AccessibilityEvent executeAndWaitForEvent(Runnable command,
- Predicate<AccessibilityEvent> filter, long timeoutMillis) throws TimeoutException {
+ AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException {
synchronized (mLock) {
throwIfNotConnectedLocked();
@@ -363,7 +465,7 @@ public final class UiAutomation {
if (event.getEventTime() <= executionStartTimeMillis) {
continue;
}
- if (filter.apply(event)) {
+ if (filter.accept(event)) {
return event;
}
event.recycle();
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index c7576057e196..9cb904d4c400 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -135,6 +135,18 @@ public class NetworkStats implements Parcelable {
builder.append(" operations=").append(operations);
return builder.toString();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Entry) {
+ final Entry e = (Entry) o;
+ return uid == e.uid && set == e.set && tag == e.tag && rxBytes == e.rxBytes
+ && rxPackets == e.rxPackets && txBytes == e.txBytes
+ && txPackets == e.txPackets && operations == e.operations
+ && iface.equals(e.iface);
+ }
+ return false;
+ }
}
public NetworkStats(long elapsedRealtime, int initialSize) {
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 09ff7bec739b..028317f1a84a 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -703,7 +703,13 @@ public abstract class PreferenceActivity extends ListActivity implements
* show for the initial UI.
*/
public Header onGetInitialHeader() {
- return mHeaders.get(0);
+ for (int i=0; i<mHeaders.size(); i++) {
+ Header h = mHeaders.get(i);
+ if (h.fragment != null) {
+ return h;
+ }
+ }
+ throw new IllegalStateException("Must have at least one header with a fragment");
}
/**
@@ -1167,6 +1173,9 @@ public abstract class PreferenceActivity extends ListActivity implements
getFragmentManager().popBackStack(BACK_STACK_PREFS,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
+ if (header.fragment == null) {
+ throw new IllegalStateException("can't switch to header that has no fragment");
+ }
int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader);
switchToHeaderInner(header.fragment, header.fragmentArguments, direction);
setSelectedHeader(header);
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index f8a49e66f0c9..651693a970f4 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -78,7 +78,7 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int insert(String name, byte[] item) throws RemoteException {
+ public int insert(String name, byte[] item, int uid) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -86,6 +86,7 @@ public interface IKeystoreService extends IInterface {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeByteArray(item);
+ _data.writeInt(uid);
mRemote.transact(Stub.TRANSACTION_insert, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -96,13 +97,14 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int del(String name) throws RemoteException {
+ public int del(String name, int uid) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
+ _data.writeInt(uid);
mRemote.transact(Stub.TRANSACTION_del, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -113,13 +115,14 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int exist(String name) throws RemoteException {
+ public int exist(String name, int uid) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
+ _data.writeInt(uid);
mRemote.transact(Stub.TRANSACTION_exist, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -130,13 +133,14 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public String[] saw(String name) throws RemoteException {
+ public String[] saw(String name, int uid) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
String[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
+ _data.writeInt(uid);
mRemote.transact(Stub.TRANSACTION_saw, _data, _reply, 0);
_reply.readException();
int size = _reply.readInt();
@@ -235,13 +239,14 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int generate(String name) throws RemoteException {
+ public int generate(String name, int uid) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
+ _data.writeInt(uid);
mRemote.transact(Stub.TRANSACTION_generate, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -252,7 +257,7 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int import_key(String name, byte[] data) throws RemoteException {
+ public int import_key(String name, byte[] data, int uid) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -260,6 +265,7 @@ public interface IKeystoreService extends IInterface {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeByteArray(data);
+ _data.writeInt(uid);
mRemote.transact(Stub.TRANSACTION_import, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -324,13 +330,14 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int del_key(String name) throws RemoteException {
+ public int del_key(String name, int uid) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
+ _data.writeInt(uid);
mRemote.transact(Stub.TRANSACTION_del_key, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -467,13 +474,13 @@ public interface IKeystoreService extends IInterface {
public byte[] get(String name) throws RemoteException;
- public int insert(String name, byte[] item) throws RemoteException;
+ public int insert(String name, byte[] item, int uid) throws RemoteException;
- public int del(String name) throws RemoteException;
+ public int del(String name, int uid) throws RemoteException;
- public int exist(String name) throws RemoteException;
+ public int exist(String name, int uid) throws RemoteException;
- public String[] saw(String name) throws RemoteException;
+ public String[] saw(String name, int uid) throws RemoteException;
public int reset() throws RemoteException;
@@ -485,9 +492,9 @@ public interface IKeystoreService extends IInterface {
public int zero() throws RemoteException;
- public int generate(String name) throws RemoteException;
+ public int generate(String name, int uid) throws RemoteException;
- public int import_key(String name, byte[] data) throws RemoteException;
+ public int import_key(String name, byte[] data, int uid) throws RemoteException;
public byte[] sign(String name, byte[] data) throws RemoteException;
@@ -495,7 +502,7 @@ public interface IKeystoreService extends IInterface {
public byte[] get_pubkey(String name) throws RemoteException;
- public int del_key(String name) throws RemoteException;
+ public int del_key(String name, int uid) throws RemoteException;
public int grant(String name, int granteeUid) throws RemoteException;
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index 831ccc556bf9..60545e5cbdf2 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -38,7 +38,7 @@ extends CharSequence
* {@hide}
*/
void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd,
- float x, float y, int flags, Paint p);
+ float x, float y, Paint p);
/**
* Just like {@link Paint#measureText}.
@@ -55,19 +55,12 @@ extends CharSequence
* @hide
*/
float getTextRunAdvances(int start, int end, int contextStart, int contextEnd,
- int flags, float[] advances, int advancesIndex, Paint paint);
-
- /**
- * Just like {@link Paint#getTextRunAdvances}.
- * @hide
- */
- float getTextRunAdvances(int start, int end, int contextStart, int contextEnd,
- int flags, float[] advances, int advancesIndex, Paint paint, int reserved);
+ float[] advances, int advancesIndex, Paint paint);
/**
* Just like {@link Paint#getTextRunCursor}.
* @hide
*/
- int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset,
+ int getTextRunCursor(int contextStart, int contextEnd, int offset,
int cursorOpt, Paint p);
}
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index bd9310c1b1b2..0c881a4da6b3 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -159,18 +159,15 @@ class MeasuredText {
mPos = p + len;
if (mEasy) {
- int flags = mDir == Layout.DIR_LEFT_TO_RIGHT
- ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL;
- return paint.getTextRunAdvances(mChars, p, len, p, len, flags, mWidths, p);
+ return paint.getTextRunAdvances(mChars, p, len, p, len, mWidths, p);
}
float totalAdvance = 0;
int level = mLevels[p];
for (int q = p, i = p + 1, e = p + len;; ++i) {
if (i == e || mLevels[i] != level) {
- int flags = (level & 0x1) == 0 ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL;
totalAdvance +=
- paint.getTextRunAdvances(mChars, q, i - q, q, i - q, flags, mWidths, q);
+ paint.getTextRunAdvances(mChars, q, i - q, q, i - q, mWidths, q);
if (i == e) {
break;
}
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 0f30d25f7d15..9e4367117a15 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1130,20 +1130,20 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
* {@hide}
*/
public void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd,
- float x, float y, int flags, Paint p) {
+ float x, float y, Paint p) {
checkRange("drawTextRun", start, end);
int contextLen = contextEnd - contextStart;
int len = end - start;
if (contextEnd <= mGapStart) {
- c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, flags, p);
+ c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, p);
} else if (contextStart >= mGapStart) {
c.drawTextRun(mText, start + mGapLength, len, contextStart + mGapLength,
- contextLen, x, y, flags, p);
+ contextLen, x, y, p);
} else {
char[] buf = TextUtils.obtain(contextLen);
getChars(contextStart, contextEnd, buf, 0);
- c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, flags, p);
+ c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, p);
TextUtils.recycle(buf);
}
}
@@ -1200,7 +1200,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
* Don't call this yourself -- exists for Paint to use internally.
* {@hide}
*/
- public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags,
+ public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd,
float[] advances, int advancesPos, Paint p) {
float ret;
@@ -1210,44 +1210,15 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
if (end <= mGapStart) {
ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen,
- flags, advances, advancesPos);
+ advances, advancesPos);
} else if (start >= mGapStart) {
ret = p.getTextRunAdvances(mText, start + mGapLength, len,
- contextStart + mGapLength, contextLen, flags, advances, advancesPos);
+ contextStart + mGapLength, contextLen, advances, advancesPos);
} else {
char[] buf = TextUtils.obtain(contextLen);
getChars(contextStart, contextEnd, buf, 0);
ret = p.getTextRunAdvances(buf, start - contextStart, len,
- 0, contextLen, flags, advances, advancesPos);
- TextUtils.recycle(buf);
- }
-
- return ret;
- }
-
- /**
- * Don't call this yourself -- exists for Paint to use internally.
- * {@hide}
- */
- public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags,
- float[] advances, int advancesPos, Paint p, int reserved) {
-
- float ret;
-
- int contextLen = contextEnd - contextStart;
- int len = end - start;
-
- if (end <= mGapStart) {
- ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen,
- flags, advances, advancesPos, reserved);
- } else if (start >= mGapStart) {
- ret = p.getTextRunAdvances(mText, start + mGapLength, len,
- contextStart + mGapLength, contextLen, flags, advances, advancesPos, reserved);
- } else {
- char[] buf = TextUtils.obtain(contextLen);
- getChars(contextStart, contextEnd, buf, 0);
- ret = p.getTextRunAdvances(buf, start - contextStart, len,
- 0, contextLen, flags, advances, advancesPos, reserved);
+ 0, contextLen, advances, advancesPos);
TextUtils.recycle(buf);
}
@@ -1270,7 +1241,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
*
* @param contextStart the start index of the context
* @param contextEnd the (non-inclusive) end index of the context
- * @param flags either DIRECTION_RTL or DIRECTION_LTR
+ * @param flags reserved
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of CURSOR_AFTER,
* CURSOR_AT_OR_AFTER, CURSOR_BEFORE,
@@ -1281,22 +1252,30 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
*/
@Deprecated
public int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset,
- int cursorOpt, Paint p) {
+ int cursorOpt, Paint p) {
+ return getTextRunCursor(contextStart, contextEnd, offset, cursorOpt, p);
+ }
+
+ /**
+ * @hide
+ */
+ public int getTextRunCursor(int contextStart, int contextEnd, int offset,
+ int cursorOpt, Paint p) {
int ret;
int contextLen = contextEnd - contextStart;
if (contextEnd <= mGapStart) {
ret = p.getTextRunCursor(mText, contextStart, contextLen,
- flags, offset, cursorOpt);
+ offset, cursorOpt);
} else if (contextStart >= mGapStart) {
ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen,
- flags, offset + mGapLength, cursorOpt) - mGapLength;
+ offset + mGapLength, cursorOpt) - mGapLength;
} else {
char[] buf = TextUtils.obtain(contextLen);
getChars(contextStart, contextEnd, buf, 0);
ret = p.getTextRunCursor(buf, 0, contextLen,
- flags, offset - contextStart, cursorOpt) + contextStart;
+ offset - contextStart, cursorOpt) + contextStart;
TextUtils.recycle(buf);
}
diff --git a/core/java/android/text/TextDirectionHeuristic.java b/core/java/android/text/TextDirectionHeuristic.java
index 513e11ce34d5..8a4ba42bcc91 100644
--- a/core/java/android/text/TextDirectionHeuristic.java
+++ b/core/java/android/text/TextDirectionHeuristic.java
@@ -17,10 +17,30 @@
package android.text;
/**
- * Interface for objects that guess at the paragraph direction by examining text.
- *
- * @hide
+ * Interface for objects that use a heuristic for guessing at the paragraph direction by examining text.
*/
public interface TextDirectionHeuristic {
- boolean isRtl(char[] text, int start, int count);
+ /**
+ * Guess if a chars array is in the RTL direction or not.
+ *
+ * @param array the char array.
+ * @param start start index, inclusive.
+ * @param count the length to check, must not be negative and not greater than
+ * {@code array.length - start}.
+ * @return true if all chars in the range are to be considered in a RTL direction,
+ * false otherwise.
+ */
+ boolean isRtl(char[] array, int start, int count);
+
+ /**
+ * Guess if a {@code CharSequence} is in the RTL direction or not.
+ *
+ * @param cs the CharSequence.
+ * @param start start index, inclusive.
+ * @param count the length to check, must not be negative and not greater than
+ * {@code CharSequence.length() - start}.
+ * @return true if all chars in the range are to be considered in a RTL direction,
+ * false otherwise.
+ */
+ boolean isRtl(CharSequence cs, int start, int count);
}
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index df8c4c665f1e..7d7e3a935f94 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -19,43 +19,45 @@ package android.text;
import android.view.View;
+import java.nio.CharBuffer;
+
/**
* Some objects that implement TextDirectionHeuristic.
*
- * @hide
*/
public class TextDirectionHeuristics {
- /** Always decides that the direction is left to right. */
+ /**
+ * Always decides that the direction is left to right.
+ */
public static final TextDirectionHeuristic LTR =
new TextDirectionHeuristicInternal(null /* no algorithm */, false);
- /** Always decides that the direction is right to left. */
+ /**
+ * Always decides that the direction is right to left.
+ */
public static final TextDirectionHeuristic RTL =
new TextDirectionHeuristicInternal(null /* no algorithm */, true);
/**
- * Determines the direction based on the first strong directional character,
- * including bidi format chars, falling back to left to right if it
- * finds none. This is the default behavior of the Unicode Bidirectional
- * Algorithm.
+ * Determines the direction based on the first strong directional character, including bidi
+ * format chars, falling back to left to right if it finds none. This is the default behavior
+ * of the Unicode Bidirectional Algorithm.
*/
public static final TextDirectionHeuristic FIRSTSTRONG_LTR =
new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false);
/**
- * Determines the direction based on the first strong directional character,
- * including bidi format chars, falling back to right to left if it
- * finds none. This is similar to the default behavior of the Unicode
- * Bidirectional Algorithm, just with different fallback behavior.
+ * Determines the direction based on the first strong directional character, including bidi
+ * format chars, falling back to right to left if it finds none. This is similar to the default
+ * behavior of the Unicode Bidirectional Algorithm, just with different fallback behavior.
*/
public static final TextDirectionHeuristic FIRSTSTRONG_RTL =
new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
/**
- * If the text contains any strong right to left non-format character, determines
- * that the direction is right to left, falling back to left to right if it
- * finds none.
+ * If the text contains any strong right to left non-format character, determines that the
+ * direction is right to left, falling back to left to right if it finds none.
*/
public static final TextDirectionHeuristic ANYRTL_LTR =
new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
@@ -65,8 +67,39 @@ public class TextDirectionHeuristics {
*/
public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE;
- private static enum TriState {
- TRUE, FALSE, UNKNOWN;
+ /**
+ * State constants for taking care about true / false / unknown
+ */
+ private static final int STATE_TRUE = 0;
+ private static final int STATE_FALSE = 1;
+ private static final int STATE_UNKNOWN = 2;
+
+ private static int isRtlText(int directionality) {
+ switch (directionality) {
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+ return STATE_FALSE;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+ return STATE_TRUE;
+ default:
+ return STATE_UNKNOWN;
+ }
+ }
+
+ private static int isRtlTextOrFormat(int directionality) {
+ switch (directionality) {
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
+ return STATE_FALSE;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
+ return STATE_TRUE;
+ default:
+ return STATE_UNKNOWN;
+ }
}
/**
@@ -87,21 +120,26 @@ public class TextDirectionHeuristics {
abstract protected boolean defaultIsRtl();
@Override
- public boolean isRtl(char[] chars, int start, int count) {
- if (chars == null || start < 0 || count < 0 || chars.length - count < start) {
+ public boolean isRtl(char[] array, int start, int count) {
+ return isRtl(CharBuffer.wrap(array), start, count);
+ }
+
+ @Override
+ public boolean isRtl(CharSequence cs, int start, int count) {
+ if (cs == null || start < 0 || count < 0 || cs.length() - count < start) {
throw new IllegalArgumentException();
}
if (mAlgorithm == null) {
return defaultIsRtl();
}
- return doCheck(chars, start, count);
+ return doCheck(cs, start, count);
}
- private boolean doCheck(char[] chars, int start, int count) {
- switch(mAlgorithm.checkRtl(chars, start, count)) {
- case TRUE:
+ private boolean doCheck(CharSequence cs, int start, int count) {
+ switch(mAlgorithm.checkRtl(cs, start, count)) {
+ case STATE_TRUE:
return true;
- case FALSE:
+ case STATE_FALSE:
return false;
default:
return defaultIsRtl();
@@ -124,58 +162,26 @@ public class TextDirectionHeuristics {
}
}
- private static TriState isRtlText(int directionality) {
- switch (directionality) {
- case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
- return TriState.FALSE;
- case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
- case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
- return TriState.TRUE;
- default:
- return TriState.UNKNOWN;
- }
- }
-
- private static TriState isRtlTextOrFormat(int directionality) {
- switch (directionality) {
- case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
- case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
- case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
- return TriState.FALSE;
- case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
- case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
- case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
- case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
- return TriState.TRUE;
- default:
- return TriState.UNKNOWN;
- }
- }
-
/**
* Interface for an algorithm to guess the direction of a paragraph of text.
- *
*/
private static interface TextDirectionAlgorithm {
/**
* Returns whether the range of text is RTL according to the algorithm.
- *
*/
- TriState checkRtl(char[] text, int start, int count);
+ int checkRtl(CharSequence cs, int start, int count);
}
/**
- * Algorithm that uses the first strong directional character to determine
- * the paragraph direction. This is the standard Unicode Bidirectional
- * algorithm.
- *
+ * Algorithm that uses the first strong directional character to determine the paragraph
+ * direction. This is the standard Unicode Bidirectional algorithm.
*/
private static class FirstStrong implements TextDirectionAlgorithm {
@Override
- public TriState checkRtl(char[] text, int start, int count) {
- TriState result = TriState.UNKNOWN;
- for (int i = start, e = start + count; i < e && result == TriState.UNKNOWN; ++i) {
- result = isRtlTextOrFormat(Character.getDirectionality(text[i]));
+ public int checkRtl(CharSequence cs, int start, int count) {
+ int result = STATE_UNKNOWN;
+ for (int i = start, e = start + count; i < e && result == STATE_UNKNOWN; ++i) {
+ result = isRtlTextOrFormat(Character.getDirectionality(cs.charAt(i)));
}
return result;
}
@@ -190,25 +196,24 @@ public class TextDirectionHeuristics {
* Algorithm that uses the presence of any strong directional non-format
* character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
* direction of text.
- *
*/
private static class AnyStrong implements TextDirectionAlgorithm {
private final boolean mLookForRtl;
@Override
- public TriState checkRtl(char[] text, int start, int count) {
+ public int checkRtl(CharSequence cs, int start, int count) {
boolean haveUnlookedFor = false;
for (int i = start, e = start + count; i < e; ++i) {
- switch (isRtlText(Character.getDirectionality(text[i]))) {
- case TRUE:
+ switch (isRtlText(Character.getDirectionality(cs.charAt(i)))) {
+ case STATE_TRUE:
if (mLookForRtl) {
- return TriState.TRUE;
+ return STATE_TRUE;
}
haveUnlookedFor = true;
break;
- case FALSE:
+ case STATE_FALSE:
if (!mLookForRtl) {
- return TriState.FALSE;
+ return STATE_FALSE;
}
haveUnlookedFor = true;
break;
@@ -217,9 +222,9 @@ public class TextDirectionHeuristics {
}
}
if (haveUnlookedFor) {
- return mLookForRtl ? TriState.FALSE : TriState.TRUE;
+ return mLookForRtl ? STATE_FALSE : STATE_TRUE;
}
- return TriState.UNKNOWN;
+ return STATE_UNKNOWN;
}
private AnyStrong(boolean lookForRtl) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 1fecf81d5a4f..e34a0efccf21 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -664,14 +664,13 @@ class TextLine {
}
}
- int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
if (mCharsValid) {
return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
- flags, offset, cursorOpt);
+ offset, cursorOpt);
} else {
return wp.getTextRunCursor(mText, mStart + spanStart,
- mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart;
+ mStart + spanLimit, mStart + offset, cursorOpt) - mStart;
}
}
@@ -738,15 +737,13 @@ class TextLine {
int contextLen = contextEnd - contextStart;
if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
- int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
if (mCharsValid) {
ret = wp.getTextRunAdvances(mChars, start, runLen,
- contextStart, contextLen, flags, null, 0);
+ contextStart, contextLen, null, 0);
} else {
int delta = mStart;
- ret = wp.getTextRunAdvances(mText, delta + start,
- delta + end, delta + contextStart, delta + contextEnd,
- flags, null, 0);
+ ret = wp.getTextRunAdvances(mText, delta + start, delta + end,
+ delta + contextStart, delta + contextEnd, null, 0);
}
}
@@ -786,8 +783,7 @@ class TextLine {
wp.setAntiAlias(previousAntiAlias);
}
- drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
- x, y + wp.baselineShift);
+ drawTextRun(c, wp, start, end, contextStart, contextEnd, x, y + wp.baselineShift);
}
return runIsRtl ? -ret : ret;
@@ -970,23 +966,21 @@ class TextLine {
* @param end the end of the run
* @param contextStart the start of context for the run
* @param contextEnd the end of the context for the run
- * @param runIsRtl true if the run is right-to-left
* @param x the x position of the left edge of the run
* @param y the baseline of the run
*/
private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
- int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
+ int contextStart, int contextEnd, float x, int y) {
- int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
if (mCharsValid) {
int count = end - start;
int contextCount = contextEnd - contextStart;
c.drawTextRun(mChars, start, count, contextStart, contextCount,
- x, y, flags, wp);
+ x, y, wp);
} else {
int delta = mStart;
c.drawTextRun(mText, delta + start, delta + end,
- delta + contextStart, delta + contextEnd, x, y, flags, wp);
+ delta + contextStart, delta + contextEnd, x, y, wp);
}
}
diff --git a/core/java/android/text/bidi/BidiFormatter.java b/core/java/android/text/bidi/BidiFormatter.java
new file mode 100644
index 000000000000..370cbf77f2c0
--- /dev/null
+++ b/core/java/android/text/bidi/BidiFormatter.java
@@ -0,0 +1,1123 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.bidi;
+
+import android.text.TextDirectionHeuristic;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.view.View;
+
+import static android.text.TextDirectionHeuristics.FIRSTSTRONG_LTR;
+
+import java.util.Locale;
+
+
+/**
+ * Utility class for formatting text for display in a potentially opposite-directionality context
+ * without garbling. The directionality of the context is set at formatter creation and the
+ * directionality of the text can be either estimated or passed in when known. Provides the
+ * following functionality:
+ * <p>
+ * 1. Bidi Wrapping
+ * When text in one language is mixed into a document in another, opposite-directionality language,
+ * e.g. when an English business name is embedded in a Hebrew web page, both the inserted string
+ * and the text surrounding it may be displayed incorrectly unless the inserted string is explicitly
+ * separated from the surrounding text in a "wrapper" that:
+ * <p>
+ * - Declares its directionality so that the string is displayed correctly. This can be done in HTML
+ * markup (e.g. a 'span dir="rtl"' element) by {@link #spanWrap} and similar methods, or - only in
+ * contexts where markup can't be used - in Unicode bidi formatting codes by {@link #unicodeWrap}
+ * and similar methods.
+ * <p>
+ * - Isolates the string's directionality, so it does not unduly affect the surrounding content.
+ * Currently, this can only be done using invisible Unicode characters of the same direction as
+ * the context (LRM or RLM) in addition to the directionality declaration above, thus "resetting"
+ * the directionality to that of the context. The "reset" may need to be done at both ends of the
+ * string. Without "reset" after the string, the string will "stick" to a number or logically
+ * separate opposite-direction text that happens to follow it in-line (even if separated by
+ * neutral content like spaces and punctuation). Without "reset" before the string, the same can
+ * happen there, but only with more opposite-direction text, not a number. One approach is to
+ * "reset" the direction only after each string, on the theory that if the preceding opposite-
+ * direction text is itself bidi-wrapped, the "reset" after it will prevent the sticking. (Doing
+ * the "reset" only before each string definitely does not work because we do not want to require
+ * bidi-wrapping numbers, and a bidi-wrapped opposite-direction string could be followed by a
+ * number.) Still, the safest policy is to do the "reset" on both ends of each string, since RTL
+ * message translations often contain untranslated Latin-script brand names and technical terms,
+ * and one of these can be followed by a bidi-wrapped inserted value. On the other hand, when one
+ * has such a message, it is best to do the "reset" manually in the message translation itself,
+ * since the message's opposite-direction text could be followed by an inserted number, which we
+ * would not bidi-wrap anyway. Thus, "reset" only after the string is the current default. In an
+ * alternative to "reset", recent additions to the HTML, CSS, and Unicode standards allow the
+ * isolation to be part of the directionality declaration. This form of isolation is better than
+ * "reset" because it takes less space, does not require knowing the context directionality, has a
+ * gentler effect than "reset", and protects both ends of the string. However, we do not yet allow
+ * using it because required platforms do not yet support it.
+ * <p>
+ * Providing these wrapping services is the basic purpose of the bidi formatter.
+ * <p>
+ * 2. Directionality estimation
+ * How does one know whether a string about to be inserted into surrounding text has the same
+ * directionality? Well, in many cases, one knows that this must be the case when writing the code
+ * doing the insertion, e.g. when a localized message is inserted into a localized page. In such
+ * cases there is no need to involve the bidi formatter at all. In some other cases, it need not be
+ * the same as the context, but is either constant (e.g. urls are always LTR) or otherwise known.
+ * In the remaining cases, e.g. when the string is user-entered or comes from a database, the
+ * language of the string (and thus its directionality) is not known a priori, and must be
+ * estimated at run-time. The bidi formatter can do this automatically using the default
+ * first-strong estimation algorithm. It can also be configured to use a custom directionality
+ * estimation object.
+ * <p>
+ * 3. Escaping
+ * When wrapping plain text - i.e. text that is not already HTML or HTML-escaped - in HTML markup,
+ * the text must first be HTML-escaped to prevent XSS attacks and other nasty business. This of
+ * course is always true, but the escaping can not be done after the string has already been wrapped
+ * in markup, so the bidi formatter also serves as a last chance and includes escaping services.
+ * <p>
+ * Thus, in a single call, the formatter will escape the input string as specified, determine its
+ * directionality, and wrap it as necessary. It is then up to the caller to insert the return value
+ * in the output.
+ */
+public final class BidiFormatter {
+
+ /**
+ * The default text direction heuristic.
+ */
+ private static TextDirectionHeuristic DEFAULT_TEXT_DIRECTION_HEURISTIC = FIRSTSTRONG_LTR;
+
+ /**
+ * Unicode "Left-To-Right Embedding" (LRE) character.
+ */
+ private static final char LRE = '\u202A';
+
+ /**
+ * Unicode "Right-To-Left Embedding" (RLE) character.
+ */
+ private static final char RLE = '\u202B';
+
+ /**
+ * Unicode "Pop Directional Formatting" (PDF) character.
+ */
+ private static final char PDF = '\u202C';
+
+ /**
+ * Unicode "Left-To-Right Mark" (LRM) character.
+ */
+ private static final char LRM = '\u200E';
+
+ /*
+ * Unicode "Right-To-Left Mark" (RLM) character.
+ */
+ private static final char RLM = '\u200F';
+
+ /*
+ * String representation of LRM
+ */
+ private static final String LRM_STRING = Character.toString(LRM);
+
+ /*
+ * String representation of RLM
+ */
+ private static final String RLM_STRING = Character.toString(RLM);
+
+ /**
+ * "ltr" string constant.
+ */
+ private static final String LTR_STRING = "ltr";
+
+ /**
+ * "rtl" string constant.
+ */
+ private static final String RTL_STRING = "rtl";
+
+ /**
+ * "dir=\"ltr\"" string constant.
+ */
+ private static final String DIR_LTR_STRING = "dir=\"ltr\"";
+
+ /**
+ * "dir=\"rtl\"" string constant.
+ */
+ private static final String DIR_RTL_STRING = "dir=\"rtl\"";
+
+ /**
+ * "right" string constant.
+ */
+ private static final String RIGHT = "right";
+
+ /**
+ * "left" string constant.
+ */
+ private static final String LEFT = "left";
+
+ /**
+ * Empty string constant.
+ */
+ private static final String EMPTY_STRING = "";
+
+ /**
+ * A class for building a BidiFormatter with non-default options.
+ */
+ public static final class Builder {
+ private boolean isRtlContext;
+ private int flags;
+ private TextDirectionHeuristic textDirectionHeuristic;
+
+ /**
+ * Constructor.
+ *
+ */
+ public Builder() {
+ initialize(isRtlLocale(Locale.getDefault()));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param rtlContext Whether the context directionality is RTL.
+ */
+ public Builder(boolean rtlContext) {
+ initialize(rtlContext);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param locale The context locale.
+ */
+ public Builder(Locale locale) {
+ initialize(isRtlLocale(locale));
+ }
+
+ /**
+ * Initializes the builder with the given context directionality and default options.
+ *
+ * @param isRtlContext Whether the context is RTL or not.
+ */
+ private void initialize(boolean isRtlContext) {
+ this.isRtlContext = isRtlContext;
+ textDirectionHeuristic = DEFAULT_TEXT_DIRECTION_HEURISTIC;
+ this.flags = DEFAULT_FLAGS;
+ }
+
+ /**
+ * Specifies whether the BidiFormatter to be built should also "reset" directionality before
+ * a string being bidi-wrapped, not just after it. The default is false.
+ */
+ public Builder stereoReset(boolean stereoReset) {
+ if (stereoReset) {
+ flags |= FLAG_STEREO_RESET;
+ } else {
+ flags &= ~FLAG_STEREO_RESET;
+ }
+ return this;
+ }
+
+ /**
+ * Specifies the default directionality estimation algorithm to be used by the BidiFormatter.
+ * By default, uses the first-strong heuristic.
+ *
+ * @param heuristic the {@code TextDirectionHeuristic} to use.
+ * @return the builder itself.
+ */
+ public Builder setTextDirectionHeuristic(TextDirectionHeuristic heuristic) {
+ this.textDirectionHeuristic = heuristic;
+ return this;
+ }
+
+ private static BidiFormatter getDefaultInstanceFromContext(boolean isRtlContext) {
+ return isRtlContext ? DEFAULT_RTL_INSTANCE : DEFAULT_LTR_INSTANCE;
+ }
+
+ /**
+ * @return A BidiFormatter with the specified options.
+ */
+ public BidiFormatter build() {
+ if (flags == DEFAULT_FLAGS &&
+ textDirectionHeuristic == DEFAULT_TEXT_DIRECTION_HEURISTIC) {
+ return getDefaultInstanceFromContext(isRtlContext);
+ }
+ return new BidiFormatter(isRtlContext, flags, textDirectionHeuristic);
+ }
+ }
+
+ //
+ private static final int FLAG_STEREO_RESET = 2;
+ private static final int DEFAULT_FLAGS = FLAG_STEREO_RESET;
+
+ private static final BidiFormatter DEFAULT_LTR_INSTANCE = new BidiFormatter(
+ false /* LTR context */,
+ DEFAULT_FLAGS,
+ DEFAULT_TEXT_DIRECTION_HEURISTIC);
+
+ private static final BidiFormatter DEFAULT_RTL_INSTANCE = new BidiFormatter(
+ true /* RTL context */,
+ DEFAULT_FLAGS,
+ DEFAULT_TEXT_DIRECTION_HEURISTIC);
+
+ private final boolean isRtlContext;
+ private final int flags;
+ private final TextDirectionHeuristic defaultTextDirectionHeuristic;
+
+ /**
+ * Factory for creating an instance of BidiFormatter given the context directionality.
+ *
+ * @param rtlContext Whether the context directionality is RTL.
+ */
+ public static BidiFormatter getInstance(boolean rtlContext) {
+ return new Builder(rtlContext).build();
+ }
+
+ /**
+ * Factory for creating an instance of BidiFormatter given the context locale.
+ *
+ * @param locale The context locale.
+ */
+ public static BidiFormatter getInstance(Locale locale) {
+ return new Builder(locale).build();
+ }
+
+ /**
+ * @param isRtlContext Whether the context directionality is RTL or not.
+ * @param flags The option flags.
+ * @param heuristic The default text direction heuristic.
+ */
+ private BidiFormatter(boolean isRtlContext, int flags, TextDirectionHeuristic heuristic) {
+ this.isRtlContext = isRtlContext;
+ this.flags = flags;
+ this.defaultTextDirectionHeuristic = heuristic;
+ }
+
+ /**
+ * @return Whether the context directionality is RTL
+ */
+ public boolean isRtlContext() {
+ return isRtlContext;
+ }
+
+ /**
+ * @return Whether directionality "reset" should also be done before a string being
+ * bidi-wrapped, not just after it.
+ */
+ public boolean getStereoReset() {
+ return (flags & FLAG_STEREO_RESET) != 0;
+ }
+
+ /**
+ * Returns "rtl" if {@code str}'s estimated directionality is RTL, and "ltr" if it is LTR.
+ *
+ * @param str String whose directionality is to be estimated.
+ * @return "rtl" if {@code str}'s estimated directionality is RTL, and "ltr" otherwise.
+ */
+ public String dirAttrValue(String str) {
+ return dirAttrValue(isRtl(str));
+ }
+
+ /**
+ * Operates like {@link #dirAttrValue(String)}, but uses a given heuristic to estimate the
+ * {@code str}'s directionality.
+ *
+ * @param str String whose directionality is to be estimated.
+ * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s
+ * directionality.
+ * @return "rtl" if {@code str}'s estimated directionality is RTL, and "ltr" otherwise.
+ */
+ public String dirAttrValue(String str, TextDirectionHeuristic heuristic) {
+ return dirAttrValue(heuristic.isRtl(str, 0, str.length()));
+ }
+
+ /**
+ * Returns "rtl" if the given directionality is RTL, and "ltr" if it is LTR.
+ *
+ * @param isRtl Whether the directionality is RTL or not.
+ * @return "rtl" if the given directionality is RTL, and "ltr" otherwise.
+ */
+ public String dirAttrValue(boolean isRtl) {
+ return isRtl ? RTL_STRING : LTR_STRING;
+ }
+
+ /**
+ * Returns "dir=\"ltr\"" or "dir=\"rtl\"", depending on {@code str}'s estimated directionality,
+ * if it is not the same as the context directionality. Otherwise, returns the empty string.
+ *
+ * @param str String whose directionality is to be estimated.
+ * @return "dir=\"rtl\"" for RTL text in non-RTL context; "dir=\"ltr\"" for LTR text in non-LTR
+ * context; else, the empty string.
+ */
+ public String dirAttr(String str) {
+ return dirAttr(isRtl(str));
+ }
+
+ /**
+ * Operates like {@link #dirAttr(String)}, but uses a given heuristic to estimate the
+ * {@code str}'s directionality.
+ *
+ * @param str String whose directionality is to be estimated.
+ * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s
+ * directionality.
+ * @return "dir=\"rtl\"" for RTL text in non-RTL context; "dir=\"ltr\"" for LTR text in non-LTR
+ * context; else, the empty string.
+ */
+ public String dirAttr(String str, TextDirectionHeuristic heuristic) {
+ return dirAttr(heuristic.isRtl(str, 0, str.length()));
+ }
+
+ /**
+ * Returns "dir=\"ltr\"" or "dir=\"rtl\"", depending on the given directionality, if it is not
+ * the same as the context directionality. Otherwise, returns the empty string.
+ *
+ * @param isRtl Whether the directionality is RTL or not
+ * @return "dir=\"rtl\"" for RTL text in non-RTL context; "dir=\"ltr\"" for LTR text in non-LTR
+ * context; else, the empty string.
+ */
+ public String dirAttr(boolean isRtl) {
+ return (isRtl != isRtlContext) ? (isRtl ? DIR_RTL_STRING : DIR_LTR_STRING) : EMPTY_STRING;
+ }
+
+ /**
+ * Returns a Unicode bidi mark matching the context directionality (LRM or RLM) if either the
+ * overall or the exit directionality of a given string is opposite to the context directionality.
+ * Putting this after the string (including its directionality declaration wrapping) prevents it
+ * from "sticking" to other opposite-directionality text or a number appearing after it inline
+ * with only neutral content in between. Otherwise returns the empty string. While the exit
+ * directionality is determined by scanning the end of the string, the overall directionality is
+ * given explicitly in {@code dir}.
+ *
+ * @param str String after which the mark may need to appear.
+ * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context;
+ * else, the empty string.
+ */
+ public String markAfter(String str) {
+ return markAfter(str, defaultTextDirectionHeuristic);
+ }
+
+ /**
+ * Operates like {@link #markAfter(String)}, but uses a given heuristic to estimate the
+ * {@code str}'s directionality.
+ *
+ * @param str String after which the mark may need to appear.
+ * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s
+ * directionality.
+ * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context;
+ * else, the empty string.
+ */
+ public String markAfter(String str, TextDirectionHeuristic heuristic) {
+ final boolean isRtl = heuristic.isRtl(str, 0, str.length());
+ // getExitDir() is called only if needed (short-circuit).
+ if (!isRtlContext && (isRtl || getExitDir(str) == DIR_RTL)) {
+ return LRM_STRING;
+ }
+ if (isRtlContext && (!isRtl || getExitDir(str) == DIR_LTR)) {
+ return RLM_STRING;
+ }
+ return EMPTY_STRING;
+ }
+
+ /**
+ * Returns a Unicode bidi mark matching the context directionality (LRM or RLM) if either the
+ * overall or the entry directionality of a given string is opposite to the context
+ * directionality. Putting this before the string (including its directionality declaration
+ * wrapping) prevents it from "sticking" to other opposite-directionality text appearing before it
+ * inline with only neutral content in between. Otherwise returns the empty string. While the
+ * entry directionality is determined by scanning the beginning of the string, the overall
+ * directionality is given explicitly in {@code dir}.
+ *
+ * @param str String before which the mark may need to appear.
+ * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context;
+ * else, the empty string.
+ */
+ public String markBefore(String str) {
+ return markBefore(str, defaultTextDirectionHeuristic);
+ }
+
+ /**
+ * Operates like {@link #markBefore(String)}, but uses a given heuristic to estimate the
+ * {@code str}'s directionality.
+ *
+ * @param str String before which the mark may need to appear.
+ * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s
+ * directionality.
+ * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context;
+ * else, the empty string.
+ */
+ public String markBefore(String str, TextDirectionHeuristic heuristic) {
+ final boolean isRtl = heuristic.isRtl(str, 0, str.length());
+ // getEntryDir() is called only if needed (short-circuit).
+ if (!isRtlContext && (isRtl || getEntryDir(str) == DIR_RTL)) {
+ return LRM_STRING;
+ }
+ if (isRtlContext && (!isRtl || getEntryDir(str) == DIR_LTR)) {
+ return RLM_STRING;
+ }
+ return EMPTY_STRING;
+ }
+
+ /**
+ * Returns the Unicode bidi mark matching the context directionality (LRM for LTR context
+ * directionality, RLM for RTL context directionality).
+ */
+ public String mark() {
+ return isRtlContext ? RLM_STRING : LRM_STRING;
+ }
+
+ /**
+ * Returns "right" for RTL context directionality. Otherwise for LTR context directionality
+ * returns "left".
+ */
+ public String startEdge() {
+ return isRtlContext ? RIGHT : LEFT;
+ }
+
+ /**
+ * Returns "left" for RTL context directionality. Otherwise for LTR context directionality
+ * returns "right".
+ */
+ public String endEdge() {
+ return isRtlContext ? LEFT : RIGHT;
+ }
+
+ /**
+ * Estimates the directionality of a string using the default text direction heuristic.
+ *
+ * @param str String whose directionality is to be estimated.
+ * @return true if {@code str}'s estimated overall directionality is RTL. Otherwise returns
+ * false.
+ */
+ public boolean isRtl(String str) {
+ return defaultTextDirectionHeuristic.isRtl(str, 0, str.length());
+ }
+
+ /**
+ * Formats a given string of unknown directionality for use in HTML output of the context
+ * directionality, so an opposite-directionality string is neither garbled nor garbles its
+ * surroundings.
+ * <p>
+ * The algorithm: estimates the directionality of the given string using the given heuristic.
+ * If the directionality is known, pass TextDirectionHeuristics.LTR or RTL for heuristic.
+ * In case its directionality doesn't match the context directionality, wraps it with a 'span'
+ * element and adds a "dir" attribute (either 'dir=\"rtl\"' or 'dir=\"ltr\"').
+ * <p>
+ * If {@code isolate}, directionally isolates the string so that it does not garble its
+ * surroundings. Currently, this is done by "resetting" the directionality after the string by
+ * appending a trailing Unicode bidi mark matching the context directionality (LRM or RLM) when
+ * either the overall directionality or the exit directionality of the string is opposite to that
+ * of the context. If the formatter was built using {@link Builder#stereoReset(boolean)} and
+ * passing "true" as an argument, also prepends a Unicode bidi mark matching the context
+ * directionality when either the overall directionality or the entry directionality of the
+ * string is opposite to that of the context.
+ * <p>
+ *
+ * @param str The input string.
+ * @param heuristic The algorithm to be used to estimate the string's overall direction.
+ * @param isolate Whether to directionally isolate the string to prevent it from garbling the
+ * content around it.
+ * @return Input string after applying the above processing.
+ */
+ public String spanWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) {
+ final boolean isRtl = heuristic.isRtl(str, 0, str.length());
+ String origStr = str;
+ str = TextUtils.htmlEncode(str);
+
+ StringBuilder result = new StringBuilder();
+ if (getStereoReset() && isolate) {
+ result.append(markBefore(origStr,
+ isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR));
+ }
+ if (isRtl != isRtlContext) {
+ result.append("<span ").append(dirAttr(isRtl)).append('>').append(str).append("</span>");
+ } else {
+ result.append(str);
+ }
+ if (isolate) {
+ result.append(markAfter(origStr,
+ isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR));
+ }
+ return result.toString();
+ }
+
+ /**
+ * Operates like {@link #spanWrap(String, TextDirectionHeuristic, boolean)}, but assumes
+ * {@code isolate} is true.
+ *
+ * @param str The input string.
+ * @param heuristic The algorithm to be used to estimate the string's overall direction.
+ * @return Input string after applying the above processing.
+ */
+ public String spanWrap(String str, TextDirectionHeuristic heuristic) {
+ return spanWrap(str, heuristic, true /* isolate */);
+ }
+
+ /**
+ * Operates like {@link #spanWrap(String, TextDirectionHeuristic, boolean)}, but uses the
+ * formatter's default direction estimation algorithm.
+ *
+ * @param str The input string.
+ * @param isolate Whether to directionally isolate the string to prevent it from garbling the
+ * content around it
+ * @return Input string after applying the above processing.
+ */
+ public String spanWrap(String str, boolean isolate) {
+ return spanWrap(str, defaultTextDirectionHeuristic, isolate);
+ }
+
+ /**
+ * Operates like {@link #spanWrap(String, TextDirectionHeuristic, boolean)}, but uses the
+ * formatter's default direction estimation algorithm and assumes {@code isolate} is true.
+ *
+ * @param str The input string.
+ * @return Input string after applying the above processing.
+ */
+ public String spanWrap(String str) {
+ return spanWrap(str, defaultTextDirectionHeuristic, true /* isolate */);
+ }
+
+ /**
+ * Formats a string of given directionality for use in plain-text output of the context
+ * directionality, so an opposite-directionality string is neither garbled nor garbles its
+ * surroundings. As opposed to {@link #spanWrap}, this makes use of Unicode bidi
+ * formatting characters. In HTML, its *only* valid use is inside of elements that do not allow
+ * markup, e.g. the 'option' and 'title' elements.
+ * <p>
+ * The algorithm: In case the given directionality doesn't match the context directionality, wraps
+ * the string with Unicode bidi formatting characters: RLE+{@code str}+PDF for RTL text, or
+ * LRE+{@code str}+PDF for LTR text.
+ * <p>
+ * If {@code isolate}, directionally isolates the string so that it does not garble its
+ * surroundings. Currently, this is done by "resetting" the directionality after the string by
+ * appending a trailing Unicode bidi mark matching the context directionality (LRM or RLM) when
+ * either the overall directionality or the exit directionality of the string is opposite to that
+ * of the context. If the formatter was built using {@link Builder#stereoReset(boolean)} and
+ * passing "true" as an argument, also prepends a Unicode bidi mark matching the context
+ * directionality when either the overall directionality or the entry directionality of the
+ * string is opposite to that of the context. Note that as opposed to the overall
+ * directionality, the entry and exit directionalities are determined from the string itself.
+ * <p>
+ * Does *not* do HTML-escaping.
+ *
+ * @param str The input string.
+ * @param heuristic The algorithm to be used to estimate the string's overall direction.
+ * @param isolate Whether to directionally isolate the string to prevent it from garbling the
+ * content around it
+ * @return Input string after applying the above processing.
+ */
+ public String unicodeWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) {
+ final boolean isRtl = heuristic.isRtl(str, 0, str.length());
+ StringBuilder result = new StringBuilder();
+ if (getStereoReset() && isolate) {
+ result.append(markBefore(str,
+ isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR));
+ }
+ if (isRtl != isRtlContext) {
+ result.append(isRtl ? RLE : LRE);
+ result.append(str);
+ result.append(PDF);
+ } else {
+ result.append(str);
+ }
+ if (isolate) {
+ result.append(markAfter(str,
+ isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR));
+ }
+ return result.toString();
+ }
+
+ /**
+ * Operates like {@link #unicodeWrap(String, TextDirectionHeuristic, boolean)}, but assumes
+ * {@code isolate} is true.
+ *
+ * @param str The input string.
+ * @param heuristic The algorithm to be used to estimate the string's overall direction.
+ * @return Input string after applying the above processing.
+ */
+ public String unicodeWrap(String str, TextDirectionHeuristic heuristic) {
+ return unicodeWrap(str, heuristic, true /* isolate */);
+ }
+
+ /**
+ * Operates like {@link #unicodeWrap(String, TextDirectionHeuristic, boolean)}, but uses the
+ * formatter's default direction estimation algorithm.
+ *
+ * @param str The input string.
+ * @param isolate Whether to directionally isolate the string to prevent it from garbling the
+ * content around it
+ * @return Input string after applying the above processing.
+ */
+ public String unicodeWrap(String str, boolean isolate) {
+ return unicodeWrap(str, defaultTextDirectionHeuristic, isolate);
+ }
+
+ /**
+ * Operates like {@link #unicodeWrap(String, TextDirectionHeuristic, boolean)}, but uses the
+ * formatter's default direction estimation algorithm and assumes {@code isolate} is true.
+ *
+ * @param str The input string.
+ * @return Input string after applying the above processing.
+ */
+ public String unicodeWrap(String str) {
+ return unicodeWrap(str, defaultTextDirectionHeuristic, true /* isolate */);
+ }
+
+ /**
+ * Helper method to return true if the Locale directionality is RTL.
+ *
+ * @param locale The Locale whose directionality will be checked to be RTL or LTR
+ * @return true if the {@code locale} directionality is RTL. False otherwise.
+ */
+ private static boolean isRtlLocale(Locale locale) {
+ return (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL);
+ }
+
+ /**
+ * Enum for directionality type.
+ */
+ private static final int DIR_LTR = -1;
+ private static final int DIR_UNKNOWN = 0;
+ private static final int DIR_RTL = +1;
+
+ /**
+ * Returns the directionality of the last character with strong directionality in the string, or
+ * DIR_UNKNOWN if none was encountered. For efficiency, actually scans backwards from the end of
+ * the string. Treats a non-BN character between an LRE/RLE/LRO/RLO and its matching PDF as a
+ * strong character, LTR after LRE/LRO, and RTL after RLE/RLO. The results are undefined for a
+ * string containing unbalanced LRE/RLE/LRO/RLO/PDF characters. The intended use is to check
+ * whether a logically separate item that starts with a number or a character of the string's
+ * exit directionality and follows this string inline (not counting any neutral characters in
+ * between) would "stick" to it in an opposite-directionality context, thus being displayed in
+ * an incorrect position. An LRM or RLM character (the one of the context's directionality)
+ * between the two will prevent such sticking.
+ *
+ * @param str the string to check.
+ */
+ private static int getExitDir(String str) {
+ return new DirectionalityEstimator(str, false /* isHtml */).getExitDir();
+ }
+
+ /**
+ * Returns the directionality of the first character with strong directionality in the string,
+ * or DIR_UNKNOWN if none was encountered. Treats a non-BN character between an
+ * LRE/RLE/LRO/RLO and its matching PDF as a strong character, LTR after LRE/LRO, and RTL after
+ * RLE/RLO. The results are undefined for a string containing unbalanced LRE/RLE/LRO/RLO/PDF
+ * characters. The intended use is to check whether a logically separate item that ends with a
+ * character of the string's entry directionality and precedes the string inline (not counting
+ * any neutral characters in between) would "stick" to it in an opposite-directionality context,
+ * thus being displayed in an incorrect position. An LRM or RLM character (the one of the
+ * context's directionality) between the two will prevent such sticking.
+ *
+ * @param str the string to check.
+ */
+ private static int getEntryDir(String str) {
+ return new DirectionalityEstimator(str, false /* isHtml */).getEntryDir();
+ }
+
+ /**
+ * An object that estimates the directionality of a given string by various methods.
+ *
+ */
+ private static class DirectionalityEstimator {
+
+ // Internal static variables and constants.
+
+ /**
+ * Size of the bidi character class cache. The results of the Character.getDirectionality()
+ * calls on the lowest DIR_TYPE_CACHE_SIZE codepoints are kept in an array for speed.
+ * The 0x700 value is designed to leave all the European and Near Eastern languages in the
+ * cache. It can be reduced to 0x180, restricting the cache to the Western European
+ * languages.
+ */
+ private static final int DIR_TYPE_CACHE_SIZE = 0x700;
+
+ /**
+ * The bidi character class cache.
+ */
+ private static final byte DIR_TYPE_CACHE[];
+
+ static {
+ DIR_TYPE_CACHE = new byte[DIR_TYPE_CACHE_SIZE];
+ for (int i = 0; i < DIR_TYPE_CACHE_SIZE; i++) {
+ DIR_TYPE_CACHE[i] = Character.getDirectionality(i);
+ }
+ }
+
+ // Internal instance variables.
+
+ /**
+ * The text to be scanned.
+ */
+ private final String text;
+
+ /**
+ * Whether the text to be scanned is to be treated as HTML, i.e. skipping over tags and
+ * entities when looking for the next / preceding dir type.
+ */
+ private final boolean isHtml;
+
+ /**
+ * The length of the text in chars.
+ */
+ private final int length;
+
+ /**
+ * The current position in the text.
+ */
+ private int charIndex;
+
+ /**
+ * The char encountered by the last dirTypeForward or dirTypeBackward call. If it
+ * encountered a supplementary codepoint, this contains a char that is not a valid
+ * codepoint. This is ok, because this member is only used to detect some well-known ASCII
+ * syntax, e.g. "http://" and the beginning of an HTML tag or entity.
+ */
+ private char lastChar;
+
+ /**
+ * Constructor.
+ *
+ * @param text The string to scan.
+ * @param isHtml Whether the text to be scanned is to be treated as HTML, i.e. skipping over
+ * tags and entities.
+ */
+ DirectionalityEstimator(String text, boolean isHtml) {
+ this.text = text;
+ this.isHtml = isHtml;
+ length = text.length();
+ }
+
+ /**
+ * Returns the directionality of the first character with strong directionality in the
+ * string, or DIR_UNKNOWN if none was encountered. Treats a non-BN character between an
+ * LRE/RLE/LRO/RLO and its matching PDF as a strong character, LTR after LRE/LRO, and RTL
+ * after RLE/RLO. The results are undefined for a string containing unbalanced
+ * LRE/RLE/LRO/RLO/PDF characters.
+ */
+ int getEntryDir() {
+ // The reason for this method name, as opposed to getFirstStrongDir(), is that
+ // "first strong" is a commonly used description of Unicode's estimation algorithm,
+ // but the two must treat formatting characters quite differently. Thus, we are staying
+ // away from both "first" and "last" in these method names to avoid confusion.
+ charIndex = 0;
+ int embeddingLevel = 0;
+ int embeddingLevelDir = DIR_UNKNOWN;
+ int firstNonEmptyEmbeddingLevel = 0;
+ while (charIndex < length && firstNonEmptyEmbeddingLevel == 0) {
+ switch (dirTypeForward()) {
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
+ ++embeddingLevel;
+ embeddingLevelDir = DIR_LTR;
+ break;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
+ ++embeddingLevel;
+ embeddingLevelDir = DIR_RTL;
+ break;
+ case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT:
+ --embeddingLevel;
+ // To restore embeddingLevelDir to its previous value, we would need a
+ // stack, which we want to avoid. Thus, at this point we do not know the
+ // current embedding's directionality.
+ embeddingLevelDir = DIR_UNKNOWN;
+ break;
+ case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL:
+ break;
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+ if (embeddingLevel == 0) {
+ return DIR_LTR;
+ }
+ firstNonEmptyEmbeddingLevel = embeddingLevel;
+ break;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+ if (embeddingLevel == 0) {
+ return DIR_RTL;
+ }
+ firstNonEmptyEmbeddingLevel = embeddingLevel;
+ break;
+ default:
+ firstNonEmptyEmbeddingLevel = embeddingLevel;
+ break;
+ }
+ }
+
+ // We have either found a non-empty embedding or scanned the entire string finding
+ // neither a non-empty embedding nor a strong character outside of an embedding.
+ if (firstNonEmptyEmbeddingLevel == 0) {
+ // We have not found a non-empty embedding. Thus, the string contains neither a
+ // non-empty embedding nor a strong character outside of an embedding.
+ return DIR_UNKNOWN;
+ }
+
+ // We have found a non-empty embedding.
+ if (embeddingLevelDir != DIR_UNKNOWN) {
+ // We know the directionality of the non-empty embedding.
+ return embeddingLevelDir;
+ }
+
+ // We do not remember the directionality of the non-empty embedding we found. So, we go
+ // backwards to find the start of the non-empty embedding and get its directionality.
+ while (charIndex > 0) {
+ switch (dirTypeBackward()) {
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
+ if (firstNonEmptyEmbeddingLevel == embeddingLevel) {
+ return DIR_LTR;
+ }
+ --embeddingLevel;
+ break;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
+ if (firstNonEmptyEmbeddingLevel == embeddingLevel) {
+ return DIR_RTL;
+ }
+ --embeddingLevel;
+ break;
+ case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT:
+ ++embeddingLevel;
+ break;
+ }
+ }
+ // We should never get here.
+ return DIR_UNKNOWN;
+ }
+
+ /**
+ * Returns the directionality of the last character with strong directionality in the
+ * string, or DIR_UNKNOWN if none was encountered. For efficiency, actually scans backwards
+ * from the end of the string. Treats a non-BN character between an LRE/RLE/LRO/RLO and its
+ * matching PDF as a strong character, LTR after LRE/LRO, and RTL after RLE/RLO. The results
+ * are undefined for a string containing unbalanced LRE/RLE/LRO/RLO/PDF characters.
+ */
+ int getExitDir() {
+ // The reason for this method name, as opposed to getLastStrongDir(), is that "last
+ // strong" sounds like the exact opposite of "first strong", which is a commonly used
+ // description of Unicode's estimation algorithm (getUnicodeDir() above), but the two
+ // must treat formatting characters quite differently. Thus, we are staying away from
+ // both "first" and "last" in these method names to avoid confusion.
+ charIndex = length;
+ int embeddingLevel = 0;
+ int lastNonEmptyEmbeddingLevel = 0;
+ while (charIndex > 0) {
+ switch (dirTypeBackward()) {
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+ if (embeddingLevel == 0) {
+ return DIR_LTR;
+ }
+ if (lastNonEmptyEmbeddingLevel == 0) {
+ lastNonEmptyEmbeddingLevel = embeddingLevel;
+ }
+ break;
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
+ if (lastNonEmptyEmbeddingLevel == embeddingLevel) {
+ return DIR_LTR;
+ }
+ --embeddingLevel;
+ break;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+ if (embeddingLevel == 0) {
+ return DIR_RTL;
+ }
+ if (lastNonEmptyEmbeddingLevel == 0) {
+ lastNonEmptyEmbeddingLevel = embeddingLevel;
+ }
+ break;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
+ if (lastNonEmptyEmbeddingLevel == embeddingLevel) {
+ return DIR_RTL;
+ }
+ --embeddingLevel;
+ break;
+ case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT:
+ ++embeddingLevel;
+ break;
+ case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL:
+ break;
+ default:
+ if (lastNonEmptyEmbeddingLevel == 0) {
+ lastNonEmptyEmbeddingLevel = embeddingLevel;
+ }
+ break;
+ }
+ }
+ return DIR_UNKNOWN;
+ }
+
+ // Internal methods
+
+ /**
+ * Gets the bidi character class, i.e. Character.getDirectionality(), of a given char, using
+ * a cache for speed. Not designed for supplementary codepoints, whose results we do not
+ * cache.
+ */
+ private static byte getCachedDirectionality(char c) {
+ return c < DIR_TYPE_CACHE_SIZE ? DIR_TYPE_CACHE[c] : Character.getDirectionality(c);
+ }
+
+ /**
+ * Returns the Character.DIRECTIONALITY_... value of the next codepoint and advances
+ * charIndex. If isHtml, and the codepoint is '<' or '&', advances through the tag/entity,
+ * and returns Character.DIRECTIONALITY_WHITESPACE. For an entity, it would be best to
+ * figure out the actual character, and return its dirtype, but treating it as whitespace is
+ * good enough for our purposes.
+ *
+ * @throws java.lang.IndexOutOfBoundsException if called when charIndex >= length or < 0.
+ */
+ byte dirTypeForward() {
+ lastChar = text.charAt(charIndex);
+ if (Character.isHighSurrogate(lastChar)) {
+ int codePoint = Character.codePointAt(text, charIndex);
+ charIndex += Character.charCount(codePoint);
+ return Character.getDirectionality(codePoint);
+ }
+ charIndex++;
+ byte dirType = getCachedDirectionality(lastChar);
+ if (isHtml) {
+ // Process tags and entities.
+ if (lastChar == '<') {
+ dirType = skipTagForward();
+ } else if (lastChar == '&') {
+ dirType = skipEntityForward();
+ }
+ }
+ return dirType;
+ }
+
+ /**
+ * Returns the Character.DIRECTIONALITY_... value of the preceding codepoint and advances
+ * charIndex backwards. If isHtml, and the codepoint is the end of a complete HTML tag or
+ * entity, advances over the whole tag/entity and returns
+ * Character.DIRECTIONALITY_WHITESPACE. For an entity, it would be best to figure out the
+ * actual character, and return its dirtype, but treating it as whitespace is good enough
+ * for our purposes.
+ *
+ * @throws java.lang.IndexOutOfBoundsException if called when charIndex > length or <= 0.
+ */
+ byte dirTypeBackward() {
+ lastChar = text.charAt(charIndex - 1);
+ if (Character.isLowSurrogate(lastChar)) {
+ int codePoint = Character.codePointBefore(text, charIndex);
+ charIndex -= Character.charCount(codePoint);
+ return Character.getDirectionality(codePoint);
+ }
+ charIndex--;
+ byte dirType = getCachedDirectionality(lastChar);
+ if (isHtml) {
+ // Process tags and entities.
+ if (lastChar == '>') {
+ dirType = skipTagBackward();
+ } else if (lastChar == ';') {
+ dirType = skipEntityBackward();
+ }
+ }
+ return dirType;
+ }
+
+ /**
+ * Advances charIndex forward through an HTML tag (after the opening &lt; has already been
+ * read) and returns Character.DIRECTIONALITY_WHITESPACE. If there is no matching &gt;,
+ * does not change charIndex and returns Character.DIRECTIONALITY_OTHER_NEUTRALS (for the
+ * &lt; that hadn't been part of a tag after all).
+ */
+ private byte skipTagForward() {
+ int initialCharIndex = charIndex;
+ while (charIndex < length) {
+ lastChar = text.charAt(charIndex++);
+ if (lastChar == '>') {
+ // The end of the tag.
+ return Character.DIRECTIONALITY_WHITESPACE;
+ }
+ if (lastChar == '"' || lastChar == '\'') {
+ // Skip over a quoted attribute value inside the tag.
+ char quote = lastChar;
+ while (charIndex < length && (lastChar = text.charAt(charIndex++)) != quote) {}
+ }
+ }
+ // The original '<' wasn't the start of a tag after all.
+ charIndex = initialCharIndex;
+ lastChar = '<';
+ return Character.DIRECTIONALITY_OTHER_NEUTRALS;
+ }
+
+ /**
+ * Advances charIndex backward through an HTML tag (after the closing &gt; has already been
+ * read) and returns Character.DIRECTIONALITY_WHITESPACE. If there is no matching &lt;, does
+ * not change charIndex and returns Character.DIRECTIONALITY_OTHER_NEUTRALS (for the &gt;
+ * that hadn't been part of a tag after all). Nevertheless, the running time for calling
+ * skipTagBackward() in a loop remains linear in the size of the text, even for a text like
+ * "&gt;&gt;&gt;&gt;", because skipTagBackward() also stops looking for a matching &lt;
+ * when it encounters another &gt;.
+ */
+ private byte skipTagBackward() {
+ int initialCharIndex = charIndex;
+ while (charIndex > 0) {
+ lastChar = text.charAt(--charIndex);
+ if (lastChar == '<') {
+ // The start of the tag.
+ return Character.DIRECTIONALITY_WHITESPACE;
+ }
+ if (lastChar == '>') {
+ break;
+ }
+ if (lastChar == '"' || lastChar == '\'') {
+ // Skip over a quoted attribute value inside the tag.
+ char quote = lastChar;
+ while (charIndex > 0 && (lastChar = text.charAt(--charIndex)) != quote) {}
+ }
+ }
+ // The original '>' wasn't the end of a tag after all.
+ charIndex = initialCharIndex;
+ lastChar = '>';
+ return Character.DIRECTIONALITY_OTHER_NEUTRALS;
+ }
+
+ /**
+ * Advances charIndex forward through an HTML character entity tag (after the opening
+ * &amp; has already been read) and returns Character.DIRECTIONALITY_WHITESPACE. It would be
+ * best to figure out the actual character and return its dirtype, but this is good enough.
+ */
+ private byte skipEntityForward() {
+ while (charIndex < length && (lastChar = text.charAt(charIndex++)) != ';') {}
+ return Character.DIRECTIONALITY_WHITESPACE;
+ }
+
+ /**
+ * Advances charIndex backward through an HTML character entity tag (after the closing ;
+ * has already been read) and returns Character.DIRECTIONALITY_WHITESPACE. It would be best
+ * to figure out the actual character and return its dirtype, but this is good enough.
+ * If there is no matching &amp;, does not change charIndex and returns
+ * Character.DIRECTIONALITY_OTHER_NEUTRALS (for the ';' that did not start an entity after
+ * all). Nevertheless, the running time for calling skipEntityBackward() in a loop remains
+ * linear in the size of the text, even for a text like ";;;;;;;", because skipTagBackward()
+ * also stops looking for a matching &amp; when it encounters another ;.
+ */
+ private byte skipEntityBackward() {
+ int initialCharIndex = charIndex;
+ while (charIndex > 0) {
+ lastChar = text.charAt(--charIndex);
+ if (lastChar == '&') {
+ return Character.DIRECTIONALITY_WHITESPACE;
+ }
+ if (lastChar == ';') {
+ break;
+ }
+ }
+ charIndex = initialCharIndex;
+ lastChar = ';';
+ return Character.DIRECTIONALITY_OTHER_NEUTRALS;
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index e856501e54bb..dae47b88c4e2 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -74,6 +74,15 @@ public class DisplayMetrics {
public static final int DENSITY_XXHIGH = 480;
/**
+ * Standard quantized DPI for extra-extra-extra-high-density screens. Applications
+ * should not generally worry about this density; relying on XHIGH graphics
+ * being scaled up to it should be sufficient for almost all cases. A typical
+ * use of this density would be 4K television screens -- 3840x2160, which
+ * is 2x a traditional HD 1920x1080 screen which runs at DENSITY_XHIGH.
+ */
+ public static final int DENSITY_XXXHIGH = 640;
+
+ /**
* The reference density used throughout the system.
*/
public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 80c9324aae99..181020536759 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -515,22 +515,22 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) {
- return nQuickReject(mRenderer, left, top, right, bottom);
+ return nQuickReject(mRenderer, left, top, right, bottom, type.nativeInt);
}
private static native boolean nQuickReject(int renderer, float left, float top,
- float right, float bottom);
+ float right, float bottom, int edge);
@Override
public boolean quickReject(Path path, EdgeType type) {
path.computeBounds(mPathBounds, true);
return nQuickReject(mRenderer, mPathBounds.left, mPathBounds.top,
- mPathBounds.right, mPathBounds.bottom);
+ mPathBounds.right, mPathBounds.bottom, type.nativeInt);
}
@Override
public boolean quickReject(RectF rect, EdgeType type) {
- return nQuickReject(mRenderer, rect.left, rect.top, rect.right, rect.bottom);
+ return nQuickReject(mRenderer, rect.left, rect.top, rect.right, rect.bottom, type.nativeInt);
}
///////////////////////////////////////////////////////////////////////////
@@ -1176,14 +1176,14 @@ class GLES20Canvas extends HardwareCanvas {
int modifiers = setupModifiers(paint);
try {
- nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
+ nDrawText(mRenderer, text, index, count, x, y, paint.mNativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
private static native void nDrawText(int renderer, char[] text, int index, int count,
- float x, float y, int bidiFlags, int paint);
+ float x, float y, int paint);
@Override
public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
@@ -1191,16 +1191,14 @@ class GLES20Canvas extends HardwareCanvas {
try {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
- nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
- paint.mNativePaint);
+ nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mNativePaint);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawText(this, start, end, x, y,
paint);
} else {
char[] buf = TemporaryBuffer.obtain(end - start);
TextUtils.getChars(text, start, end, buf, 0);
- nDrawText(mRenderer, buf, 0, end - start, x, y,
- paint.mBidiFlags, paint.mNativePaint);
+ nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mNativePaint);
TemporaryBuffer.recycle(buf);
}
} finally {
@@ -1216,21 +1214,20 @@ class GLES20Canvas extends HardwareCanvas {
int modifiers = setupModifiers(paint);
try {
- nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
+ nDrawText(mRenderer, text, start, end, x, y, paint.mNativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
private static native void nDrawText(int renderer, String text, int start, int end,
- float x, float y, int bidiFlags, int paint);
+ float x, float y, int paint);
@Override
public void drawText(String text, float x, float y, Paint paint) {
int modifiers = setupModifiers(paint);
try {
- nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
- paint.mNativePaint);
+ nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mNativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
@@ -1246,14 +1243,14 @@ class GLES20Canvas extends HardwareCanvas {
int modifiers = setupModifiers(paint);
try {
nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
+ paint.mNativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
private static native void nDrawTextOnPath(int renderer, char[] text, int index, int count,
- int path, float hOffset, float vOffset, int bidiFlags, int nativePaint);
+ int path, float hOffset, float vOffset, int nativePaint);
@Override
public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
@@ -1262,28 +1259,25 @@ class GLES20Canvas extends HardwareCanvas {
int modifiers = setupModifiers(paint);
try {
nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
+ paint.mNativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
private static native void nDrawTextOnPath(int renderer, String text, int start, int end,
- int path, float hOffset, float vOffset, int bidiFlags, int nativePaint);
+ int path, float hOffset, float vOffset, int nativePaint);
@Override
public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
- float x, float y, int dir, Paint paint) {
+ float x, float y, Paint paint) {
if ((index | count | text.length - index - count) < 0) {
throw new IndexOutOfBoundsException();
}
- if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
- throw new IllegalArgumentException("Unknown direction: " + dir);
- }
int modifiers = setupModifiers(paint);
try {
- nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
+ nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y,
paint.mNativePaint);
} finally {
if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
@@ -1291,32 +1285,31 @@ class GLES20Canvas extends HardwareCanvas {
}
private static native void nDrawTextRun(int renderer, char[] text, int index, int count,
- int contextIndex, int contextCount, float x, float y, int dir, int nativePaint);
+ int contextIndex, int contextCount, float x, float y, int nativePaint);
@Override
public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
- float x, float y, int dir, Paint paint) {
+ float x, float y, Paint paint) {
if ((start | end | end - start | text.length() - end) < 0) {
throw new IndexOutOfBoundsException();
}
int modifiers = setupModifiers(paint);
try {
- int flags = dir == 0 ? 0 : 1;
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
- contextEnd, x, y, flags, paint.mNativePaint);
+ contextEnd, x, y, paint.mNativePaint);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawTextRun(this, start, end,
- contextStart, contextEnd, x, y, flags, paint);
+ contextStart, contextEnd, x, y, paint);
} else {
int contextLen = contextEnd - contextStart;
int len = end - start;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
- x, y, flags, paint.mNativePaint);
+ x, y, paint.mNativePaint);
TemporaryBuffer.recycle(buf);
}
} finally {
@@ -1325,7 +1318,7 @@ class GLES20Canvas extends HardwareCanvas {
}
private static native void nDrawTextRun(int renderer, String text, int start, int end,
- int contextStart, int contextEnd, float x, float y, int flags, int nativePaint);
+ int contextStart, int contextEnd, float x, float y, int nativePaint);
@Override
public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index 7da245157949..947cf4413178 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -267,15 +267,15 @@ class GLES20RecordingCanvas extends GLES20Canvas {
@Override
public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
- float x, float y, int dir, Paint paint) {
- super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, dir, paint);
+ float x, float y, Paint paint) {
+ super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, paint);
recordShaderBitmap(paint);
}
@Override
public void drawTextRun(CharSequence text, int start, int end, int contextStart,
- int contextEnd, float x, float y, int dir, Paint paint) {
- super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, dir, paint);
+ int contextEnd, float x, float y, Paint paint) {
+ super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, paint);
recordShaderBitmap(paint);
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index e0d48a4dfa28..7c4bcfde5a53 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -48,7 +48,7 @@ import java.util.concurrent.locks.ReentrantLock;
import static javax.microedition.khronos.egl.EGL10.*;
/**
- * Interface for rendering a ViewAncestor using hardware acceleration.
+ * Interface for rendering a view hierarchy using hardware acceleration.
*
* @hide
*/
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index c10f28742888..a972b750743e 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -215,6 +215,7 @@ public class Surface implements Parcelable {
private int mNativeSurfaceControl; // SurfaceControl*
private int mGenerationId; // incremented each time mNativeSurface changes
private final Canvas mCanvas = new CompatibleCanvas();
+ private int mCanvasSaveCount; // Canvas save count at time of lockCanvas()
// The Translator for density compatibility mode. This is used for scaling
// the canvas to perform the appropriate density transformation.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index df8232b0fc32..25cad87a21e0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9929,8 +9929,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
outRect.set(mLeft, mTop, mRight, mBottom);
} else {
final RectF tmpRect = mAttachInfo.mTmpTransformRect;
- tmpRect.set(-info.mPivotX, -info.mPivotY,
- getWidth() - info.mPivotX, getHeight() - info.mPivotY);
+ tmpRect.set(0, 0, getWidth(), getHeight());
info.mMatrix.mapRect(tmpRect);
outRect.set((int) tmpRect.left + mLeft, (int) tmpRect.top + mTop,
(int) tmpRect.right + mLeft, (int) tmpRect.bottom + mTop);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 214541998931..cad7ae329530 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8801,11 +8801,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
public void drawTextRun(Canvas c, int start, int end,
- int contextStart, int contextEnd, float x, float y, int flags, Paint p) {
+ int contextStart, int contextEnd, float x, float y, Paint p) {
int count = end - start;
int contextCount = contextEnd - contextStart;
c.drawTextRun(mChars, start + mStart, count, contextStart + mStart,
- contextCount, x, y, flags, p);
+ contextCount, x, y, p);
}
public float measureText(int start, int end, Paint p) {
@@ -8817,30 +8817,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
public float getTextRunAdvances(int start, int end, int contextStart,
- int contextEnd, int flags, float[] advances, int advancesIndex,
+ int contextEnd, float[] advances, int advancesIndex,
Paint p) {
int count = end - start;
int contextCount = contextEnd - contextStart;
return p.getTextRunAdvances(mChars, start + mStart, count,
- contextStart + mStart, contextCount, flags, advances,
+ contextStart + mStart, contextCount, advances,
advancesIndex);
}
- public float getTextRunAdvances(int start, int end, int contextStart,
- int contextEnd, int flags, float[] advances, int advancesIndex,
- Paint p, int reserved) {
- int count = end - start;
- int contextCount = contextEnd - contextStart;
- return p.getTextRunAdvances(mChars, start + mStart, count,
- contextStart + mStart, contextCount, flags, advances,
- advancesIndex, reserved);
- }
-
- public int getTextRunCursor(int contextStart, int contextEnd, int flags,
+ public int getTextRunCursor(int contextStart, int contextEnd,
int offset, int cursorOpt, Paint p) {
int contextCount = contextEnd - contextStart;
return p.getTextRunCursor(mChars, contextStart + mStart,
- contextCount, flags, offset + mStart, cursorOpt);
+ contextCount, offset + mStart, cursorOpt);
}
}
diff --git a/core/java/com/android/internal/app/IAppOpsCallback.aidl b/core/java/com/android/internal/app/IAppOpsCallback.aidl
index 4e75a61bc03f..afbc6096ede1 100644
--- a/core/java/com/android/internal/app/IAppOpsCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsCallback.aidl
@@ -16,6 +16,8 @@
package com.android.internal.app;
+// This interface is also used by native code, so must
+// be kept in sync with frameworks/native/include/binder/IAppOpsCallback.h
oneway interface IAppOpsCallback {
void opChanged(int op, String packageName);
}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 83967f60e5b1..a9da86376153 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -20,13 +20,17 @@ import android.app.AppOpsManager;
import com.android.internal.app.IAppOpsCallback;
interface IAppOpsService {
- List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
- List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
- void setMode(int code, int uid, String packageName, int mode);
+ // These first methods are also called by native code, so must
+ // be kept in sync with frameworks/native/include/binder/IAppOpsService.h
int checkOperation(int code, int uid, String packageName);
int noteOperation(int code, int uid, String packageName);
int startOperation(int code, int uid, String packageName);
void finishOperation(int code, int uid, String packageName);
void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
void stopWatchingMode(IAppOpsCallback callback);
+
+ // Remaining methods are only used in Java.
+ List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
+ List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
+ void setMode(int code, int uid, String packageName, int mode);
}
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index c517a68e2b90..8282d237b324 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -31,6 +31,7 @@ import com.android.internal.util.ProcFileReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.net.ProtocolException;
import libcore.io.IoUtils;
@@ -41,7 +42,8 @@ import libcore.io.IoUtils;
public class NetworkStatsFactory {
private static final String TAG = "NetworkStatsFactory";
- // TODO: consider moving parsing to native code
+ private static final boolean USE_NATIVE_PARSING = true;
+ private static final boolean SANITY_CHECK_NATIVE = false;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
private final File mStatsXtIfaceAll;
@@ -69,7 +71,7 @@ public class NetworkStatsFactory {
*
* @throws IllegalStateException when problem parsing stats.
*/
- public NetworkStats readNetworkStatsSummaryDev() throws IllegalStateException {
+ public NetworkStats readNetworkStatsSummaryDev() throws IOException {
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
@@ -105,11 +107,9 @@ public class NetworkStatsFactory {
reader.finishLine();
}
} catch (NullPointerException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new ProtocolException("problem parsing stats", e);
} catch (NumberFormatException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
- } catch (IOException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new ProtocolException("problem parsing stats", e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
@@ -124,7 +124,7 @@ public class NetworkStatsFactory {
*
* @throws IllegalStateException when problem parsing stats.
*/
- public NetworkStats readNetworkStatsSummaryXt() throws IllegalStateException {
+ public NetworkStats readNetworkStatsSummaryXt() throws IOException {
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
// return null when kernel doesn't support
@@ -154,11 +154,9 @@ public class NetworkStatsFactory {
reader.finishLine();
}
} catch (NullPointerException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new ProtocolException("problem parsing stats", e);
} catch (NumberFormatException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
- } catch (IOException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new ProtocolException("problem parsing stats", e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
@@ -166,17 +164,33 @@ public class NetworkStatsFactory {
return stats;
}
- public NetworkStats readNetworkStatsDetail() {
+ public NetworkStats readNetworkStatsDetail() throws IOException {
return readNetworkStatsDetail(UID_ALL);
}
+ public NetworkStats readNetworkStatsDetail(int limitUid) throws IOException {
+ if (USE_NATIVE_PARSING) {
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
+ if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid) != 0) {
+ throw new IOException("Failed to parse network stats");
+ }
+ if (SANITY_CHECK_NATIVE) {
+ final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
+ assertEquals(javaStats, stats);
+ }
+ return stats;
+ } else {
+ return javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
+ }
+ }
+
/**
- * Parse and return {@link NetworkStats} with UID-level details. Values
- * monotonically increase since device boot.
- *
- * @throws IllegalStateException when problem parsing stats.
+ * Parse and return {@link NetworkStats} with UID-level details. Values are
+ * expected to monotonically increase since device boot.
*/
- public NetworkStats readNetworkStatsDetail(int limitUid) throws IllegalStateException {
+ @VisibleForTesting
+ public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid)
+ throws IOException {
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
@@ -188,13 +202,13 @@ public class NetworkStatsFactory {
ProcFileReader reader = null;
try {
// open and consume header line
- reader = new ProcFileReader(new FileInputStream(mStatsXtUid));
+ reader = new ProcFileReader(new FileInputStream(detailPath));
reader.finishLine();
while (reader.hasMoreData()) {
idx = reader.nextInt();
if (idx != lastIdx + 1) {
- throw new IllegalStateException(
+ throw new ProtocolException(
"inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
}
lastIdx = idx;
@@ -215,11 +229,9 @@ public class NetworkStatsFactory {
reader.finishLine();
}
} catch (NullPointerException e) {
- throw new IllegalStateException("problem parsing idx " + idx, e);
+ throw new ProtocolException("problem parsing idx " + idx, e);
} catch (NumberFormatException e) {
- throw new IllegalStateException("problem parsing idx " + idx, e);
- } catch (IOException e) {
- throw new IllegalStateException("problem parsing idx " + idx, e);
+ throw new ProtocolException("problem parsing idx " + idx, e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
@@ -227,4 +239,30 @@ public class NetworkStatsFactory {
return stats;
}
+
+ public void assertEquals(NetworkStats expected, NetworkStats actual) {
+ if (expected.size() != actual.size()) {
+ throw new AssertionError(
+ "Expected size " + expected.size() + ", actual size " + actual.size());
+ }
+
+ NetworkStats.Entry expectedRow = null;
+ NetworkStats.Entry actualRow = null;
+ for (int i = 0; i < expected.size(); i++) {
+ expectedRow = expected.getValues(i, expectedRow);
+ actualRow = actual.getValues(i, actualRow);
+ if (!expectedRow.equals(actualRow)) {
+ throw new AssertionError(
+ "Expected row " + i + ": " + expectedRow + ", actual row " + actualRow);
+ }
+ }
+ }
+
+ /**
+ * Parse statistics from file into given {@link NetworkStats} object. Values
+ * are expected to monotonically increase since device boot.
+ */
+ @VisibleForTesting
+ public static native int nativeReadNetworkStatsDetail(
+ NetworkStats stats, String path, int limitUid);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4d35a6bc3a2a..04b9884113fc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5956,7 +5956,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
try {
mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummaryDev();
- } catch (IllegalStateException e) {
+ } catch (IOException e) {
Log.wtf(TAG, "problem reading network stats", e);
}
}
@@ -5980,7 +5980,7 @@ public final class BatteryStatsImpl extends BatteryStats {
try {
mNetworkDetailCache = mNetworkStatsFactory
.readNetworkStatsDetail().groupedByUid();
- } catch (IllegalStateException e) {
+ } catch (IOException e) {
Log.wtf(TAG, "problem reading network stats", e);
}
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 44e87573af93..5337329e9db1 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -145,7 +145,8 @@ LOCAL_SRC_FILES:= \
android_app_backup_FullBackup.cpp \
android_content_res_ObbScanner.cpp \
android_content_res_Configuration.cpp \
- android_animation_PropertyValuesHolder.cpp
+ android_animation_PropertyValuesHolder.cpp \
+ com_android_internal_net_NetworkStatsFactory.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
@@ -155,13 +156,12 @@ LOCAL_C_INCLUDES += \
$(call include-path-for, bluedroid) \
$(call include-path-for, libhardware)/hardware \
$(call include-path-for, libhardware_legacy)/hardware_legacy \
- $(TOP)/frameworks/av/include \
+ $(TOP)/frameworks/av/include \
external/skia/include/core \
external/skia/include/effects \
external/skia/include/images \
external/skia/include/ports \
- external/skia/src/core \
- external/skia/src/images \
+ external/skia/src/ports \
external/skia/include/utils \
external/sqlite/dist \
external/sqlite/android \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 94324f8b374f..aa59b43fa847 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -173,6 +173,7 @@ extern int register_android_content_res_ObbScanner(JNIEnv* env);
extern int register_android_content_res_Configuration(JNIEnv* env);
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
+extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1204,6 +1205,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_animation_PropertyValuesHolder),
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
+ REG_JNI(register_com_android_internal_net_NetworkStatsFactory),
};
/*
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index daabce3d8b10..b7fdecf9b7b4 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -231,7 +231,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
}
SkAutoTDelete<SkImageDecoder> add(decoder);
- SkAutoTDelete<SkBitmap> adb(!useExistingBitmap ? bitmap : NULL);
+ SkAutoTDelete<SkBitmap> adb(bitmap, !useExistingBitmap);
decoder->setPeeker(&peeker);
if (!isPurgeable) {
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 7208c570f391..886eb6e2e2f6 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -93,6 +93,14 @@ public:
return canvas->getDevice()->accessBitmap(false).height();
}
+ static void setBitmap(JNIEnv* env, jobject, SkCanvas* canvas, SkBitmap* bitmap) {
+ if (bitmap) {
+ canvas->setBitmapDevice(*bitmap);
+ } else {
+ canvas->setDevice(NULL);
+ }
+ }
+
static int saveAll(JNIEnv* env, jobject jcanvas) {
NPE_CHECK_RETURN_ZERO(env, jcanvas);
return GraphicsJNI::getNativeCanvas(env, jcanvas)->save();
@@ -270,25 +278,25 @@ public:
canvas->setDrawFilter(filter);
}
- static jboolean quickReject__RectF(JNIEnv* env, jobject, SkCanvas* canvas,
- jobject rect) {
+ static jboolean quickReject__RectFI(JNIEnv* env, jobject, SkCanvas* canvas,
+ jobject rect, int edgetype) {
SkRect rect_;
GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
- return canvas->quickReject(rect_);
+ return canvas->quickReject(rect_, (SkCanvas::EdgeType)edgetype);
}
-
- static jboolean quickReject__Path(JNIEnv* env, jobject, SkCanvas* canvas,
- SkPath* path) {
- return canvas->quickReject(*path);
+
+ static jboolean quickReject__PathI(JNIEnv* env, jobject, SkCanvas* canvas,
+ SkPath* path, int edgetype) {
+ return canvas->quickReject(*path, (SkCanvas::EdgeType)edgetype);
}
-
- static jboolean quickReject__FFFF(JNIEnv* env, jobject, SkCanvas* canvas,
+
+ static jboolean quickReject__FFFFI(JNIEnv* env, jobject, SkCanvas* canvas,
jfloat left, jfloat top, jfloat right,
- jfloat bottom) {
+ jfloat bottom, int edgetype) {
SkRect r;
r.set(SkFloatToScalar(left), SkFloatToScalar(top),
SkFloatToScalar(right), SkFloatToScalar(bottom));
- return canvas->quickReject(r);
+ return canvas->quickReject(r, (SkCanvas::EdgeType)edgetype);
}
static void drawRGB(JNIEnv* env, jobject, SkCanvas* canvas,
@@ -725,37 +733,37 @@ public:
}
- static void drawText___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
+ static void drawText___CIIFFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
jcharArray text, int index, int count,
- jfloat x, jfloat y, int flags, SkPaint* paint) {
+ jfloat x, jfloat y, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint);
+ drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, paint);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
- static void drawText__StringIIFFIPaint(JNIEnv* env, jobject,
+ static void drawText__StringIIFFPaint(JNIEnv* env, jobject,
SkCanvas* canvas, jstring text,
int start, int end,
- jfloat x, jfloat y, int flags, SkPaint* paint) {
+ jfloat x, jfloat y, SkPaint* paint) {
const jchar* textArray = env->GetStringChars(text, NULL);
- drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint);
+ drawTextWithGlyphs(canvas, textArray, start, end, x, y, paint);
env->ReleaseStringChars(text, textArray);
}
static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
int start, int end,
- jfloat x, jfloat y, int flags, SkPaint* paint) {
+ jfloat x, jfloat y, SkPaint* paint) {
jint count = end - start;
- drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, flags, paint);
+ drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, paint);
}
static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
int start, int count, int contextCount,
- jfloat x, jfloat y, int flags, SkPaint* paint) {
+ jfloat x, jfloat y, SkPaint* paint) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- textArray, start, count, contextCount, flags);
+ textArray, start, count, contextCount);
if (value == NULL) {
return;
}
@@ -766,7 +774,7 @@ public:
x -= value->getTotalAdvance();
}
paint->setTextAlign(SkPaint::kLeft_Align);
- doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), x, y, flags, paint);
+ doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), x, y, paint);
doDrawTextDecorations(canvas, x, y, value->getTotalAdvance(), paint);
paint->setTextAlign(align);
}
@@ -808,14 +816,8 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
}
}
- static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count,
- jfloat x, jfloat y, int flags, SkPaint* paint) {
- // Beware: this needs Glyph encoding (already done on the Paint constructor)
- canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint);
- }
-
static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
- int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) {
+ int index, int count, jfloat x, jfloat y, SkPaint* paint) {
SkPoint* posPtr = new SkPoint[count];
for (int indx = 0; indx < count; indx++) {
posPtr[indx].fX = SkFloatToScalar(x + posArray[indx * 2]);
@@ -825,27 +827,27 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
delete[] posPtr;
}
- static void drawTextRun___CIIIIFFIPaint(
+ static void drawTextRun___CIIIIFFPaint(
JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
int count, int contextIndex, int contextCount,
- jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
+ jfloat x, jfloat y, SkPaint* paint) {
jchar* chars = env->GetCharArrayElements(text, NULL);
drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
- count, contextCount, x, y, dirFlags, paint);
+ count, contextCount, x, y, paint);
env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
}
- static void drawTextRun__StringIIIIFFIPaint(
+ static void drawTextRun__StringIIIIFFPaint(
JNIEnv* env, jobject obj, SkCanvas* canvas, jstring text, jint start,
jint end, jint contextStart, jint contextEnd,
- jfloat x, jfloat y, jint dirFlags, SkPaint* paint) {
+ jfloat x, jfloat y, SkPaint* paint) {
jint count = end - start;
jint contextCount = contextEnd - contextStart;
const jchar* chars = env->GetStringChars(text, NULL);
drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
- count, contextCount, x, y, dirFlags, paint);
+ count, contextCount, x, y, paint);
env->ReleaseStringChars(text, chars);
}
@@ -908,21 +910,19 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
SkCanvas* canvas, jcharArray text, int index, int count,
- SkPath* path, jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
+ SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- TextLayout::drawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
- path, canvas);
+ TextLayout::drawTextOnPath(paint, textArray + index, count, hOffset, vOffset, path, canvas);
env->ReleaseCharArrayElements(text, textArray, 0);
}
static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
SkCanvas* canvas, jstring text, SkPath* path,
- jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
+ jfloat hOffset, jfloat vOffset, SkPaint* paint) {
const jchar* text_ = env->GetStringChars(text, NULL);
int count = env->GetStringLength(text);
- TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
- path, canvas);
+ TextLayout::drawTextOnPath(paint, text_, count, hOffset, vOffset, path, canvas);
env->ReleaseStringChars(text, text_);
}
@@ -930,19 +930,12 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
jobject bounds) {
SkRect r;
SkIRect ir;
- bool result = canvas->getClipBounds(&r);
+ bool result = canvas->getClipBounds(&r, SkCanvas::kBW_EdgeType);
if (!result) {
r.setEmpty();
- } else {
- // ensure the clip is not larger than the canvas
- SkRect canvasRect;
- SkISize deviceSize = canvas->getDeviceSize();
- canvasRect.iset(0, 0, deviceSize.fWidth, deviceSize.fHeight);
- r.intersect(canvasRect);
}
r.round(&ir);
-
(void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
return result;
}
@@ -959,6 +952,7 @@ static JNINativeMethod gCanvasMethods[] = {
{"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque},
{"getWidth","()I", (void*) SkCanvasGlue::getWidth},
{"getHeight","()I", (void*) SkCanvasGlue::getHeight},
+ {"native_setBitmap","(II)V", (void*) SkCanvasGlue::setBitmap},
{"save","()I", (void*) SkCanvasGlue::saveAll},
{"save","(I)I", (void*) SkCanvasGlue::save},
{"native_saveLayer","(ILandroid/graphics/RectF;II)I",
@@ -990,10 +984,10 @@ static JNINativeMethod gCanvasMethods[] = {
{"native_getClipBounds","(ILandroid/graphics/Rect;)Z",
(void*) SkCanvasGlue::getClipBounds},
{"native_getCTM", "(II)V", (void*)SkCanvasGlue::getCTM},
- {"native_quickReject","(ILandroid/graphics/RectF;)Z",
- (void*) SkCanvasGlue::quickReject__RectF},
- {"native_quickReject","(II)Z", (void*) SkCanvasGlue::quickReject__Path},
- {"native_quickReject","(IFFFF)Z", (void*)SkCanvasGlue::quickReject__FFFF},
+ {"native_quickReject","(ILandroid/graphics/RectF;I)Z",
+ (void*) SkCanvasGlue::quickReject__RectFI},
+ {"native_quickReject","(III)Z", (void*) SkCanvasGlue::quickReject__PathI},
+ {"native_quickReject","(IFFFFI)Z", (void*)SkCanvasGlue::quickReject__FFFFI},
{"native_drawRGB","(IIII)V", (void*) SkCanvasGlue::drawRGB},
{"native_drawARGB","(IIIII)V", (void*) SkCanvasGlue::drawARGB},
{"native_drawColor","(II)V", (void*) SkCanvasGlue::drawColor__I},
@@ -1031,21 +1025,21 @@ static JNINativeMethod gCanvasMethods[] = {
(void*)SkCanvasGlue::drawBitmapMesh},
{"nativeDrawVertices", "(III[FI[FI[II[SIII)V",
(void*)SkCanvasGlue::drawVertices},
- {"native_drawText","(I[CIIFFII)V",
- (void*) SkCanvasGlue::drawText___CIIFFIPaint},
- {"native_drawText","(ILjava/lang/String;IIFFII)V",
- (void*) SkCanvasGlue::drawText__StringIIFFIPaint},
- {"native_drawTextRun","(I[CIIIIFFII)V",
- (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaint},
- {"native_drawTextRun","(ILjava/lang/String;IIIIFFII)V",
- (void*) SkCanvasGlue::drawTextRun__StringIIIIFFIPaint},
+ {"native_drawText","(I[CIIFFI)V",
+ (void*) SkCanvasGlue::drawText___CIIFFPaint},
+ {"native_drawText","(ILjava/lang/String;IIFFI)V",
+ (void*) SkCanvasGlue::drawText__StringIIFFPaint},
+ {"native_drawTextRun","(I[CIIIIFFI)V",
+ (void*) SkCanvasGlue::drawTextRun___CIIIIFFPaint},
+ {"native_drawTextRun","(ILjava/lang/String;IIIIFFI)V",
+ (void*) SkCanvasGlue::drawTextRun__StringIIIIFFPaint},
{"native_drawPosText","(I[CII[FI)V",
(void*) SkCanvasGlue::drawPosText___CII_FPaint},
{"native_drawPosText","(ILjava/lang/String;[FI)V",
(void*) SkCanvasGlue::drawPosText__String_FPaint},
- {"native_drawTextOnPath","(I[CIIIFFII)V",
+ {"native_drawTextOnPath","(I[CIIIFFI)V",
(void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
- {"native_drawTextOnPath","(ILjava/lang/String;IFFII)V",
+ {"native_drawTextOnPath","(ILjava/lang/String;IFFI)V",
(void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
{"native_drawPicture", "(II)V", (void*) SkCanvasGlue::drawPicture},
diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp
index 01e7e3e0eb6a..ff0eb45b986d 100644
--- a/core/jni/android/graphics/NinePatchImpl.cpp
+++ b/core/jni/android/graphics/NinePatchImpl.cpp
@@ -105,7 +105,7 @@ SkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint,
void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
const SkPaint* paint, SkRegion** outRegion) {
- if (canvas && canvas->quickReject(bounds)) {
+ if (canvas && canvas->quickReject(bounds, SkCanvas::kBW_EdgeType)) {
return;
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 29a36de50288..e8304923230f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -401,7 +401,7 @@ public:
jfloat result = 0;
TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength,
- paint->getFlags(), NULL /* dont need all advances */, &result);
+ NULL /* dont need all advances */, &result);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
return result;
@@ -426,7 +426,7 @@ public:
jfloat width = 0;
TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength,
- paint->getFlags(), NULL /* dont need all advances */, &width);
+ NULL /* dont need all advances */, &width);
env->ReleaseStringChars(text, textArray);
return width;
@@ -446,7 +446,7 @@ public:
jfloat width = 0;
TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength,
- paint->getFlags(), NULL /* dont need all advances */, &width);
+ NULL /* dont need all advances */, &width);
env->ReleaseStringChars(text, textArray);
return width;
@@ -473,7 +473,7 @@ public:
jfloat* widthsArray = autoWidths.ptr();
TextLayout::getTextRunAdvances(paint, text, 0, count, count,
- paint->getFlags(), widthsArray, NULL /* dont need totalAdvance */);
+ widthsArray, NULL /* dont need totalAdvance */);
return count;
}
@@ -494,48 +494,8 @@ public:
return count;
}
- static int doTextGlyphs(JNIEnv* env, SkPaint* paint, const jchar* text, jint start, jint count,
- jint contextCount, jint flags, jcharArray glyphs) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- NPE_CHECK_RETURN_ZERO(env, text);
-
- if ((start | count | contextCount) < 0 || contextCount < count || !glyphs) {
- doThrowAIOOBE(env);
- return 0;
- }
- if (count == 0) {
- return 0;
- }
- size_t glypthsLength = env->GetArrayLength(glyphs);
- if ((size_t)count > glypthsLength) {
- doThrowAIOOBE(env);
- return 0;
- }
-
- jchar* glyphsArray = env->GetCharArrayElements(glyphs, NULL);
-
- sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, start, count, contextCount, flags);
- const jchar* shapedGlyphs = value->getGlyphs();
- size_t glyphsCount = value->getGlyphsCount();
- memcpy(glyphsArray, shapedGlyphs, sizeof(jchar) * glyphsCount);
-
- env->ReleaseCharArrayElements(glyphs, glyphsArray, JNI_ABORT);
- return glyphsCount;
- }
-
- static int getTextGlyphs__StringIIIII_C(JNIEnv* env, jobject clazz, SkPaint* paint,
- jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
- jcharArray glyphs) {
- const jchar* textArray = env->GetStringChars(text, NULL);
- int count = doTextGlyphs(env, paint, textArray + contextStart, start - contextStart,
- end - start, contextEnd - contextStart, flags, glyphs);
- env->ReleaseStringChars(text, textArray);
- return count;
- }
-
static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text,
- jint start, jint count, jint contextCount, jint flags,
+ jint start, jint count, jint contextCount,
jfloatArray advances, jint advancesIndex) {
NPE_CHECK_RETURN_ZERO(env, paint);
NPE_CHECK_RETURN_ZERO(env, text);
@@ -557,7 +517,7 @@ public:
jfloat advancesArray[count];
jfloat totalAdvance = 0;
- TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags,
+ TextLayout::getTextRunAdvances(paint, text, start, count, contextCount,
advancesArray, &totalAdvance);
if (advances != NULL) {
@@ -566,61 +526,23 @@ public:
return totalAdvance;
}
- static jfloat doTextRunAdvancesICU(JNIEnv *env, SkPaint *paint, const jchar *text,
- jint start, jint count, jint contextCount, jint flags,
- jfloatArray advances, jint advancesIndex) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- NPE_CHECK_RETURN_ZERO(env, text);
-
- if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) {
- doThrowAIOOBE(env);
- return 0;
- }
- if (count == 0) {
- return 0;
- }
- if (advances) {
- size_t advancesLength = env->GetArrayLength(advances);
- if ((size_t)count > advancesLength) {
- doThrowAIOOBE(env);
- return 0;
- }
- }
-
- jfloat advancesArray[count];
- jfloat totalAdvance = 0;
-
- TextLayout::getTextRunAdvancesICU(paint, text, start, count, contextCount, flags,
- advancesArray, totalAdvance);
-
- if (advances != NULL) {
- env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray);
- }
- return totalAdvance;
- }
-
- static float getTextRunAdvances___CIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint,
+ static float getTextRunAdvances___CIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
- jint flags, jfloatArray advances, jint advancesIndex, jint reserved) {
+ jfloatArray advances, jint advancesIndex) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- jfloat result = (reserved == 0) ?
- doTextRunAdvances(env, paint, textArray + contextIndex, index - contextIndex,
- count, contextCount, flags, advances, advancesIndex) :
- doTextRunAdvancesICU(env, paint, textArray + contextIndex, index - contextIndex,
- count, contextCount, flags, advances, advancesIndex);
+ jfloat result = doTextRunAdvances(env, paint, textArray + contextIndex,
+ index - contextIndex, count, contextCount, advances, advancesIndex);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
return result;
}
- static float getTextRunAdvances__StringIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint,
- jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
- jfloatArray advances, jint advancesIndex, jint reserved) {
+ static float getTextRunAdvances__StringIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
+ jstring text, jint start, jint end, jint contextStart, jint contextEnd,
+ jfloatArray advances, jint advancesIndex) {
const jchar* textArray = env->GetStringChars(text, NULL);
- jfloat result = (reserved == 0) ?
- doTextRunAdvances(env, paint, textArray + contextStart, start - contextStart,
- end - start, contextEnd - contextStart, flags, advances, advancesIndex) :
- doTextRunAdvancesICU(env, paint, textArray + contextStart, start - contextStart,
- end - start, contextEnd - contextStart, flags, advances, advancesIndex);
+ jfloat result = doTextRunAdvances(env, paint, textArray + contextStart,
+ start - contextStart, end - start, contextEnd - contextStart,
+ advances, advancesIndex);
env->ReleaseStringChars(text, textArray);
return result;
}
@@ -629,7 +551,7 @@ public:
jint count, jint flags, jint offset, jint opt) {
jfloat scalarArray[count];
- TextLayout::getTextRunAdvances(paint, text, start, count, start + count, flags,
+ TextLayout::getTextRunAdvances(paint, text, start, count, start + count,
scalarArray, NULL /* dont need totalAdvance */);
jint pos = offset - start;
@@ -688,21 +610,21 @@ public:
}
static void getTextPath(JNIEnv* env, SkPaint* paint, const jchar* text, jint count,
- jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
- TextLayout::getTextPath(paint, text, count, bidiFlags, x, y, path);
+ jfloat x, jfloat y, SkPath *path) {
+ TextLayout::getTextPath(paint, text, count, x, y, path);
}
- static void getTextPath___C(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags,
+ static void getTextPath___C(JNIEnv* env, jobject clazz, SkPaint* paint,
jcharArray text, int index, int count, jfloat x, jfloat y, SkPath* path) {
const jchar* textArray = env->GetCharArrayElements(text, NULL);
- getTextPath(env, paint, textArray + index, count, bidiFlags, x, y, path);
+ getTextPath(env, paint, textArray + index, count, x, y, path);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
}
- static void getTextPath__String(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags,
+ static void getTextPath__String(JNIEnv* env, jobject clazz, SkPaint* paint,
jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) {
const jchar* textArray = env->GetStringChars(text, NULL);
- getTextPath(env, paint, textArray + start, end - start, bidiFlags, x, y, path);
+ getTextPath(env, paint, textArray + start, end - start, x, y, path);
env->ReleaseStringChars(text, textArray);
}
@@ -726,7 +648,7 @@ public:
int count, float maxWidth, jfloatArray jmeasured,
SkPaint::TextBufferDirection tbd) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
- text, 0, count, count, paint.getFlags());
+ text, 0, count, count);
if (value == NULL) {
return 0;
}
@@ -798,7 +720,7 @@ public:
SkIRect ir;
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
- text, 0, count, count, paint.getFlags());
+ text, 0, count, count);
if (value == NULL) {
return;
}
@@ -886,19 +808,15 @@ static JNINativeMethod methods[] = {
{"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS},
{"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F},
{"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F},
- {"native_getTextRunAdvances","(I[CIIIII[FII)F",
- (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FII},
- {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FII)F",
- (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FII},
-
-
- {"native_getTextGlyphs","(ILjava/lang/String;IIIII[C)I",
- (void*) SkPaintGlue::getTextGlyphs__StringIIIII_C},
- {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
- {"native_getTextRunCursor", "(ILjava/lang/String;IIIII)I",
+ {"native_getTextRunAdvances","(I[CIIII[FI)F",
+ (void*) SkPaintGlue::getTextRunAdvances___CIIII_FI},
+ {"native_getTextRunAdvances","(ILjava/lang/String;IIII[FI)F",
+ (void*) SkPaintGlue::getTextRunAdvances__StringIIII_FI},
+ {"native_getTextRunCursor", "(I[CIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
+ {"native_getTextRunCursor", "(ILjava/lang/String;IIII)I",
(void*) SkPaintGlue::getTextRunCursor__String},
- {"native_getTextPath","(II[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C},
- {"native_getTextPath","(IILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String},
+ {"native_getTextPath","(I[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C},
+ {"native_getTextPath","(ILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String},
{"nativeGetStringBounds", "(ILjava/lang/String;IILandroid/graphics/Rect;)V",
(void*) SkPaintGlue::getStringBounds },
{"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V",
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index ded21860b8b9..ab7cf4698c1e 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -177,7 +177,7 @@ static SkRegion* Region_createFromParcel(JNIEnv* env, jobject clazz, jobject par
SkRegion* region = new SkRegion;
size_t size = p->readInt32();
- region->readFromMemory(p->readInplace(size));
+ region->unflatten(p->readInplace(size));
return region;
}
@@ -190,9 +190,9 @@ static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, const SkRegion*
android::Parcel* p = android::parcelForJavaObject(env, parcel);
- size_t size = region->writeToMemory(NULL);
+ size_t size = region->flatten(NULL);
p->writeInt32(size);
- region->writeToMemory(p->writeInplace(size));
+ region->flatten(p->writeInplace(size));
return true;
}
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index 2beedada409f..b77236c57262 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -28,33 +28,13 @@
namespace android {
-// Returns true if we might need layout. If bidiFlags force LTR, assume no layout, if
-// bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text
-// looking for a character >= the first RTL character in unicode and assume we do if
-// we find one.
-bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) {
- if (bidiFlags == kBidi_Force_LTR) {
- return false;
- }
- if ((bidiFlags == kBidi_RTL) || (bidiFlags == kBidi_Default_RTL) ||
- bidiFlags == kBidi_Force_RTL) {
- return true;
- }
- for (int i = 0; i < len; ++i) {
- if (text[i] >= UNICODE_FIRST_RTL_CHAR) {
- return true;
- }
- }
- return false;
-}
-
// Draws or gets the path of a paragraph of text on a single line, running bidi and shaping.
// This will draw if canvas is not null, otherwise path must be non-null and it will create
// a path representing the text that would have been drawn.
void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len,
- jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
+ jfloat x, jfloat y, SkPath *path) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, 0, len, len, bidiFlags);
+ text, 0, len, len);
if (value == NULL) {
return ;
}
@@ -65,10 +45,10 @@ void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len,
}
void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
- jint count, jint contextCount, jint dirFlags,
+ jint count, jint contextCount,
jfloat* resultAdvances, jfloat* resultTotalAdvance) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- chars, start, count, contextCount, dirFlags);
+ chars, start, count, contextCount);
if (value == NULL) {
return ;
}
@@ -80,29 +60,20 @@ void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint sta
}
}
-void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start,
- jint count, jint contextCount, jint dirFlags,
- jfloat* resultAdvances, jfloat& resultTotalAdvance) {
- // Compute advances and return them
- computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags,
- resultAdvances, &resultTotalAdvance);
-}
-
void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len,
- jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
- handleText(paint, text, len, bidiFlags, x, y, path);
+ jfloat x, jfloat y, SkPath *path) {
+ handleText(paint, text, len, x, y, path);
}
-
void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count,
- int bidiFlags, jfloat hOffset, jfloat vOffset,
+ jfloat hOffset, jfloat vOffset,
SkPath* path, SkCanvas* canvas) {
SkScalar h_ = SkFloatToScalar(hOffset);
SkScalar v_ = SkFloatToScalar(vOffset);
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, 0, count, count, bidiFlags);
+ text, 0, count, count);
if (value == NULL) {
return;
}
@@ -111,73 +82,4 @@ void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count,
canvas->drawTextOnPathHV(value->getGlyphs(), value->getGlyphsCount() * 2, *path, h_, v_, *paint);
}
-void TextLayout::computeAdvancesWithICU(SkPaint* paint, const UChar* chars,
- size_t start, size_t count, size_t contextCount, int dirFlags,
- jfloat* outAdvances, jfloat* outTotalAdvance) {
- SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount);
- jchar* buffer = tempBuffer.get();
- SkScalar* scalarArray = (SkScalar*)outAdvances;
-
- // this is where we'd call harfbuzz
- // for now we just use ushape.c
- size_t widths;
- const jchar* text;
- if (dirFlags & 0x1) { // rtl, call arabic shaping in case
- UErrorCode status = U_ZERO_ERROR;
- // Use fixed length since we need to keep start and count valid
- u_shapeArabic(chars, contextCount, buffer, contextCount,
- U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
- U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
- U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
- // we shouldn't fail unless there's an out of memory condition,
- // in which case we're hosed anyway
- for (int i = start, e = i + count; i < e; ++i) {
- if (buffer[i] == UNICODE_NOT_A_CHAR) {
- buffer[i] = UNICODE_ZWSP; // zero-width-space for skia
- }
- }
- text = buffer + start;
- widths = paint->getTextWidths(text, count << 1, scalarArray);
- } else {
- text = chars + start;
- widths = paint->getTextWidths(text, count << 1, scalarArray);
- }
-
- jfloat totalAdvance = 0;
- if (widths < count) {
-#if DEBUG_ADVANCES
- ALOGD("ICU -- count=%d", widths);
-#endif
- // Skia operates on code points, not code units, so surrogate pairs return only
- // one value. Expand the result so we have one value per UTF-16 code unit.
-
- // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
- // leaving the remaining widths zero. Not nice.
- for (size_t i = 0, p = 0; i < widths; ++i) {
- totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]);
- if (p < count &&
- text[p] >= UNICODE_FIRST_LOW_SURROGATE &&
- text[p] < UNICODE_FIRST_PRIVATE_USE &&
- text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE &&
- text[p-1] < UNICODE_FIRST_LOW_SURROGATE) {
- outAdvances[p++] = 0;
- }
-#if DEBUG_ADVANCES
- ALOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
-#endif
- }
- } else {
-#if DEBUG_ADVANCES
- ALOGD("ICU -- count=%d", count);
-#endif
- for (size_t i = 0; i < count; i++) {
- totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]);
-#if DEBUG_ADVANCES
- ALOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
-#endif
- }
- }
- *outTotalAdvance = totalAdvance;
-}
-
}
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index a0f94024fcf8..fa388c8ed74f 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -42,17 +42,6 @@ namespace android {
#define USE_TEXT_LAYOUT_CACHE 1
enum {
- kBidi_LTR = 0,
- kBidi_RTL = 1,
- kBidi_Default_LTR = 2,
- kBidi_Default_RTL = 3,
- kBidi_Force_LTR = 4,
- kBidi_Force_RTL = 5,
-
- kBidi_Mask = 0x7
-};
-
-enum {
kDirection_LTR = 0,
kDirection_RTL = 1,
@@ -63,28 +52,18 @@ class TextLayout {
public:
static void getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
- jint count, jint contextCount, jint dirFlags,
+ jint count, jint contextCount,
jfloat* resultAdvances, jfloat* resultTotalAdvance);
- static void getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start,
- jint count, jint contextCount, jint dirFlags,
- jfloat* resultAdvances, jfloat& resultTotalAdvance);
-
static void getTextPath(SkPaint* paint, const jchar* text, jsize len,
- jint bidiFlags, jfloat x, jfloat y, SkPath* path);
+ jfloat x, jfloat y, SkPath* path);
static void drawTextOnPath(SkPaint* paint, const jchar* text, jsize len,
- int bidiFlags, jfloat hOffset, jfloat vOffset,
+ jfloat hOffset, jfloat vOffset,
SkPath* path, SkCanvas* canvas);
private:
- static bool needsLayout(const jchar* text, jint len, jint bidiFlags);
-
static void handleText(SkPaint* paint, const jchar* text, jsize len,
- int bidiFlags, jfloat x, jfloat y, SkPath* path);
-
- static void computeAdvancesWithICU(SkPaint* paint, const UChar* chars,
- size_t start, size_t count, size_t contextCount, int dirFlags,
- jfloat* outAdvances, jfloat* outTotalAdvance);
+ jfloat x, jfloat y, SkPath* path);
};
} // namespace android
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 75446459665e..60c6183ee4f2 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -87,7 +87,7 @@ void TextLayoutCache::purgeCaches() {
* Caching
*/
sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
- const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) {
+ const jchar* text, jint start, jint count, jint contextCount) {
AutoMutex _l(mLock);
nsecs_t startTime = 0;
if (mDebugEnabled) {
@@ -95,7 +95,7 @@ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
}
// Create the key
- TextLayoutCacheKey key(paint, text, start, count, contextCount, dirFlags);
+ TextLayoutCacheKey key(paint, text, start, count, contextCount);
// Get value from cache if possible
sp<TextLayoutValue> value = mCache.get(key);
@@ -111,7 +111,7 @@ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
// Compute advances and store them
mShaper->computeValues(value.get(), paint,
reinterpret_cast<const UChar*>(key.getText()), start, count,
- size_t(contextCount), int(dirFlags));
+ size_t(contextCount));
if (mDebugEnabled) {
value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime);
@@ -218,14 +218,13 @@ void TextLayoutCache::dumpCacheStats() {
* TextLayoutCacheKey
*/
TextLayoutCacheKey::TextLayoutCacheKey(): start(0), count(0), contextCount(0),
- dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
+ typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
hinting(SkPaint::kNo_Hinting), variant(SkPaint::kDefault_Variant), language() {
}
TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text,
- size_t start, size_t count, size_t contextCount, int dirFlags) :
- start(start), count(count), contextCount(contextCount),
- dirFlags(dirFlags) {
+ size_t start, size_t count, size_t contextCount) :
+ start(start), count(count), contextCount(contextCount) {
textCopy.setTo(text, contextCount);
typeface = paint->getTypeface();
textSize = paint->getTextSize();
@@ -242,7 +241,6 @@ TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
start(other.start),
count(other.count),
contextCount(other.contextCount),
- dirFlags(other.dirFlags),
typeface(other.typeface),
textSize(other.textSize),
textSkewX(other.textSkewX),
@@ -281,9 +279,6 @@ int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutC
deltaInt = lhs.hinting - rhs.hinting;
if (deltaInt != 0) return (deltaInt);
- deltaInt = lhs.dirFlags - rhs.dirFlags;
- if (deltaInt) return (deltaInt);
-
deltaInt = lhs.variant - rhs.variant;
if (deltaInt) return (deltaInt);
@@ -345,7 +340,7 @@ TextLayoutShaper::TextLayoutShaper() {
}
void TextLayoutShaper::init() {
- mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
+ mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal);
}
void TextLayoutShaper::unrefTypefaces() {
@@ -359,9 +354,9 @@ TextLayoutShaper::~TextLayoutShaper() {
}
void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
- size_t start, size_t count, size_t contextCount, int dirFlags) {
+ size_t start, size_t count, size_t contextCount) {
- computeValues(paint, chars, start, count, contextCount, dirFlags,
+ computeValues(paint, chars, start, count, contextCount,
&value->mAdvances, &value->mTotalAdvance, &value->mGlyphs, &value->mPos);
#if DEBUG_ADVANCES
ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
@@ -370,7 +365,7 @@ void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* pain
}
void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
- size_t start, size_t count, size_t contextCount, int dirFlags,
+ size_t start, size_t count, size_t contextCount,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
*outTotalAdvance = 0;
@@ -378,110 +373,94 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
return;
}
- UBiDiLevel bidiReq = 0;
- bool forceLTR = false;
- bool forceRTL = false;
-
- switch (dirFlags & kBidi_Mask) {
- case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
- case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
- case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
- case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
- case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
- case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
- }
-
+ UBiDiLevel bidiReq = UBIDI_DEFAULT_LTR;
bool useSingleRun = false;
- bool isRTL = forceRTL;
- if (forceLTR || forceRTL) {
- useSingleRun = true;
- } else {
- UBiDi* bidi = ubidi_open();
- if (bidi) {
- UErrorCode status = U_ZERO_ERROR;
+ bool isRTL = false;
+
+ UBiDi* bidi = ubidi_open();
+ if (bidi) {
+ UErrorCode status = U_ZERO_ERROR;
#if DEBUG_GLYPHS
- ALOGD("******** ComputeValues -- start");
- ALOGD(" -- string = '%s'", String8(chars + start, count).string());
- ALOGD(" -- start = %d", start);
- ALOGD(" -- count = %d", count);
- ALOGD(" -- contextCount = %d", contextCount);
- ALOGD(" -- bidiReq = %d", bidiReq);
+ ALOGD("******** ComputeValues -- start");
+ ALOGD(" -- string = '%s'", String8(chars + start, count).string());
+ ALOGD(" -- start = %d", start);
+ ALOGD(" -- count = %d", count);
+ ALOGD(" -- contextCount = %d", contextCount);
+ ALOGD(" -- bidiReq = %d", bidiReq);
#endif
- ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
- if (U_SUCCESS(status)) {
- int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
- ssize_t rc = ubidi_countRuns(bidi, &status);
+ ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
+ if (U_SUCCESS(status)) {
+ int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
+ ssize_t rc = ubidi_countRuns(bidi, &status);
#if DEBUG_GLYPHS
- ALOGD(" -- dirFlags = %d", dirFlags);
- ALOGD(" -- paraDir = %d", paraDir);
- ALOGD(" -- run-count = %d", int(rc));
+ ALOGD(" -- paraDir = %d", paraDir);
+ ALOGD(" -- run-count = %d", int(rc));
#endif
- if (U_SUCCESS(status) && rc == 1) {
- // Normal case: one run, status is ok
- isRTL = (paraDir == 1);
- useSingleRun = true;
- } else if (!U_SUCCESS(status) || rc < 1) {
- ALOGW("Need to force to single run -- string = '%s',"
- " status = %d, rc = %d",
- String8(chars + start, count).string(), status, int(rc));
- isRTL = (paraDir == 1);
- useSingleRun = true;
- } else {
- int32_t end = start + count;
- for (size_t i = 0; i < size_t(rc); ++i) {
- int32_t startRun = -1;
- int32_t lengthRun = -1;
- UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
-
- if (startRun == -1 || lengthRun == -1) {
- // Something went wrong when getting the visual run, need to clear
- // already computed data before doing a single run pass
- ALOGW("Visual run is not valid");
- outGlyphs->clear();
- outAdvances->clear();
- outPos->clear();
- *outTotalAdvance = 0;
- isRTL = (paraDir == 1);
- useSingleRun = true;
- break;
- }
-
- if (startRun >= end) {
- continue;
- }
- int32_t endRun = startRun + lengthRun;
- if (endRun <= int32_t(start)) {
- continue;
- }
- if (startRun < int32_t(start)) {
- startRun = int32_t(start);
- }
- if (endRun > end) {
- endRun = end;
- }
-
- lengthRun = endRun - startRun;
- isRTL = (runDir == UBIDI_RTL);
+ if (U_SUCCESS(status) && rc == 1) {
+ // Normal case: one run, status is ok
+ isRTL = (paraDir == 1);
+ useSingleRun = true;
+ } else if (!U_SUCCESS(status) || rc < 1) {
+ ALOGW("Need to force to single run -- string = '%s',"
+ " status = %d, rc = %d",
+ String8(chars + start, count).string(), status, int(rc));
+ isRTL = (paraDir == 1);
+ useSingleRun = true;
+ } else {
+ int32_t end = start + count;
+ for (size_t i = 0; i < size_t(rc); ++i) {
+ int32_t startRun = -1;
+ int32_t lengthRun = -1;
+ UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
+
+ if (startRun == -1 || lengthRun == -1) {
+ // Something went wrong when getting the visual run, need to clear
+ // already computed data before doing a single run pass
+ ALOGW("Visual run is not valid");
+ outGlyphs->clear();
+ outAdvances->clear();
+ outPos->clear();
+ *outTotalAdvance = 0;
+ isRTL = (paraDir == 1);
+ useSingleRun = true;
+ break;
+ }
+
+ if (startRun >= end) {
+ continue;
+ }
+ int32_t endRun = startRun + lengthRun;
+ if (endRun <= int32_t(start)) {
+ continue;
+ }
+ if (startRun < int32_t(start)) {
+ startRun = int32_t(start);
+ }
+ if (endRun > end) {
+ endRun = end;
+ }
+
+ lengthRun = endRun - startRun;
+ isRTL = (runDir == UBIDI_RTL);
#if DEBUG_GLYPHS
- ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
- i, startRun, lengthRun, isRTL);
+ ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
+ i, startRun, lengthRun, isRTL);
#endif
- computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
- outAdvances, outTotalAdvance, outGlyphs, outPos);
+ computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
+ outAdvances, outTotalAdvance, outGlyphs, outPos);
- }
}
- } else {
- ALOGW("Cannot set Para");
- useSingleRun = true;
- isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
}
- ubidi_close(bidi);
} else {
- ALOGW("Cannot ubidi_open()");
+ ALOGW("Cannot set Para");
useSingleRun = true;
isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
}
+ ubidi_close(bidi);
+ } else {
+ ALOGW("Cannot ubidi_open()");
+ useSingleRun = true;
+ isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
}
// Default single run case
@@ -785,10 +764,6 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* conte
/**
* Return the first typeface in the logical change, starting with this typeface,
* that contains the specified unichar, or NULL if none is found.
- *
- * Note that this function does _not_ increment the reference count on the typeface, as the
- * assumption is that its lifetime is managed elsewhere - in particular, the fallback typefaces
- * for the default font live in a global cache.
*/
SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
hb_script_t script) {
@@ -798,7 +773,7 @@ SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface
}
typeface = SkCreateTypefaceForScriptNG(script, currentStyle);
#if DEBUG_GLYPHS
- ALOGD("Using Harfbuzz Script %d, Style %d", script, currentStyle);
+ ALOGD("Using Harfbuzz Script %c%c%c%c, Style %d", HB_UNTAG(script), currentStyle);
#endif
return typeface;
}
@@ -845,6 +820,7 @@ size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
if (baseGlyphCount != 0) {
typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer));
if (!typeface) {
+ baseGlyphCount = 0;
typeface = mDefaultTypeface;
SkSafeRef(typeface);
#if DEBUG_GLYPHS
@@ -921,11 +897,11 @@ TextLayoutEngine::~TextLayoutEngine() {
}
sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar* text,
- jint start, jint count, jint contextCount, jint dirFlags) {
+ jint start, jint count, jint contextCount) {
sp<TextLayoutValue> value;
#if USE_TEXT_LAYOUT_CACHE
value = mTextLayoutCache->getValue(paint, text, start, count,
- contextCount, dirFlags);
+ contextCount);
if (value == NULL) {
ALOGE("Cannot get TextLayoutCache value for text = '%s'",
String8(text + start, count).string());
@@ -933,7 +909,7 @@ sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar
#else
value = new TextLayoutValue(count);
mShaper->computeValues(value.get(), paint,
- reinterpret_cast<const UChar*>(text), start, count, contextCount, dirFlags);
+ reinterpret_cast<const UChar*>(text), start, count, contextCount);
#endif
return value;
}
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 704c7738d540..29805ee708f9 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -71,7 +71,7 @@ public:
TextLayoutCacheKey();
TextLayoutCacheKey(const SkPaint* paint, const UChar* text, size_t start, size_t count,
- size_t contextCount, int dirFlags);
+ size_t contextCount);
TextLayoutCacheKey(const TextLayoutCacheKey& other);
@@ -98,7 +98,6 @@ private:
size_t start;
size_t count;
size_t contextCount;
- int dirFlags;
SkTypeface* typeface;
SkScalar textSize;
SkScalar textSkewX;
@@ -182,7 +181,7 @@ public:
virtual ~TextLayoutShaper();
void computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
- size_t start, size_t count, size_t contextCount, int dirFlags);
+ size_t start, size_t count, size_t contextCount);
void purgeCaches();
@@ -216,7 +215,7 @@ private:
size_t shapeFontRun(const SkPaint* paint);
void computeValues(const SkPaint* paint, const UChar* chars,
- size_t start, size_t count, size_t contextCount, int dirFlags,
+ size_t start, size_t count, size_t contextCount,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
@@ -253,7 +252,7 @@ public:
void operator()(TextLayoutCacheKey& text, sp<TextLayoutValue>& desc);
sp<TextLayoutValue> getValue(const SkPaint* paint, const jchar* text, jint start,
- jint count, jint contextCount, jint dirFlags);
+ jint count, jint contextCount);
/**
* Clear the cache
@@ -305,7 +304,7 @@ public:
* the call. Be careful of this when doing optimization.
**/
sp<TextLayoutValue> getValue(const SkPaint* paint, const jchar* text, jint start,
- jint count, jint contextCount, jint dirFlags);
+ jint count, jint contextCount);
void purgeCaches();
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index e056b618bca4..7f4c37bff34d 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -147,6 +147,25 @@ static SkTypeface* Typeface_createFromFile(JNIEnv* env, jobject, jstring jpath)
return SkTypeface::CreateFromFile(str.c_str());
}
+#define MIN_GAMMA (0.1f)
+#define MAX_GAMMA (10.0f)
+static float pinGamma(float gamma) {
+ if (gamma < MIN_GAMMA) {
+ gamma = MIN_GAMMA;
+ } else if (gamma > MAX_GAMMA) {
+ gamma = MAX_GAMMA;
+ }
+ return gamma;
+}
+
+extern void skia_set_text_gamma(float, float);
+
+static void Typeface_setGammaForText(JNIEnv* env, jobject, jfloat blackGamma,
+ jfloat whiteGamma) {
+ // Comment this out for release builds. This is only used during development
+ skia_set_text_gamma(pinGamma(blackGamma), pinGamma(whiteGamma));
+}
+
///////////////////////////////////////////////////////////////////////////////
static JNINativeMethod gTypefaceMethods[] = {
@@ -158,6 +177,7 @@ static JNINativeMethod gTypefaceMethods[] = {
(void*)Typeface_createFromAsset },
{ "nativeCreateFromFile", "(Ljava/lang/String;)I",
(void*)Typeface_createFromFile },
+ { "setGammaForText", "(FF)V", (void*)Typeface_setGammaForText },
};
int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp
index 9e60904e0eb1..0df86387bd17 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/core/jni/android_net_TrafficStats.cpp
@@ -87,8 +87,8 @@ static int parseIfaceStats(const char* iface, struct Stats* stats) {
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
int matched = sscanf(buffer, "%31s %llu %llu %llu %llu "
- "%*llu %llu %*llu %*llu %*llu %*llu "
- "%*llu %llu %*llu %*llu %*llu %*llu", cur_iface, &rxBytes,
+ "%*u %llu %*u %*u %*u %*u "
+ "%*u %llu %*u %*u %*u %*u", cur_iface, &rxBytes,
&rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets);
if (matched >= 5) {
if (matched == 7) {
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 895a0dc61fea..80573a7495b9 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -269,7 +269,8 @@ static jint android_view_GLES20Canvas_saveLayerAlphaClip(JNIEnv* env, jobject cl
// ----------------------------------------------------------------------------
static bool android_view_GLES20Canvas_quickReject(JNIEnv* env, jobject clazz,
- OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom) {
+ OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
+ SkCanvas::EdgeType edge) {
return renderer->quickReject(left, top, right, bottom);
}
@@ -567,9 +568,9 @@ static void android_view_GLES20Canvas_resetPaintFilter(JNIEnv* env, jobject claz
// ----------------------------------------------------------------------------
static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
- jfloat x, jfloat y, int flags, SkPaint* paint) {
+ jfloat x, jfloat y, SkPaint* paint) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, 0, count, count, flags);
+ text, 0, count, count);
if (value == NULL) {
return;
}
@@ -583,9 +584,9 @@ static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
}
static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count,
- SkPath* path, jfloat hOffset, jfloat vOffset, int flags, SkPaint* paint) {
+ SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, 0, count, count, flags);
+ text, 0, count, count);
if (value == NULL) {
return;
}
@@ -598,9 +599,9 @@ static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int co
static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
jint start, jint count, jint contextCount, jfloat x, jfloat y,
- int flags, SkPaint* paint) {
+ SkPaint* paint) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, start, count, contextCount, flags);
+ text, start, count, contextCount);
if (value == NULL) {
return;
}
@@ -615,64 +616,62 @@ static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jcharArray text, jint index, jint count,
- jfloat x, jfloat y, jint flags, SkPaint* paint) {
+ jfloat x, jfloat y, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- renderText(renderer, textArray + index, count, x, y, flags, paint);
+ renderText(renderer, textArray + index, count, x, y, paint);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jstring text, jint start, jint end,
- jfloat x, jfloat y, jint flags, SkPaint* paint) {
+ jfloat x, jfloat y, SkPaint* paint) {
const jchar* textArray = env->GetStringChars(text, NULL);
- renderText(renderer, textArray + start, end - start, x, y, flags, paint);
+ renderText(renderer, textArray + start, end - start, x, y, paint);
env->ReleaseStringChars(text, textArray);
}
static void android_view_GLES20Canvas_drawTextArrayOnPath(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jcharArray text, jint index, jint count,
- SkPath* path, jfloat hOffset, jfloat vOffset, jint flags, SkPaint* paint) {
+ SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
renderTextOnPath(renderer, textArray + index, count, path,
- hOffset, vOffset, flags, paint);
+ hOffset, vOffset, paint);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
static void android_view_GLES20Canvas_drawTextOnPath(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jstring text, jint start, jint end,
- SkPath* path, jfloat hOffset, jfloat vOffset, jint flags, SkPaint* paint) {
+ SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) {
const jchar* textArray = env->GetStringChars(text, NULL);
renderTextOnPath(renderer, textArray + start, end - start, path,
- hOffset, vOffset, flags, paint);
+ hOffset, vOffset, paint);
env->ReleaseStringChars(text, textArray);
}
static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jcharArray text, jint index, jint count,
- jint contextIndex, jint contextCount, jfloat x, jfloat y, jint dirFlags,
- SkPaint* paint) {
+ jint contextIndex, jint contextCount, jfloat x, jfloat y, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
renderTextRun(renderer, textArray + contextIndex, index - contextIndex,
- count, contextCount, x, y, dirFlags, paint);
+ count, contextCount, x, y, paint);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jstring text, jint start, jint end,
- jint contextStart, int contextEnd, jfloat x, jfloat y, jint dirFlags,
- SkPaint* paint) {
+ jint contextStart, int contextEnd, jfloat x, jfloat y, SkPaint* paint) {
const jchar* textArray = env->GetStringChars(text, NULL);
jint count = end - start;
jint contextCount = contextEnd - contextStart;
renderTextRun(renderer, textArray + contextStart, start - contextStart,
- count, contextCount, x, y, dirFlags, paint);
+ count, contextCount, x, y, paint);
env->ReleaseStringChars(text, textArray);
}
static void renderPosText(OpenGLRenderer* renderer, const jchar* text, int count,
- const jfloat* positions, jint dirFlags, SkPaint* paint) {
+ const jfloat* positions, SkPaint* paint) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, 0, count, count, dirFlags);
+ text, 0, count, count);
if (value == NULL) {
return;
}
@@ -690,7 +689,7 @@ static void android_view_GLES20Canvas_drawPosTextArray(JNIEnv* env, jobject claz
jchar* textArray = env->GetCharArrayElements(text, NULL);
jfloat* positions = env->GetFloatArrayElements(pos, NULL);
- renderPosText(renderer, textArray + index, count, positions, kBidi_LTR, paint);
+ renderPosText(renderer, textArray + index, count, positions, paint);
env->ReleaseFloatArrayElements(pos, positions, JNI_ABORT);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
@@ -702,7 +701,7 @@ static void android_view_GLES20Canvas_drawPosText(JNIEnv* env, jobject clazz,
const jchar* textArray = env->GetStringChars(text, NULL);
jfloat* positions = env->GetFloatArrayElements(pos, NULL);
- renderPosText(renderer, textArray + start, end - start, positions, kBidi_LTR, paint);
+ renderPosText(renderer, textArray + start, end - start, positions, paint);
env->ReleaseFloatArrayElements(pos, positions, JNI_ABORT);
env->ReleaseStringChars(text, textArray);
@@ -980,7 +979,7 @@ static JNINativeMethod gMethods[] = {
{ "nSaveLayerAlpha", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayerAlpha },
{ "nSaveLayerAlpha", "(III)I", (void*) android_view_GLES20Canvas_saveLayerAlphaClip },
- { "nQuickReject", "(IFFFF)Z", (void*) android_view_GLES20Canvas_quickReject },
+ { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
{ "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF },
{ "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
{ "nClipPath", "(III)Z", (void*) android_view_GLES20Canvas_clipPath },
@@ -1025,16 +1024,16 @@ static JNINativeMethod gMethods[] = {
{ "nSetupPaintFilter", "(III)V", (void*) android_view_GLES20Canvas_setupPaintFilter },
{ "nResetPaintFilter", "(I)V", (void*) android_view_GLES20Canvas_resetPaintFilter },
- { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray },
- { "nDrawText", "(ILjava/lang/String;IIFFII)V",
+ { "nDrawText", "(I[CIIFFI)V", (void*) android_view_GLES20Canvas_drawTextArray },
+ { "nDrawText", "(ILjava/lang/String;IIFFI)V",
(void*) android_view_GLES20Canvas_drawText },
- { "nDrawTextOnPath", "(I[CIIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath },
- { "nDrawTextOnPath", "(ILjava/lang/String;IIIFFII)V",
+ { "nDrawTextOnPath", "(I[CIIIFFI)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath },
+ { "nDrawTextOnPath", "(ILjava/lang/String;IIIFFI)V",
(void*) android_view_GLES20Canvas_drawTextOnPath },
- { "nDrawTextRun", "(I[CIIIIFFII)V", (void*) android_view_GLES20Canvas_drawTextRunArray },
- { "nDrawTextRun", "(ILjava/lang/String;IIIIFFII)V",
+ { "nDrawTextRun", "(I[CIIIIFFI)V", (void*) android_view_GLES20Canvas_drawTextRunArray },
+ { "nDrawTextRun", "(ILjava/lang/String;IIIIFFI)V",
(void*) android_view_GLES20Canvas_drawTextRun },
{ "nDrawPosText", "(I[CII[FI)V", (void*) android_view_GLES20Canvas_drawPosTextArray },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index ed92e43c9ddb..1f1537082a90 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -66,6 +66,7 @@ static struct {
jfieldID mNativeSurfaceControl;
jfieldID mGenerationId;
jfieldID mCanvas;
+ jfieldID mCanvasSaveCount;
jmethodID ctor;
} gSurfaceClassInfo;
@@ -77,16 +78,11 @@ static struct {
} gRectClassInfo;
static struct {
- jfieldID mFinalizer;
jfieldID mNativeCanvas;
jfieldID mSurfaceFormat;
} gCanvasClassInfo;
static struct {
- jfieldID mNativeCanvas;
-} gCanvasFinalizerClassInfo;
-
-static struct {
jfieldID width;
jfieldID height;
jfieldID refreshRate;
@@ -139,7 +135,6 @@ public:
return mScreenshot.getFormat();
}
- SK_DECLARE_UNFLATTENABLE_OBJECT()
protected:
// overrides from SkPixelRef
virtual void* onLockPixels(SkColorTable** ct) {
@@ -378,15 +373,6 @@ static inline SkBitmap::Config convertPixelFormat(PixelFormat format) {
}
}
-static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
- jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
- SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
- env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
- env->SetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas, (int)newCanvas);
- env->SetIntField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int)newCanvas);
- SkSafeUnref(previousCanvas);
-}
-
static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRectObj) {
sp<Surface> surface(getSurface(env, surfaceObj));
if (!Surface::isValid(surface)) {
@@ -423,6 +409,8 @@ static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRe
jobject canvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas);
env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, info.format);
+ SkCanvas* nativeCanvas = reinterpret_cast<SkCanvas*>(
+ env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
SkBitmap bitmap;
ssize_t bpr = info.s * bytesPerPixel(info.format);
bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr);
@@ -435,9 +423,7 @@ static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRe
// be safe with an empty bitmap.
bitmap.setPixels(NULL);
}
-
- SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
- swapCanvasPtr(env, canvasObj, nativeCanvas);
+ nativeCanvas->setBitmapDevice(bitmap);
SkRegion clipReg;
if (dirtyRegion.isRect()) { // very common case
@@ -454,6 +440,9 @@ static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRe
nativeCanvas->clipRegion(clipReg);
+ int saveCount = nativeCanvas->save();
+ env->SetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount, saveCount);
+
if (dirtyRectObj) {
const Rect& bounds(dirtyRegion.getBounds());
env->SetIntField(dirtyRectObj, gRectClassInfo.left, bounds.left);
@@ -478,8 +467,12 @@ static void nativeUnlockCanvasAndPost(JNIEnv* env, jobject surfaceObj, jobject c
}
// detach the canvas from the surface
- SkCanvas* nativeCanvas = SkNEW(SkCanvas);
- swapCanvasPtr(env, canvasObj, nativeCanvas);
+ SkCanvas* nativeCanvas = reinterpret_cast<SkCanvas*>(
+ env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
+ int saveCount = env->GetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount);
+ nativeCanvas->restoreToCount(saveCount);
+ nativeCanvas->setBitmapDevice(SkBitmap());
+ env->SetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount, 0);
// unlock surface
status_t err = surface->unlockAndPost();
@@ -895,16 +888,14 @@ int register_android_view_Surface(JNIEnv* env)
env->GetFieldID(gSurfaceClassInfo.clazz, "mGenerationId", "I");
gSurfaceClassInfo.mCanvas =
env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvas", "Landroid/graphics/Canvas;");
+ gSurfaceClassInfo.mCanvasSaveCount =
+ env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvasSaveCount", "I");
gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "()V");
clazz = env->FindClass("android/graphics/Canvas");
- gCanvasClassInfo.mFinalizer = env->GetFieldID(clazz, "mFinalizer", "Landroid/graphics/Canvas$CanvasFinalizer;");
gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "I");
gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat", "I");
- clazz = env->FindClass("android/graphics/Canvas$CanvasFinalizer");
- gCanvasFinalizerClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "I");
-
clazz = env->FindClass("android/graphics/Rect");
gRectClassInfo.left = env->GetFieldID(clazz, "left", "I");
gRectClassInfo.top = env->GetFieldID(clazz, "top", "I");
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 64cbda3091a4..87b312f4cbfe 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -43,16 +43,11 @@ static struct {
} gRectClassInfo;
static struct {
- jfieldID mFinalizer;
- jfieldID mNativeCanvas;
- jfieldID mSurfaceFormat;
+ jfieldID nativeCanvas;
+ jfieldID surfaceFormat;
} gCanvasClassInfo;
static struct {
- jfieldID mNativeCanvas;
-} gCanvasFinalizerClassInfo;
-
-static struct {
jfieldID nativeWindow;
} gTextureViewClassInfo;
@@ -125,15 +120,6 @@ static void android_view_TextureView_destroyNativeWindow(JNIEnv* env, jobject te
}
}
-static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
- jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
- SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
- env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
- env->SetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas, (int)newCanvas);
- env->SetIntField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int)newCanvas);
- SkSafeUnref(previousCanvas);
-}
-
static void android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
jint nativeWindow, jobject canvas, jobject dirtyRect) {
@@ -171,10 +157,9 @@ static void android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
bitmap.setPixels(NULL);
}
- SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format);
-
- SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
- swapCanvasPtr(env, canvas, nativeCanvas);
+ SET_INT(canvas, gCanvasClassInfo.surfaceFormat, buffer.format);
+ SkCanvas* nativeCanvas = (SkCanvas*) GET_INT(canvas, gCanvasClassInfo.nativeCanvas);
+ nativeCanvas->setBitmapDevice(bitmap);
SkRect clipRect;
clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
@@ -189,8 +174,8 @@ static void android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
jint nativeWindow, jobject canvas) {
- SkCanvas* nativeCanvas = SkNEW(SkCanvas);
- swapCanvasPtr(env, canvas, nativeCanvas);
+ SkCanvas* nativeCanvas = (SkCanvas*) GET_INT(canvas, gCanvasClassInfo.nativeCanvas);
+ nativeCanvas->setBitmapDevice(SkBitmap());
if (nativeWindow) {
sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
@@ -241,12 +226,8 @@ int register_android_view_TextureView(JNIEnv* env) {
GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I");
FIND_CLASS(clazz, "android/graphics/Canvas");
- GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer", "Landroid/graphics/Canvas$CanvasFinalizer;");
- GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
- GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
-
- FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer");
- GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
+ GET_FIELD_ID(gCanvasClassInfo.nativeCanvas, clazz, "mNativeCanvas", "I");
+ GET_FIELD_ID(gCanvasClassInfo.surfaceFormat, clazz, "mSurfaceFormat", "I");
FIND_CLASS(clazz, "android/view/TextureView");
GET_FIELD_ID(gTextureViewClassInfo.nativeWindow, clazz, "mNativeWindow", "I");
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
new file mode 100644
index 000000000000..090659356bb6
--- /dev/null
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NetworkStats"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <cutils/logger.h>
+#include <jni.h>
+
+#include <ScopedUtfChars.h>
+#include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
+
+#include <utils/Log.h>
+#include <utils/misc.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+static jclass gStringClass;
+
+static struct {
+ jfieldID size;
+ jfieldID iface;
+ jfieldID uid;
+ jfieldID set;
+ jfieldID tag;
+ jfieldID rxBytes;
+ jfieldID rxPackets;
+ jfieldID txBytes;
+ jfieldID txPackets;
+ jfieldID operations;
+} gNetworkStatsClassInfo;
+
+struct stats_line {
+ int32_t idx;
+ char iface[32];
+ int32_t uid;
+ int32_t set;
+ int32_t tag;
+ int64_t rxBytes;
+ int64_t rxPackets;
+ int64_t txBytes;
+ int64_t txPackets;
+};
+
+static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
+ jstring path, jint limitUid) {
+ ScopedUtfChars path8(env, path);
+ if (path8.c_str() == NULL) {
+ return -1;
+ }
+
+ FILE *fp = fopen(path8.c_str(), "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ Vector<stats_line> lines;
+
+ int lastIdx = 1;
+ char buffer[384];
+ while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+ stats_line s;
+ int64_t rawTag;
+ if (sscanf(buffer, "%d %31s 0x%llx %u %u %llu %llu %llu %llu", &s.idx,
+ &s.iface, &rawTag, &s.uid, &s.set, &s.rxBytes, &s.rxPackets,
+ &s.txBytes, &s.txPackets) == 9) {
+ if (s.idx != lastIdx + 1) {
+ ALOGE("inconsistent idx=%d after lastIdx=%d", s.idx, lastIdx);
+ return -1;
+ }
+ lastIdx = s.idx;
+
+ s.tag = rawTag >> 32;
+ lines.push_back(s);
+ }
+ }
+
+ if (fclose(fp) != 0) {
+ return -1;
+ }
+
+ int size = lines.size();
+
+ ScopedLocalRef<jobjectArray> iface(env, env->NewObjectArray(size, gStringClass, NULL));
+ if (iface.get() == NULL) return -1;
+ ScopedIntArrayRW uid(env, env->NewIntArray(size));
+ if (uid.get() == NULL) return -1;
+ ScopedIntArrayRW set(env, env->NewIntArray(size));
+ if (set.get() == NULL) return -1;
+ ScopedIntArrayRW tag(env, env->NewIntArray(size));
+ if (tag.get() == NULL) return -1;
+ ScopedLongArrayRW rxBytes(env, env->NewLongArray(size));
+ if (rxBytes.get() == NULL) return -1;
+ ScopedLongArrayRW rxPackets(env, env->NewLongArray(size));
+ if (rxPackets.get() == NULL) return -1;
+ ScopedLongArrayRW txBytes(env, env->NewLongArray(size));
+ if (txBytes.get() == NULL) return -1;
+ ScopedLongArrayRW txPackets(env, env->NewLongArray(size));
+ if (txPackets.get() == NULL) return -1;
+ ScopedLongArrayRW operations(env, env->NewLongArray(size));
+ if (operations.get() == NULL) return -1;
+
+ for (int i = 0; i < size; i++) {
+ ScopedLocalRef<jstring> ifaceString(env, env->NewStringUTF(lines[i].iface));
+ env->SetObjectArrayElement(iface.get(), i, ifaceString.get());
+
+ uid[i] = lines[i].uid;
+ set[i] = lines[i].set;
+ tag[i] = lines[i].tag;
+ rxBytes[i] = lines[i].rxBytes;
+ rxPackets[i] = lines[i].rxPackets;
+ txBytes[i] = lines[i].txBytes;
+ txPackets[i] = lines[i].txPackets;
+ }
+
+ env->SetIntField(stats, gNetworkStatsClassInfo.size, size);
+ env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray());
+
+ return 0;
+}
+
+static jclass findClass(JNIEnv* env, const char* name) {
+ ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
+ jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
+ if (result == NULL) {
+ ALOGE("failed to find class '%s'", name);
+ abort();
+ }
+ return result;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "nativeReadNetworkStatsDetail",
+ "(Landroid/net/NetworkStats;Ljava/lang/String;I)I",
+ (void*) readNetworkStatsDetail }
+};
+
+int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) {
+ int err = AndroidRuntime::registerNativeMethods(env,
+ "com/android/internal/net/NetworkStatsFactory", gMethods,
+ NELEM(gMethods));
+
+ gStringClass = findClass(env, "java/lang/String");
+
+ jclass clazz = env->FindClass("android/net/NetworkStats");
+ gNetworkStatsClassInfo.size = env->GetFieldID(clazz, "size", "I");
+ gNetworkStatsClassInfo.iface = env->GetFieldID(clazz, "iface", "[Ljava/lang/String;");
+ gNetworkStatsClassInfo.uid = env->GetFieldID(clazz, "uid", "[I");
+ gNetworkStatsClassInfo.set = env->GetFieldID(clazz, "set", "[I");
+ gNetworkStatsClassInfo.tag = env->GetFieldID(clazz, "tag", "[I");
+ gNetworkStatsClassInfo.rxBytes = env->GetFieldID(clazz, "rxBytes", "[J");
+ gNetworkStatsClassInfo.rxPackets = env->GetFieldID(clazz, "rxPackets", "[J");
+ gNetworkStatsClassInfo.txBytes = env->GetFieldID(clazz, "txBytes", "[J");
+ gNetworkStatsClassInfo.txPackets = env->GetFieldID(clazz, "txPackets", "[J");
+ gNetworkStatsClassInfo.operations = env->GetFieldID(clazz, "operations", "[J");
+
+ return err;
+}
+
+}
diff --git a/core/res/Android.mk b/core/res/Android.mk
index 22f4fdc90a05..cfc791df43eb 100644
--- a/core/res/Android.mk
+++ b/core/res/Android.mk
@@ -42,7 +42,3 @@ include $(BUILD_PACKAGE)
# Make sure the system .rs files get compiled before building the package-export.apk.
# $(resource_export_package): $(framework_RenderScript_STAMP_FILE)
-
-# define a global intermediate target that other module may depend on.
-.PHONY: framework-res-package-target
-framework-res-package-target: $(LOCAL_BUILT_MODULE)
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 1dfdf977deba..53fbeb4677cc 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -617,7 +617,7 @@
<string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"앱이 네트워크 정책을 관리하고 앱별 규칙을 정의할 수 있도록 허용합니다."</string>
<string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"네트워크 사용량 계산 수정"</string>
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"애플리케이션이 애플리케이션의 네트워크 사용량을 계산하는 방식을 수정할 수 있도록 허용합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
- <string name="permlab_accessNotifications" msgid="7673416487873432268">"액세스 알림"</string>
+ <string name="permlab_accessNotifications" msgid="7673416487873432268">"알림 액세스"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"앱이 다른 앱에서 게시한 알림을 비롯하여 알림을 검색하고 살펴보며 삭제할 수 있도록 허용합니다."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"비밀번호 규칙 설정"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"화면 잠금해제 비밀번호에 허용되는 길이 및 문자 수를 제어합니다."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d0b151015261..554921bf24c5 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -190,8 +190,8 @@
<string name="permgroupdesc_dictionary" msgid="7921166355964764490">"Soma maneno katika kamusi ya mtumiaji."</string>
<string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Andika Kamusi ya Mtumiaji"</string>
<string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Ongeza maneno katika kamusi mtumiaji."</string>
- <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Vialamisho na Historia"</string>
- <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Kufikia moja kwa moja vialamisho na historia ya kivinjari"</string>
+ <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Alamisho na Historia"</string>
+ <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Kufikia, moja kwa moja, alamisho na historia ya kivinjari."</string>
<string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"Kengele"</string>
<string name="permgroupdesc_deviceAlarms" msgid="4769356362251641175">"Weka saa ya kengele."</string>
<string name="permgrouplab_voicemail" msgid="4162237145027592133">"Barua ya sauti"</string>
@@ -863,8 +863,8 @@
<string name="js_dialog_before_unload" msgid="730366588032430474">"Toka kwa ukurasa huu?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Gusa Sawa ili kuendelea, au Ghairi ili kubaki kwenye ukurasa wa sasa."</string>
<string name="save_password_label" msgid="6860261758665825069">"Thibitisha"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Kidokezo: Gonga mara mbili ili kukuza ndani na nje."</string>
- <string name="autofill_this_form" msgid="4616758841157816676">"Mjazo-otomatiki"</string>
- <string name="setup_autofill" msgid="7103495070180590814">"Sanidi Mjazo-otomati"</string>
+ <string name="autofill_this_form" msgid="4616758841157816676">"Kujaza kiotomatiki"</string>
+ <string name="setup_autofill" msgid="7103495070180590814">"Weka uwezo wa kujaza kiotomatiki"</string>
<string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string>
<string name="autofill_address_summary_name_format" msgid="3268041054899214945">"$1$2$3"</string>
<string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
@@ -885,7 +885,7 @@
<string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Inaruhusu programu kusoma historia ya URL zote ambazo zimetembelewa na Kivinjari, na alamisho zote za Kivinjari. Kumbuka: idhini hii haiwezi kutekelezwa vivinjari vya vingine au programu zingine zenye uwezo wa kuvinjari."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"andika alamisho na historia ya wavuti"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Inaruhusu programu kurekebisha historia ya Kivinjari au alamisho zilizohifadhiwa kwenye kompyuta kibao yako. Hii inaruhusu programu kufuta au kurekebisha data ya Kivinjari. Kumbuka: huenda idhini hii isitekelezwe na kivinjari kingine au programu nyingine zenye uwezo wa kuvinjari wavuti."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Inaruhusu programu kurekebisha historia ya Kivinjari au vialamisho vilivyohifadhiwa kwenye simu yako. Hii huenda ikaruhusu programu kufuta au kurekebisha data ya Kivinjari. Kumbuka: huenda idhini hii isitekelezwe na vivinjari vingine au programu nyingine zenye uwezo wa kuvinjari wavuti."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Inaruhusu programu kurekebisha historia ya Kivinjari au alamisho zilizohifadhiwa kwenye simu yako. Hii huenda ikaruhusu programu kufuta au kurekebisha data ya Kivinjari. Kumbuka: huenda idhini hii isitekelezwe na vivinjari vingine au programu nyingine zenye uwezo wa kuvinjari wavuti."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"weka kengele"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Inaruhusu programu kuweka kengele katika programu iliyosakinishwa ya kengele. Programu zingine za kengele zinawezakosa kutekeleza kipengee hiki."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ongeza barua ya sauti"</string>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index 649e39d0cacb..0461c0bfdc11 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -213,8 +213,8 @@ public class AccessPointParserHelper {
config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
// Initialize other fields.
config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE);
- config.enterpriseConfig.setCaCertificate("");
- config.enterpriseConfig.setClientCertificate("");
+ config.enterpriseConfig.setCaCertificateAlias("");
+ config.enterpriseConfig.setClientCertificateAlias("");
config.enterpriseConfig.setIdentity("");
config.enterpriseConfig.setAnonymousIdentity("");
break;
@@ -288,12 +288,12 @@ public class AccessPointParserHelper {
}
if (cacert) {
String cacertValue = new String(ch, start, length);
- config.enterpriseConfig.setCaCertificate(cacertValue);
+ config.enterpriseConfig.setCaCertificateAlias(cacertValue);
cacert = false;
}
if (usercert) {
String usercertValue = new String(ch, start, length);
- config.enterpriseConfig.setClientCertificate(usercertValue);
+ config.enterpriseConfig.setClientCertificateAlias(usercertValue);
usercert = false;
}
if (ip) {
diff --git a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
new file mode 100644
index 000000000000..2174be5dc0a3
--- /dev/null
+++ b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net;
+
+import android.net.NetworkStats;
+import android.os.SystemClock;
+
+import com.google.caliper.SimpleBenchmark;
+
+import java.io.File;
+
+public class NetworkStatsFactoryBenchmark extends SimpleBenchmark {
+ private File mStats;
+
+ // TODO: consider staging stats file with different number of rows
+
+ @Override
+ protected void setUp() {
+ mStats = new File("/proc/net/xt_qtaguid/stats");
+ }
+
+ @Override
+ protected void tearDown() {
+ mStats = null;
+ }
+
+ public void timeReadNetworkStatsDetailJava(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ NetworkStatsFactory.javaReadNetworkStatsDetail(mStats, NetworkStats.UID_ALL);
+ }
+ }
+
+ public void timeReadNetworkStatsDetailNative(int reps) {
+ for (int i = 0; i < reps; i++) {
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
+ NetworkStatsFactory.nativeReadNetworkStatsDetail(
+ stats, mStats.getAbsolutePath(), NetworkStats.UID_ALL);
+ }
+ }
+}
diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs
index 8b09fe62d5c5..81982a130e65 100644
--- a/docs/html/google/google_toc.cs
+++ b/docs/html/google/google_toc.cs
@@ -80,6 +80,9 @@
<span class="en">Reference</span></a></li>
</ul>
</li>
+ <li><a href="<?cs var:toroot?>google/play/billing/billing_subscriptions.html">
+ <span class="en">Subscriptions</span></a>
+ </li>
<li><a href="<?cs var:toroot?>google/play/billing/billing_best_practices.html">
<span class="en">Security and Design</span></a>
</li>
diff --git a/docs/html/google/play/billing/api.jd b/docs/html/google/play/billing/api.jd
index 9091f511469a..3d467150991f 100644
--- a/docs/html/google/play/billing/api.jd
+++ b/docs/html/google/play/billing/api.jd
@@ -11,12 +11,13 @@ parent.link=index.html
<li><a href="#producttypes">Product Types</a>
<ol>
<li><a href="#managed">Managed In-app Products</a><li>
+ <li><a href="#subs">Subscriptions</a><li>
</ol>
</li>
<li><a href="#purchase">Purchasing Items</a></li>
- <li><a href="#consume">Consuming Items</a>
+ <li><a href="#consume">Consuming In-app Products</a>
<ol>
- <li><a href="#consumetypes">Non-consumable and Consumable Items</a><li>
+ <li><a href="#consumetypes">Non-consumable and Consumable In-app Products</a><li>
<li><a href="#managingconsumables">Managing Consumable Purchases</a><li>
</ol>
</li>
@@ -40,11 +41,22 @@ parent.link=index.html
<h2 id="producttypes">Product Types</h2>
<p>You define your products using the Google Play Developer Console, including product type, SKU, price, description, and so on. For more information, see <a
-href="{@docRoot}google/play/billing/billing_admin.html">Administering In-app Billing</a>. The Version 3 API only supports the managed in-app product type.</p>
+href="{@docRoot}google/play/billing/billing_admin.html">Administering In-app Billing</a>. The Version 3 API supports managed in-app products and subscriptions.</p>
<h3 id="managed">Managed In-app Products</h3>
<p>Managed in-app products are items that have their ownership information tracked and managed by Google Play. When a user purchases a managed in-app item, Google Play stores the purchase information for each item on a per-user basis. This enables you to later query Google Play at any time to restore the state of the items a specific user has purchased. This information is persistent on the Google Play servers even if the user uninstalls the application or if they change devices.</p>
<p>If you are using the Version 3 API, you can also consume managed items within your application. You would typically implement consumption for items that can be purchased multiple times (such as in-game currency, fuel, or magic spells). Once purchased, a managed item cannot be purchased again until you consume the item, by sending a consumption request to Google Play. To learn more about in-app product consumption, see <a href="#consume">Consuming Items</a></p>
+<h3 id="subs">Subscriptions</h3>
+<p>A subscription is a product type offered in In-app Billing that lets you sell
+content, services, or features to users from inside your app with recurring
+monthly or annual billing. You can sell subscriptions to almost any type of
+digital content, from any type of app or game. To understand how
+subscriptions work, see <a href="{@docRoot}google/play/billing/billing_subscriptions.html">In-app Billing Subscriptions</a>.</p>
+<p>With the Version 3 API, you can use the same purchase flow for buying
+subscriptions and retrieving subscription purchase information as with in-app
+products. For a code example, see <a href="{@docRoot}google/play/billing/billing_integrate.html#Subs">Implementing Subscriptions</a>.</p>
+<p class="caution"><strong>Important</strong>: Unlike in-app products,
+subscriptions cannot be consumed.</p>
<h2 id="purchase">Purchasing Items</h2>
@@ -72,29 +84,38 @@ href="{@docRoot}google/play/billing/billing_admin.html">Administering In-app Bil
</p>
<p>To learn more about the Version 3 API calls and server responses, see <a href="{@docRoot}google/play/billing/billing_reference.html">In-app Billing Reference</a>.</p>
-<h2 id="consume">Consuming Items</h2>
-<p>You can use the consumption mechanism to track the user's ownership of in-app products.</p>
-<p>In Version 3, all in-app products are managed. This means that the user's ownership of all in-app item purchases is maintained by Google Play, and your application can query the user's purchase information when needed. When the user successfully purchases an item, that purchase is recorded in Google Play. Once an item is purchased, it is considered to be "owned". Items in the "owned" state cannot be purchased from Google Play. You must send a consumption request for the "owned" item before Google Play makes it available for purchase again. Consuming the item reverts it to the "unowned" state, and discards the previous purchase data.</p>
+<h2 id="consume">Consuming In-app Products</h2>
+<p>You can use the consumption mechanism to track the user's ownership of in-app
+products.</p>
+<p>In Version 3, all in-app products are managed. This means that the user's
+ownership of all in-app item purchases is maintained by Google Play, and your
+application can query the user's purchase information when needed. When the user
+successfully purchases an in-app product, that purchase is recorded in Google
+Play. Once an in-app product is purchased, it is considered to be "owned".
+In-app products in the "owned" state cannot be purchased from Google Play. You
+must send a consumption request for the "owned" in-app product before Google
+Play makes it available for purchase again. Consuming the in-app product reverts
+it to the "unowned" state, and discards the previous purchase data.</p>
<div class="figure" style="width:420px">
<img src="{@docRoot}images/in-app-billing/v3/iab_v3_consumption_flow.png" id="figure2" height="300"/>
<p class="img-caption">
<strong>Figure 2.</strong> The basic sequence for a consumption request.
</p>
</div>
-<p>To retrieve the list of product's owned by the user, your application sends a {@code getPurchases} call to Google Play. Your application can make a consumption request by sending a {@code consumePurchase} call. In the request argument, you must specify the item's unique {@code purchaseToken} String that you obtained from Google Play when it was purchased. Google Play returns a status code indicating if the consumption was recorded successfully.</p>
+<p>To retrieve the list of product's owned by the user, your application sends a {@code getPurchases} call to Google Play. Your application can make a consumption request by sending a {@code consumePurchase} call. In the request argument, you must specify the in-app product's unique {@code purchaseToken} String that you obtained from Google Play when it was purchased. Google Play returns a status code indicating if the consumption was recorded successfully.</p>
-<h3 id="consumetypes">Non-consumable and Consumable Items</h3>
+<h3 id="consumetypes">Non-consumable and Consumable In-app Products</h3>
<p>It's up to you to decide if you want to handle your in-app products as non-consumable or consumable items.</p>
<dl>
<dt>Non-consumable Items</dt>
-<dd>Typically, you would not implement consumption for items that can only be purchased once in your application and provide a permanent benefit. Once purchased, these items will be permanently associated to the user's Google account. An example of a non-consumable item is a premium upgrade or a level pack.</dd>
+<dd>Typically, you would not implement consumption for in-app products that can only be purchased once in your application and provide a permanent benefit. Once purchased, these items will be permanently associated to the user's Google account. An example of a non-consumable in-app product is a premium upgrade or a level pack.</dd>
<dt>Consumable items</dt>
<dd>In contrast, you can implement consumption for items that can be made available for purchase multiple times. Typically, these items provide certain temporary effects. For example, the user's in-game character might gain life points or gain extra gold coins in their inventory. Dispensing the benefits or effects of the purchased item in your application is called <em>provisioning</em> the in-app product. You are responsible for controlling and tracking how in-app products are provisioned to the users.
-<p class="note"><strong>Important:</strong> Before provisioning the consumable item in your application, you must send a consumption request to Google Play and receive a successful response indicating that the consumption was recorded.</p>
+<p class="note"><strong>Important:</strong> Before provisioning the consumable in-app product in your application, you must send a consumption request to Google Play and receive a successful response indicating that the consumption was recorded.</p>
</dd>
</dl>
<h3 id="managingconsumables">Managing consumable purchases in your application</h3>
-<p>Here is the basic flow for purchasing a consumable item:</p>
+<p>Here is the basic flow for purchasing a consumable in-app product:</p>
<ol>
<li>Launch a purchase flow with a {@code getBuyIntent} call</li>
<li>Get a response {@code Bundle}from Google Play indicating if the purchase completed successfully.</li>
@@ -102,10 +123,10 @@ href="{@docRoot}google/play/billing/billing_admin.html">Administering In-app Bil
<li>Get a response code from Google Play indicating if the consumption completed successfully.</li>
<li>If the consumption was successful, provision the product in your application.</li>
</ol>
-<p>Subsequently, when the user starts up or logs in to your application, you should check if the user owns any outstanding consumable items; if so, make sure to consume and provision those items. Here's the recommended application startup flow if you implement consumable items in your application:</p>
+<p>Subsequently, when the user starts up or logs in to your application, you should check if the user owns any outstanding consumable in-app products; if so, make sure to consume and provision those items. Here's the recommended application startup flow if you implement consumable in-app products in your application:</p>
<ol>
-<li>Send a {@code getPurchases} request to query the owned items for the user.</li>
-<li>If there are any consumable items, consume the items by calling {@code consumePurchase}. This step is necessary because the application might have completed the purchase order for the consumable item, but stopped or got disconnected before the application had the chance to send a consumption request.</li>
+<li>Send a {@code getPurchases} request to query the owned in-app products for the user.</li>
+<li>If there are any consumable in-app products, consume the items by calling {@code consumePurchase}. This step is necessary because the application might have completed the purchase order for the consumable item, but stopped or got disconnected before the application had the chance to send a consumption request.</li>
<li>Get a response code from Google Play indicating if the consumption completed successfully.</li>
<li>If the consumption was successful, provision the product in your application.</li>
</ol>
diff --git a/docs/html/google/play/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd
index 315befa51cfd..297e90604db1 100644
--- a/docs/html/google/play/billing/billing_integrate.jd
+++ b/docs/html/google/play/billing/billing_integrate.jd
@@ -16,6 +16,7 @@ parent.link=index.html
<li><a href="#Purchase">Purchasing an Item</a></li>
<li><a href="#QueryPurchases">Querying Purchased Items</a></li>
<li><a href="#Consume">Consuming a Purchase</a><li>
+ <li><a href="#Subs">Implementing Subscriptions</a><li>
</ol>
</li>
</ol>
@@ -176,7 +177,7 @@ if (response == 0) {
</pre>
<h3 id="Purchase">Purchasing an Item</h3>
-<p>To start a purchase request from your app, call the {@code getBuyIntent} method on the In-app Billing service. Pass in to the method the In-app Billing API version (“3”), the package name of your calling app, the product ID for the item to purchase, the purchase type (“inapp”), and a {@code developerPayload} String. The {@code developerPayload} String is used to specify any additional arguments that you want Google Play to send back along with the purchase information.</p>
+<p>To start a purchase request from your app, call the {@code getBuyIntent} method on the In-app Billing service. Pass in to the method the In-app Billing API version (“3”), the package name of your calling app, the product ID for the item to purchase, the purchase type (“inapp” or "subs"), and a {@code developerPayload} String. The {@code developerPayload} String is used to specify any additional arguments that you want Google Play to send back along with the purchase information.</p>
<pre>
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(),
@@ -238,7 +239,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
<p class="note"><strong>Security Recommendation:</strong> When you send a purchase request, create a String token that uniquely identifies this purchase request and include this token in the {@code developerPayload}.You can use a randomly generated string as the token. When you receive the purchase response from Google Play, make sure to check the returned data signature, the {@code orderId}, and the {@code developerPayload} String. For added security, you should perform the checking on your own secure server. Make sure to verify that the {@code orderId} is a unique value that you have not previously processed, and the {@code developerPayload} String matches the token that you sent previously with the purchase request.</p>
<h3 id="QueryPurchases">Querying for Purchased Items</h3>
-<p>To retrieve information about purchases made by a user from your app, call the {@code getPurchases} method on the In-app Billing Version 3 service. Pass in to the method the In-app Billing API version (“3”), the package name of your calling app, and the purchase type (“inapp”).</p>
+<p>To retrieve information about purchases made by a user from your app, call the {@code getPurchases} method on the In-app Billing Version 3 service. Pass in to the method the In-app Billing API version (“3”), the package name of your calling app, and the purchase type (“inapp” or "subs").</p>
<pre>
Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
</pre>
@@ -273,8 +274,26 @@ if (response == 0) {
</pre>
<h3 id="Consume">Consuming a Purchase</h3>
-<p>You can use the In-app Billing Version 3 API to track the ownership of purchased items in Google Play. Once an item is purchased, it is considered to be "owned" and cannot be purchased from Google Play. You must send a consumption request for the item before Google Play makes it available for purchase again. All managed in-app products are consumable. How you use the consumption mechanism in your app is up to you. Typically, you would implement consumption for products with temporary benefits that users may want to purchase multiple times (for example, in-game currency or equipment). You would typically not want to implement consumption for products that are purchased once and provide a permanent effect (for example, a premium upgrade).</p>
-<p>To record a purchase consumption, send the {@code consumePurchase} method to the In-app Billing service and pass in the {@code purchaseToken} String value that identifies the purchase to be removed. The {@code purchaseToken} is part of the data returned in the {@code INAPP_PURCHASE_DATA} String by the Google Play service following a successful purchase request. In this example, you are recording the consumption of a product that is identified with the {@code purchaseToken} in the {@code token} variable.</p>
+<p>You can use the In-app Billing Version 3 API to track the ownership of
+purchased in-app products in Google Play. Once an in-app product is purchased,
+it is considered to be "owned" and cannot be purchased from Google Play. You
+must send a consumption request for the in-app product before Google Play makes
+it available for purchase again.</p>
+<p class="caution"><strong>Important</strong>: Managed in-app products are
+consumable, but subscriptions are not.</p>
+<p>How you use the consumption mechanism in your app is up to you. Typically,
+you would implement consumption for in-app products with temporary benefits that
+users may want to purchase multiple times (for example, in-game currency or
+equipment). You would typically not want to implement consumption for in-app
+products that are purchased once and provide a permanent effect (for example,
+a premium upgrade).</p>
+<p>To record a purchase consumption, send the {@code consumePurchase} method to
+the In-app Billing service and pass in the {@code purchaseToken} String value
+that identifies the purchase to be removed. The {@code purchaseToken} is part
+of the data returned in the {@code INAPP_PURCHASE_DATA} String by the Google
+Play service following a successful purchase request. In this example, you are
+recording the consumption of a product that is identified with the
+{@code purchaseToken} in the {@code token} variable.</p>
<pre>
int response = mService.consumePurchase(3, getPackageName(), token);
</pre>
@@ -282,6 +301,33 @@ int response = mService.consumePurchase(3, getPackageName(), token);
<p>It's your responsibility to control and track how the in-app product is provisioned to the user. For example, if the user purchased in-game currency, you should update the player's inventory with the amount of currency purchased.</p>
<p class="note"><strong>Security Recommendation:</strong> You must send a consumption request before provisioning the benefit of the consumable in-app purchase to the user. Make sure that you have received a successful consumption response from Google Play before you provision the item.</p>
+<h3 id="Subs">Implementing Subscriptions</h3>
+<p>Launching a purchase flow for a subscription is similar to launching the
+purchase flow for a product, with the exception that the product type must be set
+to "subs". The purchase result is delivered to your Activity's
+{@link android.app.Activity#onActivityResult onActivityResult} method, exactly
+as in the case of in-app products.</p>
+<pre>
+Bundle bundle = mService.getBuyIntent(3, "com.example.myapp",
+ MY_SKU, "subs", developerPayload);
+
+PendingIntent pendingIntent = bundle.getParcelable(RESPONSE_BUY_INTENT);
+if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) {
+ // Start purchase flow (this brings up the Google Play UI).
+ // Result will be delivered through onActivityResult().
+ startIntentSenderForResult(pendingIntent, RC_BUY, new Intent(),
+ Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
+}
+</pre>
+<p>To query for active subscriptions, use the {@code getPurchases} method, again
+with the product type parameter set to "subs".</p>
+<pre>
+Bundle activeSubs = mService.getPurchases(3, "com.example.myapp",
+ "subs", continueToken);
+</pre>
+<p>The call returns a {@code Bundle} with all the active subscriptions owned by
+the user. Once a subscription expires without renewal, it will no longer appear
+in the returned {@code Bundle}.</p>
diff --git a/docs/html/google/play/billing/billing_overview.jd b/docs/html/google/play/billing/billing_overview.jd
index aa48fc8195f7..bda9237774ba 100644
--- a/docs/html/google/play/billing/billing_overview.jd
+++ b/docs/html/google/play/billing/billing_overview.jd
@@ -7,9 +7,12 @@ parent.link=index.html
<div id="qv">
<h2>Quickview</h2>
<ul>
- <li>Use In-app Billing to sell digital goods, including one-time items and recurring subscriptions.</li>
- <li>Supported for any app published on Google Play. You only need a Google Play publisher account and a Google Checkout Merchant account.</li>
- <li>Checkout processing is automatically handled by Google Play, with the same look-and-feel as for app purchases.</li>
+ <li>Use In-app Billing to sell digital goods, including one-time items and
+recurring subscriptions.</li>
+ <li>Supported for any app published on Google Play. You only need a Google
+Play Developer Console account and a Google Checkout Merchant account.</li>
+ <li>Checkout processing is automatically handled by Google Play, with the
+same look-and-feel as for app purchases.</li>
</ul>
<h2>In this document</h2>
<ol>
@@ -21,14 +24,12 @@ parent.link=index.html
</li>
<li><a href="#console">Google Play Developer Console</a></li>
<li><a href="#checkout">Google Play Purchase Flow</a></li>
- <li><a href="#samples">Sample Apps</a></li>
+ <li><a href="#samples">Sample App</a></li>
<li><a href="#migration">Migration Considerations</a></li>
</ol>
<h2>Related Samples</h2>
<ol>
<li><a href="{@docRoot}training/in-app-billing/preparing-iab-app.html#GetSample">Sample Application (V3)</a></li>
- <li><a href="{@docRoot}google/play/billing/v2/billing_integrate.html#billing-download">Sample
- Application (V2)</a></li>
</ol>
</div>
</div>
@@ -51,10 +52,12 @@ the Google Play server.</p>
through Google Play. To complete in-app purchase requests, the Google Play app
must be able to access the Google Play server over the network.</p>
-<p>Currently, Google Play supports two versions of the In-app Billing API.
-To determine which version you should use, see <a href="#migration">Migration
-Considerations</a>.</p>
-<h4><a href="{@docRoot}google/play/billing/api.html">Version 3</a> (recommended)</h4>
+<p>In-app billing Version 3 is the latest version, and maintains very broad
+compatibility across the range of Android devices. In-app Billing Version 3 is
+supported on devices running Android 2.2 or higher that have the latest version
+of the Google Play store installed (<a href="{@docRoot}about/dashboards/index.html">a vast majority</a> of active devices).</p>
+
+<h4>Version 3 features</h4>
<ul>
<li>Requests are sent through a streamlined API that allows you to easily request
product details from Google Play, order in-app products, and quickly restore
@@ -66,21 +69,10 @@ ownership of in-app products). The user cannot own multiple copies of an in-app
item; only one copy can be owned at any point in time</li>
<li>Purchased items can be consumed. When consumed, the item reverts to the
"unowned" state and can be purchased again from Google Play</li>
+<li>Provides support for subscriptions</li>
</ul>
-<h4><a href="{@docRoot}google/play/billing/v2/api.html">Version 2</a></h4>
-<ul>
-<li>Requests are sent via a single API interface ({@code sendBillingRequest})</li>
-<li>Responses from Google Play are asynchronous, in the form of broadcast intents</li>
-<li>No consumption model provided. You have to implement your own solution</li>
-<li>Provides support for subscriptions and unmanaged in-app purchase items,
-as well as managed in-app products</li>
-</ul>
-<p>Both versions offer very broad compatibility across the range of Android
-devices. In-app Billing Version 3 is supported on devices running Android 2.2 or
-higher that have the latest version of the Google Play store installed
-(over 90% of active devices). Version 2 offers similar compatibility. See
-<a href="{@docRoot}google/play/billing/versions.html">Version Notes</a> for
-more details.</p>
+<p>For details about other versions of In-app Billing, see the
+<a href="{@docRoot}google/play/billing/versions.html">Version Notes</a>.</p>
<h2 id="products">In-app Products</h2>
<p>In-app products are the digital goods that you offer for sale from inside your
@@ -102,12 +94,9 @@ published for another app, even if they are from the same developer.</p>
how you monetize your application. In all cases, you define your products using
the Google Play Developer Console.</p>
<p>You can specify these types of products for your In-app Billing application
-— <em>managed in-app products</em>, <em>subscriptions</em>, and <em>unmanaged
-in-app products</em>. The term “managed” indicates that Google Play handles and
-tracks ownership for in-app products on your application on a per user account
-basis, while “unmanaged” indicates that you will manage the ownership information yourself.</p>
-<p>To learn more about the product types supported by the different API versions,
-see the related documentation for <a href="{@docRoot}google/play/billing/v2/api.html#billing-types">Version 2</a> and <a href="{@docRoot}google/play/billing/api.html#producttypes">Version 3</a>.</p>
+— <em>managed in-app products</em> and <em>subscriptions</em>. Google Play
+handles and tracks ownership for in-app products and subscriptions on your
+application on a per user account basis. <a href="{@docRoot}google/play/billing/api.html#producttypes">Learn more about the product types supported by In-app Billing Version 3</a>.</p>
<h2 id="console">Google Play Developer Console</h2>
<p>The Developer Console is where you can publish your
@@ -148,70 +137,31 @@ providing the checkout user interface (middle screen). When checkout is
complete, the application resumes.
</p>
-<h2 id="samples">Sample Applications</h2>
+<h2 id="samples">Sample Application</h2>
<p>To help you integrate In-app Billing into your application, the Android SDK
-provides two sample applications that demonstrate how to sell in-app products
+provides a sample application that demonstrates how to sell in-app products and subscriptions
from inside an app.</p>
-<dl>
-<dt><a href="{@docRoot}training/in-app-billing/preparing-iab-app.html#GetSample">TrivialDrive sample for the Version 3 API</a></dt>
-<dd>This sample shows how to use the In-app Billing Version 3 API to implement
-in-app product purchases for a driving game. The application demonstrates how to
-send In-app Billing requests, and handle synchronous responses from Google Play.
-The application also shows how to record item consumption with the API. The
-Version 3 sample includes convenience classes for processing In-app Billing
-operations as well as perform automatic signature verification.</dd>
-
-<dt><a href="{@docRoot}google/play/billing/v2/billing_integrate.html#billing-download">Dungeons sample for the Version 2 API</a></dt>
-<dd>This sample demonstrates how to use the In-app Billing Version 2 API to sell
-standard in-app products and subscriptions for an adventuring game. It also
-contains examples of the database, user interface, and business logic you might
-use to implement In-app Billing.</dd>
-</dl>
-<p class="caution"><strong>Important</strong>: It's <em>strongly recommended</em>
-that you obfuscate the code in your application before you publish it. For
-more information, see
+<p>The <a href="{@docRoot}training/in-app-billing/preparing-iab-app.html#GetSample">TrivialDrive sample for the Version 3 API</a> sample shows how to use the In-app Billing Version 3 API
+to implement in-app product and subscription purchases for a driving game. The
+application demonstrates how to send In-app Billing requests, and handle
+synchronous responses from Google Play. The application also shows how to record
+item consumption with the API. The Version 3 sample includes convenience classes
+for processing In-app Billing operations as well as perform automatic signature
+verification.</p>
+
+<p class="caution"><strong>Recommendation</strong>: Make sure to obfuscate the
+code in your application before you publish it. For more information, see
<a href="{@docRoot}google/play/billing/billing_best_practices.html">Security
and Design</a>.</p>
<h2 id="migration">Migration Considerations</h2>
-<p>The following considerations may be applicable if you are planning to create a new
-in-app biling application, or migrate your existing In-app Billing implementation
-from the <a href="{@docRoot}google/play/billing/v2/api.html">Version 2</a> or
-earlier API to the <a href="{@docRoot}google/play/billing/api.html">Version 3</a> API.</p>
-<p>Google Play will continue to support both the Version 2 and Version 3 APIs for
-some time, so you can plan to migrate to Version 3 at your own pace. The Google
-Play team will give advance notice of any upcoming changes to the support
-status of In-app Billing Version 2.</p>
-<p>You can use the following table to decide which version of the API to use,
-depending on the needs of your application.</p>
-<p class="table-caption" id="table1">
- <strong>Table 1.</strong> Selecting the In-app Billing API Version for Your
-Project</p>
-
-<table>
-<tr>
-<th scope="col">Choose Version 3 if ...</th>
-<th scope="col">Choose Version 2 if ...</th>
-</tr>
-<tr>
-<td>
- <ul>
- <li>You want to sell in-app products only (and not subscriptions)</li>
- <li>You need synchronous order confirmations when purchases complete</li>
- <li>You need to synchronously restore a user's current purchases</li>
- </ul>
-</td>
-<td>
- <ul>
- <li>You want to sell subscriptions in your app</li>
- </ul>
-</td>
-</tr>
-</table>
+<p>If you have an existing In-app Billing implementation that uses Version 2 or
+earlier, it is strongly recommended that you migrate to <a href="{@docRoot}google/play/billing/api.html">In-app Billing Version 3</a> at your earliest convenience.</p>
+
<p>If you have published apps selling in-app products, note that:</p>
<ul>
-<li>Managed items that you have previously defined in the Developer Console will
+<li>Managed items and subscriptions that you have previously defined in the Developer Console will
work with Version 3 as before.</li>
<li>Unmanaged items that you have defined for existing applications will be
treated as managed products if you make a purchase request for these items using
diff --git a/docs/html/google/play/billing/billing_reference.jd b/docs/html/google/play/billing/billing_reference.jd
index 758e21d7d29f..ae41521bc24e 100644
--- a/docs/html/google/play/billing/billing_reference.jd
+++ b/docs/html/google/play/billing/billing_reference.jd
@@ -102,11 +102,13 @@ parent.link=index.html
</tr>
<tr>
<td>{@code type}</td>
- <td>Value must be “inapp” for an in-app purchase type.</td>
+ <td>Value must be “inapp” for an in-app product or "subs" for
+subscriptions.</td>
</tr>
<tr>
<td>{@code price}</td>
- <td>Formatted price of the item, including its currency sign. The price does not include tax.</td>
+ <td>Formatted price of the item, including its currency sign. The price
+does not include tax.</td>
</tr>
<tr>
<td>{@code title}</td>
diff --git a/docs/html/google/play/billing/billing_subscriptions.jd b/docs/html/google/play/billing/billing_subscriptions.jd
new file mode 100644
index 000000000000..2840dbc60119
--- /dev/null
+++ b/docs/html/google/play/billing/billing_subscriptions.jd
@@ -0,0 +1,414 @@
+page.title=Subscriptions
+parent.title=In-app Billing
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>Quickview</h2>
+ <ul>
+ <li>Users purchase your subscriptions from inside your apps, rather than
+directly from Google Play.</li>
+ <li>Subscriptions let you sell products with automated, recurring billing
+(monthly or annual).</li>
+ <li>You can offer a configurable trial period for any subscription.</li>
+
+ </ul>
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#overview">Overview</a></li>
+ <li><a href="#administering">Configuring Subscriptions Items</a></li>
+ <li><a href="#cancellation">Cancellation</a></li>
+ <li><a href="#payment">Payment Processing</a></li>
+ <li><a href="#play-dev-api">Google Play Android Developer API</a></li>
+ </ol>
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}google/play/billing/billing_integrate.html#Subs">Implementing Subscriptions (V3)</a></li>
+ </ol>
+</div>
+</div>
+
+<p>Subscriptions let you sell content, services, or features in your app with
+automated, recurring billing. You can easily adapt an existing In-app Billing
+implementation to sell subscriptions.</p>
+<p>This document is focused on highlighting implementation details that are
+specific to subscriptions, along with some strategies for the associated billing
+and business models.</p>
+
+<h2 id="overview">Overview of Subscriptions</h2>
+<p>A <em>subscription</em> is a product type offered in In-app Billing that
+lets you sell content, services, or features to users from inside your app with
+recurring monthly or annual billing. You can sell subscriptions to almost any
+type of digital content, from any type of app or game.</p>
+
+<p>As with other in-app products, you configure and publish subscriptions using
+the Developer Console and then sell them from inside apps installed on
+Android devices. In the Developer console, you create subscription
+products and add them to a product list, then set a price and optional trial
+period for each, choose a billing interval (monthly or annual), and then
+publish. For more information about using the Developer Console, see
+<a href="#administering">Configuring Subscription Items</a>.</p>
+
+<p>When users purchase subscriptions in your apps, Google Play handles all
+checkout details so your apps never have to directly process any financial
+transactions. Google Play processes all payments for subscriptions through
+Google Checkout, just as it does for standard in-app products and app purchases.
+This ensures a consistent and familiar purchase flow for your users.</p>
+
+<img src="{@docRoot}images/in-app-billing/v3/billing_subscription_v3.png" style="float:right; border:4px solid ddd;">
+
+<p>After users have purchase subscriptions, they can view the subscriptions and
+cancel them from the <strong>My Apps</strong> screen in the Play Store app or
+from the app's product details page in the Play Store app. For more information
+about handling user cancellations, see <a href="#cancellation">Subscription Cancellation</a>.</p>
+
+<p>In adddition to client-side API calls, you can use the server-side API for
+In-app Billing to provide subscription purchasers with extended access to
+content (for example, from your web site or another service).
+The server-side API lets you validate the status of a subscription when users
+sign into your other services. For more information about the API, see <a
+href="#play-dev-api">Google Play Android Developer API</a>. </p>
+
+<p>You can also build on your existing external subscriber base from inside your
+Android apps.</p>
+<ul>
+<li>If you sell subscriptions on a web site, for example, you can add
+your own business logic to your Android app to determine whether the user has
+already purchased a subscription elsewhere, then allow access to your content if
+so or offer a subscription purchase from Google Play if not.</li>
+<li>You can implement your own solution for sharing subscriptions across as
+many different apps or products as you want. For example, you could sell a
+subscription that gives a subscriber access to an entire collection of apps,
+games, or other content for a monthly or annual fee. To implement this solution,
+you could add your own business logic to your app to determine whether the user
+has already purchased a given subscription and if so, allow access to your
+content.</li>
+</ul>
+</p>
+
+<p>In general the same basic policies and terms apply to subscriptions as to
+standard in-app products, however there are some differences. For complete
+information about the current policies and terms, please read the <a
+href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en
+&answer=140504">policies document</a>.</p>
+
+<p>To learn about the minimum system requirements for
+subscriptions, see the <a href="{@docRoot}google/play/billing/versions.html#Subs">Version Notes</a>.</p>
+
+<h2 id="administering">Configuring Subscription Items</h2>
+<p>To create and manage subscriptions, use the Developer Console to set up a
+product list for the app then configure these attributes for each subscription
+product:</p>
+
+<ul>
+<li>Purchase Type: always set to <strong>Subscription</strong></li>
+<li>Subscription ID: An identifier for the subscription</li>
+<li>Publishing State: Unpublished/Published</li>
+<li>Language: The default language for displaying the subscription</li>
+<li>Title: The title of the subscription product</li>
+<li>Description: Details that tell the user about the subscription</li>
+<li>Price: USD price of subscription per recurrence</li>
+<li>Recurrence: monthly or yearly</li>
+<li>Additional currency pricing (can be auto-filled)</li>
+</ul>
+
+<p>For details on how to add and configure products in the Developer Console,
+see <a href="{@docRoot}google/play/billing/billing_admin.html">Administering
+In-app Billing</a>.</p>
+
+<h3 id="pricing">Subscription pricing</h3>
+
+<p>When you create a subscription in the Developer Console, you can set a price
+for it in any available currencies. Each subscription must have a non-zero
+price. You can price multiple subscriptions for the same content differently
+&mdash; for example you could offer a discount on an annual subscription
+relative to the monthly equivalent. </p>
+
+<p class="caution"><strong>Important</strong>: To change the price of a
+subscription, you can publish a new subscription product ID at a new price,
+then offer it in your app instead of the original product. Users who have
+already purchased will continue to be charged at the
+original price, but new users will be charged at the new price.</p>
+
+<h3 id="user-billing">User billing</h3>
+
+<p>In the Developer Console, you can configure subscription products with
+automated recurring billing at either of two intervals:</p>
+
+<ul>
+ <li>Monthly &mdash; Google Play bills the customer’s Google Checkout account at
+ the time of purchase and monthly subsequent to the purchase date (exact billing
+ intervals can vary slightly over time)</li>
+ <li>Annually &mdash; Google Play bills the customer's Google Checkout account at
+ the time of purchase and again on the same date in subsequent years.</li>
+</ul>
+
+<p>Billing continues indefinitely at the interval and price specified for the
+subscription. At each subscription renewal, Google Play charges the user account
+automatically, then notifies the user of the charges afterward by email. Billing
+cycles will always match subscription cycles, based on the purchase date.</p>
+
+<p>Over the life of a subscription, the form of payment billed remains the same
+&mdash; Google Play always bills the same form of payment (such as credit card
+or by Direct Carrier Billing) that was originally used to purchase the
+subscription.</p>
+
+<p>When the subscription payment is approved by Google Checkout, Google Play
+provides a purchase token back to the purchasing app through the In-app Billing
+API. Your apps can store the token locally or pass it to your backend servers,
+which can then use it to validate or cancel the subscription remotely using the <a
+href="#play-dev-api">Google Play Android Developer API</a>.</p>
+
+<p>If a recurring payment fails (for example, because the customer’s credit
+card has become invalid), the subscription does not renew. How your app is
+notified depends on the In-app Billing API version that you are using:</p>
+<ul>
+<li>With In-app Billing Version 3, the failed or expired subscription is no longer
+returned when you call {@code getPurchases}.</li>
+<li>With In-app Billing Version 2, Google Play notifies your app at the end of
+the active cycle that the purchase state of the subscription is now "Expired".
+</li>
+</ul>
+
+<p class="note"><strong>Recommendation</strong>: Include business logic in your
+app to notify your backend servers of subscription purchases, tokens, and any
+billing errors that may occur. Your backend servers can use the server-side API
+to query and update your records and follow up with customers directly, if needed.</p>
+
+<h3 id="trials">Free trials</h3>
+
+<p>In the Developer Console, you can set up a free trial period that lets users
+try your subscription content before buying it. The trial period runs for the
+period of time that you set and then automatically converts to a full
+subscription managed according to the subscription's billing interval and
+price.</p>
+
+<p>To take advantage of a free trial, a user must "purchase" the full
+subscription through the standard In-app Billing flow, providing a valid form of
+payment to use for billing and completing the normal purchase transaction.
+However, the user is not charged any money, since the initial period corresponds
+to the free trial. Instead, Google Play records a transaction of $0.00 and the
+subscription is marked as purchased for the duration of the trial period or
+until cancellation. When the transaction is complete, Google Play notifies users
+by email that they have purchased a subscription that includes a free trial
+period and that the initial charge was $0.00. </p>
+
+<p>When the trial period ends, Google Play automatically initiates billing
+against the credit card that the user provided during the initial purchase, at
+the amount set
+for the full subscription, and continuing at the subscription interval. If
+necessary, the user can cancel the subscription at any time during the trial
+period. In this case, Google Play <em>marks the subscription as expired immediately</em>,
+rather than waiting until the end of the trial period. The user has not
+paid for the trial period and so is not entitled to continued access after
+cancellation.</p>
+
+<p>You can set up a trial period for a subscription in the Developer Console,
+without needing to modify or update your APK. Just locate and edit the
+subscription in your product list, set a valid number of days for the trial
+(must be 7 days or longer), and publish. You can change the period any time,
+although note that Google Play does not apply the change to users who have
+already "purchased" a trial period for the subscription. Only new subscription
+purchases will use the updated trial period. You can create one free trial
+period per subscription product.</p>
+
+<h3 id="publishing">Subscription publishing</h3>
+<p>When you have finished configuring your subscription product details in the
+Developer Console, you can publish the subscription in the app product list.</p>
+
+<p>In the product list, you can add subscriptions, in-app products, or both. You
+can add multiple subscriptions that give access to different content or
+services, or you can add multiple subscriptions that give access to the same
+content but for different intervals or different prices, such as for a
+promotion. For example, a news outlet might decide to offer both monthly and
+annual subscriptions to the same content, with annual having a discount. You can
+also offer in-app purchase equivalents for subscription products, to ensure that
+your content is available to users of older devices that do not support
+subscriptions.</p>
+
+<p>After you add a subscription or in-app product to the product list, you must
+publish the product before Google Play can make it available for purchase. Note
+that you must also publish the app itself before Google Play will make the
+products available for purchase inside the app. </p>
+
+<p class="caution"><strong>Important</strong>: You can remove the subscription
+product from the product list offered in your app to prevent users from seeing
+or purchasing it.</p>
+
+<h2 id="cancellation">Subscription Cancellation</h2>
+
+<p>Users can view the status of all of their subscriptions and cancel them if
+necessary from the <strong>My Apps</strong> screen in the Play Store app.
+Currently, the In-app Billing API does not provide support for programatically
+canceling subscriptions from inside the purchasing app.</p>
+
+<p>When the user cancels a subscription, Google Play does not offer a refund for
+the current billing cycle. Instead, it allows the user to have access to the
+cancelled subscription until the end of the current billing cycle, at which time
+it terminates the subscription. For example, if a user purchases a monthly
+subscription and cancels it on the 15th day of the cycle, Google Play will
+consider the subscription valid until the end of the 30th day (or other day,
+depending on the month).</p>
+
+<p>In some cases, the user may contact you directly to request cancellation of a
+subscription. In this and similar cases, you can use the server-side API to
+query and directly cancel the user’s subscription from your servers.
+
+<p class="caution"><strong>Important:</strong> In all cases, you must continue
+to offer the content that your subscribers have purchased through their
+subscriptions, for as long any users are able to access it. That is, you must
+not remove any subscriber’s content while any user still has an active
+subscription to it, even if that subscription will terminate at the end of the
+current billing cycle. Removing content that a subscriber is entitled to access
+will result in penalties. Please see the <a
+href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=140504">policies document</a> for more information. </p>
+
+<h3 id="uninstall">App uninstallation</h3>
+
+<p>When the user uninstalls an app that includes purchased subscriptions, the
+Play Store app will notify the user that there are active subscriptions. If the
+user chooses to continue with the uninstallation, the app is removed and the
+subscriptions remain active and recurring billing continues. The user can return
+to cancel the associated subscriptions at any time in the <strong>My Apps</strong>
+screen of the Play Store app. If the user chooses to cancel the uninstallation,
+the app and subscriptions remain as they were.</p>
+
+<h3 id="refunds">Refunds</h3>
+
+<p>With subscriptions, Google Play does not provide a refund window, so users
+will need to contact you directly to request a refund.
+
+<p>If you receive requests for refunds, you can use the server-side API to
+cancel the subscription or verify that it is already cancelled. However, keep in
+mind that Google Play considers cancelled subscriptions valid until the end of
+their current billing cycles, so even if you grant a refund and cancel the
+subscription, the user will still have access to the content.
+
+<p class="caution"><strong>Important:</strong> Partial refunds for canceled
+subscriptions are not available at this time.</p>
+
+<h2 id="payment">Payment Processing and Policies</h2>
+
+<p>In general, the terms of Google Play allow you to sell in-app subscriptions
+only through the standard payment processor, Google Checkout. For purchases of
+any subscription products, the transaction fee is the same as the transaction
+fee for application purchases (30%).</p>
+
+<p>Apps published on Google Play that are selling subscriptions must use In-app
+Billing to handle the transaction and may not provide links to a purchase flow
+outside of the app and Google Play (such as to a web site).</p>
+
+<p>For complete details about terms and policies, see the <a
+href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=140504">policies
+document</a>.</p>
+
+<h3 id="orderId">Subscription order numbers</h3>
+
+<p>To help you track transactions relating to a given subscription, Google
+Checkout provides a base Merchant Order Number for all recurrences of the
+subscription and denotes
+each recurring transaction by appending an integer as follows: </p>
+
+<p><span style="color:#777"><code style="color:#777">12999556515565155651.5565135565155651</code> (base order number)</span><br />
+<code>12999556515565155651.5565135565155651..0</code> (initial purchase orderID)<br />
+<code>12999556515565155651.5565135565155651..1</code> (first recurrence orderID)<br />
+<code>12999556515565155651.5565135565155651..2</code> (second recurrence orderID)<br />
+...<br /></p>
+
+<p>Google Play provides the order number as the value of the
+{@code orderId} field of the {@code INAPP_PURCHASE_DATA} JSON field (in V3)
+or the {@code PURCHASE_STATE_CHANGED} intent (in V2).</p>
+
+<h2 id="play-dev-api">Google Play Android Developer API</h2>
+
+<p>Google Play offers an HTTP-based API that you can use to remotely query the
+validity of a specific subscription at any time or cancel a subscription. The
+API is designed to be used from your backend servers as a way of securely
+managing subscriptions, as well as extending and integrating subscriptions with
+other services.</p>
+
+<h3 id="using">Using the API</h3>
+
+<p>To use the API, you must first register a project at the <a
+href="https://code.google.com/apis/console">Google APIs Console</a> and receive
+a Client ID and shared secret that your app will present when calling the
+Google Play Android Developer API. All calls to the API are authenticated with
+OAuth 2.0.</p>
+
+<p>Once your app is registered, you can access the API directly, using standard
+HTTP methods to retrieve and manipulate resources, or you can use the Google
+APIs Client Libraries, which are extended to support the API.</p>
+
+<p>The Google Play Android Developer API is built on a RESTful design that uses
+HTTP and JSON, so any standard web stack can send requests and parse the
+responses. However, if you don’t want to send HTTP requests and parse responses
+manually, you can access the API using the client libraries, which provide
+better language integration, improved security, and support for making calls
+that require user authorization.</p>
+
+<p>For more information about the API and how to access it through the Google
+APIs Client Libraries, see the documentation at:</p>
+
+<p style="margin-left:1.5em;"><a
+href="https://developers.google.com/android-publisher/v1/">https://developers.
+google.com/android-publisher/v1/</a></p>
+
+<h3 id="quota">Quota</h3>
+
+<p>Applications using the Google Play Android Developer API are limited to an
+initial courtesy usage quota of <strong>15000 requests per day</strong> (per
+application). This should provide enough access for normal
+subscription-validation needs, assuming that you follow the recommendation in
+this section.</p>
+
+<p>If you need to request a higher limit for your application, please use the
+“Request more” link in the <a
+href="https://code.google.com/apis/console/#:quotas">Google APIs Console</a>.
+Also, please read the section below on design best practices for minimizing your
+use of the API.</p>
+
+<h3 id="auth">Authorization</h3>
+
+<p>Calls to the Google Play Android Developer API require authorization. Google
+uses the OAuth 2.0 protocol to allow authorized applications to access user
+data. To learn more, see <a
+href="https://developers.google.com/android-publisher/authorization">Authorization</a>
+in the Google Play Android Developer API documentation.</p>
+
+<h3 id="practices">Using the API efficiently</h3>
+
+<p>Access to the Google Play Android Developer API is regulated to help ensure a
+high-performance environment for all applications that use it. While you can
+request a higher daily quota for your application, we highly recommend that you
+minimize your access using the technique(s) below. </p>
+
+<ul>
+ <li><em>Store subscription expiry on your servers</em> &mdash; your servers
+ should use the Google Play Android Developer API to query the expiration date
+ for new subscription tokens, then store the expiration date locally. This allows
+ you to check the status of subscriptions only at or after the expiration (see
+ below). </li>
+ <li><em>Cache expiration and purchaseState</em> &mdash; If your app contacts
+ your backend servers at runtime to verify subscription validity, your server
+ should cache the expiration and purchaseState to ensure the fastest possible
+ response (and best experience) for the user.</li>
+ <li><em>Query for subscription status only at expiration</em> &mdash; Once your
+ server has retrieved the expiration date of subscription tokens, it should not
+ query the Google Play servers for the subscription status again until the
+ subscription is reaching or has passed the expiration date. Typically, your
+ servers would run a batch query each day to check the status of
+ <em>expiring</em> subscriptions, then update the database. Note that:
+ <ul>
+ <li>Your servers should not query all subscriptions every day</li>
+ <li>Your servers should never query subscription status dynamically, based on
+ individual requests from your Android application. </li>
+ </ul>
+ </li>
+</ul>
+
+<p>By following those general guidelines, your implementation will offer the
+best possible performance for users and minimize use of the Google Play Android
+Developer API.</p>
+
+
diff --git a/docs/html/google/play/billing/index.jd b/docs/html/google/play/billing/index.jd
index b0d1d13f3776..44aa00191e26 100644
--- a/docs/html/google/play/billing/index.jd
+++ b/docs/html/google/play/billing/index.jd
@@ -10,8 +10,8 @@ and features, and more. You can use In-app Billing to sell products as</p>
<div class="sidebox">
<h2><strong>New in In-App Billing</strong></h2>
<ul>
- <li><strong>In-app Billing Version 3</strong>&mdash;The <a href="{@docRoot}google/play/billing/api.html">latest version</a> of In-app Billing features a synchronous API that is easier to implement and lets you manage products and purchases more effectively.</li>
- <li><strong>New order number format</strong>&mdash;Starting 5 December, orders are reported in Merchant Order Number format. See <a href="/google/play/billing/billing_admin.html#orderId">Working with Order Numbers</a> for an example.</li>
+ <li><strong>In-app Billing Version 3</strong>&mdash;The <a href="{@docRoot}google/play/billing/api.html">latest version</a> of In-app Billing features a synchronous API that is easier to implement and lets you manage in-app products and subscriptions more effectively.</li>
+ <li><strong>Subscriptions now supported in Version 3</strong>&mdash;You can query and launch purchase flows for subscription items using the V3 API.</li>
<li><strong>Free trials</strong>&mdash;You can now offer users a configurable <a href="/google/play/billing/v2/billing_subscriptions.html#trials">free trial period</a> for your in-app subscriptions. You can set up trials with a simple change in the Developer Console&mdash;no change to your app code is needed.</li>
</ul>
</div>
@@ -30,7 +30,7 @@ is used for application purchases, so your users experience a consistent and
familiar purchase flow.</p>
<p>Any application that you publish through Google Play can implement In-app Billing. No special
-account or registration is required other than an Android Market publisher account and a Google
+account or registration is required other than a Google Play Developer Console account and a Google
Checkout merchant account.</p>
<p>To help you integrate in-app billing into your application, the Android SDK
diff --git a/docs/html/google/play/billing/v2/api.jd b/docs/html/google/play/billing/v2/api.jd
index 6b3b7585c2cb..9d3a045fa70b 100644
--- a/docs/html/google/play/billing/v2/api.jd
+++ b/docs/html/google/play/billing/v2/api.jd
@@ -39,15 +39,6 @@ Version 2, rather than Version 3. </p>
<p>If you do not need to sell subscriptions, you
should implement In-app Billing Version 3 instead.</p>
-<div class="sidebox-wrapper">
-<div class="sidebox">
- <h2>New in In-app Billing V2</h2>
- <p><strong>Free trials</strong>—You can now offer users a configurable free trial period for
- your in-app subscriptions. You can set up trials with a simple change in the Developer
- Console—no change to your app code is needed.</p>
-</div>
-</div>
-
<h2 id="billing-types">Product Types</h2>
<p>In-app Billing Version supports three different product types
diff --git a/docs/html/google/play/billing/v2/billing_subscriptions.jd b/docs/html/google/play/billing/v2/billing_subscriptions.jd
index 82a662fc84fd..5e3bd2859a4a 100644
--- a/docs/html/google/play/billing/v2/billing_subscriptions.jd
+++ b/docs/html/google/play/billing/v2/billing_subscriptions.jd
@@ -1,4 +1,4 @@
-page.title=Subscriptions <span style="font-size:16px;">(IAB Version 2)</span>
+page.title=Implementing Subscriptions <span style="font-size:16px;">(IAB Version 2)</span>
@jd:body
<div style="background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;">In-app Billing Version 2 is superseded. Please <a href="{@docRoot}google/play/billing/billing_overview.html#migration">migrate to Version 3</a> at your earliest convenience.</div>
@@ -6,404 +6,26 @@ page.title=Subscriptions <span style="font-size:16px;">(IAB Version 2)</span>
<div id="qv">
<h2>In this document</h2>
<ol>
- <li><a href="#overview">Overview of Subscriptions</a>
- <!--<ol>
- <li><a href="#publishing">Subscription publishing and unpublishing</a></li>
- <li><a href="#pricing">Subscription pricing</a></li>
- <li><a href="#user-billing">User billing</a></li>
- <li><a href="#trials">Free trial period</a></li>
- <li><a href="#cancellation">Subscription cancellation</a></li>
- <li><a href="#uninstallation">App uninstallation</a></li>
- <li><a href="#refunds">Refunds</a></li>
- <li><a href="#payment">Payment processing and policies</a></li>
- <li><a href="#requirements">System requirements for subscriptions</a></li>
- <li><a href="#compatibility">Compatibility considerations</a></li>
- </ol> -->
- </li>
- <li><a href="#implementing">Implementing Subscriptions</a>
- <!-- <ol>
- <li><a href="#sample">Sample application</a></li>
- <li><a href="#model">Application model</a></li>
- <li><a href="#token">Purchase token</a></li>
- <li><a href="#version">Checking the In-app Billing API version</a></li>
- <li><a href="purchase">Requesting purchase of a subscription</a></li>
- <li><a href="#restore">Restoring transactions</a></li>
- <li><a href="#validity">Checking subscription validity</a></li>
- <li><a href="#viewstatus">Launching your product page to let the user cancel or view status</a></li>
- <li><a href="#purchase-state-changes">Recurring billing and changes in purchase state</a></li>
- <li><a href="modifying">Modifying your app for subscriptions</a></li>
- </ol> -->
- </li>
- <li><a href="#administering">Administering Subscriptions</a></li>
-
- <li><a href="#play-dev-api">Google Play Android Developer API</a>
- <!-- <ol>
- <li><a href="#using">Using the API</a></li>
- <li><a href="#quota">Quota</a></li>
- <li><a href="#auth">Authorization</a></li>
- <li><a href="#practices">Using the API efficiently</a></li>
- </ol> -->
- </li>
-</ol>
+ <li><a href="#sample">Sample Application</a></li>
+ <li><a href="#model">Application Model</a></li>
+ <li><a href="#token">Purchase Token</a></li>
+ <li><a href="#version">Checking the In-app Billing API Version</a></li>
+ <li><a href="purchase">Purchasing a Subscription</a></li>
+ <li><a href="#restore">Restoring Transactions</a></li>
+ <li><a href="#validity">Checking Subscription Validity</a></li>
+ <li><a href="#viewstatus">Letting Users Cancel or View Status</a></li>
+ <li><a href="#purchase-state-changes">Recurring Billing and Changes in Purchase State</a></li>
+ <li><a href="modifying">Modifying Your App for Subscriptions</a></li>
+ </ol>
</div>
</div>
-<p class="note"><strong>Important:</strong> This documentation describes how to implement subscriptions with the Version 2 API. Subscription support for the in-app billing <a href="{@docRoot}google/play/billing/api.html">Version 3 API</a> is coming soon.</p></li>
+<p>This document is focused on highlighting implementation details that are
+specific to subscriptions with the Version 2 API. To understand how
+subscriptions work, see <a href="{@docRoot}google/play/billing/billing_subscriptions.html">In-app Billing Subscriptions</a>.</p>
-<p>Subscriptions let you sell content, services, or features in your app with
-automated, recurring billing. Adding support for subscriptions is
-straightforward and you can easily adapt an existing In-app Billing
-implementation to sell subscriptions. </p>
-<p>If you have already implemented In-app Billing for one-time purchase
-products, you will find that you can add support for subscriptions with minimal
-impact on your code. If you are new to In-app Billing, you can implement
-subscriptions using the standard communication model, data structures, and user
-interactions as for other in-app products.subscriptions. Because the
-implementation of subscriptions follows the same path as for other in-app
-products, details are provided outside of this document, starting with the <a
-href="{@docRoot}google/play/billing/v2/api.html">In-app Billing
-Overview</a>. </p>
-
-<p>This document is focused on highlighting implementation details that are
-specific to subscriptions, along with some strategies for the associated billing
-and business models.</p>
-
-<p class="note"><strong>Note:</strong> Subscriptions are supported in In-app Billing Version 2 only. Support for subscriptions will be added to Version 3 in the weeks ahead.</p>
-
-<h2 id="overview">Overview of Subscriptions</h2>
-
-<p>A <em>subscription</em> is a new product type offered in In-app Billing that lets you
-sell content, services, or features to users from inside your app with recurring
-monthly or annual billing. You can sell subscriptions to almost any type of
-digital content, from any type of app or game.</p>
-
-<p>As with other in-app products, you configure and publish subscriptions using
-the Developer Console and then sell them from inside apps installed on an
-Android-powered devices. In the Developer console, you create subscription
-products and add them to a product list, then set a price and optional trial
-period for each, choose a billing interval (monthly or annual), and then publish.</p>
-
-<p>In your apps, it’s
-straightforward to add support for subscription purchases. The implementation
-extends the standard In-app Billing API to support a new product type but uses
-the same communication model, data structures, and user interactions as for
-other in-app products.</p>
-
-<p>When users purchase subscriptions in your apps, Google Play handles all
-checkout details so your apps never have to directly process any financial
-transactions. Google Play processes all payments for subscriptions through
-Google Checkout, just as it does for standard in-app products and app purchases.
-This ensures a consistent and familiar purchase flow for your users.</p>
-
-<img src="{@docRoot}images/billing_subscription_flow.png" style="border:4px solid ddd;">
-
-
-<p>After users have purchase subscriptions, they can view the subscriptions and
-cancel them, if necessary, from the My Apps screen in the Play Store app or
-from the app's product details page in the Play Store app.</p>
-
-<!--<img src="{@docRoot}images/billing_subscription_cancel.png" style="border:4px solid ddd;">-->
-
-<p>Once users have purchased a subscription through In-app Billing, you can
-easily give them extended access to additional content on your web site (or
-other service) through the use of a server-side API provided for In-app Billing.
-The server-side API lets you validate the status of a subscription when users
-sign into your other services. For more information about the API, see <a
-href="#play-dev-api">Google Play Android Developer API</a>, below. </p>
-
-<p>You can also build on your existing external subscriber base from inside your
-Android apps. If you sell subscriptions on a web site, for example, you can add
-your own business logic to your Android app to determine whether the user has
-already purchased a subscription elsewhere, then allow access to your content if
-so or offer a subscription purchase from Google Play if not.</p>
-
-<p>With the flexibility of In-app Billing, you can even implement your own
-solution for sharing subscriptions across as many different apps or products as
-you want. For example, you could sell a subscription that gives a subscriber
-access to an entire collection of apps, games, or other content for a monthly or
-annual fee. To implement this solution, you could add your own business logic to
-your app to determine whether the user has already purchased a given
-subscription and if so, allow access to your content. </p>
-
-<div class="sidebox-wrapper">
-<div class="sidebox">
- <h2>Subscriptions at a glance</h2>
- <ul>
- <li>Subscriptions let you sell products with automated, recurring billing</li>
- <li>You can set up subscriptions with either monthly or annual billing</li>
- <li>You can sell multiple subscription items in an app with various billing
- intervals or prices, such as for promotions</li>
- <li>You can offer a configurable trial period for any subscription. <span class="new" style="font-size:.78em;">New!</span></li>
- <li>Users purchase your subscriptions from inside your apps, rather than
- directly from Google Play</li>
- <li>Users manage their purchased subscriptions from the My Apps screen in
- the Play Store app</li>
- <li>Google Play uses the original form of payment for recurring billing</li>
- <li>If a user cancels a subscription, Google Play considers the subscription valid
- until the end of the current billing cycle. The user continues to enjoy the content
- for the rest of the cycle and is not granted a refund.</li>
- </ul>
-</div>
-</div>
-
-<p>In general the same basic policies and terms apply to subscriptions as to
-standard in-app products, however there are some differences. For complete
-information about the current policies and terms, please read the <a
-href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en
-&answer=140504">policies document</a>.</p>
-
-
-<h3 id="publishing">Subscription publishing and unpublishing</h3>
-
-<p>To sell a subscription in an app, you use the tools in the Developer Console
-to set up a product list for the app and then create and configure a new
-subscription. In the subscription, you set the price and billing interval and
-define a subscription ID, title, and description. When you are ready, you can
-then publish the subscription in the app product list.</p>
-
-<p>In the product list, you can add subscriptions, in-app products, or both. You
-can add multiple subscriptions that give access to different content or
-services, or you can add multiple subscriptions that give access to the same
-content but for different intervals or different prices, such as for a
-promotion. For example, a news outlet might decide to offer both monthly and
-annual subscriptions to the same content, with annual having a discount. You can
-also offer in-app purchase equivalents for subscription products, to ensure that
-your content is available to users of older devices that do not support
-subscriptions.</p>
-
-<p>After you add a subscription or in-app product to the product list, you must
-publish the product before Google Play can make it available for purchase. Note
-that you must also publish the app itself before Google Play will make the
-products available for purchase inside the app. </p>
-
-<p class="caution"><strong>Important:</strong> At this time, the capability to
-unpublish a subscription is not available. Support for unpublishing a
-subscription is coming to the Developer Console in the weeks ahead, so this is a
-temporary limitation. In the short term, instead of unpublishing,
-you can remove the subscription product from the product list offered in your
-app to prevent users from seeing or purchasing it.</p>
-
-<h3 id="pricing">Subscription pricing</h3>
-
-<p>When you create a subscription in the Developer Console, you can set a price
-for it in any available currencies. Each subscription must have a non-zero
-price. You can price multiple subscriptions for the same content differently
-&mdash; for example you could offer a discount on an annual subscription
-relative to the monthly equivalent. </p>
-
-<p class="caution"><strong>Important:</strong> At this time, once you publish a
-subscription product, you cannot change its price in any currency. Support for
-changing the price of published subscriptions is coming to the Developer Console
-in the weeks ahead. In the short term, you can work around this limitation by
-publishing a new subscription product ID at a new price, then offer it in your
-app instead of the original product. Users who have already purchased will
-continue to be charged at the original price, but new users will be charged at
-the new price.</p>
-
-<h3 id="user-billing">User billing</h3>
-
-<p>You can sell subscription products with automated recurring billing at
-either of two intervals:</p>
-
-<ul>
- <li>Monthly &mdash; Google Play bills the customer’s Google Checkout account at
- the time of purchase and monthly subsequent to the purchase date (exact billing
- intervals can vary slightly over time)</li>
- <li>Annually &mdash; Google Play bills the customer's Google Checkout account at
- the time of purchase and again on the same date in subsequent years.</li>
-</ul>
-
-<p>Billing continues indefinitely at the interval and price specified for the
-subscription. At each subscription renewal, Google Play charges the user account
-automatically, then notifies the user of the charges afterward by email. Billing
-cycles will always match subscription cycles, based on the purchase date.</p>
-
-<p>Over the life of a subscription, the form of payment billed remains the same
-&mdash; Google Play always bills the same form of payment (such as credit card,
-Direct Carrier Billing) that was originally used to purchase the
-subscription.</p>
-
-<p>When the subscription payment is approved by Google Checkout, Google Play
-provides a purchase token back to the purchasing app through the In-app Billing
-API. For details, see <a href="#token">Purchase token</a>, below. Your apps can
-store the token locally or pass it to your backend servers, which can then use
-it to validate or cancel the subscription remotely using the <a
-href="#play-dev-api">Google Play Android Developer API</a>.</p>
-
-<p>If a recurring payment fails, such as could happen if the customer’s credit
-card has become invalid, the subscription does not renew. Google Play notifies your
-app at the end of the active cycle that the purchase state of the subscription is now "Expired".
-Your app does not need to grant the user further access to the subscription content.</p>
-
-<p>As a best practice, we recommend that your app includes business logic to
-notify your backend servers of subscription purchases, tokens, and any billing
-errors that may occur. Your backend servers can use the server-side API to query
-and update your records and follow up with customers directly, if needed.</p>
-
-<h3 id="trials">Free Trial Period</h3>
-
-<p>For any subscription, you can set up a free trial period that lets users
-try your subscription content before buying it. The trial period
-runs for the period of time that you set and then automatically converts to a full subscription
-managed according to the subscription's billing interval and price.</p>
-
-<p>To take advantage of a free trial, a user must "purchase" the full
-subscription through the standard In-app Billing flow, providing a valid form of
-payment to use for billing and completing the normal purchase transaction.
-However, the user is not charged any money, since the initial period corresponds
-to the free trial. Instead, Google Play records a transaction of $0.00 and the
-subscription is marked as purchased for the duration of the trial period or
-until cancellation. When the transaction is complete, Google Play notifies users
-by email that they have purchased a subscription that includes a free trial
-period and that the initial charge was $0.00. </p>
-
-<p>When the trial period ends, Google Play automatically initiates billing
-against the credit card that the user provided during the initial purchase, at the amount set
-for the full subscription, and continuing at the subscription interval. If
-necessary, the user can cancel the subscription at any time during the trial
-period. In this case, Google Play <em>marks the subscription as expired immediately</em>,
-rather than waiting until the end of the trial period. The user has not
-paid for the trial period and so is not entitled to continued access after
-cancellation.</p>
-
-<p>You can set up a trial period for a subscription in the Developer Console,
-without needing to modify or update your APK. Just locate and edit the
-subscription in your product list, set a valid number of days for the trial
-(must be 7 days or longer), and publish. You can change the period any time,
-although note that Google Play does not apply the change to users who have
-already "purchased" a trial period for the subscription. Only new subscription
-purchases will use the updated trial period. You can create one free trial
-period per subscription product.</p>
-
-<h3 id="cancellation">Subscription cancellation</h3>
-
-<p>Users can view the status of all of their subscriptions and cancel them if
-necessary from the My Apps screen in the Play Store app. Currently, the In-app
-Billing API does not provide support for canceling subscriptions direct from
-inside the purchasing app, although your app can broadcast an Intent to launch
-the Play Store app directly to the My Apps screen.</p>
-
-<p>When the user cancels a subscription, Google Play does not offer a refund for
-the current billing cycle. Instead, it allows the user to have access to the
-cancelled subscription until the end of the current billing cycle, at which time
-it terminates the subscription. For example, if a user purchases a monthly
-subscription and cancels it on the 15th day of the cycle, Google Play will
-consider the subscription valid until the end of the 30th day (or other day,
-depending on the month).</p>
-
-<p>In some cases, the user may contact you directly to request cancellation of a
-subscription. In this and similar cases, you can use the server-side API to
-query and directly cancel the user’s subscription from your servers.
-
-<p class="caution"><strong>Important:</strong> In all cases, you must continue
-to offer the content that your subscribers have purchased through their
-subscriptions, for as long any users are able to access it. That is, you must
-not remove any subscriber’s content while any user still has an active
-subscription to it, even if that subscription will terminate at the end of the
-current billing cycle. Removing content that a subscriber is entitled to access
-will result in penalties. Please see the <a
-href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=140504">policies document</a> for more information. </p>
-
-<h3 id="uninstall">App uninstallation</h3>
-
-<p>When the user uninstalls an app that includes purchased subscriptions, the Play Store app will notify the user that there are active subscriptions. If the user chooses to continue with the uninstalltion, the app is removed and the subscriptions remain active and recurring billing continues. The user can return to cancel the associated subscriptions at any time in the My Apps screen of the Play Store app. If the user chooses to cancel the uninstallation, the app and subscriptions remain as they were.</p>
-
-<h3 id="refunds">Refunds</h3>
-
-<p>As with other in-app products, Google Play does not provide a refund window
-for subscription purchases. For example, users who purchase an app can ask for a
-refund from Google Play within a 15-minute window. With subscriptions, Google
-Play does not provide a refund window, so users will need to contact you
-directly to request a refund.
-
-<p>If you receive requests for refunds, you can use the server-side API to
-cancel the subscription or verify that it is already cancelled. However, keep in
-mind that Google Play considers cancelled subscriptions valid until the end of
-their current billing cycles, so even if you grant a refund and cancel the
-subscription, the user will still have access to the content.
-
-<p class="note"><strong>Note:</strong> Partial refunds for canceled
-subscriptions are not available at this time.</p>
-
-<h3 id="payment">Payment processing and policies</h3>
-
-<p>In general, the terms of Google Play allow you to sell in-app subscriptions
-only through the standard payment processor, Google Checkout. For purchases of any
-subscription products, just as for other in-app products and apps, the
-transaction fee for subscriptions, just as for other in-app purchases, is the
-same as the transaction fee for application purchases (30%).</p>
-
-<p>Apps published on Google Play that are selling subscriptions must use In-app
-Billing to handle the transaction and may not provide links to a purchase flow
-outside of the app and Google Play (such as to a web site).</p>
-
-<p>For complete details about terms and policies, see the <a
-href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=140504">policies
-document</a>.</p>
-
-<h3 id="orderId">Subscription Order Numbers</h3>
-
-<p>To help you track transactions relating to a given subscription, Google
-Checkout provides a base Merchant Order Number for all recurrences of the subscription and denotes
-each recurring transaction by appending an integer as follows: </p>
-
-<p><span style="color:#777"><code style="color:#777">12999556515565155651.5565135565155651</code> (base order number)</span><br />
-<code>12999556515565155651.5565135565155651..0</code> (initial purchase orderID)<br />
-<code>12999556515565155651.5565135565155651..1</code> (first recurrence orderID)<br />
-<code>12999556515565155651.5565135565155651..2</code> (second recurrence orderID)<br />
-...<br /></p>
-
-<p>Google Play provides that order number to as the value of the
-<code>orderId</code> field of the <code>PURCHASE_STATE_CHANGED</code>
-intent.</p>
-
-<h3 id="requirements">System requirements for subscriptions</h3>
-
-<p>In-app purchases of subscriptions are supported only on devices that meet
-these minimum requirements:</p>
-
-<ul>
- <li>Must run Android 2.2 or higher</li>
- <li>Google Play Store app, Version 3.5 or higher, must be installed</li>
-</ul>
-
-<p>Google Play 3.5 and later versions include support for the In-app Billing
-v2 API or higher, which is needed to support handling of subscription
-products.</p>
-
-<h3 id="compatibility">Compatibility considerations</h3>
-
-<p>As noted in the previous section, support for subscriptions is available only
-on devices that meet the system requirements. Not all devices will receive or
-install Google Play 3.5, so not all users who install your apps will have access
-to the In-app Billing API and subscriptions.</p>
-
-<p>If you are targeting older devices that run Android 2.1 or earlier, we
-recommend that you offer those users an alternative way buy the content that is
-available through subscriptions. For example, you could create standard in-app
-products (one-time purchases) that give access to similar content as your
-subscriptions, possibly for a longer interval such as a year. </p>
-
-
-<h2 id="implementing">Implementing Subscriptions</h2>
-
-<p>Subscriptions are a standard In-app Billing product type. If you have already
-implemented In-app Billing for one-time purchase products, you will find that
-adding support for subscriptions is straightforward, with minimal impact on your
-code. If you are new to In-app Billing, you can implement subscriptions using
-the standard communication model, data structures, and user interactions as for
-other in-app products.subscriptions. </p>
-
-<p>The full implementation details for In-app Billing are provided outside of
-this document, starting with the <a
-href="{@docRoot}google/play/billing/v2/api.html">In-app Billing
-Overview</a>. This document is focused on highlighting implementation details
-that are specific to subscriptions, along with some strategies for the
-associated billing and business models.</p>
-
-
-<h3 id="sample">Sample application</h3>
+<h2 id="sample">Sample Application</h2>
<p>To help you get started with your In-app Billing implementation and
subscriptions, an updated Version of the In-app Billing sample app is available.
@@ -412,7 +34,7 @@ Android SDK Manager. For details, see <a
href="{@docRoot}google/play/billing/v2/billing_integrate.html#billing-download">
Downloading the Sample Application</a>.</p>
-<h3 id="model">Application model</h3>
+<h2 id="model">Application Model</h2>
<p>With subscriptions, your app uses the standard In-app Billing application
model, sending billing requests to the Play Store application over interprocess
@@ -436,7 +58,7 @@ app will use the standard set of in-app billing requests and receive the same
responses. Inside the requests and responses are two new fields described below.
</p>
-<h3 id="token">Purchase token</h3>
+<h2 id="token">Purchase Token</h2>
<p>Central to the end-to-end architecture for subscriptions is the purchase
token, a string value that uniquely identifies (and associates) a user ID and a
@@ -476,7 +98,7 @@ href="{@docRoot}google/play/billing/billing_best_practices.html">Security and
Design</a> document for best practices for maintaining the security of your
data.</p>
-<h3 id="version">Checking the In-app Billing API version</h3>
+<h2 id="version">Checking the In-app Billing API Version</h2>
<p>Subscriptions support is available only in versions of Google Play that
support the In-app Billing v2 API (Google Play 3.5 and higher). For your app,
@@ -555,7 +177,7 @@ indicates that subscriptions are supported.</p>
}
</pre>
-<h3 id="purchase">Requesting a subscription purchase</h3>
+<h2 id="purchase">Requesting a Subscription Purchase</h2>
<p>Once you’ve checked the API Version as described above and determined that
subscriptions are supported, you can present subscription products to the user
@@ -630,7 +252,7 @@ BillingService.java).</p>
}
</pre>
-<h3 id="restoring">Restoring transactions</h3>
+<h2 id="restoring">Restoring Transactions</h2>
<p>Subscriptions always use the <em>managed by user account</em> purchase type,
so that you can restore a record of subscription transactions on the device when
@@ -660,7 +282,7 @@ href="{@docRoot}google/play/billing/billing_best_practices.html">Security and
Design</a> document for best practices for maintaining the security of your
data.</p>
-<h3 id="validity">Checking subscription validity</h3>
+<h2 id="validity">Checking Subscription Validity</h2>
<p>Subscriptions are time-bound purchases that require successful billing
recurrences over time to remain valid. Your app should check the validity of
@@ -736,7 +358,7 @@ values for subscription purchases, as received with a
</table>
-<h3 id="viewstatus">Launching your product page to let the user cancel or view subscriptions</h3>
+<h2 id="viewstatus">Letting the User Cancel or View Subscriptions</h2>
<p>In-app Billing does not currently provide an API to let users directly view or cancel
subscriptions from within the purchasing app. Instead, users can launch the Play
@@ -761,7 +383,7 @@ startActivity(intent);</pre>
<p>For more information, see
<a href="{@docRoot}distribute/googleplay/promote/linking.html">Linking to Your Products</a>.</p>
-<h3 id="purchase-state-changes">Recurring billing, cancellation, and changes in purchase state</h3>
+<h2 id="purchase-state-changes">Recurring Billing, Cancellation, and Changes In Purchase State</h2>
<p>Google Play notifies your app when the user completes the purchase of a
subscription, but the purchase state does not change over time, provided that
@@ -786,7 +408,7 @@ purchase state has changed to "Expired".</p>
a change to the same "Expired" purchase state. Once the purchase state has become "Expired",
your app does not need to grant further access to the subscription content.</p>
-<h3 id="modifying">Modifying your app for subscriptions</h3>
+<h2 id="modifying">Modifying Your App for Subscriptions</h2>
<p>For subscriptions, you make the same types of modifications to your app as
are described in <a
@@ -798,118 +420,7 @@ purchase, you should add logic to check for purchased subscriptions and validate
them. Your UI should not present subscriptions if the user has already purchased
them.</p>
-<h2 id="administering">Administering Subscriptions</h2>
-
-<p>To create and manage subscriptions, you use the tools in the Developer
-Console, just as for other in-app products.</p>
-
-<p>At the Developer Console, you can configure these attributes for each
-subscription product:</p>
-
-<ul>
-<li>Purchase Type: always set to “subscription”</li>
-<li>Subscription ID: An identifier for the subscription</li>
-<li>Publishing State: Unpublished/Published</li>
-<li>Language: The default language for displaying the subscription</li>
-<li>Title: The title of the subscription product</li>
-<li>Description: Details that tell the user about the subscription</li>
-<li>Price: USD price of subscription per recurrence</li>
-<li>Recurrence: monthly or yearly</li>
-<li>Additional currency pricing (can be auto-filled)</li>
-</ul>
-
-<p>For details, please see <a href="{@docRoot}google/play/billing/billing_admin.html">Administering
-In-app Billing</a>.</p>
-
-
-<h2 id="play-dev-api">Google Play Android Developer API</h2>
-
-<p>Google Play offers an HTTP-based API that you can use to remotely query the
-validity of a specific subscription at any time or cancel a subscription. The
-API is designed to be used from your backend servers as a way of securely
-managing subscriptions, as well as extending and integrating subscriptions with
-other services.</p>
-
-<h3 id="using">Using the API</h3>
-<p>To use the API, you must first register a project at the <a
-href="https://code.google.com/apis/console">Google APIs Console</a> and receive
-a Client ID and shared secret that your app will present when calling the
-Google Play Android Developer API. All calls to the API are authenticated with
-OAuth 2.0.</p>
-<p>Once your app is registered, you can access the API directly, using standard
-HTTP methods to retrieve and manipulate resources, or you can use the Google
-APIs Client Libraries, which are extended to support the API.</p>
-
-<p>The Google Play Android Developer API is built on a RESTful design that uses
-HTTP and JSON, so any standard web stack can send requests and parse the
-responses. However, if you don’t want to send HTTP requests and parse responses
-manually, you can access the API using the client libraries, which provide
-better language integration, improved security, and support for making calls
-that require user authorization.</p>
-
-<p>For more information about the API and how to access it through the Google
-APIs Client Libraries, see the documentation at:</p>
-
-<p style="margin-left:1.5em;"><a
-href="https://developers.google.com/android-publisher/v1/">https://developers.
-google.com/android-publisher/v1/</a></p>
-
-<h3 id="quota">Quota</h3>
-
-<p>Applications using the Google Play Android Developer API are limited to an
-initial courtesy usage quota of <strong>15000 requests per day</strong> (per
-application). This should provide enough access for normal
-subscription-validation needs, assuming that you follow the recommendation in
-this section.</p>
-
-<p>If you need to request a higher limit for your application, please use the
-“Request more” link in the <a
-href="https://code.google.com/apis/console/#:quotas">Google APIs Console</a>.
-Also, please read the section below on design best practices for minimizing your
-use of the API.</p>
-
-<h3 id="auth">Authorization</h3>
-
-<p>Calls to the Google Play Android Developer API require authorization. Google
-uses the OAuth 2.0 protocol to allow authorized applications to access user
-data. To learn more, see <a
-href="https://developers.google.com/android-publisher/authorization">Authorization</a>
-in the Google Play Android Developer API documentation.</p>
-
-<h3 id="practices">Using the API efficiently</h3>
-
-<p>Access to the Google Play Android Developer API is regulated to help ensure a
-high-performance environment for all applications that use it. While you can
-request a higher daily quota for your application, we highly recommend that you
-minimize your access using the technique(s) below. </p>
-
-<ul>
- <li><em>Store subscription expiry on your servers</em> &mdash; your servers
- should use the Google Play Android Developer API to query the expiration date
- for new subscription tokens, then store the expiration date locally. This allows
- you to check the status of subscriptions only at or after the expiration (see
- below). </li>
- <li><em>Cache expiration and purchaseState</em> &mdash; If your app contacts
- your backend servers at runtime to verify subscription validity, your server
- should cache the expiration and purchaseState to ensure the fastest possible
- response (and best experience) for the user.</li>
- <li><em>Query for subscription status only at expiration</em> &mdash; Once your
- server has retrieved the expiration date of subscription tokens, it should not
- query the Google Play servers for the subscription status again until the
- subscription is reaching or has passed the expiration date. Typically, your
- servers would run a batch query each day to check the status of
- <em>expiring</em> subscriptions, then update the database. Note that:
- <ul>
- <li>Your servers should not query all subscriptions every day</li>
- <li>Your servers should never query subscription status dynamically, based on
- individual requests from your Android application. </li>
- </ul>
- </li>
-</ul>
-<p>By following those general guidelines, your implementation will offer the
-best possible performance for users and minimize use of the Google Play Android
-Developer API.</p>
diff --git a/docs/html/google/play/billing/versions.jd b/docs/html/google/play/billing/versions.jd
index ac7761f3d9e4..1271a1578cc1 100644
--- a/docs/html/google/play/billing/versions.jd
+++ b/docs/html/google/play/billing/versions.jd
@@ -15,9 +15,12 @@ page.title=In-app Billing Version Notes
</ul>
<h3 id="version_3">In-app Billing version 3</h3>
-<p><em>December 2012</em></p>
+<p><em>February 2013</em></p>
<ul>
-<li>Requires Google Play client version 3.9.16 or higher.
+<li>Purchasing and querying managed in-app items requires Google Play client
+version 3.9.16 or higher.</li>
+<li>Purchasing and querying subscription items requires Google Play client
+version 3.10.10 or higher.</li>
<li>Provides a new Android Interface Definition Language (AIDL) file named {@code IInAppBillingService.aidl}. The new interface offers these features:
<ul>
<li>Provides a new API to get details of in-app items published for the app including price, type, title and description.</li>
@@ -27,7 +30,6 @@ page.title=In-app Billing Version Notes
<li>An API to get current purchases of the user immediately. This list will not contain any consumed purchases.</li>
</ul>
</li>
-<li>Subscriptions are not yet supported in this version of the API.</li>
</ul>
<h3 id="version_2">In-app Billing version 2</h3>
diff --git a/docs/html/images/in-app-billing/v3/billing_subscription_v3.png b/docs/html/images/in-app-billing/v3/billing_subscription_v3.png
new file mode 100644
index 000000000000..0ba472e91d06
--- /dev/null
+++ b/docs/html/images/in-app-billing/v3/billing_subscription_v3.png
Binary files differ
diff --git a/docs/html/reference/gcm-lists.js b/docs/html/reference/gcm-lists.js
new file mode 100644
index 000000000000..44c60230ee15
--- /dev/null
+++ b/docs/html/reference/gcm-lists.js
@@ -0,0 +1,16 @@
+var GCM_DATA = [
+ { id:0, label:"com.google.android.gcm", link:"reference/com/google/android/gcm/package-summary.html", type:"package" },
+ { id:1, label:"com.google.android.gcm.GCMBaseIntentService", link:"reference/com/google/android/gcm/GCMBaseIntentService.html", type:"class" },
+ { id:2, label:"com.google.android.gcm.GCMBroadcastReceiver", link:"reference/com/google/android/gcm/GCMBroadcastReceiver.html", type:"class" },
+ { id:3, label:"com.google.android.gcm.GCMConstants", link:"reference/com/google/android/gcm/GCMConstants.html", type:"class" },
+ { id:4, label:"com.google.android.gcm.GCMRegistrar", link:"reference/com/google/android/gcm/GCMRegistrar.html", type:"class" },
+ { id:5, label:"com.google.android.gcm.server", link:"reference/com/google/android/gcm/server/package-summary.html", type:"package" },
+ { id:6, label:"com.google.android.gcm.server.Constants", link:"reference/com/google/android/gcm/server/Constants.html", type:"class" },
+ { id:7, label:"com.google.android.gcm.server.InvalidRequestException", link:"reference/com/google/android/gcm/server/InvalidRequestException.html", type:"class" },
+ { id:8, label:"com.google.android.gcm.server.Message", link:"reference/com/google/android/gcm/server/Message.html", type:"class" },
+ { id:9, label:"com.google.android.gcm.server.Message.Builder", link:"reference/com/google/android/gcm/server/Message.Builder.html", type:"class" },
+ { id:10, label:"com.google.android.gcm.server.MulticastResult", link:"reference/com/google/android/gcm/server/MulticastResult.html", type:"class" },
+ { id:11, label:"com.google.android.gcm.server.Result", link:"reference/com/google/android/gcm/server/Result.html", type:"class" },
+ { id:12, label:"com.google.android.gcm.server.Sender", link:"reference/com/google/android/gcm/server/Sender.html", type:"class" }
+
+ ];
diff --git a/docs/html/reference/gms-lists.js b/docs/html/reference/gms-lists.js
new file mode 100644
index 000000000000..a5999f7b669b
--- /dev/null
+++ b/docs/html/reference/gms-lists.js
@@ -0,0 +1,93 @@
+var GMS_DATA = [
+ { id:0, label:"com.google.android.gms", link:"reference/com/google/android/gms/package-summary.html", type:"package" },
+ { id:1, label:"com.google.android.gms.R", link:"reference/com/google/android/gms/R.html", type:"class" },
+ { id:2, label:"com.google.android.gms.R.attr", link:"reference/com/google/android/gms/R.attr.html", type:"class" },
+ { id:3, label:"com.google.android.gms.R.id", link:"reference/com/google/android/gms/R.id.html", type:"class" },
+ { id:4, label:"com.google.android.gms.R.string", link:"reference/com/google/android/gms/R.string.html", type:"class" },
+ { id:5, label:"com.google.android.gms.R.styleable", link:"reference/com/google/android/gms/R.styleable.html", type:"class" },
+ { id:6, label:"com.google.android.gms.analytics", link:"reference/com/google/android/gms/analytics/package-summary.html", type:"package" },
+ { id:7, label:"com.google.android.gms.analytics.CampaignTrackingReceiver", link:"reference/com/google/android/gms/analytics/CampaignTrackingReceiver.html", type:"class" },
+ { id:8, label:"com.google.android.gms.analytics.CampaignTrackingService", link:"reference/com/google/android/gms/analytics/CampaignTrackingService.html", type:"class" },
+ { id:9, label:"com.google.android.gms.analytics.EasyTracker", link:"reference/com/google/android/gms/analytics/EasyTracker.html", type:"class" },
+ { id:10, label:"com.google.android.gms.analytics.ExceptionParser", link:"reference/com/google/android/gms/analytics/ExceptionParser.html", type:"class" },
+ { id:11, label:"com.google.android.gms.analytics.GoogleAnalytics", link:"reference/com/google/android/gms/analytics/GoogleAnalytics.html", type:"class" },
+ { id:12, label:"com.google.android.gms.analytics.GoogleAnalytics.AppOptOutCallback", link:"reference/com/google/android/gms/analytics/GoogleAnalytics.AppOptOutCallback.html", type:"class" },
+ { id:13, label:"com.google.android.gms.analytics.ModelFields", link:"reference/com/google/android/gms/analytics/ModelFields.html", type:"class" },
+ { id:14, label:"com.google.android.gms.analytics.StandardExceptionParser", link:"reference/com/google/android/gms/analytics/StandardExceptionParser.html", type:"class" },
+ { id:15, label:"com.google.android.gms.analytics.Tracker", link:"reference/com/google/android/gms/analytics/Tracker.html", type:"class" },
+ { id:16, label:"com.google.android.gms.analytics.Transaction", link:"reference/com/google/android/gms/analytics/Transaction.html", type:"class" },
+ { id:17, label:"com.google.android.gms.analytics.Transaction.Builder", link:"reference/com/google/android/gms/analytics/Transaction.Builder.html", type:"class" },
+ { id:18, label:"com.google.android.gms.analytics.Transaction.Item", link:"reference/com/google/android/gms/analytics/Transaction.Item.html", type:"class" },
+ { id:19, label:"com.google.android.gms.analytics.Transaction.Item.Builder", link:"reference/com/google/android/gms/analytics/Transaction.Item.Builder.html", type:"class" },
+ { id:20, label:"com.google.android.gms.auth", link:"reference/com/google/android/gms/auth/package-summary.html", type:"package" },
+ { id:21, label:"com.google.android.gms.auth.GoogleAuthException", link:"reference/com/google/android/gms/auth/GoogleAuthException.html", type:"class" },
+ { id:22, label:"com.google.android.gms.auth.GoogleAuthUtil", link:"reference/com/google/android/gms/auth/GoogleAuthUtil.html", type:"class" },
+ { id:23, label:"com.google.android.gms.auth.GooglePlayServicesAvailabilityException", link:"reference/com/google/android/gms/auth/GooglePlayServicesAvailabilityException.html", type:"class" },
+ { id:24, label:"com.google.android.gms.auth.UserRecoverableAuthException", link:"reference/com/google/android/gms/auth/UserRecoverableAuthException.html", type:"class" },
+ { id:25, label:"com.google.android.gms.auth.UserRecoverableNotifiedException", link:"reference/com/google/android/gms/auth/UserRecoverableNotifiedException.html", type:"class" },
+ { id:26, label:"com.google.android.gms.common", link:"reference/com/google/android/gms/common/package-summary.html", type:"package" },
+ { id:27, label:"com.google.android.gms.common.AccountPicker", link:"reference/com/google/android/gms/common/AccountPicker.html", type:"class" },
+ { id:28, label:"com.google.android.gms.common.ConnectionResult", link:"reference/com/google/android/gms/common/ConnectionResult.html", type:"class" },
+ { id:29, label:"com.google.android.gms.common.GooglePlayServicesClient", link:"reference/com/google/android/gms/common/GooglePlayServicesClient.html", type:"class" },
+ { id:30, label:"com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks", link:"reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html", type:"class" },
+ { id:31, label:"com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener", link:"reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html", type:"class" },
+ { id:32, label:"com.google.android.gms.common.GooglePlayServicesNotAvailableException", link:"reference/com/google/android/gms/common/GooglePlayServicesNotAvailableException.html", type:"class" },
+ { id:33, label:"com.google.android.gms.common.GooglePlayServicesUtil", link:"reference/com/google/android/gms/common/GooglePlayServicesUtil.html", type:"class" },
+ { id:34, label:"com.google.android.gms.common.Scopes", link:"reference/com/google/android/gms/common/Scopes.html", type:"class" },
+ { id:35, label:"com.google.android.gms.maps", link:"reference/com/google/android/gms/maps/package-summary.html", type:"package" },
+ { id:36, label:"com.google.android.gms.maps.CameraUpdate", link:"reference/com/google/android/gms/maps/CameraUpdate.html", type:"class" },
+ { id:37, label:"com.google.android.gms.maps.CameraUpdateFactory", link:"reference/com/google/android/gms/maps/CameraUpdateFactory.html", type:"class" },
+ { id:38, label:"com.google.android.gms.maps.GoogleMap", link:"reference/com/google/android/gms/maps/GoogleMap.html", type:"class" },
+ { id:39, label:"com.google.android.gms.maps.GoogleMap.CancelableCallback", link:"reference/com/google/android/gms/maps/GoogleMap.CancelableCallback.html", type:"class" },
+ { id:40, label:"com.google.android.gms.maps.GoogleMap.InfoWindowAdapter", link:"reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter.html", type:"class" },
+ { id:41, label:"com.google.android.gms.maps.GoogleMap.OnCameraChangeListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnCameraChangeListener.html", type:"class" },
+ { id:42, label:"com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnInfoWindowClickListener.html", type:"class" },
+ { id:43, label:"com.google.android.gms.maps.GoogleMap.OnMapClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMapClickListener.html", type:"class" },
+ { id:44, label:"com.google.android.gms.maps.GoogleMap.OnMapLongClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMapLongClickListener.html", type:"class" },
+ { id:45, label:"com.google.android.gms.maps.GoogleMap.OnMarkerClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMarkerClickListener.html", type:"class" },
+ { id:46, label:"com.google.android.gms.maps.GoogleMap.OnMarkerDragListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMarkerDragListener.html", type:"class" },
+ { id:47, label:"com.google.android.gms.maps.GoogleMapOptions", link:"reference/com/google/android/gms/maps/GoogleMapOptions.html", type:"class" },
+ { id:48, label:"com.google.android.gms.maps.LocationSource", link:"reference/com/google/android/gms/maps/LocationSource.html", type:"class" },
+ { id:49, label:"com.google.android.gms.maps.LocationSource.OnLocationChangedListener", link:"reference/com/google/android/gms/maps/LocationSource.OnLocationChangedListener.html", type:"class" },
+ { id:50, label:"com.google.android.gms.maps.MapFragment", link:"reference/com/google/android/gms/maps/MapFragment.html", type:"class" },
+ { id:51, label:"com.google.android.gms.maps.MapView", link:"reference/com/google/android/gms/maps/MapView.html", type:"class" },
+ { id:52, label:"com.google.android.gms.maps.MapsInitializer", link:"reference/com/google/android/gms/maps/MapsInitializer.html", type:"class" },
+ { id:53, label:"com.google.android.gms.maps.Projection", link:"reference/com/google/android/gms/maps/Projection.html", type:"class" },
+ { id:54, label:"com.google.android.gms.maps.SupportMapFragment", link:"reference/com/google/android/gms/maps/SupportMapFragment.html", type:"class" },
+ { id:55, label:"com.google.android.gms.maps.UiSettings", link:"reference/com/google/android/gms/maps/UiSettings.html", type:"class" },
+ { id:56, label:"com.google.android.gms.maps.model", link:"reference/com/google/android/gms/maps/model/package-summary.html", type:"package" },
+ { id:57, label:"com.google.android.gms.maps.model.BitmapDescriptor", link:"reference/com/google/android/gms/maps/model/BitmapDescriptor.html", type:"class" },
+ { id:58, label:"com.google.android.gms.maps.model.BitmapDescriptorFactory", link:"reference/com/google/android/gms/maps/model/BitmapDescriptorFactory.html", type:"class" },
+ { id:59, label:"com.google.android.gms.maps.model.CameraPosition", link:"reference/com/google/android/gms/maps/model/CameraPosition.html", type:"class" },
+ { id:60, label:"com.google.android.gms.maps.model.CameraPosition.Builder", link:"reference/com/google/android/gms/maps/model/CameraPosition.Builder.html", type:"class" },
+ { id:61, label:"com.google.android.gms.maps.model.GroundOverlay", link:"reference/com/google/android/gms/maps/model/GroundOverlay.html", type:"class" },
+ { id:62, label:"com.google.android.gms.maps.model.GroundOverlayOptions", link:"reference/com/google/android/gms/maps/model/GroundOverlayOptions.html", type:"class" },
+ { id:63, label:"com.google.android.gms.maps.model.LatLng", link:"reference/com/google/android/gms/maps/model/LatLng.html", type:"class" },
+ { id:64, label:"com.google.android.gms.maps.model.LatLngBounds", link:"reference/com/google/android/gms/maps/model/LatLngBounds.html", type:"class" },
+ { id:65, label:"com.google.android.gms.maps.model.LatLngBounds.Builder", link:"reference/com/google/android/gms/maps/model/LatLngBounds.Builder.html", type:"class" },
+ { id:66, label:"com.google.android.gms.maps.model.Marker", link:"reference/com/google/android/gms/maps/model/Marker.html", type:"class" },
+ { id:67, label:"com.google.android.gms.maps.model.MarkerOptions", link:"reference/com/google/android/gms/maps/model/MarkerOptions.html", type:"class" },
+ { id:68, label:"com.google.android.gms.maps.model.Polygon", link:"reference/com/google/android/gms/maps/model/Polygon.html", type:"class" },
+ { id:69, label:"com.google.android.gms.maps.model.PolygonOptions", link:"reference/com/google/android/gms/maps/model/PolygonOptions.html", type:"class" },
+ { id:70, label:"com.google.android.gms.maps.model.Polyline", link:"reference/com/google/android/gms/maps/model/Polyline.html", type:"class" },
+ { id:71, label:"com.google.android.gms.maps.model.PolylineOptions", link:"reference/com/google/android/gms/maps/model/PolylineOptions.html", type:"class" },
+ { id:72, label:"com.google.android.gms.maps.model.RuntimeRemoteException", link:"reference/com/google/android/gms/maps/model/RuntimeRemoteException.html", type:"class" },
+ { id:73, label:"com.google.android.gms.maps.model.Tile", link:"reference/com/google/android/gms/maps/model/Tile.html", type:"class" },
+ { id:74, label:"com.google.android.gms.maps.model.TileOverlay", link:"reference/com/google/android/gms/maps/model/TileOverlay.html", type:"class" },
+ { id:75, label:"com.google.android.gms.maps.model.TileOverlayOptions", link:"reference/com/google/android/gms/maps/model/TileOverlayOptions.html", type:"class" },
+ { id:76, label:"com.google.android.gms.maps.model.TileProvider", link:"reference/com/google/android/gms/maps/model/TileProvider.html", type:"class" },
+ { id:77, label:"com.google.android.gms.maps.model.UrlTileProvider", link:"reference/com/google/android/gms/maps/model/UrlTileProvider.html", type:"class" },
+ { id:78, label:"com.google.android.gms.maps.model.VisibleRegion", link:"reference/com/google/android/gms/maps/model/VisibleRegion.html", type:"class" },
+ { id:79, label:"com.google.android.gms.panorama", link:"reference/com/google/android/gms/panorama/package-summary.html", type:"package" },
+ { id:80, label:"com.google.android.gms.panorama.PanoramaClient", link:"reference/com/google/android/gms/panorama/PanoramaClient.html", type:"class" },
+ { id:81, label:"com.google.android.gms.panorama.PanoramaClient.OnPanoramaInfoLoadedListener", link:"reference/com/google/android/gms/panorama/PanoramaClient.OnPanoramaInfoLoadedListener.html", type:"class" },
+ { id:82, label:"com.google.android.gms.plus", link:"reference/com/google/android/gms/plus/package-summary.html", type:"package" },
+ { id:83, label:"com.google.android.gms.plus.GooglePlusUtil", link:"reference/com/google/android/gms/plus/GooglePlusUtil.html", type:"class" },
+ { id:84, label:"com.google.android.gms.plus.PlusClient", link:"reference/com/google/android/gms/plus/PlusClient.html", type:"class" },
+ { id:85, label:"com.google.android.gms.plus.PlusOneButton", link:"reference/com/google/android/gms/plus/PlusOneButton.html", type:"class" },
+ { id:86, label:"com.google.android.gms.plus.PlusOneButton.OnPlusOneClickListener", link:"reference/com/google/android/gms/plus/PlusOneButton.OnPlusOneClickListener.html", type:"class" },
+ { id:87, label:"com.google.android.gms.plus.PlusShare", link:"reference/com/google/android/gms/plus/PlusShare.html", type:"class" },
+ { id:88, label:"com.google.android.gms.plus.PlusShare.Builder", link:"reference/com/google/android/gms/plus/PlusShare.Builder.html", type:"class" },
+ { id:89, label:"com.google.android.gms.plus.PlusSignInButton", link:"reference/com/google/android/gms/plus/PlusSignInButton.html", type:"class" }
+
+ ];
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index cb9d2ef0ebbd..6307c6916884 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -3,43 +3,44 @@ page.template=sdk
header.hide=1
page.metaDescription=Download the official Android SDK to develop apps for Android-powered devices.
-sdk.win32_bundle_download=adt-bundle-windows-x86.zip
-sdk.win32_bundle_bytes=418030942
-sdk.win32_bundle_checksum=ce32861d8f7c93ff6ff6971bd99d228e
-sdk.win64_bundle_download=adt-bundle-windows-x86_64.zip
-sdk.win64_bundle_bytes=418155677
-sdk.win64_bundle_checksum=f09aa4557bd1dc2703fde95dcdd6b92e
+sdk.linux32_bundle_download=adt-bundle-linux-x86.zip
+sdk.linux32_bundle_bytes=418614971
+sdk.linux32_bundle_checksum=24506708af221a887326c2a9ca9625dc
+
+sdk.linux64_bundle_download=adt-bundle-linux-x86_64.zip
+sdk.linux64_bundle_bytes=418889835
+sdk.linux64_bundle_checksum=464c1fbe92ea293d6b2292c27af5066a
sdk.mac64_bundle_download=adt-bundle-mac-x86_64.zip
-sdk.mac64_bundle_bytes=383216991
-sdk.mac64_bundle_checksum=ea6c074ee30c426c503dab5c225a5076
+sdk.mac64_bundle_bytes=390649300
+sdk.mac64_bundle_checksum=f557bc61a4bff466633037839771bffb
-sdk.linux32_bundle_download=adt-bundle-linux-x86.zip
-sdk.linux32_bundle_bytes=411205048
-sdk.linux32_bundle_checksum=e64594cd339b8d9a400b9d16c616b3c3
+sdk.win32_bundle_download=adt-bundle-windows-x86.zip
+sdk.win32_bundle_bytes=425429957
+sdk.win32_bundle_checksum=cca97f12904774385a57d542e70a490f
-sdk.linux64_bundle_download=adt-bundle-linux-x86_64.zip
-sdk.linux64_bundle_bytes=411478695
-sdk.linux64_bundle_checksum=582bfc9083ff4cbcfacc8223bd8c3be1
+sdk.win64_bundle_download=adt-bundle-windows-x86_64.zip
+sdk.win64_bundle_bytes=425553759
+sdk.win64_bundle_checksum=c51679f4517e1c3ddefa1e662bbf17f6
-sdk.win_installer=installer_r21.0.1-windows.exe
-sdk.win_installer_bytes=76520869
-sdk.win_installer_checksum=e2012262471a2583d4a559b15fcf45ff
+sdk.linux_download=android-sdk_r21.1-linux.tgz
+sdk.linux_bytes=91617112
+sdk.linux_checksum=3369a439240cf3dbe165d6b4173900a8
-sdk.win_download=android-sdk_r21.0.1-windows.zip
-sdk.win_bytes=99107847
-sdk.win_checksum=613568d774c3bf25c5d24db16601af83
+sdk.mac_download=android-sdk_r21.1-macosx.zip
+sdk.mac_bytes=66077080
+sdk.mac_checksum=49903cf79e1f8e3fde54a95bd3666385
-sdk.mac_download=android-sdk_r21.0.1-macosx.zip
-sdk.mac_bytes=65804128
-sdk.mac_checksum=30401c43a014cd5d6ec9d0c62854a1d9
+sdk.win_download=android-sdk_r21.1-windows.zip
+sdk.win_bytes=99360755
+sdk.win_checksum=dbece8859da9b66a1e8e7cd47b1e647e
-sdk.linux_download=android-sdk_r21.0.1-linux.tgz
-sdk.linux_bytes=91394975
-sdk.linux_checksum=eaa5a8d76d692d1d027f2bbcee019644
+sdk.win_installer=installer_r21.1-windows.exe
+sdk.win_installer_bytes=77767013
+sdk.win_installer_checksum=594d8ff8e349db9e783a5f2229561353
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index 804030b15a13..d956af299800 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -1,8 +1,8 @@
page.title=Installing the Eclipse Plugin
adt.zip.version=21.0.1
-adt.zip.download=ADT-21.0.1.zip
-adt.zip.bytes=13569302
-adt.zip.checksum=acfb01bf3fd1240f1fc21488c3dd16bf
+adt.zip.download=ADT-21.1.0.zip
+adt.zip.bytes=13564671
+adt.zip.checksum=f1ae183891229784bb9c33bcc9c5ef1e
@jd:body
diff --git a/docs/html/tools/device.jd b/docs/html/tools/device.jd
index 61cd08a8b0f7..cf7b63f2dbf4 100644
--- a/docs/html/tools/device.jd
+++ b/docs/html/tools/device.jd
@@ -111,7 +111,17 @@ rules</a>.</p>
</li>
</ol>
-<p>When plugged in over USB, can verify that your device is connected by executing <code>adb
+
+<p class="note"><strong>Note:</strong> When you connect a device running Android 4.2.2 or higher
+to your computer, the system shows a dialog asking whether to accept an RSA key that allows
+debugging through this computer. This security mechanism protects user devices because it ensures
+that USB debugging and other adb commands cannot be executed unless you're able to unlock the
+device and acknowledge the dialog. This requires that you have adb version 1.0.31 (available with
+SDK Platform-tools r16.0.1 and higher) in order to debug on a device running Android 4.2.2 or
+higher.</p>
+
+
+<p>When plugged in over USB, you can verify that your device is connected by executing <code>adb
devices</code> from your SDK {@code platform-tools/} directory. If connected,
you'll see the device name listed as a "device."</p>
diff --git a/docs/html/tools/help/adb.jd b/docs/html/tools/help/adb.jd
index e0ee0e61fcd7..c8afca5ded1c 100644
--- a/docs/html/tools/help/adb.jd
+++ b/docs/html/tools/help/adb.jd
@@ -69,6 +69,13 @@ instances and handles commands from multiple adb clients, you can control any em
instance from any client (or from a script).</p>
+<p class="note"><strong>Note:</strong> When you connect a device running Android 4.2.2 or higher
+to your computer, the system shows a dialog asking whether to accept an RSA key that allows
+debugging through this computer. This security mechanism protects user devices because it ensures
+that USB debugging and other adb commands cannot be executed unless you're able to unlock the
+device and acknowledge the dialog. This requires that you have adb version 1.0.31 (available with
+SDK Platform-tools r16.0.1 and higher) in order to debug on a device running Android 4.2.2 or
+higher.</p>
<h2 id="issuingcommands">Syntax</h2>
@@ -871,8 +878,14 @@ list permission-groups
<td><code>
list permissions [options] &lt;GROUP>
</code></td>
-<td>Prints all known permissions, optionally only those in {@code &lt;GROUP>}. <p>Options:
-<ul> <li>{@code -g}: Organize by group. <li>{@code -f}: Print all information. <li>{@code -s}: Short summary. <li>{@code -d}: Only list dangerous permissions. <li>{@code -u}: List only the permissions users will see.
+<td>Prints all known permissions, optionally only
+ those in {@code &lt;GROUP>}. <p>Options:
+<ul>
+ <li>{@code -g}: Organize by group.
+ <li>{@code -f}: Print all information.
+ <li>{@code -s}: Short summary.
+ <li>{@code -d}: Only list dangerous permissions.
+ <li>{@code -u}: List only the permissions users will see.
</ul>
</td>
</tr>
@@ -882,7 +895,8 @@ list permissions [options] &lt;GROUP>
list instrumentation
</code></td>
<td>List all test packages. <p>Options:
- <ul> <li>{@code -f}: List the APK file for the test package.
+ <ul>
+ <li>{@code -f}: List the APK file for the test package.
<li>{@code &lt;TARGET_PACKAGE>}: List test packages for only this app.
</ul>
</td>
@@ -925,7 +939,14 @@ path &lt;PACKAGE>
install [options] &lt;PATH>
</code></td>
<td>Installs a package (specified by {@code &lt;PATH>}) to the system. <p>Options:
- <ul> <li>{@code -l}: Install the package with forward lock. <li>{@code -r}: Reinstall an exisiting app, keeping its data. <li>{@code -t}: Allow test APKs to be installed. <li>{@code -i &lt;INSTALLER_PACKAGE_NAME>}: Specify the installer package name. <li>{@code -s}: Install package on the shared mass storage (such as sdcard). <li>{@code -f}: Install package on the internal system memory. <li>{@code -d}: Allow version code downgrade.
+ <ul>
+ <li>{@code -l}: Install the package with forward lock.
+ <li>{@code -r}: Reinstall an exisiting app, keeping its data.
+ <li>{@code -t}: Allow test APKs to be installed.
+ <li>{@code -i &lt;INSTALLER_PACKAGE_NAME>}: Specify the installer package name.
+ <li>{@code -s}: Install package on the shared mass storage (such as sdcard).
+ <li>{@code -f}: Install package on the internal system memory.
+ <li>{@code -d}: Allow version code downgrade.
</ul>
</td>
</tr>
@@ -935,7 +956,8 @@ install [options] &lt;PATH>
uninstall [options] &lt;PACKAGE>
</code></td>
<td>Removes a package from the system. <p>Options:
- <ul> <li>{@code -k}: Keep the data and cache directories around after package removal.
+ <ul>
+ <li>{@code -k}: Keep the data and cache directories around after package removal.
</ul>
</td>
</tr>
diff --git a/docs/html/tools/publishing/app-signing.jd b/docs/html/tools/publishing/app-signing.jd
index ac45242516b5..608780e8ea14 100644
--- a/docs/html/tools/publishing/app-signing.jd
+++ b/docs/html/tools/publishing/app-signing.jd
@@ -96,8 +96,7 @@ you compile.</p>
you don't have a private key, you can use the Keytool utility to create one for you. When you
compile your application in release mode, the build tools use your private key along with the
Jarsigner utility to sign your application's <code>.apk</code> file. Because the certificate and
-private key you use are your own, you will have to provide the password for the keystore and key
-alias.</p>
+private key you use are your own, you must provide the password for the keystore and key alias.</p>
<p>The debug signing process happens automatically when you run or debug your application using
Eclipse with the ADT plugin. Debug signing also happens automatically when you use the Ant build
@@ -117,13 +116,13 @@ lifespan of your applications. There are several reasons why you should do so: <
<ul>
<li>Application upgrade &ndash; As you release updates to your application, you
-will want to continue to sign the updates with the same certificate or set of
-certificates, if you want users to upgrade seamlessly to the new version. When
+must continue to sign the updates with the same certificate or set of certificates,
+if you want users to be able to upgrade seamlessly to the new version. When
the system is installing an update to an application, it compares the
certificate(s) in the new version with those in the existing version. If the
certificates match exactly, including both the certificate data and order, then
the system allows the update. If you sign the new version without using matching
-certificates, you will also need to assign a different package name to the
+certificates, you must also assign a different package name to the
application &mdash; in this case, the user installs the new version as a
completely new application. </li>
@@ -314,6 +313,13 @@ your key secure and why doing so is critically important to you and to users. In
particular, when you are generating your key, you should select strong passwords
for both the keystore and key.</p>
+<p class="warning"><strong>Warning:</strong> Keep the keystore file you generate with Keytool
+in a safe, secure place. You must use the same key to sign future versions of your application. If
+you republish your app with a new key, Google Play will consider it a new app. For more information
+on settings that must remain constant over the life of your app, see the Android Developer Blog post
+<a href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">Things
+That Cannot Change</a>.</p>
+
<table>
<tr>
<th>Keytool Option</th>
@@ -597,6 +603,10 @@ replace your authentic applications or corrupt them. Such a person could also
sign and distribute applications under your identity that attack other
applications or the system itself, or corrupt or steal user data. </p>
+<p>Your private key is required for signing all future versions of your application. If you lose or
+misplace your key, you will not be able to publish updates to your existing application. You cannot
+regenerate a previously generated key.</p>
+
<p>Your reputation as a developer entity depends on your securing your private
key properly, at all times, until the key is expired. Here are some tips for
keeping your key secure: </p>
@@ -612,7 +622,9 @@ which any user on your computer could access.</li>
options at the command line. </li>
<li>Do not give or lend anyone your private key, and do not let unauthorized
persons know your keystore and key passwords.</li>
+<li>Keep the keystore file containing your private key that you <a href="#cert">generate with the
+Keytool</a> in a safe, secure place.</li>
</ul>
<p>In general, if you follow common-sense precautions when generating, using,
-and storing your key, it will remain secure. </p> \ No newline at end of file
+and storing your key, it will remain secure. </p>
diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd
index 243683ccb593..4adb7b29ffff 100644
--- a/docs/html/tools/sdk/eclipse-adt.jd
+++ b/docs/html/tools/sdk/eclipse-adt.jd
@@ -57,6 +57,63 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>ADT 21.1.0</a> <em>(February 2013)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+<dl>
+ <dt>Dependencies:</dt>
+
+ <dd>
+ <ul>
+ <li>Java 1.6 or higher is required for ADT 21.1.0.</li>
+ <li>Eclipse Helios (Version 3.6.2) or higher is required for ADT 21.1.0.</li>
+ <li>ADT 21.1.0 is designed for use with <a href="{@docRoot}tools/sdk/tools-notes.html">SDK
+ Tools r21.1.0</a>. If you haven't already installed SDK Tools r21.1.0 into your SDK, use the
+ Android SDK Manager to do so.</li>
+ </ul>
+ </dd>
+
+ <dt>General Notes:</dt>
+ <dd>
+ <ul>
+ <li>Added new <a href="{@docRoot}tools/projects/templates.html">code templates</a> for
+ notifications, blank fragments and list fragments.</li>
+ <li>Added support for resource rename refactoring. Renaming a resource XML file, drawable
+ icon, an {@code R.} field name or ID in the layout editor invokes a refactoring routine
+ to update all resource references.</li>
+ <li>Added more than 15 new Lint checks, including checks for overriding older APIs, XML
+ resource problems, graphic asset issues and manifest tags.
+ <li>Updated XML Editor to respond to refactoring shortcut keys such as <strong>Refactor
+ &gt; Rename</strong>.</li>
+ <li>Updated XML Editor to improve double click handling.</li>
+ <li>Added code completion improvements for custom views, theme references and class
+ references. For example, code completion in a {@code &lt;fragment android:name=””&gt;} tag
+ now suggests completion with a list of fragment classes. Similarly, code completion in the
+ manifest now offers implementations suitable for the given tag.</li>
+ <li>Updated the <strong>Project Import</strong> dialog so that it shows a table for all
+ imported projects where you can edit the name of the imported project.</li>
+ <li>Added support for layout aliases in the Layout Editor.</li>
+ </ul>
+ </dd>
+
+ <dt>Bug fixes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed issued with refactoring support for renaming and moving classes and packages.
+ </li>
+ </ul>
+ </dd>
+
+</dl>
+</div>
+</div>
+
+
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>ADT 21.0.1</a> <em>(December 2012)</em>
</p>
diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd
index 9349a4e2dbc0..a5b7eee19ad1 100644
--- a/docs/html/tools/sdk/tools-notes.jd
+++ b/docs/html/tools/sdk/tools-notes.jd
@@ -28,6 +28,41 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>SDK Tools, Revision 21.1.0</a> <em>(February 2013)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+
+ <dl>
+ <dt>Dependencies:</dt>
+ <dd>
+ <ul>
+ <li>Android SDK Platform-tools revision 16 or later.</li>
+ <li>If you are developing in Eclipse with ADT, note that the SDK Tools r21.1.0 is
+ designed for use with ADT 21.1.0 and later. If you haven't already, update your
+ <a href="{@docRoot}tools/sdk/eclipse-adt.html">ADT Plugin</a> to 21.1.0.</li>
+ <li>If you are developing outside Eclipse, you must have
+ <a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later.</li>
+ </ul>
+ </dd>
+
+ <dt>General Notes:</dt>
+ <dd>
+ <ul>
+ <li>Improved error reporting in {@code dx} when dex merging fails in the build
+ system.</li>
+ <li>Added more than 15 new Lint checks, including checks for overriding older APIs, XML
+ resource problems, graphic asset issues and manifest tags.</li>
+ <li>Added new aapt feature to compile resources.</li>
+ </ul>
+ </dd>
+ </dl>
+ </div>
+</div>
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>SDK Tools, Revision 21.0.1</a> <em>(December 2012)</em>
</p>
@@ -40,7 +75,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues
<li>Android SDK Platform-tools revision 16 or later.</li>
<li>If you are developing in Eclipse with ADT, note that the SDK Tools r21.0.1 is
designed for use with ADT 21.0.1 and later. If you haven't already, update your
- <a href="{@docRoot}tools/sdk/eclipse-adt.html">ADT Plugin</a> to 21.0.0.</li>
+ <a href="{@docRoot}tools/sdk/eclipse-adt.html">ADT Plugin</a> to 21.0.1.</li>
<li>If you are developing outside Eclipse, you must have
<a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later.</li>
</ul>
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index 4baa9c3a0494..91a018c17283 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -32,33 +32,33 @@
<li class="nav-section">
<div class="nav-section-header">
- <a href="/tools/workflow/index.html"><span class="en">Workflow</span></a>
+ <a href="<?cs var:toroot ?>tools/workflow/index.html"><span class="en">Workflow</span></a>
</div>
<ul>
<li class="nav-section">
- <div class="nav-section-header"><a href="/tools/devices/index.html"><span class="en">Setting Up Virtual Devices</span></a></div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/devices/index.html"><span class="en">Setting Up Virtual Devices</span></a></div>
<ul>
- <li><a href="/tools/devices/managing-avds.html"><span class="en">With AVD Manager</span></a></li>
- <li><a href="/tools/devices/managing-avds-cmdline.html"><span class="en">From the Command Line</span></a></li>
- <li><a href="/tools/devices/emulator.html"><span class="en">Using the Android Emulator</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/devices/managing-avds.html"><span class="en">With AVD Manager</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/devices/managing-avds-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/devices/emulator.html"><span class="en">Using the Android Emulator</span></a></li>
</ul>
</li>
- <li><a href="/tools/device.html"><span class="en">Using Hardware Devices</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/device.html"><span class="en">Using Hardware Devices</span></a></li>
<li class="nav-section">
- <div class="nav-section-header"><a href="/tools/projects/index.html"><span class="en">Setting Up Projects</span></a></div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/projects/index.html"><span class="en">Setting Up Projects</span></a></div>
<ul>
- <li><a href="/tools/projects/projects-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
- <li><a href="/tools/projects/projects-cmdline.html"><span class="en">From the Command Line</span></a></li>
- <li><a href="/tools/projects/templates.html"><span class="en">Using Code Templates</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/projects/projects-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/projects/projects-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/projects/templates.html"><span class="en">Using Code Templates</span></a></li>
</ul>
</li>
<li class="nav-section">
- <div class="nav-section-header"><a href="/tools/building/index.html"><span class="en">Building and Running</span></a></div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/building/index.html"><span class="en">Building and Running</span></a></div>
<ul>
- <li><a href="/tools/building/building-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
- <li><a href="/tools/building/building-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/building/building-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
+ <li><a href="<?cs var:toroot ?>tools/building/building-cmdline.html"><span class="en">From the Command Line</span></a></li>
</ul>
</li>
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 483d11afb8f3..8cbf299ba31d 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -37,8 +37,8 @@ import javax.microedition.khronos.opengles.GL;
* Canvas and Drawables</a> developer guide.</p></div>
*/
public class Canvas {
- // assigned in constructors or setBitmap, freed in finalizer
- int mNativeCanvas;
+ // assigned in constructors, freed in finalizer
+ final int mNativeCanvas;
// may be null
private Bitmap mBitmap;
@@ -62,18 +62,6 @@ public class Canvas {
@SuppressWarnings({"UnusedDeclaration"})
private int mSurfaceFormat;
- /**
- * Flag for drawTextRun indicating left-to-right run direction.
- * @hide
- */
- public static final int DIRECTION_LTR = 0;
-
- /**
- * Flag for drawTextRun indicating right-to-left run direction.
- * @hide
- */
- public static final int DIRECTION_RTL = 1;
-
// Maximum bitmap size as defined in Skia's native code
// (see SkCanvas.cpp, SkDraw.cpp)
private static final int MAXMIMUM_BITMAP_SIZE = 32766;
@@ -83,7 +71,7 @@ public class Canvas {
private final CanvasFinalizer mFinalizer;
private static class CanvasFinalizer {
- private int mNativeCanvas;
+ private final int mNativeCanvas;
public CanvasFinalizer(int nativeCanvas) {
mNativeCanvas = nativeCanvas;
@@ -143,17 +131,6 @@ public class Canvas {
}
/**
- * Replace existing canvas while ensuring that the swap has occurred before
- * the previous native canvas is unreferenced.
- */
- private void safeCanvasSwap(int nativeCanvas) {
- final int oldCanvas = mNativeCanvas;
- mNativeCanvas = nativeCanvas;
- mFinalizer.mNativeCanvas = nativeCanvas;
- finalizer(oldCanvas);
- }
-
- /**
* Returns null.
*
* @deprecated This method is not supported and should not be invoked.
@@ -179,11 +156,11 @@ public class Canvas {
}
/**
- * Specify a bitmap for the canvas to draw into. As a side-effect, the
- * canvas' target density is updated to match that of the bitmap while all
- * other state such as the layers, filters, matrix, and clip are reset.
+ * Specify a bitmap for the canvas to draw into. As a side-effect, also
+ * updates the canvas's target density to match that of the bitmap.
*
* @param bitmap Specifies a mutable bitmap for the canvas to draw into.
+ *
* @see #setDensity(int)
* @see #getDensity()
*/
@@ -192,19 +169,17 @@ public class Canvas {
throw new RuntimeException("Can't set a bitmap device on a GL canvas");
}
- if (bitmap == null) {
- safeCanvasSwap(initRaster(0));
- mDensity = Bitmap.DENSITY_NONE;
- } else {
+ int pointer = 0;
+ if (bitmap != null) {
if (!bitmap.isMutable()) {
throw new IllegalStateException();
}
throwIfRecycled(bitmap);
-
- safeCanvasSwap(initRaster(bitmap.ni()));
mDensity = bitmap.mDensity;
+ pointer = bitmap.ni();
}
+ native_setBitmap(mNativeCanvas, pointer);
mBitmap = bitmap;
}
@@ -719,7 +694,7 @@ public class Canvas {
* does not intersect with the canvas' clip
*/
public boolean quickReject(RectF rect, EdgeType type) {
- return native_quickReject(mNativeCanvas, rect);
+ return native_quickReject(mNativeCanvas, rect, type.nativeInt);
}
/**
@@ -739,7 +714,7 @@ public class Canvas {
* does not intersect with the canvas' clip
*/
public boolean quickReject(Path path, EdgeType type) {
- return native_quickReject(mNativeCanvas, path.ni());
+ return native_quickReject(mNativeCanvas, path.ni(), type.nativeInt);
}
/**
@@ -762,9 +737,9 @@ public class Canvas {
* @return true if the rect (transformed by the canvas' matrix)
* does not intersect with the canvas' clip
*/
- public boolean quickReject(float left, float top, float right, float bottom,
- EdgeType type) {
- return native_quickReject(mNativeCanvas, left, top, right, bottom);
+ public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) {
+ return native_quickReject(mNativeCanvas, left, top, right, bottom,
+ type.nativeInt);
}
/**
@@ -1341,8 +1316,7 @@ public class Canvas {
(text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_drawText(mNativeCanvas, text, index, count, x, y, paint.mBidiFlags,
- paint.mNativePaint);
+ native_drawText(mNativeCanvas, text, index, count, x, y, paint.mNativePaint);
}
/**
@@ -1355,8 +1329,7 @@ public class Canvas {
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(String text, float x, float y, Paint paint) {
- native_drawText(mNativeCanvas, text, 0, text.length(), x, y, paint.mBidiFlags,
- paint.mNativePaint);
+ native_drawText(mNativeCanvas, text, 0, text.length(), x, y, paint.mNativePaint);
}
/**
@@ -1374,8 +1347,7 @@ public class Canvas {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_drawText(mNativeCanvas, text, start, end, x, y, paint.mBidiFlags,
- paint.mNativePaint);
+ native_drawText(mNativeCanvas, text, start, end, x, y, paint.mNativePaint);
}
/**
@@ -1394,16 +1366,14 @@ public class Canvas {
public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
- native_drawText(mNativeCanvas, text.toString(), start, end, x, y,
- paint.mBidiFlags, paint.mNativePaint);
+ native_drawText(mNativeCanvas, text.toString(), start, end, x, y, paint.mNativePaint);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawText(this, start, end, x, y,
paint);
} else {
char[] buf = TemporaryBuffer.obtain(end - start);
TextUtils.getChars(text, start, end, buf, 0);
- native_drawText(mNativeCanvas, buf, 0, end - start, x, y,
- paint.mBidiFlags, paint.mNativePaint);
+ native_drawText(mNativeCanvas, buf, 0, end - start, x, y, paint.mNativePaint);
TemporaryBuffer.recycle(buf);
}
}
@@ -1424,13 +1394,11 @@ public class Canvas {
* + count.
* @param x the x position at which to draw the text
* @param y the y position at which to draw the text
- * @param dir the run direction, either {@link #DIRECTION_LTR} or
- * {@link #DIRECTION_RTL}.
* @param paint the paint
* @hide
*/
public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
- float x, float y, int dir, Paint paint) {
+ float x, float y, Paint paint) {
if (text == null) {
throw new NullPointerException("text is null");
@@ -1441,12 +1409,9 @@ public class Canvas {
if ((index | count | text.length - index - count) < 0) {
throw new IndexOutOfBoundsException();
}
- if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
- throw new IllegalArgumentException("unknown dir: " + dir);
- }
native_drawTextRun(mNativeCanvas, text, index, count,
- contextIndex, contextCount, x, y, dir, paint.mNativePaint);
+ contextIndex, contextCount, x, y, paint.mNativePaint);
}
/**
@@ -1462,12 +1427,11 @@ public class Canvas {
* position can be used for shaping context.
* @param x the x position at which to draw the text
* @param y the y position at which to draw the text
- * @param dir the run direction, either 0 for LTR or 1 for RTL.
* @param paint the paint
* @hide
*/
public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
- float x, float y, int dir, Paint paint) {
+ float x, float y, Paint paint) {
if (text == null) {
throw new NullPointerException("text is null");
@@ -1479,22 +1443,20 @@ public class Canvas {
throw new IndexOutOfBoundsException();
}
- int flags = dir == 0 ? 0 : 1;
-
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
native_drawTextRun(mNativeCanvas, text.toString(), start, end,
- contextStart, contextEnd, x, y, flags, paint.mNativePaint);
+ contextStart, contextEnd, x, y, paint.mNativePaint);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawTextRun(this, start, end,
- contextStart, contextEnd, x, y, flags, paint);
+ contextStart, contextEnd, x, y, paint);
} else {
int contextLen = contextEnd - contextStart;
int len = end - start;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
native_drawTextRun(mNativeCanvas, buf, start - contextStart, len,
- 0, contextLen, x, y, flags, paint.mNativePaint);
+ 0, contextLen, x, y, paint.mNativePaint);
TemporaryBuffer.recycle(buf);
}
}
@@ -1560,8 +1522,7 @@ public class Canvas {
throw new ArrayIndexOutOfBoundsException();
}
native_drawTextOnPath(mNativeCanvas, text, index, count,
- path.ni(), hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
+ path.ni(), hOffset, vOffset, paint.mNativePaint);
}
/**
@@ -1580,7 +1541,7 @@ public class Canvas {
public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
if (text.length() > 0) {
native_drawTextOnPath(mNativeCanvas, text, path.ni(), hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
+ paint.mNativePaint);
}
}
@@ -1638,6 +1599,7 @@ public class Canvas {
public static native void freeTextLayoutCaches();
private static native int initRaster(int nativeBitmapOrZero);
+ private static native void native_setBitmap(int nativeCanvas, int bitmap);
private static native int native_saveLayer(int nativeCanvas, RectF bounds,
int paint, int layerFlags);
private static native int native_saveLayer(int nativeCanvas, float l,
@@ -1668,12 +1630,15 @@ public class Canvas {
Rect bounds);
private static native void native_getCTM(int canvas, int matrix);
private static native boolean native_quickReject(int nativeCanvas,
- RectF rect);
+ RectF rect,
+ int native_edgeType);
private static native boolean native_quickReject(int nativeCanvas,
- int path);
+ int path,
+ int native_edgeType);
private static native boolean native_quickReject(int nativeCanvas,
float left, float top,
- float right, float bottom);
+ float right, float bottom,
+ int native_edgeType);
private static native void native_drawRGB(int nativeCanvas, int r, int g,
int b);
private static native void native_drawARGB(int nativeCanvas, int a, int r,
@@ -1737,18 +1702,18 @@ public class Canvas {
private static native void native_drawText(int nativeCanvas, char[] text,
int index, int count, float x,
- float y, int flags, int paint);
+ float y, int paint);
private static native void native_drawText(int nativeCanvas, String text,
int start, int end, float x,
- float y, int flags, int paint);
+ float y, int paint);
private static native void native_drawTextRun(int nativeCanvas, String text,
int start, int end, int contextStart, int contextEnd,
- float x, float y, int flags, int paint);
+ float x, float y, int paint);
private static native void native_drawTextRun(int nativeCanvas, char[] text,
int start, int count, int contextStart, int contextCount,
- float x, float y, int flags, int paint);
+ float x, float y, int paint);
private static native void native_drawPosText(int nativeCanvas,
char[] text, int index,
@@ -1761,13 +1726,13 @@ public class Canvas {
char[] text, int index,
int count, int path,
float hOffset,
- float vOffset, int bidiFlags,
+ float vOffset,
int paint);
private static native void native_drawTextOnPath(int nativeCanvas,
String text, int path,
float hOffset,
float vOffset,
- int flags, int paint);
+ int paint);
private static native void native_drawPicture(int nativeCanvas,
int nativePicture);
private static native void finalizer(int nativeCanvas);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3285e518c920..3a83d121516e 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -69,11 +69,6 @@ public class Paint {
*/
public int shadowColor;
- /**
- * @hide
- */
- public int mBidiFlags = BIDI_DEFAULT_LTR;
-
static final Style[] sStyleArray = {
Style.FILL, Style.STROKE, Style.FILL_AND_STROKE
};
@@ -120,74 +115,6 @@ public class Paint {
public static final int HINTING_ON = 0x1;
/**
- * Bidi flag to set LTR paragraph direction.
- *
- * @hide
- */
- public static final int BIDI_LTR = 0x0;
-
- /**
- * Bidi flag to set RTL paragraph direction.
- *
- * @hide
- */
- public static final int BIDI_RTL = 0x1;
-
- /**
- * Bidi flag to detect paragraph direction via heuristics, defaulting to
- * LTR.
- *
- * @hide
- */
- public static final int BIDI_DEFAULT_LTR = 0x2;
-
- /**
- * Bidi flag to detect paragraph direction via heuristics, defaulting to
- * RTL.
- *
- * @hide
- */
- public static final int BIDI_DEFAULT_RTL = 0x3;
-
- /**
- * Bidi flag to override direction to all LTR (ignore bidi).
- *
- * @hide
- */
- public static final int BIDI_FORCE_LTR = 0x4;
-
- /**
- * Bidi flag to override direction to all RTL (ignore bidi).
- *
- * @hide
- */
- public static final int BIDI_FORCE_RTL = 0x5;
-
- /**
- * Maximum Bidi flag value.
- * @hide
- */
- private static final int BIDI_MAX_FLAG_VALUE = BIDI_FORCE_RTL;
-
- /**
- * Mask for bidi flags.
- * @hide
- */
- private static final int BIDI_FLAG_MASK = 0x7;
-
- /**
- * Flag for getTextRunAdvances indicating left-to-right run direction.
- * @hide
- */
- public static final int DIRECTION_LTR = 0;
-
- /**
- * Flag for getTextRunAdvances indicating right-to-left run direction.
- * @hide
- */
- public static final int DIRECTION_RTL = 1;
-
- /**
* Option for getTextRunCursor to compute the valid cursor after
* offset or the limit of the context, whichever is less.
* @hide
@@ -395,7 +322,6 @@ public class Paint {
shadowRadius = 0;
shadowColor = 0;
- mBidiFlags = BIDI_DEFAULT_LTR;
setTextLocale(Locale.getDefault());
}
@@ -435,7 +361,6 @@ public class Paint {
shadowRadius = paint.shadowRadius;
shadowColor = paint.shadowColor;
- mBidiFlags = paint.mBidiFlags;
mLocale = paint.mLocale;
}
@@ -452,29 +377,6 @@ public class Paint {
}
/**
- * Return the bidi flags on the paint.
- *
- * @return the bidi flags on the paint
- * @hide
- */
- public int getBidiFlags() {
- return mBidiFlags;
- }
-
- /**
- * Set the bidi flags on the paint.
- * @hide
- */
- public void setBidiFlags(int flags) {
- // only flag value is the 3-bit BIDI control setting
- flags &= BIDI_FLAG_MASK;
- if (flags > BIDI_MAX_FLAG_VALUE) {
- throw new IllegalArgumentException("unknown bidi flag: " + flags);
- }
- mBidiFlags = flags;
- }
-
- /**
* Return the paint's flags. Use the Flag enum to test flag values.
*
* @return the paint's flags (see enums ending in _Flag for bit masks)
@@ -1666,76 +1568,19 @@ public class Paint {
}
/**
- * Return the glyph Ids for the characters in the string.
- *
- * @param text The text to measure
- * @param start The index of the first char to to measure
- * @param end The end of the text slice to measure
- * @param contextStart the index of the first character to use for shaping context,
- * must be <= start
- * @param contextEnd the index past the last character to use for shaping context,
- * must be >= end
- * @param flags the flags to control the advances, either {@link #DIRECTION_LTR}
- * or {@link #DIRECTION_RTL}
- * @param glyphs array to receive the glyph Ids of the characters.
- * Must be at least a large as the text.
- * @return the number of glyphs in the returned array
- *
- * @hide
- *
- * Used only for BiDi / RTL Tests
- */
- public int getTextGlyphs(String text, int start, int end, int contextStart, int contextEnd,
- int flags, char[] glyphs) {
- if (text == null) {
- throw new IllegalArgumentException("text cannot be null");
- }
- if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
- throw new IllegalArgumentException("unknown flags value: " + flags);
- }
- if ((start | end | contextStart | contextEnd | (end - start)
- | (start - contextStart) | (contextEnd - end) | (text.length() - end)
- | (text.length() - contextEnd)) < 0) {
- throw new IndexOutOfBoundsException();
- }
- if (end - start > glyphs.length) {
- throw new ArrayIndexOutOfBoundsException();
- }
- return native_getTextGlyphs(mNativePaint, text, start, end, contextStart, contextEnd,
- flags, glyphs);
- }
-
- /**
* Convenience overload that takes a char array instead of a
* String.
*
- * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+ * @see #getTextRunAdvances(String, int, int, int, int, float[], int)
* @hide
*/
public float getTextRunAdvances(char[] chars, int index, int count,
- int contextIndex, int contextCount, int flags, float[] advances,
+ int contextIndex, int contextCount, float[] advances,
int advancesIndex) {
- return getTextRunAdvances(chars, index, count, contextIndex, contextCount, flags,
- advances, advancesIndex, 0 /* use Harfbuzz*/);
- }
-
- /**
- * Convenience overload that takes a char array instead of a
- * String.
- *
- * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int, int)
- * @hide
- */
- public float getTextRunAdvances(char[] chars, int index, int count,
- int contextIndex, int contextCount, int flags, float[] advances,
- int advancesIndex, int reserved) {
if (chars == null) {
throw new IllegalArgumentException("text cannot be null");
}
- if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
- throw new IllegalArgumentException("unknown flags value: " + flags);
- }
if ((index | count | contextIndex | contextCount | advancesIndex
| (index - contextIndex) | (contextCount - count)
| ((contextIndex + contextCount) - (index + count))
@@ -1750,13 +1595,13 @@ public class Paint {
}
if (!mHasCompatScaling) {
return native_getTextRunAdvances(mNativePaint, chars, index, count,
- contextIndex, contextCount, flags, advances, advancesIndex, reserved);
+ contextIndex, contextCount, advances, advancesIndex);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
float res = native_getTextRunAdvances(mNativePaint, chars, index, count,
- contextIndex, contextCount, flags, advances, advancesIndex, reserved);
+ contextIndex, contextCount, advances, advancesIndex);
setTextSize(oldSize);
if (advances != null) {
@@ -1771,26 +1616,12 @@ public class Paint {
* Convenience overload that takes a CharSequence instead of a
* String.
*
- * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+ * @see #getTextRunAdvances(String, int, int, int, int, float[], int)
* @hide
*/
public float getTextRunAdvances(CharSequence text, int start, int end,
- int contextStart, int contextEnd, int flags, float[] advances,
+ int contextStart, int contextEnd, float[] advances,
int advancesIndex) {
- return getTextRunAdvances(text, start, end, contextStart, contextEnd, flags,
- advances, advancesIndex, 0 /* use Harfbuzz */);
- }
-
- /**
- * Convenience overload that takes a CharSequence instead of a
- * String.
- *
- * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
- * @hide
- */
- public float getTextRunAdvances(CharSequence text, int start, int end,
- int contextStart, int contextEnd, int flags, float[] advances,
- int advancesIndex, int reserved) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
@@ -1805,16 +1636,16 @@ public class Paint {
if (text instanceof String) {
return getTextRunAdvances((String) text, start, end,
- contextStart, contextEnd, flags, advances, advancesIndex, reserved);
+ contextStart, contextEnd, advances, advancesIndex);
}
if (text instanceof SpannedString ||
text instanceof SpannableString) {
return getTextRunAdvances(text.toString(), start, end,
- contextStart, contextEnd, flags, advances, advancesIndex, reserved);
+ contextStart, contextEnd, advances, advancesIndex);
}
if (text instanceof GraphicsOperations) {
return ((GraphicsOperations) text).getTextRunAdvances(start, end,
- contextStart, contextEnd, flags, advances, advancesIndex, this);
+ contextStart, contextEnd, advances, advancesIndex, this);
}
if (text.length() == 0 || end == start) {
return 0f;
@@ -1825,7 +1656,7 @@ public class Paint {
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
float result = getTextRunAdvances(buf, start - contextStart, len,
- 0, contextLen, flags, advances, advancesIndex, reserved);
+ 0, contextLen, advances, advancesIndex);
TemporaryBuffer.recycle(buf);
return result;
}
@@ -1862,74 +1693,20 @@ public class Paint {
* must be <= start
* @param contextEnd the index past the last character to use for shaping context,
* must be >= end
- * @param flags the flags to control the advances, either {@link #DIRECTION_LTR}
- * or {@link #DIRECTION_RTL}
- * @param advances array to receive the advances, must have room for all advances,
- * can be null if only total advance is needed
- * @param advancesIndex the position in advances at which to put the
- * advance corresponding to the character at start
- * @return the total advance
- *
- * @hide
- */
- public float getTextRunAdvances(String text, int start, int end, int contextStart,
- int contextEnd, int flags, float[] advances, int advancesIndex) {
- return getTextRunAdvances(text, start, end, contextStart, contextEnd, flags,
- advances, advancesIndex, 0 /* use Harfbuzz*/);
- }
-
- /**
- * Returns the total advance width for the characters in the run
- * between start and end, and if advances is not null, the advance
- * assigned to each of these characters (java chars).
- *
- * <p>The trailing surrogate in a valid surrogate pair is assigned
- * an advance of 0. Thus the number of returned advances is
- * always equal to count, not to the number of unicode codepoints
- * represented by the run.
- *
- * <p>In the case of conjuncts or combining marks, the total
- * advance is assigned to the first logical character, and the
- * following characters are assigned an advance of 0.
- *
- * <p>This generates the sum of the advances of glyphs for
- * characters in a reordered cluster as the width of the first
- * logical character in the cluster, and 0 for the widths of all
- * other characters in the cluster. In effect, such clusters are
- * treated like conjuncts.
- *
- * <p>The shaping bounds limit the amount of context available
- * outside start and end that can be used for shaping analysis.
- * These bounds typically reflect changes in bidi level or font
- * metrics across which shaping does not occur.
- *
- * @param text the text to measure. Cannot be null.
- * @param start the index of the first character to measure
- * @param end the index past the last character to measure
- * @param contextStart the index of the first character to use for shaping context,
- * must be <= start
- * @param contextEnd the index past the last character to use for shaping context,
- * must be >= end
- * @param flags the flags to control the advances, either {@link #DIRECTION_LTR}
- * or {@link #DIRECTION_RTL}
* @param advances array to receive the advances, must have room for all advances,
* can be null if only total advance is needed
* @param advancesIndex the position in advances at which to put the
* advance corresponding to the character at start
- * @param reserved int reserved value
* @return the total advance
*
* @hide
*/
public float getTextRunAdvances(String text, int start, int end, int contextStart,
- int contextEnd, int flags, float[] advances, int advancesIndex, int reserved) {
+ int contextEnd, float[] advances, int advancesIndex) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
- if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
- throw new IllegalArgumentException("unknown flags value: " + flags);
- }
if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
| (start - contextStart) | (contextEnd - end)
| (text.length() - contextEnd)
@@ -1944,13 +1721,13 @@ public class Paint {
if (!mHasCompatScaling) {
return native_getTextRunAdvances(mNativePaint, text, start, end,
- contextStart, contextEnd, flags, advances, advancesIndex, reserved);
+ contextStart, contextEnd, advances, advancesIndex);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
float totalAdvance = native_getTextRunAdvances(mNativePaint, text, start, end,
- contextStart, contextEnd, flags, advances, advancesIndex, reserved);
+ contextStart, contextEnd, advances, advancesIndex);
setTextSize(oldSize);
if (advances != null) {
@@ -1979,7 +1756,6 @@ public class Paint {
* @param text the text
* @param contextStart the start of the context
* @param contextLength the length of the context
- * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
* {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
@@ -1988,7 +1764,7 @@ public class Paint {
* @hide
*/
public int getTextRunCursor(char[] text, int contextStart, int contextLength,
- int flags, int offset, int cursorOpt) {
+ int offset, int cursorOpt) {
int contextEnd = contextStart + contextLength;
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
@@ -1998,7 +1774,7 @@ public class Paint {
}
return native_getTextRunCursor(mNativePaint, text,
- contextStart, contextLength, flags, offset, cursorOpt);
+ contextStart, contextLength, offset, cursorOpt);
}
/**
@@ -2019,7 +1795,6 @@ public class Paint {
* @param text the text
* @param contextStart the start of the context
* @param contextEnd the end of the context
- * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
* {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
@@ -2028,22 +1803,22 @@ public class Paint {
* @hide
*/
public int getTextRunCursor(CharSequence text, int contextStart,
- int contextEnd, int flags, int offset, int cursorOpt) {
+ int contextEnd, int offset, int cursorOpt) {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
return getTextRunCursor(text.toString(), contextStart, contextEnd,
- flags, offset, cursorOpt);
+ offset, cursorOpt);
}
if (text instanceof GraphicsOperations) {
return ((GraphicsOperations) text).getTextRunCursor(
- contextStart, contextEnd, flags, offset, cursorOpt, this);
+ contextStart, contextEnd, offset, cursorOpt, this);
}
int contextLen = contextEnd - contextStart;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- int result = getTextRunCursor(buf, 0, contextLen, flags, offset - contextStart, cursorOpt);
+ int result = getTextRunCursor(buf, 0, contextLen, offset - contextStart, cursorOpt);
TemporaryBuffer.recycle(buf);
return result;
}
@@ -2066,7 +1841,6 @@ public class Paint {
* @param text the text
* @param contextStart the start of the context
* @param contextEnd the end of the context
- * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
* {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
@@ -2084,7 +1858,7 @@ public class Paint {
}
return native_getTextRunCursor(mNativePaint, text,
- contextStart, contextEnd, flags, offset, cursorOpt);
+ contextStart, contextEnd, offset, cursorOpt);
}
/**
@@ -2105,7 +1879,7 @@ public class Paint {
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, mBidiFlags, text, index, count, x, y,
+ native_getTextPath(mNativePaint, text, index, count, x, y,
path.ni());
}
@@ -2127,7 +1901,7 @@ public class Paint {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, mBidiFlags, text, start, end, x, y,
+ native_getTextPath(mNativePaint, text, start, end, x, y,
path.ni());
}
@@ -2219,26 +1993,22 @@ public class Paint {
private static native int native_getTextWidths(int native_object,
String text, int start, int end, float[] widths);
- private static native int native_getTextGlyphs(int native_object,
- String text, int start, int end, int contextStart, int contextEnd,
- int flags, char[] glyphs);
-
private static native float native_getTextRunAdvances(int native_object,
char[] text, int index, int count, int contextIndex, int contextCount,
- int flags, float[] advances, int advancesIndex, int reserved);
+ float[] advances, int advancesIndex);
private static native float native_getTextRunAdvances(int native_object,
String text, int start, int end, int contextStart, int contextEnd,
- int flags, float[] advances, int advancesIndex, int reserved);
+ float[] advances, int advancesIndex);
private native int native_getTextRunCursor(int native_object, char[] text,
- int contextStart, int contextLength, int flags, int offset, int cursorOpt);
+ int contextStart, int contextLength, int offset, int cursorOpt);
private native int native_getTextRunCursor(int native_object, String text,
- int contextStart, int contextEnd, int flags, int offset, int cursorOpt);
+ int contextStart, int contextEnd, int offset, int cursorOpt);
- private static native void native_getTextPath(int native_object, int bidiFlags,
- char[] text, int index, int count, float x, float y, int path);
- private static native void native_getTextPath(int native_object, int bidiFlags,
- String text, int start, int end, float x, float y, int path);
+ private static native void native_getTextPath(int native_object, char[] text,
+ int index, int count, float x, float y, int path);
+ private static native void native_getTextPath(int native_object, String text,
+ int start, int end, float x, float y, int path);
private static native void nativeGetStringBounds(int nativePaint,
String text, int start, int end, Rect bounds);
private static native void nativeGetCharArrayBounds(int nativePaint,
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 157c7d1470cd..f6b5ffc9ad7c 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -375,9 +375,9 @@ public class Path {
*/
public enum Direction {
/** clockwise */
- CW (1), // must match enum in SkPath.h
+ CW (0), // must match enum in SkPath.h
/** counter-clockwise */
- CCW (2); // must match enum in SkPath.h
+ CCW (1); // must match enum in SkPath.h
Direction(int ni) {
nativeInt = ni;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c68c9f7dd9fd..4487a3c47328 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -225,4 +225,16 @@ public class Typeface {
private static native int nativeGetStyle(int native_instance);
private static native int nativeCreateFromAsset(AssetManager mgr, String path);
private static native int nativeCreateFromFile(String path);
+
+ /**
+ * Set the global gamma coefficients for black and white text. This call is
+ * usually a no-op in shipping products, and only exists for testing during
+ * development.
+ *
+ * @param blackGamma gamma coefficient for black text
+ * @param whiteGamma gamma coefficient for white text
+ *
+ * @hide - this is just for calibrating devices, not for normal apps
+ */
+ public static native void setGammaForText(float blackGamma, float whiteGamma);
}
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 50d888fa242b..c3ddd32e39b6 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -519,6 +519,8 @@ public class RenderScript {
native void rsnScriptForEach(int con, int id, int slot, int ain, int aout);
native void rsnScriptForEachClipped(int con, int id, int slot, int ain, int aout, byte[] params,
int xstart, int xend, int ystart, int yend, int zstart, int zend);
+ native void rsnScriptForEachClipped(int con, int id, int slot, int ain, int aout,
+ int xstart, int xend, int ystart, int yend, int zstart, int zend);
synchronized void nScriptForEach(int id, int slot, int ain, int aout, byte[] params) {
validate();
if (params == null) {
@@ -531,7 +533,11 @@ public class RenderScript {
synchronized void nScriptForEachClipped(int id, int slot, int ain, int aout, byte[] params,
int xstart, int xend, int ystart, int yend, int zstart, int zend) {
validate();
- rsnScriptForEachClipped(mContext, id, slot, ain, aout, params, xstart, xend, ystart, yend, zstart, zend);
+ if (params == null) {
+ rsnScriptForEachClipped(mContext, id, slot, ain, aout, xstart, xend, ystart, yend, zstart, zend);
+ } else {
+ rsnScriptForEachClipped(mContext, id, slot, ain, aout, params, xstart, xend, ystart, yend, zstart, zend);
+ }
}
native void rsnScriptInvokeV(int con, int id, int slot, byte[] params);
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
index b53ba0d7e80a..b4055882ab2a 100644
--- a/graphics/java/android/renderscript/Script.java
+++ b/graphics/java/android/renderscript/Script.java
@@ -171,6 +171,11 @@ public class Script extends BaseObj {
throw new RSIllegalArgumentException(
"At least one of ain or aout is required to be non-null.");
}
+
+ if (sc == null) {
+ forEach(slot, ain, aout, v);
+ return;
+ }
int in_id = 0;
if (ain != null) {
in_id = ain.getID(mRS);
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 9a8a6e85f5ba..88306856cada 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -1055,10 +1055,30 @@ nScriptForEachV(JNIEnv *_env, jobject _this, RsContext con,
static void
nScriptForEachClipped(JNIEnv *_env, jobject _this, RsContext con,
jint script, jint slot, jint ain, jint aout,
- jbyteArray params, jint xstart, jint xend,
+ jint xstart, jint xend,
jint ystart, jint yend, jint zstart, jint zend)
{
LOG_API("nScriptForEachClipped, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+ RsScriptCall sc;
+ sc.xStart = xstart;
+ sc.xEnd = xend;
+ sc.yStart = ystart;
+ sc.yEnd = yend;
+ sc.zStart = zstart;
+ sc.zEnd = zend;
+ sc.strategy = RS_FOR_EACH_STRATEGY_DONT_CARE;
+ sc.arrayStart = 0;
+ sc.arrayEnd = 0;
+ rsScriptForEach(con, (RsScript)script, slot, (RsAllocation)ain, (RsAllocation)aout, NULL, 0, &sc, sizeof(sc));
+}
+
+static void
+nScriptForEachClippedV(JNIEnv *_env, jobject _this, RsContext con,
+ jint script, jint slot, jint ain, jint aout,
+ jbyteArray params, jint xstart, jint xend,
+ jint ystart, jint yend, jint zstart, jint zend)
+{
+ LOG_API("nScriptForEachClipped, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
jint len = _env->GetArrayLength(params);
jbyte *ptr = _env->GetByteArrayElements(params, NULL);
RsScriptCall sc;
@@ -1536,7 +1556,8 @@ static JNINativeMethod methods[] = {
{"rsnScriptInvokeV", "(III[B)V", (void*)nScriptInvokeV },
{"rsnScriptForEach", "(IIIII)V", (void*)nScriptForEach },
{"rsnScriptForEach", "(IIIII[B)V", (void*)nScriptForEachV },
-{"rsnScriptForEachClipped", "(IIIII[BIIIIII)V", (void*)nScriptForEachClipped },
+{"rsnScriptForEachClipped", "(IIIIIIIIIII)V", (void*)nScriptForEachClipped },
+{"rsnScriptForEachClipped", "(IIIII[BIIIIII)V", (void*)nScriptForEachClippedV },
{"rsnScriptSetVarI", "(IIII)V", (void*)nScriptSetVarI },
{"rsnScriptSetVarJ", "(IIIJ)V", (void*)nScriptSetVarJ },
{"rsnScriptSetVarF", "(IIIF)V", (void*)nScriptSetVarF },
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 48f5bf3ec162..5b45d708726c 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -855,6 +855,7 @@ struct ResTable_config
DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH,
DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH,
DENSITY_XXHIGH = ACONFIGURATION_DENSITY_XXHIGH,
+ DENSITY_XXXHIGH = ACONFIGURATION_DENSITY_XXXHIGH,
DENSITY_NONE = ACONFIGURATION_DENSITY_NONE
};
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 31c38d58febf..d7119fff170f 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -336,7 +336,12 @@ public final class KeyChain {
KeyChainConnection keyChainConnection = bind(context);
try {
IKeyChainService keyChainService = keyChainConnection.getService();
- byte[] certificateBytes = keyChainService.getCertificate(alias);
+
+ final byte[] certificateBytes = keyChainService.getCertificate(alias);
+ if (certificateBytes == null) {
+ return null;
+ }
+
TrustedCertificateStore store = new TrustedCertificateStore();
List<X509Certificate> chain = store
.getCertificateChain(toCertificate(certificateBytes));
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index ceaff37f3c59..9dd2b0d455af 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -85,7 +85,7 @@ public class KeyStore {
public boolean put(String key, byte[] value) {
try {
- return mBinder.insert(key, value) == NO_ERROR;
+ return mBinder.insert(key, value, -1) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
@@ -94,7 +94,7 @@ public class KeyStore {
public boolean delete(String key) {
try {
- return mBinder.del(key) == NO_ERROR;
+ return mBinder.del(key, -1) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
@@ -103,7 +103,7 @@ public class KeyStore {
public boolean contains(String key) {
try {
- return mBinder.exist(key) == NO_ERROR;
+ return mBinder.exist(key, -1) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
@@ -112,7 +112,7 @@ public class KeyStore {
public String[] saw(String prefix) {
try {
- return mBinder.saw(prefix);
+ return mBinder.saw(prefix, -1);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
@@ -167,7 +167,7 @@ public class KeyStore {
public boolean generate(String key) {
try {
- return mBinder.generate(key) == NO_ERROR;
+ return mBinder.generate(key, -1) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
@@ -176,7 +176,7 @@ public class KeyStore {
public boolean importKey(String keyName, byte[] key) {
try {
- return mBinder.import_key(keyName, key) == NO_ERROR;
+ return mBinder.import_key(keyName, key, -1) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
@@ -194,7 +194,7 @@ public class KeyStore {
public boolean delKey(String key) {
try {
- return mBinder.del_key(key) == NO_ERROR;
+ return mBinder.del_key(key, -1) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 2111a56578c2..6bc7aefce913 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -28,6 +28,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
PathTessellator.cpp \
Program.cpp \
ProgramCache.cpp \
+ RenderBufferCache.cpp \
ResourceCache.cpp \
ShapeCache.cpp \
SkiaColorFilter.cpp \
@@ -37,19 +38,23 @@ ifeq ($(USE_OPENGL_RENDERER),true)
TextureCache.cpp \
TextDropShadowCache.cpp
+ intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
+
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/../../include/utils \
external/skia/include/core \
external/skia/include/effects \
external/skia/include/images \
- external/skia/src/core \
external/skia/src/ports \
- external/skia/include/utils
+ external/skia/include/utils \
+ $(intermediates) \
+ frameworks/rs/cpp \
+ frameworks/rs
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
- LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui
+ LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui libRS libRScpp
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
@@ -63,5 +68,5 @@ ifeq ($(USE_OPENGL_RENDERER),true)
include $(BUILD_SHARED_LIBRARY)
- include $(call all-makefiles-under,$(LOCAL_PATH))
+ include $(call all-makefiles-under,$(LOCAL_PATH))
endif
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 492bb7d171f5..74201d1fda58 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -186,6 +186,8 @@ void Caches::dumpMemoryUsage(String8 &log) {
textureCache.getSize(), textureCache.getMaxSize());
log.appendFormat(" LayerCache %8d / %8d\n",
layerCache.getSize(), layerCache.getMaxSize());
+ log.appendFormat(" RenderBufferCache %8d / %8d\n",
+ renderBufferCache.getSize(), renderBufferCache.getMaxSize());
log.appendFormat(" GradientCache %8d / %8d\n",
gradientCache.getSize(), gradientCache.getMaxSize());
log.appendFormat(" PathCache %8d / %8d\n",
@@ -215,6 +217,7 @@ void Caches::dumpMemoryUsage(String8 &log) {
uint32_t total = 0;
total += textureCache.getSize();
total += layerCache.getSize();
+ total += renderBufferCache.getSize();
total += gradientCache.getSize();
total += pathCache.getSize();
total += dropShadowCache.getSize();
@@ -298,6 +301,7 @@ void Caches::flush(FlushMode mode) {
// fall through
case kFlushMode_Layers:
layerCache.clear();
+ renderBufferCache.clear();
break;
}
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 0fa54fb9f2f2..d70c0e3fe77a 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -29,6 +29,7 @@
#include "GammaFontRenderer.h"
#include "TextureCache.h"
#include "LayerCache.h"
+#include "RenderBufferCache.h"
#include "GradientCache.h"
#include "PatchCache.h"
#include "ProgramCache.h"
@@ -249,6 +250,7 @@ public:
TextureCache textureCache;
LayerCache layerCache;
+ RenderBufferCache renderBufferCache;
GradientCache gradientCache;
ProgramCache programCache;
PathCache pathCache;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 5f8baacb710c..20eb5e123e37 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -44,6 +44,9 @@
// Turn on to display info about layers
#define DEBUG_LAYERS 0
+// Turn on to display info about render buffers
+#define DEBUG_RENDER_BUFFERS 0
+
// Turn on to make stencil operations easier to debug
#define DEBUG_STENCIL 0
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index f3bd188ea191..b25288b1f059 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -18,8 +18,7 @@
#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
#include <SkChunkAlloc.h>
-#include <SkReader32.h>
-#include <SkWriter32.h>
+#include <SkFlattenable.h>
#include <SkMatrix.h>
#include <SkCamera.h>
#include <SkPaint.h>
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index d8297daeb636..5d5e6a58001e 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -16,13 +16,15 @@
#define LOG_TAG "OpenGLRenderer"
-#include <SkGlyph.h>
#include <SkUtils.h>
#include <cutils/properties.h>
#include <utils/Log.h>
+#include "RenderScript.h"
+
+#include "utils/Timing.h"
#include "Caches.h"
#include "Debug.h"
#include "FontRenderer.h"
@@ -31,6 +33,9 @@
namespace android {
namespace uirenderer {
+// blur inputs smaller than this constant will bypass renderscript
+#define RS_MIN_INPUT_CUTOFF 10000
+
///////////////////////////////////////////////////////////////////////////////
// FontRenderer
///////////////////////////////////////////////////////////////////////////////
@@ -544,18 +549,22 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
- uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
- for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
- dataBuffer[i] = 0;
+ // Align buffers for renderscript usage
+ if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
+ paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
}
+ int size = paddedWidth * paddedHeight;
+ uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
+ memset(dataBuffer, 0, size);
+
int penX = radius - bounds.left;
int penY = radius - bounds.bottom;
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
- blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
+ blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
DropShadow image;
image.width = paddedWidth;
@@ -752,18 +761,44 @@ void FontRenderer::verticalBlur(float* weights, int32_t radius,
}
}
+void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
+ if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
+ float *gaussian = new float[2 * radius + 1];
+ computeGaussianWeights(gaussian, radius);
-void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
- float *gaussian = new float[2 * radius + 1];
- computeGaussianWeights(gaussian, radius);
+ uint8_t* scratch = new uint8_t[width * height];
+
+ horizontalBlur(gaussian, radius, *image, scratch, width, height);
+ verticalBlur(gaussian, radius, scratch, *image, width, height);
+
+ delete[] gaussian;
+ delete[] scratch;
+ }
+
+ uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
+
+ if (mRs.get() == 0) {
+ mRs = new RSC::RS();
+ if (!mRs->init(true, true)) {
+ ALOGE("blur RS failed to init");
+ }
+
+ mRsElement = RSC::Element::A_8(mRs);
+ mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
+ }
- uint8_t* scratch = new uint8_t[width * height];
+ sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
+ sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
+ RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
+ sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
+ RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
- horizontalBlur(gaussian, radius, image, scratch, width, height);
- verticalBlur(gaussian, radius, scratch, image, width, height);
+ mRsScript->setRadius(radius);
+ mRsScript->blur(ain, aout);
- delete[] gaussian;
- delete[] scratch;
+ // replace the original image's pointer, avoiding a copy back to the original buffer
+ delete *image;
+ *image = outImage;
}
}; // namespace uirenderer
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 3964bca90646..d0c44ef5b982 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -31,6 +31,12 @@
#include "Matrix.h"
#include "Properties.h"
+namespace RSC {
+ class Element;
+ class RS;
+ class ScriptIntrinsicBlur;
+}
+
namespace android {
namespace uirenderer {
@@ -178,13 +184,19 @@ private:
Vector<uint32_t> mDrawCounts;
Vector<CacheTexture*> mDrawCacheTextures;
- /** We should consider multi-threading this code or using Renderscript **/
+ // RS constructs
+ sp<RSC::RS> mRs;
+ sp<const RSC::Element> mRsElement;
+ sp<RSC::ScriptIntrinsicBlur> mRsScript;
+
static void computeGaussianWeights(float* weights, int32_t radius);
static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
static void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
- static void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
+
+ // the input image handle may have its pointer replaced (to avoid copies)
+ void blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 9247b1da3df2..1899002004c4 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -101,14 +101,13 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
void Layer::removeFbo(bool flush) {
if (stencil) {
- // TODO: recycle & cache instead of simply deleting
GLuint previousFbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- delete stencil;
+ Caches::getInstance().renderBufferCache.put(stencil);
stencil = NULL;
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index ccf1da5539aa..664b2f84b6f6 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -23,7 +23,6 @@
#include <ui/Region.h>
-#include <SkPaint.h>
#include <SkXfermode.h>
#include "Rect.h"
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 427846459162..a0709afa010f 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -21,7 +21,6 @@
#include <utils/Log.h>
#include "Caches.h"
-#include "Debug.h"
#include "LayerCache.h"
#include "Properties.h"
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 7720b4232ae9..221bfe000de1 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -19,7 +19,6 @@
#include "Debug.h"
#include "Layer.h"
-#include "Properties.h"
#include "utils/SortedList.h"
namespace android {
@@ -54,7 +53,7 @@ public:
* size of the cache goes down.
*
* @param width The desired width of the layer
- * @param width The desired height of the layer
+ * @param height The desired height of the layer
*/
Layer* get(const uint32_t width, const uint32_t height);
@@ -91,6 +90,7 @@ public:
*/
void dump();
+private:
struct LayerEntry {
LayerEntry():
mLayer(NULL), mWidth(0), mHeight(0) {
@@ -115,12 +115,19 @@ public:
return compare(*this, other) != 0;
}
+ friend inline int strictly_order_type(const LayerEntry& lhs, const LayerEntry& rhs) {
+ return LayerEntry::compare(lhs, rhs) < 0;
+ }
+
+ friend inline int compare_type(const LayerEntry& lhs, const LayerEntry& rhs) {
+ return LayerEntry::compare(lhs, rhs);
+ }
+
Layer* mLayer;
uint32_t mWidth;
uint32_t mHeight;
}; // struct LayerEntry
-private:
void deleteLayer(Layer* layer);
SortedList<LayerEntry> mCache;
@@ -129,15 +136,6 @@ private:
uint32_t mMaxSize;
}; // class LayerCache
-inline int strictly_order_type(const LayerCache::LayerEntry& lhs,
- const LayerCache::LayerEntry& rhs) {
- return LayerCache::LayerEntry::compare(lhs, rhs) < 0;
-}
-
-inline int compare_type(const LayerCache::LayerEntry& lhs, const LayerCache::LayerEntry& rhs) {
- return LayerCache::LayerEntry::compare(lhs, rhs);
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 62f268dab291..be3cff1f4f06 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -80,7 +80,7 @@ static const Blender gBlends[] = {
{ SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
{ SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
{ SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR },
+ { SkXfermode::kMultiply_Mode, GL_ZERO, GL_SRC_COLOR },
{ SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
};
@@ -101,7 +101,7 @@ static const Blender gBlendsSwap[] = {
{ SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
{ SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
{ SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO },
+ { SkXfermode::kMultiply_Mode, GL_DST_COLOR, GL_ZERO },
{ SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
};
@@ -1273,11 +1273,8 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
// attach the new render buffer then turn tiling back on
endTiling();
- RenderBuffer* buffer = new RenderBuffer(
+ RenderBuffer* buffer = mCaches.renderBufferCache.get(
Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight());
- buffer->bind();
- buffer->allocate();
-
layer->setStencilRenderBuffer(buffer);
startTiling(layer->clipRect, layer->layer.getHeight());
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1e8765bae9a2..0c7524203826 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -34,9 +34,9 @@
// Textures used by layers must have dimensions multiples of this number
#define LAYER_SIZE 64
-// Defines the size in bits of the stencil buffer
+// Defines the size in bits of the stencil buffer for the framebuffer
// Note: Only 1 bit is required for clipping but more bits are required
-// to properly implement the winding fill rule when rasterizing paths
+// to properly implement overdraw debugging
#define STENCIL_BUFFER_SIZE 8
/**
@@ -85,6 +85,7 @@ enum DebugLevel {
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
+#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
#define PROPERTY_SHAPE_CACHE_SIZE "ro.hwui.shape_cache_size"
@@ -130,6 +131,7 @@ enum DebugLevel {
#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
+#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
#define DEFAULT_PATH_CACHE_SIZE 4.0f
#define DEFAULT_SHAPE_CACHE_SIZE 1.0f
#define DEFAULT_PATCH_CACHE_SIZE 512
diff --git a/libs/hwui/RenderBuffer.h b/libs/hwui/RenderBuffer.h
index 927f2659abd0..35a516a58022 100644
--- a/libs/hwui/RenderBuffer.h
+++ b/libs/hwui/RenderBuffer.h
@@ -39,7 +39,7 @@ struct RenderBuffer {
}
~RenderBuffer() {
- if (mName && mAllocated) {
+ if (mName) {
glDeleteRenderbuffers(1, &mName);
}
}
@@ -154,6 +154,29 @@ struct RenderBuffer {
return false;
}
+ /**
+ * Returns the name of the specified render buffer format.
+ */
+ static const char* formatName(GLenum format) {
+ switch (format) {
+ case GL_STENCIL_INDEX8:
+ return "STENCIL_8";
+ case GL_STENCIL_INDEX1_OES:
+ return "STENCIL_1";
+ case GL_STENCIL_INDEX4_OES:
+ return "STENCIL_4";
+ case GL_DEPTH_COMPONENT16:
+ return "DEPTH_16";
+ case GL_RGBA4:
+ return "RGBA_4444";
+ case GL_RGB565:
+ return "RGB_565";
+ case GL_RGB5_A1:
+ return "RGBA_5551";
+ }
+ return "Unknown";
+ }
+
private:
GLenum mFormat;
diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp
new file mode 100644
index 000000000000..830a13ab28a7
--- /dev/null
+++ b/libs/hwui/RenderBufferCache.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include "Debug.h"
+#include "Properties.h"
+#include "RenderBufferCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_RENDER_BUFFERS
+ #define RENDER_BUFFER_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+ #define RENDER_BUFFER_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+RenderBufferCache::RenderBufferCache(): mSize(0), mMaxSize(MB(DEFAULT_RENDER_BUFFER_CACHE_SIZE)) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_RENDER_BUFFER_CACHE_SIZE, property, NULL) > 0) {
+ INIT_LOGD(" Setting render buffer cache size to %sMB", property);
+ setMaxSize(MB(atof(property)));
+ } else {
+ INIT_LOGD(" Using default render buffer cache size of %.2fMB",
+ DEFAULT_RENDER_BUFFER_CACHE_SIZE);
+ }
+}
+
+RenderBufferCache::~RenderBufferCache() {
+ clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t RenderBufferCache::getSize() {
+ return mSize;
+}
+
+uint32_t RenderBufferCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void RenderBufferCache::setMaxSize(uint32_t maxSize) {
+ clear();
+ mMaxSize = maxSize;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+int RenderBufferCache::RenderBufferEntry::compare(
+ const RenderBufferCache::RenderBufferEntry& lhs,
+ const RenderBufferCache::RenderBufferEntry& rhs) {
+ int deltaInt = int(lhs.mWidth) - int(rhs.mWidth);
+ if (deltaInt != 0) return deltaInt;
+
+ deltaInt = int(lhs.mHeight) - int(rhs.mHeight);
+ if (deltaInt != 0) return deltaInt;
+
+ return int(lhs.mFormat) - int(rhs.mFormat);
+}
+
+void RenderBufferCache::deleteBuffer(RenderBuffer* buffer) {
+ if (buffer) {
+ RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d)",
+ RenderBuffer::formatName(buffer->getFormat()),
+ buffer->getWidth(), buffer->getHeight());
+
+ mSize -= buffer->getSize();
+ delete buffer;
+ }
+}
+
+void RenderBufferCache::clear() {
+ size_t count = mCache.size();
+ for (size_t i = 0; i < count; i++) {
+ deleteBuffer(mCache.itemAt(i).mBuffer);
+ }
+ mCache.clear();
+}
+
+RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const uint32_t height) {
+ RenderBuffer* buffer = NULL;
+
+ RenderBufferEntry entry(format, width, height);
+ ssize_t index = mCache.indexOf(entry);
+
+ if (index >= 0) {
+ entry = mCache.itemAt(index);
+ mCache.removeAt(index);
+
+ buffer = entry.mBuffer;
+ mSize -= buffer->getSize();
+
+ RENDER_BUFFER_LOGD("Found %s render buffer (%dx%d)",
+ RenderBuffer::formatName(format), width, height);
+ } else {
+ buffer = new RenderBuffer(format, width, height);
+
+ RENDER_BUFFER_LOGD("Created new %s render buffer (%dx%d)",
+ RenderBuffer::formatName(format), width, height);
+ }
+
+ buffer->bind();
+ buffer->allocate();
+
+ return buffer;
+}
+
+bool RenderBufferCache::put(RenderBuffer* buffer) {
+ if (!buffer) return false;
+
+ const uint32_t size = buffer->getSize();
+ if (size < mMaxSize) {
+ while (mSize + size > mMaxSize) {
+ size_t position = 0;
+
+ RenderBuffer* victim = mCache.itemAt(position).mBuffer;
+ deleteBuffer(victim);
+ mCache.removeAt(position);
+ }
+
+ RenderBufferEntry entry(buffer);
+
+ mCache.add(entry);
+ mSize += size;
+
+ RENDER_BUFFER_LOGD("Added %s render buffer (%dx%d)",
+ RenderBuffer::formatName(buffer->getFormat()),
+ buffer->getWidth(), buffer->getHeight());
+
+ return true;
+ }
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/RenderBufferCache.h b/libs/hwui/RenderBufferCache.h
new file mode 100644
index 000000000000..af8060fe2d21
--- /dev/null
+++ b/libs/hwui/RenderBufferCache.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_RENDER_BUFFER_CACHE_H
+#define ANDROID_HWUI_RENDER_BUFFER_CACHE_H
+
+#include <GLES2/gl2.h>
+
+#include "RenderBuffer.h"
+#include "utils/SortedList.h"
+
+namespace android {
+namespace uirenderer {
+
+class RenderBufferCache {
+public:
+ RenderBufferCache();
+ ~RenderBufferCache();
+
+ /**
+ * Returns a buffer with the exact specified dimensions. If no suitable
+ * buffer can be found, a new one is created and returned. If creating a
+ * new buffer fails, NULL is returned.
+ *
+ * When a buffer is obtained from the cache, it is removed and the total
+ * size of the cache goes down.
+ *
+ * The returned buffer is always allocated and bound
+ * (see RenderBuffer::isAllocated()).
+ *
+ * @param format The desired render buffer format
+ * @param width The desired width of the buffer
+ * @param height The desired height of the buffer
+ */
+ RenderBuffer* get(GLenum format, const uint32_t width, const uint32_t height);
+
+ /**
+ * Adds the buffer to the cache. The buffer will not be added if there is
+ * not enough space available. Adding a buffer can cause other buffer to
+ * be removed from the cache.
+ *
+ * @param buffer The render buffer to add to the cache
+ *
+ * @return True if the buffer was added, false otherwise.
+ */
+ bool put(RenderBuffer* buffer);
+ /**
+ * Clears the cache. This causes all layers to be deleted.
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+private:
+ struct RenderBufferEntry {
+ RenderBufferEntry():
+ mBuffer(NULL), mWidth(0), mHeight(0) {
+ }
+
+ RenderBufferEntry(GLenum format, const uint32_t width, const uint32_t height):
+ mBuffer(NULL), mFormat(format), mWidth(width), mHeight(height) {
+ }
+
+ RenderBufferEntry(RenderBuffer* buffer):
+ mBuffer(buffer), mFormat(buffer->getFormat()),
+ mWidth(buffer->getWidth()), mHeight(buffer->getHeight()) {
+ }
+
+ static int compare(const RenderBufferEntry& lhs, const RenderBufferEntry& rhs);
+
+ bool operator==(const RenderBufferEntry& other) const {
+ return compare(*this, other) == 0;
+ }
+
+ bool operator!=(const RenderBufferEntry& other) const {
+ return compare(*this, other) != 0;
+ }
+
+ friend inline int strictly_order_type(const RenderBufferEntry& lhs,
+ const RenderBufferEntry& rhs) {
+ return RenderBufferEntry::compare(lhs, rhs) < 0;
+ }
+
+ friend inline int compare_type(const RenderBufferEntry& lhs,
+ const RenderBufferEntry& rhs) {
+ return RenderBufferEntry::compare(lhs, rhs);
+ }
+
+ RenderBuffer* mBuffer;
+ GLenum mFormat;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ }; // struct RenderBufferEntry
+
+ void deleteBuffer(RenderBuffer* buffer);
+
+ SortedList<RenderBufferEntry> mCache;
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+}; // class RenderBufferCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_RENDER_BUFFER_CACHE_H
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 9c7a5ab3d077..db7bd489b404 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -222,7 +222,7 @@ ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32
}
// Cleanup shadow
- delete[] shadow.image;
+ delete shadow.image;
}
return texture;
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 24b0523cc7e1..f65359217768 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <SkGlyph.h>
#include <utils/Log.h>
#include "Debug.h"
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 1a75ea8d272f..8c5a8ff90b22 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -20,7 +20,6 @@
#include <utils/JenkinsHash.h>
-#include <SkGlyph.h>
#include <SkUtils.h>
#include "Debug.h"
diff --git a/libs/hwui/utils/Timing.h b/libs/hwui/utils/Timing.h
new file mode 100644
index 000000000000..eced987a0084
--- /dev/null
+++ b/libs/hwui/utils/Timing.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_TIMING_H
+#define ANDROID_HWUI_TIMING_H
+
+#include <sys/time.h>
+
+#define TIME_METHOD() MethodTimer __method_timer(__func__)
+class MethodTimer {
+public:
+ MethodTimer(const char* name)
+ : mMethodName(name) {
+ gettimeofday(&mStart, NULL);
+ }
+
+ ~MethodTimer() {
+ struct timeval stop;
+ gettimeofday(&stop, NULL);
+ long long elapsed = (stop.tv_sec * 1000000) - (mStart.tv_sec * 1000000)
+ + (stop.tv_usec - mStart.tv_usec);
+ ALOGD("%s took %.2fms", mMethodName, elapsed / 1000.0);
+ }
+private:
+ const char* mMethodName;
+ struct timeval mStart;
+};
+
+#endif
diff --git a/media/tests/omxjpegdecoder/SkOmxPixelRef.h b/media/tests/omxjpegdecoder/SkOmxPixelRef.h
index 374604c39dae..afedcbdf53cd 100644
--- a/media/tests/omxjpegdecoder/SkOmxPixelRef.h
+++ b/media/tests/omxjpegdecoder/SkOmxPixelRef.h
@@ -33,7 +33,6 @@ public:
//! Return the allocation size for the pixels
size_t getSize() const { return mSize; }
- SK_DECLARE_UNFLATTENABLE_OBJECT()
protected:
// overrides from SkPixelRef
virtual void* onLockPixels(SkColorTable**);
diff --git a/nfc-extras/java/com/android/nfc_extras/EeAlreadyOpenException.java b/nfc-extras/java/com/android/nfc_extras/EeAlreadyOpenException.java
new file mode 100644
index 000000000000..9fde0a3a366b
--- /dev/null
+++ b/nfc-extras/java/com/android/nfc_extras/EeAlreadyOpenException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc_extras;
+
+public class EeAlreadyOpenException extends EeIOException {
+ public EeAlreadyOpenException() {
+ super();
+ }
+
+ public EeAlreadyOpenException(String message) {
+ super(message);
+ }
+}
diff --git a/nfc-extras/java/com/android/nfc_extras/EeExternalFieldException.java b/nfc-extras/java/com/android/nfc_extras/EeExternalFieldException.java
new file mode 100644
index 000000000000..4326fabd7d51
--- /dev/null
+++ b/nfc-extras/java/com/android/nfc_extras/EeExternalFieldException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc_extras;
+
+public class EeExternalFieldException extends EeIOException {
+ public EeExternalFieldException() {
+ super();
+ }
+
+ public EeExternalFieldException(String message) {
+ super(message);
+ }
+}
diff --git a/nfc-extras/java/com/android/nfc_extras/EeIOException.java b/nfc-extras/java/com/android/nfc_extras/EeIOException.java
new file mode 100644
index 000000000000..66a3b25a0f75
--- /dev/null
+++ b/nfc-extras/java/com/android/nfc_extras/EeIOException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc_extras;
+
+import java.io.IOException;
+
+public class EeIOException extends IOException {
+ public EeIOException() {
+ super();
+ }
+
+ public EeIOException(String message) {
+ super(message);
+ }
+}
diff --git a/nfc-extras/java/com/android/nfc_extras/EeInitializationException.java b/nfc-extras/java/com/android/nfc_extras/EeInitializationException.java
new file mode 100644
index 000000000000..00e5264054d4
--- /dev/null
+++ b/nfc-extras/java/com/android/nfc_extras/EeInitializationException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc_extras;
+
+public class EeInitializationException extends EeIOException {
+ public EeInitializationException() {
+ super();
+ }
+
+ public EeInitializationException(String message) {
+ super(message);
+ }
+}
diff --git a/nfc-extras/java/com/android/nfc_extras/EeListenModeException.java b/nfc-extras/java/com/android/nfc_extras/EeListenModeException.java
new file mode 100644
index 000000000000..07f19802c41b
--- /dev/null
+++ b/nfc-extras/java/com/android/nfc_extras/EeListenModeException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc_extras;
+
+public class EeListenModeException extends EeIOException {
+ public EeListenModeException() {
+ super();
+ }
+
+ public EeListenModeException(String message) {
+ super(message);
+ }
+}
diff --git a/nfc-extras/java/com/android/nfc_extras/EeNfcDisabledException.java b/nfc-extras/java/com/android/nfc_extras/EeNfcDisabledException.java
new file mode 100644
index 000000000000..091db28cb1b8
--- /dev/null
+++ b/nfc-extras/java/com/android/nfc_extras/EeNfcDisabledException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc_extras;
+
+public class EeNfcDisabledException extends EeIOException {
+ public EeNfcDisabledException() {
+ super();
+ }
+
+ public EeNfcDisabledException(String message) {
+ super(message);
+ }
+}
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
index f47327aa2e79..fd7cb33b2db1 100644
--- a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
@@ -28,6 +28,15 @@ public class NfcExecutionEnvironment {
private final NfcAdapterExtras mExtras;
private final Binder mToken;
+ // Exception types that can be thrown by NfcService
+ // 1:1 mapped to EE_ERROR_ types in NfcService
+ private static final int EE_ERROR_IO = -1;
+ private static final int EE_ERROR_ALREADY_OPEN = -2;
+ private static final int EE_ERROR_INIT = -3;
+ private static final int EE_ERROR_LISTEN_MODE = -4;
+ private static final int EE_ERROR_EXT_FIELD = -5;
+ private static final int EE_ERROR_NFC_DISABLED = -6;
+
/**
* Broadcast Action: An ISO-DEP AID was selected.
*
@@ -118,24 +127,49 @@ public class NfcExecutionEnvironment {
/**
* Open the NFC Execution Environment on its contact interface.
*
- * <p>Only one process may open the secure element at a time. If it is
- * already open, an {@link IOException} is thrown.
- *
+ * <p>Opening a channel to the the secure element may fail
+ * for a number of reasons:
+ * <ul>
+ * <li>NFC must be enabled for the connection to the SE to be opened.
+ * If it is disabled at the time of this call, an {@link EeNfcDisabledException}
+ * is thrown.
+ *
+ * <li>Only one process may open the secure element at a time. Additionally,
+ * this method is not reentrant. If the secure element is already opened,
+ * either by this process or by a different process, an {@link EeAlreadyOpenException}
+ * is thrown.
+ *
+ * <li>If the connection to the secure element could not be initialized,
+ * an {@link EeInitializationException} is thrown.
+ *
+ * <li>If the secure element or the NFC controller is activated in listen
+ * mode - that is, it is talking over the contactless interface - an
+ * {@link EeListenModeException} is thrown.
+ *
+ * <li>If the NFC controller is in a field powered by a remote device,
+ * such as a payment terminal, an {@link EeExternalFieldException} is
+ * thrown.
+ * </ul>
* <p>All other NFC functionality is disabled while the NFC-EE is open
* on its contact interface, so make sure to call {@link #close} once complete.
*
* <p class="note">
* Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
*
- * @throws IOException if the NFC-EE is already open, or some other error occurs
+ * @throws EeAlreadyOpenException if the NFC-EE is already open
+ * @throws EeNfcDisabledException if NFC is disabled
+ * @throws EeInitializationException if the Secure Element could not be initialized
+ * @throws EeListenModeException if the NFCC or Secure Element is activated in listen mode
+ * @throws EeExternalFieldException if the NFCC is in the presence of a remote-powered field
+ * @throws EeIoException if an unknown error occurs
*/
- public void open() throws IOException {
+ public void open() throws EeIOException {
try {
Bundle b = mExtras.getService().open(mExtras.mPackageName, mToken);
throwBundle(b);
} catch (RemoteException e) {
mExtras.attemptDeadServiceRecovery(e);
- throw new IOException("NFC Service was dead, try again");
+ throw new EeIOException("NFC Service was dead, try again");
}
}
@@ -176,9 +210,20 @@ public class NfcExecutionEnvironment {
return b.getByteArray("out");
}
- private static void throwBundle(Bundle b) throws IOException {
- if (b.getInt("e") == -1) {
- throw new IOException(b.getString("m"));
+ private static void throwBundle(Bundle b) throws EeIOException {
+ switch (b.getInt("e")) {
+ case EE_ERROR_NFC_DISABLED:
+ throw new EeNfcDisabledException(b.getString("m"));
+ case EE_ERROR_IO:
+ throw new EeIOException(b.getString("m"));
+ case EE_ERROR_INIT:
+ throw new EeInitializationException(b.getString("m"));
+ case EE_ERROR_EXT_FIELD:
+ throw new EeExternalFieldException(b.getString("m"));
+ case EE_ERROR_LISTEN_MODE:
+ throw new EeListenModeException(b.getString("m"));
+ case EE_ERROR_ALREADY_OPEN:
+ throw new EeAlreadyOpenException(b.getString("m"));
}
}
}
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index a58c9a015893..a03ca3314c0a 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -196,7 +196,7 @@
<string name="quick_settings_wifi_label" msgid="9135344704899546041">"Wi-Fi"</string>
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ej ansluten"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Inget nätverk"</string>
- <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi av"</string>
+ <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi är inaktiverat"</string>
<string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Trådlös skärm"</string>
<string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådlös skärm"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ljusstyrka"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 6a12eb1916aa..8979fc2f9356 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -21,15 +21,11 @@ import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
-import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
-import java.io.IOException;
-import java.lang.IllegalStateException;
import java.lang.Thread;
import java.util.LinkedList;
@@ -90,14 +86,29 @@ public class NotificationPlayer implements OnCompletionListener {
player.prepare();
if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
&& (mCmd.uri.getEncodedPath().length() > 0)) {
- if (mCmd.looping) {
- audioManager.requestAudioFocus(null, mCmd.stream,
- AudioManager.AUDIOFOCUS_GAIN);
- } else {
- audioManager.requestAudioFocus(null, mCmd.stream,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
+ if (!audioManager.isMusicActiveRemotely()) {
+ synchronized(mQueueAudioFocusLock) {
+ if (mAudioManagerWithAudioFocus == null) {
+ if (mDebug) Log.d(mTag, "requesting AudioFocus");
+ if (mCmd.looping) {
+ audioManager.requestAudioFocus(null, mCmd.stream,
+ AudioManager.AUDIOFOCUS_GAIN);
+ } else {
+ audioManager.requestAudioFocus(null, mCmd.stream,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
+ }
+ mAudioManagerWithAudioFocus = audioManager;
+ } else {
+ if (mDebug) Log.d(mTag, "AudioFocus was previously requested");
+ }
+ }
}
}
+ // FIXME Having to start a new thread so we can receive completion callbacks
+ // is wrong, as we kill this thread whenever a new sound is to be played. This
+ // can lead to AudioFocus being released too early, before the second sound is
+ // done playing. This class should be modified to use a single thread, on which
+ // command are issued, and on which it receives the completion callbacks.
player.setOnCompletionListener(NotificationPlayer.this);
player.start();
if (mPlayer != null) {
@@ -108,7 +119,6 @@ public class NotificationPlayer implements OnCompletionListener {
catch (Exception e) {
Log.w(mTag, "error loading sound for " + mCmd.uri, e);
}
- mAudioManager = audioManager;
this.notify();
}
Looper.loop();
@@ -179,8 +189,12 @@ public class NotificationPlayer implements OnCompletionListener {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
- mAudioManager.abandonAudioFocus(null);
- mAudioManager = null;
+ synchronized(mQueueAudioFocusLock) {
+ if (mAudioManagerWithAudioFocus != null) {
+ mAudioManagerWithAudioFocus.abandonAudioFocus(null);
+ mAudioManagerWithAudioFocus = null;
+ }
+ }
if((mLooper != null)
&& (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
mLooper.quit();
@@ -207,8 +221,14 @@ public class NotificationPlayer implements OnCompletionListener {
}
public void onCompletion(MediaPlayer mp) {
- if (mAudioManager != null) {
- mAudioManager.abandonAudioFocus(null);
+ synchronized(mQueueAudioFocusLock) {
+ if (mAudioManagerWithAudioFocus != null) {
+ if (mDebug) Log.d(mTag, "onCompletion() abandonning AudioFocus");
+ mAudioManagerWithAudioFocus.abandonAudioFocus(null);
+ mAudioManagerWithAudioFocus = null;
+ } else {
+ if (mDebug) Log.d(mTag, "onCompletion() no need to abandon AudioFocus");
+ }
}
// if there are no more sounds to play, end the Looper to listen for media completion
synchronized (mCmdQueue) {
@@ -229,7 +249,8 @@ public class NotificationPlayer implements OnCompletionListener {
private final Object mCompletionHandlingLock = new Object();
private MediaPlayer mPlayer;
private PowerManager.WakeLock mWakeLock;
- private AudioManager mAudioManager;
+ private final Object mQueueAudioFocusLock = new Object();
+ private AudioManager mAudioManagerWithAudioFocus; // synchronized on mQueueAudioFocusLock
// The current state according to the caller. Reality lags behind
// because of the asynchronous nature of this class.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 5d9c7bcc0417..7b80abcadb92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
@@ -594,4 +596,20 @@ public class PanelView extends FrameLayout {
if (DEBUG) LOG("skipping expansion: is expanded");
}
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(String.format("[PanelView(%s): expandedHeight=%f fullHeight=%f closing=%s"
+ + " tracking=%s rubberbanding=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s"
+ + "]",
+ this.getClass().getSimpleName(),
+ getExpandedHeight(),
+ getFullHeight(),
+ mClosing?"T":"f",
+ mTracking?"T":"f",
+ mRubberbanding?"T":"f",
+ mJustPeeked?"T":"f",
+ mPeekAnimator, ((mPeekAnimator!=null && mPeekAnimator.isStarted())?" (started)":""),
+ mTimeAnimator, ((mTimeAnimator!=null && mTimeAnimator.isStarted())?" (started)":"")
+ ));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 01596dcdda1e..9b1c1db0103a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -246,16 +246,6 @@ public class PhoneStatusBar extends BaseStatusBar {
private ViewGroup mCling;
private boolean mSuppressStatusBarDrags; // while a cling is up, briefly deaden the bar to give things time to settle
- boolean mAnimating;
- boolean mClosing; // only valid when mAnimating; indicates the initial acceleration
- float mAnimY;
- float mAnimVel;
- float mAnimAccel;
- long mAnimLastTimeNanos;
- boolean mAnimatingReveal = false;
- int mViewDelta;
- float mFlingVelocity;
- int mFlingY;
int[] mAbsPos = new int[2];
Runnable mPostCollapseCleanup = null;
@@ -352,7 +342,7 @@ public class PhoneStatusBar extends BaseStatusBar {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
- if (mExpandedVisible && !mAnimating) {
+ if (mExpandedVisible) {
animateCollapsePanels();
}
}
@@ -963,7 +953,7 @@ public class PhoneStatusBar extends BaseStatusBar {
mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
}
- if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 && !mAnimating) {
+ if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0) {
animateCollapsePanels();
}
}
@@ -1411,10 +1401,6 @@ public class PhoneStatusBar extends BaseStatusBar {
if (SPEW) {
Slog.d(TAG, "animateCollapse():"
+ " mExpandedVisible=" + mExpandedVisible
- + " mAnimating=" + mAnimating
- + " mAnimatingReveal=" + mAnimatingReveal
- + " mAnimY=" + mAnimY
- + " mAnimVel=" + mAnimVel
+ " flags=" + flags);
}
@@ -2051,16 +2037,6 @@ public class PhoneStatusBar extends BaseStatusBar {
+ ", mTrackingPosition=" + mTrackingPosition);
pw.println(" mTicking=" + mTicking);
pw.println(" mTracking=" + mTracking);
- pw.println(" mNotificationPanel=" +
- ((mNotificationPanel == null)
- ? "null"
- : (mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""))));
- pw.println(" mAnimating=" + mAnimating
- + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
- + ", mAnimAccel=" + mAnimAccel);
- pw.println(" mAnimLastTimeNanos=" + mAnimLastTimeNanos);
- pw.println(" mAnimatingReveal=" + mAnimatingReveal
- + " mViewDelta=" + mViewDelta);
pw.println(" mDisplayMetrics=" + mDisplayMetrics);
pw.println(" mPile: " + viewInfo(mPile));
pw.println(" mTickerView: " + viewInfo(mTickerView));
@@ -2075,6 +2051,20 @@ public class PhoneStatusBar extends BaseStatusBar {
mNavigationBarView.dump(fd, pw, args);
}
+ pw.println(" Panels: ");
+ if (mNotificationPanel != null) {
+ pw.println(" mNotificationPanel=" +
+ mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
+ pw.print (" ");
+ mNotificationPanel.dump(fd, pw, args);
+ }
+ if (mSettingsPanel != null) {
+ pw.println(" mSettingsPanel=" +
+ mSettingsPanel + " params=" + mSettingsPanel.getLayoutParams().debug(""));
+ pw.print (" ");
+ mSettingsPanel.dump(fd, pw, args);
+ }
+
if (DUMPTRUCK) {
synchronized (mNotificationData) {
int N = mNotificationData.size();
diff --git a/packages/VpnDialogs/res/layout/confirm.xml b/packages/VpnDialogs/res/layout/confirm.xml
index fef00c2203b4..ee7f4b84e400 100644
--- a/packages/VpnDialogs/res/layout/confirm.xml
+++ b/packages/VpnDialogs/res/layout/confirm.xml
@@ -52,6 +52,7 @@
android:layout_height="wrap_content"
android:text="@string/accept"
android:textSize="20sp"
+ android:filterTouchesWhenObscured="true"
android:checked="false"/>
</LinearLayout>
</ScrollView>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index 7a1e66c9cde4..6faf4e09fcbb 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -78,6 +78,7 @@ public class ConfirmDialog extends AlertActivity implements
getWindow().setCloseOnTouchOutside(false);
mButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mButton.setEnabled(false);
+ mButton.setFilterTouchesWhenObscured(true);
} catch (Exception e) {
Log.e(TAG, "onResume", e);
finish();
diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp
index 8163ea0aa1fc..1f3d2cf2fdf0 100644
--- a/services/input/SpriteController.cpp
+++ b/services/input/SpriteController.cpp
@@ -208,7 +208,8 @@ void SpriteController::doUpdateSprites() {
surfaceInfo.w, surfaceInfo.h, bpr);
surfaceBitmap.setPixels(surfaceInfo.bits);
- SkCanvas surfaceCanvas(surfaceBitmap);
+ SkCanvas surfaceCanvas;
+ surfaceCanvas.setBitmapDevice(surfaceBitmap);
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 88ea1f1ba99a..dfde692e77a6 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -133,7 +133,7 @@ import javax.crypto.spec.SecretKeySpec;
class BackupManagerService extends IBackupManager.Stub {
private static final String TAG = "BackupManagerService";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final boolean MORE_DEBUG = false;
// Name and current contents version of the full-backup manifest file
diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java
index 235c6620c55c..3dade3733bb6 100644
--- a/services/java/com/android/server/BootReceiver.java
+++ b/services/java/com/android/server/BootReceiver.java
@@ -126,6 +126,7 @@ public class BootReceiver extends BroadcastReceiver {
-LOG_SIZE, "APANIC_CONSOLE");
addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads",
-LOG_SIZE, "APANIC_THREADS");
+ addAuditErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_AUDIT");
} else {
if (db != null) db.addText("SYSTEM_RESTART", headers);
}
@@ -174,4 +175,32 @@ public class BootReceiver extends BroadcastReceiver {
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"));
}
+
+ private static void addAuditErrorsToDropBox(DropBoxManager db, SharedPreferences prefs,
+ String headers, int maxSize, String tag) throws IOException {
+ if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
+ Slog.i(TAG, "Copying audit failures to DropBox");
+
+ File file = new File("/proc/last_kmsg");
+ long fileTime = file.lastModified();
+ if (fileTime <= 0) return; // File does not exist
+
+ if (prefs != null) {
+ long lastTime = prefs.getLong(tag, 0);
+ if (lastTime == fileTime) return; // Already logged this particular file
+ // TODO: move all these SharedPreferences Editor commits
+ // outside this function to the end of logBootEvents
+ prefs.edit().putLong(tag, fileTime).apply();
+ }
+
+ String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
+ StringBuilder sb = new StringBuilder();
+ for (String line : log.split("\n")) {
+ if (line.contains("audit")) {
+ sb.append(line + "\n");
+ }
+ }
+ Slog.i(TAG, "Copied " + sb.toString().length() + " worth of audits to DropBox");
+ db.addText(tag, headers + sb.toString());
+ }
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 2b0d8243a7ee..25ed27a2542b 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -1120,19 +1120,31 @@ public class NetworkManagementService extends INetworkManagementService.Stub
@Override
public NetworkStats getNetworkStatsSummaryDev() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- return mStatsFactory.readNetworkStatsSummaryDev();
+ try {
+ return mStatsFactory.readNetworkStatsSummaryDev();
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
}
@Override
public NetworkStats getNetworkStatsSummaryXt() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- return mStatsFactory.readNetworkStatsSummaryXt();
+ try {
+ return mStatsFactory.readNetworkStatsSummaryXt();
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
}
@Override
public NetworkStats getNetworkStatsDetail() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- return mStatsFactory.readNetworkStatsDetail(UID_ALL);
+ try {
+ return mStatsFactory.readNetworkStatsDetail(UID_ALL);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
}
@Override
@@ -1289,7 +1301,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub
@Override
public NetworkStats getNetworkStatsUidDetail(int uid) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- return mStatsFactory.readNetworkStatsDetail(uid);
+ try {
+ return mStatsFactory.readNetworkStatsDetail(uid);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
}
@Override
@@ -1471,9 +1487,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public void setDnsInterfaceForPid(String iface, int pid) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- String cmd = "resolver setifaceforpid " + iface + " " + pid;
-
- mConnector.execute(cmd);
+ mConnector.execute("resolver", "setifaceforpid", iface, pid);
} catch (NativeDaemonConnectorException e) {
throw new IllegalStateException(
"Error communicating with native deamon to set interface for pid" + iface, e);
@@ -1484,9 +1498,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public void clearDnsInterfaceForPid(int pid) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- String cmd = "resolver clearifaceforpid " + pid;
-
- mConnector.execute(cmd);
+ mConnector.execute("resolver", "clearifaceforpid", pid);
} catch (NativeDaemonConnectorException e) {
throw new IllegalStateException(
"Error communicating with native deamon to clear interface for pid " + pid, e);
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index aadb790dbae1..e5cba625e4d3 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -565,8 +565,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
UserState userState = getCurrentUserStateLocked();
// Automation service is not bound, so pretend it died to perform clean up.
if (userState.mUiAutomationService != null
- && userState.mUiAutomationService.mServiceInterface != null
&& serviceClient != null
+ && userState.mUiAutomationService != null
+ && userState.mUiAutomationService.mServiceInterface != null
&& userState.mUiAutomationService.mServiceInterface.asBinder()
== serviceClient.asBinder()) {
userState.mUiAutomationService.binderDied();
@@ -2104,6 +2105,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (mIsAutomation) {
// We no longer have an automation service, so restore
// the state based on values in the settings database.
+ userState.mInstalledServices.remove(mAccessibilityServiceInfo);
+ userState.mEnabledServices.remove(mComponentName);
userState.mUiAutomationService = null;
userState.mUiAutomationServiceClient = null;
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 608ad1cfb5a1..22af3d5b3357 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -2838,11 +2838,8 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<activities.size(); i++) {
ActivityRecord r = activities.get(i);
if (!r.finishing) {
- int index = mMainStack.indexOfTokenLocked(r.appToken);
- if (index >= 0) {
- mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED,
- null, "finish-heavy", true);
- }
+ mMainStack.finishActivityLocked(r, Activity.RESULT_CANCELED,
+ null, "finish-heavy", true);
}
}
@@ -8002,33 +7999,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
mMainStack.resumeTopActivityLocked(null);
} else {
- ActivityRecord r = mMainStack.topRunningActivityLocked(null);
- if (r != null && r.app == app) {
- // If the top running activity is from this crashing
- // process, then terminate it to avoid getting in a loop.
- Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- int index = mMainStack.indexOfActivityLocked(r);
- r.stack.finishActivityLocked(r, index,
- Activity.RESULT_CANCELED, null, "crashed", false);
- // Also terminate any activities below it that aren't yet
- // stopped, to avoid a situation where one will get
- // re-start our crashing activity once it gets resumed again.
- index--;
- if (index >= 0) {
- r = mMainStack.getActivityAtIndex(index);
- if (r.state == ActivityState.RESUMED
- || r.state == ActivityState.PAUSING
- || r.state == ActivityState.PAUSED) {
- if (!r.isHomeActivity || mHomeProcess != r.app) {
- Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- r.stack.finishActivityLocked(r, index,
- Activity.RESULT_CANCELED, null, "crashed", false);
- }
- }
- }
- }
+ mMainStack.finishTopRunningActivityLocked(app);
}
// Bump up the crash count of any services currently running in the proc.
@@ -12370,7 +12341,6 @@ public final class ActivityManagerService extends ActivityManagerNative
public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
Intent resultData) {
- ComponentName dest = destIntent.getComponent();
synchronized (this) {
ActivityRecord srec = ActivityRecord.forToken(token);
@@ -12378,87 +12348,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return false;
}
ActivityStack stack = srec.stack;
- final int start = stack.indexOfActivityLocked(srec);
- if (start < 0) {
- // Current activity is not in history stack; do nothing.
- return false;
- }
- int finishTo = start - 1;
- ActivityRecord parent = null;
- boolean foundParentInTask = false;
- if (dest != null) {
- TaskRecord tr = srec.task;
- for (int i = start - 1; i >= 0; i--) {
- ActivityRecord r = stack.getActivityAtIndex(i);
- if (tr != r.task) {
- // Couldn't find parent in the same task; stop at the one above this.
- // (Root of current task; in-app "home" behavior)
- // Always at least finish the current activity.
- finishTo = Math.min(start - 1, i + 1);
- parent = stack.getActivityAtIndex(finishTo);
- break;
- } else if (r.info.packageName.equals(dest.getPackageName()) &&
- r.info.name.equals(dest.getClassName())) {
- finishTo = i;
- parent = r;
- foundParentInTask = true;
- break;
- }
- }
- }
-
- if (mController != null) {
- ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
- if (next != null) {
- // ask watcher if this is allowed
- boolean resumeOK = true;
- try {
- resumeOK = mController.activityResuming(next.packageName);
- } catch (RemoteException e) {
- mController = null;
- }
-
- if (!resumeOK) {
- return false;
- }
- }
- }
- final long origId = Binder.clearCallingIdentity();
- for (int i = start; i > finishTo; i--) {
- ActivityRecord r = stack.getActivityAtIndex(i);
- mMainStack.requestFinishActivityLocked(r.appToken, resultCode, resultData,
- "navigate-up", true);
- // Only return the supplied result for the first activity finished
- resultCode = Activity.RESULT_CANCELED;
- resultData = null;
- }
-
- if (parent != null && foundParentInTask) {
- final int parentLaunchMode = parent.info.launchMode;
- final int destIntentFlags = destIntent.getFlags();
- if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
- parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
- parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
- (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent);
- } else {
- try {
- ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
- destIntent.getComponent(), 0, srec.userId);
- int res = mMainStack.startActivityLocked(srec.app.thread, destIntent,
- null, aInfo, parent.appToken, null,
- 0, -1, parent.launchedFromUid, parent.launchedFromPackage,
- 0, null, true, null);
- foundParentInTask = res == ActivityManager.START_SUCCESS;
- } catch (RemoteException e) {
- foundParentInTask = false;
- }
- mMainStack.requestFinishActivityLocked(parent.appToken, resultCode,
- resultData, "navigate-up", true);
- }
- }
- Binder.restoreCallingIdentity(origId);
- return foundParentInTask;
+ return stack.navigateUpToLocked(srec, destIntent, resultCode, resultData);
}
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index cde17c9b39ba..c1b406ef473c 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -22,6 +22,7 @@ import com.android.server.am.ActivityStack.ActivityState;
import android.app.Activity;
import android.app.ActivityOptions;
+import android.app.ResultInfo;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -95,9 +96,9 @@ final class ActivityRecord {
ActivityRecord resultTo; // who started this entry, so will get our reply
final String resultWho; // additional identifier for use by resultTo.
final int requestCode; // code given by requester (resultTo)
- ArrayList results; // pending ActivityResult objs we have received
+ ArrayList<ResultInfo> results; // pending ActivityResult objs we have received
HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
- ArrayList newIntents; // any pending new intents for single-top mode
+ ArrayList<Intent> newIntents; // any pending new intents for single-top mode
ActivityOptions pendingOptions; // most recently given options
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
UriPermissionOwner uriPermissions; // current special URI access perms.
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index d969709c9a9c..84d5a72876c5 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -24,11 +24,13 @@ import com.android.internal.os.BatteryStatsImpl;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
import com.android.server.wm.AppTransition;
+import com.android.server.wm.TaskGroup;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.IActivityController;
import android.app.IActivityManager;
import android.app.IThumbnailReceiver;
import android.app.IThumbnailRetriever;
@@ -501,14 +503,10 @@ final class ActivityStack {
return null;
}
- final int indexOfTokenLocked(IBinder token) {
+ private final int indexOfTokenLocked(IBinder token) {
return mHistory.indexOf(ActivityRecord.forToken(token));
}
- final int indexOfActivityLocked(ActivityRecord r) {
- return mHistory.indexOf(r);
- }
-
final ActivityRecord isInStackLocked(IBinder token) {
ActivityRecord r = ActivityRecord.forToken(token);
if (mHistory.contains(r)) {
@@ -517,14 +515,6 @@ final class ActivityStack {
return null;
}
- // TODO: This exposes mHistory too much, replace usage with ActivityStack methods.
- final ActivityRecord getActivityAtIndex(int index) {
- if (index >= 0 && index < mHistory.size()) {
- return mHistory.get(index);
- }
- return null;
- }
-
int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
TaskRecord lastTask = null;
final int N = mHistory.size();
@@ -745,7 +735,13 @@ final class ActivityStack {
try {
profileFd = profileFd.dup();
} catch (IOException e) {
- profileFd = null;
+ if (profileFd != null) {
+ try {
+ profileFd.close();
+ } catch (IOException o) {
+ }
+ profileFd = null;
+ }
}
}
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
@@ -1758,7 +1754,7 @@ final class ActivityStack {
try {
// Deliver all pending results.
- ArrayList a = next.results;
+ ArrayList<ResultInfo> a = next.results;
if (a != null) {
final int N = a.size();
if (!next.finishing && N > 0) {
@@ -2117,13 +2113,14 @@ final class ActivityStack {
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to new task " + target.task);
}
- mService.mWindowManager.setAppGroupId(target.appToken, task.taskId);
+ mService.mWindowManager.setAppGroupId(target.appToken, target.task.taskId);
if (replyChainEnd < 0) {
replyChainEnd = targetI;
}
int dstPos = 0;
ThumbnailHolder curThumbHolder = target.thumbHolder;
boolean gotOptions = !canMoveOptions;
+ final int taskId = target.task.taskId;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
p = mHistory.get(srcPos);
if (p.finishing) {
@@ -2148,14 +2145,15 @@ final class ActivityStack {
}
mHistory.remove(srcPos);
mHistory.add(dstPos, p);
- mService.mWindowManager.moveAppToken(dstPos, p.appToken);
- mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId);
+// mService.mWindowManager.moveAppToken(dstPos, p.appToken);
+ mService.mWindowManager.setAppGroupId(p.appToken, taskId);
dstPos++;
- if (VALIDATE_TOKENS) {
- validateAppTokensLocked();
- }
i++;
}
+ mService.mWindowManager.moveTaskToBottom(taskId);
+ if (VALIDATE_TOKENS) {
+ validateAppTokensLocked();
+ }
if (taskTop == p) {
taskTop = below;
}
@@ -2273,6 +2271,7 @@ final class ActivityStack {
if (replyChainEnd < 0) {
replyChainEnd = targetI;
}
+ final int taskId = task.taskId;
if (DEBUG_TASKS) Slog.v(TAG, "Reparenting task at index "
+ targetI + " to " + replyChainEnd);
for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
@@ -2298,11 +2297,13 @@ final class ActivityStack {
if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
+ " from " + srcPos + " to " + lastReparentPos
+ " in to resetting task " + task);
- mService.mWindowManager.moveAppToken(lastReparentPos, p.appToken);
- mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId);
- if (VALIDATE_TOKENS) {
- validateAppTokensLocked();
- }
+// mService.mWindowManager.moveAppToken(lastReparentPos, p.appToken);
+ mService.mWindowManager.setAppGroupId(p.appToken, taskId);
+ }
+ // TODO: This is wrong because it doesn't take lastReparentPos into account.
+ mService.mWindowManager.moveTaskToTop(taskId);
+ if (VALIDATE_TOKENS) {
+ validateAppTokensLocked();
}
replyChainEnd = -1;
@@ -2413,7 +2414,7 @@ final class ActivityStack {
if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
&& (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
if (!ret.finishing) {
- int index = indexOfTokenLocked(ret.appToken);
+ int index = mHistory.indexOf(ret);
if (index >= 0) {
finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
null, "clear", false);
@@ -3568,7 +3569,7 @@ final class ActivityStack {
}
// Get the activity record.
- int index = indexOfActivityLocked(r);
+ int index = mHistory.indexOf(r);
if (index >= 0) {
res = r;
@@ -3654,7 +3655,7 @@ final class ActivityStack {
// Stop any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (i=0; i<NS; i++) {
- ActivityRecord r = (ActivityRecord)stops.get(i);
+ ActivityRecord r = stops.get(i);
synchronized (mService) {
if (r.finishing) {
finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
@@ -3667,7 +3668,7 @@ final class ActivityStack {
// Finish any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (i=0; i<NF; i++) {
- ActivityRecord r = (ActivityRecord)finishes.get(i);
+ ActivityRecord r = finishes.get(i);
synchronized (mService) {
activityRemoved = destroyActivityLocked(r, true, false, "finish-idle");
}
@@ -3675,7 +3676,7 @@ final class ActivityStack {
// Report back to any thumbnail receivers.
for (i=0; i<NT; i++) {
- ActivityRecord r = (ActivityRecord)thumbnails.get(i);
+ ActivityRecord r = thumbnails.get(i);
mService.sendPendingThumbnail(r, null, null, null, true);
}
@@ -3730,7 +3731,7 @@ final class ActivityStack {
int i;
for (i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.resultTo == self && r.requestCode == requestCode) {
if ((r.resultWho == null && resultWho == null) ||
(r.resultWho != null && r.resultWho.equals(resultWho))) {
@@ -3742,6 +3743,36 @@ final class ActivityStack {
mService.updateOomAdjLocked();
}
+ final void finishTopRunningActivityLocked(ProcessRecord app) {
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null && r.app == app) {
+ // If the top running activity is from this crashing
+ // process, then terminate it to avoid getting in a loop.
+ Slog.w(TAG, " Force finishing activity "
+ + r.intent.getComponent().flattenToShortString());
+ int index = mHistory.indexOf(r);
+ r.stack.finishActivityLocked(r, index,
+ Activity.RESULT_CANCELED, null, "crashed", false);
+ // Also terminate any activities below it that aren't yet
+ // stopped, to avoid a situation where one will get
+ // re-start our crashing activity once it gets resumed again.
+ index--;
+ if (index >= 0) {
+ r = mHistory.get(index);
+ if (r.state == ActivityState.RESUMED
+ || r.state == ActivityState.PAUSING
+ || r.state == ActivityState.PAUSED) {
+ if (!r.isHomeActivity || mService.mHomeProcess != r.app) {
+ Slog.w(TAG, " Force finishing activity "
+ + r.intent.getComponent().flattenToShortString());
+ r.stack.finishActivityLocked(r, index,
+ Activity.RESULT_CANCELED, null, "crashed", false);
+ }
+ }
+ }
+ }
+ }
+
final boolean finishActivityAffinityLocked(IBinder token) {
int index = indexOfTokenLocked(token);
if (DEBUG_RESULTS) Slog.v(
@@ -3800,6 +3831,19 @@ final class ActivityStack {
* @return Returns true if this activity has been removed from the history
* list, or false if it is still in the list and will be removed later.
*/
+ final boolean finishActivityLocked(ActivityRecord r,
+ int resultCode, Intent resultData, String reason, boolean oomAdj) {
+ int index = mHistory.indexOf(r);
+ if (index >= 0) {
+ return finishActivityLocked(r, index, resultCode, resultData, reason, false, oomAdj);
+ }
+ return false;
+ }
+
+ /**
+ * @return Returns true if this activity has been removed from the history
+ * list, or false if it is still in the list and will be removed later.
+ */
final boolean finishActivityLocked(ActivityRecord r, int index,
int resultCode, Intent resultData, String reason, boolean oomAdj) {
return finishActivityLocked(r, index, resultCode, resultData, reason, false, oomAdj);
@@ -3892,7 +3936,7 @@ final class ActivityStack {
private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
int mode, boolean oomAdj) {
- final int index = indexOfActivityLocked(r);
+ final int index = mHistory.indexOf(r);
if (index < 0) {
return null;
}
@@ -3948,16 +3992,102 @@ final class ActivityStack {
resumeTopActivityLocked(null);
}
return activityRemoved ? null : r;
- } else {
- // Need to go through the full pause cycle to get this
- // activity into the stopped state and then finish it.
- if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
- mFinishingActivities.add(r);
- resumeTopActivityLocked(null);
}
+
+ // Need to go through the full pause cycle to get this
+ // activity into the stopped state and then finish it.
+ if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
+ mFinishingActivities.add(r);
+ resumeTopActivityLocked(null);
return r;
}
+ final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
+ Intent resultData) {
+ final int start = mHistory.indexOf(srec);
+ if (start < 0) {
+ // Current activity is not in history stack; do nothing.
+ return false;
+ }
+ int finishTo = start - 1;
+ ActivityRecord parent = null;
+ boolean foundParentInTask = false;
+ ComponentName dest = destIntent.getComponent();
+ if (dest != null) {
+ TaskRecord tr = srec.task;
+ for (int i = start - 1; i >= 0; i--) {
+ ActivityRecord r = mHistory.get(i);
+ if (tr != r.task) {
+ // Couldn't find parent in the same task; stop at the one above this.
+ // (Root of current task; in-app "home" behavior)
+ // Always at least finish the current activity.
+ finishTo = Math.min(start - 1, i + 1);
+ parent = mHistory.get(finishTo);
+ break;
+ } else if (r.info.packageName.equals(dest.getPackageName()) &&
+ r.info.name.equals(dest.getClassName())) {
+ finishTo = i;
+ parent = r;
+ foundParentInTask = true;
+ break;
+ }
+ }
+ }
+
+ IActivityController controller = mService.mController;
+ if (controller != null) {
+ ActivityRecord next = topRunningActivityLocked(srec.appToken, 0);
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean resumeOK = true;
+ try {
+ resumeOK = controller.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ }
+
+ if (!resumeOK) {
+ return false;
+ }
+ }
+ }
+ final long origId = Binder.clearCallingIdentity();
+ for (int i = start; i > finishTo; i--) {
+ ActivityRecord r = mHistory.get(i);
+ requestFinishActivityLocked(r.appToken, resultCode, resultData,
+ "navigate-up", true);
+ // Only return the supplied result for the first activity finished
+ resultCode = Activity.RESULT_CANCELED;
+ resultData = null;
+ }
+
+ if (parent != null && foundParentInTask) {
+ final int parentLaunchMode = parent.info.launchMode;
+ final int destIntentFlags = destIntent.getFlags();
+ if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
+ (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+ parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent);
+ } else {
+ try {
+ ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
+ destIntent.getComponent(), 0, srec.userId);
+ int res = startActivityLocked(srec.app.thread, destIntent,
+ null, aInfo, parent.appToken, null,
+ 0, -1, parent.launchedFromUid, parent.launchedFromPackage,
+ 0, null, true, null);
+ foundParentInTask = res == ActivityManager.START_SUCCESS;
+ } catch (RemoteException e) {
+ foundParentInTask = false;
+ }
+ requestFinishActivityLocked(parent.appToken, resultCode,
+ resultData, "navigate-up", true);
+ }
+ }
+ Binder.restoreCallingIdentity(origId);
+ return foundParentInTask;
+ }
/**
* Perform the common clean-up of an activity record. This is called both
* as part of destroyActivityLocked() (when destroying the client-side
@@ -4216,7 +4346,7 @@ final class ActivityStack {
mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
}
- int index = indexOfActivityLocked(r);
+ int index = mHistory.indexOf(r);
if (index >= 0) {
if (r.state == ActivityState.DESTROYING) {
cleanUpActivityLocked(r, true, false);
@@ -4230,15 +4360,15 @@ final class ActivityStack {
}
}
- private void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app,
- String listName) {
+ private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
+ ProcessRecord app, String listName) {
int i = list.size();
if (DEBUG_CLEANUP) Slog.v(
TAG, "Removing app " + app + " from list " + listName
+ " with " + i + " entries");
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)list.get(i);
+ ActivityRecord r = list.get(i);
if (DEBUG_CLEANUP) Slog.v(TAG, "Record #" + i + " " + r);
if (r.app == app) {
if (DEBUG_CLEANUP) Slog.v(TAG, "---> REMOVING this entry!");
@@ -4264,7 +4394,7 @@ final class ActivityStack {
TAG, "Removing app " + app + " from history with " + i + " entries");
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (DEBUG_CLEANUP) Slog.v(
TAG, "Record #" + i + " " + r + ": app=" + r.app);
if (r.app == app) {
@@ -4435,8 +4565,9 @@ final class ActivityStack {
} else {
updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
}
-
- mService.mWindowManager.moveAppTokensToTop(moved);
+
+// mService.mWindowManager.moveAppTokensToTop(moved);
+ mService.mWindowManager.moveTaskToTop(task);
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
@@ -4528,7 +4659,8 @@ final class ActivityStack {
mService.mWindowManager.prepareAppTransition(
AppTransition.TRANSIT_TASK_TO_BACK, false);
}
- mService.mWindowManager.moveAppTokensToBottom(moved);
+// mService.mWindowManager.moveAppTokensToBottom(moved);
+ mService.mWindowManager.moveTaskToBottom(task);
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
@@ -4561,9 +4693,8 @@ final class ActivityStack {
TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true);
if (info.numSubThumbbails <= 0) {
return info.mainThumbnail != null ? info.mainThumbnail : tr.lastThumbnail;
- } else {
- return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail;
}
+ return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail;
}
public ActivityRecord removeTaskActivitiesLocked(int taskId, int subTaskIndex,
@@ -4643,6 +4774,7 @@ final class ActivityStack {
}
if (thumbs.numSubThumbbails > 0) {
thumbs.retriever = new IThumbnailRetriever.Stub() {
+ @Override
public Bitmap getThumbnail(int index) {
if (index < 0 || index >= thumbs.subtasks.size()) {
return null;
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 5760dcd0aece..18ccf750cd7a 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -547,6 +547,11 @@ public class UserManagerService extends IUserManager.Stub {
UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
mUsers.put(0, primary);
mNextSerialNumber = MIN_USER_ID;
+
+ Bundle restrictions = new Bundle();
+ initRestrictionsToDefaults(restrictions);
+ mUserRestrictions.append(0, restrictions);
+
updateUserIdsLocked();
writeUserListLocked();
@@ -805,6 +810,9 @@ public class UserManagerService extends IUserManager.Stub {
userInfo.partial = false;
writeUserLocked(userInfo);
updateUserIdsLocked();
+ Bundle restrictions = new Bundle();
+ initRestrictionsToDefaults(restrictions);
+ mUserRestrictions.append(userId, restrictions);
}
}
if (userInfo != null) {
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 906ea57d7d50..f8e779debd6c 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -16,12 +16,16 @@
package com.android.server.wm;
+import android.os.Debug;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
class DisplayContentList extends ArrayList<DisplayContent> {
}
@@ -34,7 +38,7 @@ class DisplayContentList extends ArrayList<DisplayContent> {
* WindowManagerService.mWindowMap.
*/
class DisplayContent {
-// private final static String TAG = "DisplayContent";
+ private final static String TAG = "DisplayContent";
/** Unique identifier of this stack. */
private final int mDisplayId;
@@ -97,7 +101,7 @@ class DisplayContent {
/**
* Sorted most recent at top, oldest at [0].
*/
-// ArrayList<TaskList> mTaskLists = new ArrayList<TaskList>();
+ ArrayList<TaskList> mTaskLists = new ArrayList<TaskList>();
SparseArray<TaskList> mTaskIdToTaskList = new SparseArray<TaskList>();
/**
@@ -133,24 +137,28 @@ class DisplayContent {
/**
* Find the location to insert a new AppWindowToken into the window-ordered app token list.
* @param addPos The location the token was inserted into in mAppTokens.
- * @param atoken The token to insert.
+ * @param wtoken The token to insert.
*/
- void addAppToken(final int addPos, final AppWindowToken atoken) {
- mAppTokens.add(addPos, atoken);
+ void addAppToken(final int addPos, final AppWindowToken wtoken) {
+ mAppTokens.add(addPos, wtoken);
if (addPos == 0 || addPos == mAnimatingAppTokens.size()) {
// It was inserted into the beginning or end of mAppTokens. Honor that.
- mAnimatingAppTokens.add(addPos, atoken);
+ mAnimatingAppTokens.add(addPos, wtoken);
} else {
// Find the item immediately above the mAppTokens insertion point and put the token
// immediately below that one in mAnimatingAppTokens.
final AppWindowToken aboveAnchor = mAppTokens.get(addPos + 1);
- mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), atoken);
+ mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), wtoken);
}
- TaskList task = mTaskIdToTaskList.get(atoken.groupId);
+ TaskList task = mTaskIdToTaskList.get(wtoken.groupId);
if (task == null) {
- mTaskIdToTaskList.put(atoken.groupId, new TaskList(atoken));
+ task = new TaskList(wtoken, this);
+ mTaskIdToTaskList.put(wtoken.groupId, task);
+ mTaskLists.add(task);
+ } else {
+ task.mAppTokens.add(wtoken);
}
}
@@ -163,6 +171,7 @@ class DisplayContent {
AppTokenList appTokens = task.mAppTokens;
appTokens.remove(wtoken);
if (appTokens.size() == 0) {
+ mTaskLists.remove(task);
mTaskIdToTaskList.delete(taskId);
}
}
@@ -170,7 +179,10 @@ class DisplayContent {
void refillAnimatingAppTokens() {
mAnimatingAppTokens.clear();
- mAnimatingAppTokens.addAll(mAppTokens);
+ AppTokenIterator iterator = new AppTokenIterator();
+ while (iterator.hasNext()) {
+ mAnimatingAppTokens.add(iterator.next());
+ }
}
void setAppTaskId(AppWindowToken wtoken, int newTaskId) {
@@ -186,7 +198,7 @@ class DisplayContent {
task = mTaskIdToTaskList.get(newTaskId);
if (task == null) {
- task = new TaskList(wtoken);
+ task = new TaskList(wtoken, this);
mTaskIdToTaskList.put(newTaskId, task);
} else {
task.mAppTokens.add(wtoken);
@@ -195,6 +207,131 @@ class DisplayContent {
wtoken.groupId = newTaskId;
}
+ class TaskListsIterator implements Iterator<TaskList> {
+ private int mCur;
+ private boolean mReverse;
+
+ TaskListsIterator() {
+ this(false);
+ }
+
+ TaskListsIterator(boolean reverse) {
+ mReverse = reverse;
+ int numTaskLists = mTaskLists.size();
+ mCur = reverse ? numTaskLists - 1 : 0;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (mReverse) {
+ return mCur >= 0;
+ }
+ return mCur < mTaskLists.size();
+ }
+
+ @Override
+ public TaskList next() {
+ if (hasNext()) {
+ TaskList taskList = mTaskLists.get(mCur);
+ mCur += (mReverse ? -1 : 1);
+ return taskList;
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ class AppTokenIterator implements Iterator<AppWindowToken> {
+ final TaskListsIterator mIterator;
+ final boolean mReverse;
+ int mCur;
+ TaskList mTaskList;
+
+ public AppTokenIterator() {
+ this(false);
+ }
+
+ public AppTokenIterator(boolean reverse) {
+ mReverse = reverse;
+ mIterator = new TaskListsIterator(reverse);
+ getNextTaskList();
+ }
+
+ private void getNextTaskList() {
+ if (mIterator.hasNext()) {
+ mTaskList = mIterator.next();
+ mCur = mReverse ? mTaskList.mAppTokens.size() - 1 : 0;
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (mTaskList == null) {
+ return false;
+ }
+ if (mReverse) {
+ return mCur >= 0;
+ }
+ return mCur < mTaskList.mAppTokens.size();
+ }
+
+ @Override
+ public AppWindowToken next() {
+ if (hasNext()) {
+ AppWindowToken wtoken = mTaskList.mAppTokens.get(mCur);
+ mCur += mReverse ? -1 : 1;
+ if (!hasNext()) {
+ getNextTaskList();
+ }
+ return wtoken;
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new IllegalArgumentException();
+ }
+
+ int size() {
+ int size = 0;
+ final TaskListsIterator iterator = new TaskListsIterator();
+ while (iterator.hasNext()) {
+ size += iterator.next().mAppTokens.size();
+ }
+ return size;
+ }
+ }
+
+ void verifyAppTokens() {
+ AppTokenIterator iterator = new AppTokenIterator();
+ for (int i = 0; i < mAppTokens.size(); ++i) {
+ if (!iterator.hasNext()) {
+ Slog.e(TAG, "compareAppTokens: More mAppTokens than TaskList tokens. Callers="
+ + Debug.getCallers(4));
+ while (i < mAppTokens.size()) {
+ Slog.e(TAG, "compareAppTokens: mAppTokens[" + i + "]=" + mAppTokens.get(i));
+ i++;
+ }
+ return;
+ }
+ AppWindowToken appToken = mAppTokens.get(i);
+ AppWindowToken taskListToken = iterator.next();
+ if (appToken != taskListToken) {
+ Slog.e(TAG, "compareAppTokens: Mismatch at " + i + " appToken=" + appToken
+ + " taskListToken=" + taskListToken + ". Callers=" + Debug.getCallers(4));
+ }
+ }
+ if (iterator.hasNext()) {
+ Slog.e(TAG, "compareAppTokens: More TaskList tokens than mAppTokens Callers="
+ + Debug.getCallers(4));
+ }
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
final String subPrefix = " " + prefix;
@@ -218,13 +355,16 @@ class DisplayContent {
pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
- if (mAppTokens.size() > 0) {
+ AppTokenIterator iterator = new AppTokenIterator(true);
+ int ndx = iterator.size() - 1;
+ if (ndx >= 0) {
pw.println();
pw.println(" Application tokens in Z order:");
- for (int i=mAppTokens.size()-1; i>=0; i--) {
- pw.print(" App #"); pw.print(i);
- pw.print(' '); pw.print(mAppTokens.get(i)); pw.println(":");
- mAppTokens.get(i).dump(pw, " ");
+ while (iterator.hasNext()) {
+ AppWindowToken wtoken = iterator.next();
+ pw.print(" App #"); pw.print(ndx--);
+ pw.print(' '); pw.print(wtoken); pw.println(":");
+ wtoken.dump(pw, " ");
}
}
if (mExitingTokens.size() > 0) {
diff --git a/services/java/com/android/server/am/TaskGroup.java b/services/java/com/android/server/wm/TaskGroup.java
index 757410374f26..5e82af263aef 100644
--- a/services/java/com/android/server/am/TaskGroup.java
+++ b/services/java/com/android/server/wm/TaskGroup.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.am;
+package com.android.server.wm;
import android.view.IApplicationToken;
diff --git a/services/java/com/android/server/wm/TaskList.java b/services/java/com/android/server/wm/TaskList.java
index fe12b98efcfe..88791f206c3e 100644
--- a/services/java/com/android/server/wm/TaskList.java
+++ b/services/java/com/android/server/wm/TaskList.java
@@ -16,15 +16,15 @@
package com.android.server.wm;
-import com.android.server.am.TaskGroup;
-
-class TaskList extends TaskGroup {
+class TaskList {
// private final String TAG = "TaskGroup";
- AppTokenList mAppTokens = new AppTokenList();
+ DisplayContent mDisplayContent;
+ final AppTokenList mAppTokens = new AppTokenList();
+ final int taskId;
- TaskList(AppWindowToken wtoken) {
+ TaskList(AppWindowToken wtoken, DisplayContent displayContent) {
taskId = wtoken.groupId;
- tokens.add(wtoken.appToken);
mAppTokens.add(wtoken);
+ mDisplayContent = displayContent;
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 8c8eea899324..8806afa1015a 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -54,11 +54,11 @@ import com.android.server.AttributeCache;
import com.android.server.EventLogTags;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
-import com.android.server.am.TaskGroup;
import com.android.server.display.DisplayManagerService;
import com.android.server.input.InputManagerService;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
+import com.android.server.wm.DisplayContent.AppTokenIterator;
import android.Manifest;
import android.app.ActivityManagerNative;
@@ -197,6 +197,7 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean DEBUG_LAYOUT_REPEATS = true;
static final boolean DEBUG_SURFACE_TRACE = false;
static final boolean DEBUG_WINDOW_TRACE = false;
+ static final boolean DEBUG_TASK_MOVEMENT = false;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
@@ -330,8 +331,7 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Mapping from a token IBinder to a WindowToken object.
*/
- final HashMap<IBinder, WindowToken> mTokenMap =
- new HashMap<IBinder, WindowToken>();
+ final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<IBinder, WindowToken>();
/**
* List of window tokens that have finished starting their application,
@@ -3113,9 +3113,8 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "validateAppTokens: no Display for taskId=" + taskId);
return;
}
- AppTokenList appTokens = displayContent.mAppTokens;
- int m = appTokens.size() - 1;
+ AppTokenIterator iterator = displayContent.new AppTokenIterator(true);
for ( ; t >= 0; --t) {
task = tasks.get(t);
List<IApplicationToken> tokens = task.tokens;
@@ -3128,18 +3127,16 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- while (v >= 0 && m >= 0) {
- AppWindowToken atoken = appTokens.get(m);
+ while (v >= 0 && iterator.hasNext()) {
+ AppWindowToken atoken = iterator.next();
if (atoken.removed) {
- m--;
continue;
}
if (tokens.get(v) != atoken.token) {
Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v)
- + " @ " + v + ", internal is " + atoken.token + " @ " + m);
+ + " @ " + v + ", internal is " + atoken.token);
}
v--;
- m--;
}
while (v >= 0) {
Slog.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v);
@@ -3147,12 +3144,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- while (m >= 0) {
- AppWindowToken atoken = appTokens.get(m);
+ while (iterator.hasNext()) {
+ AppWindowToken atoken = iterator.next();
if (!atoken.removed) {
- Slog.w(TAG, "Invalid internal atoken: " + atoken.token + " @ " + m);
+ Slog.w(TAG, "Invalid internal atoken: " + atoken.token);
}
- m--;
}
}
@@ -3383,9 +3379,9 @@ public class WindowManagerService extends IWindowManager.Stub
boolean lastFullscreen = false;
// TODO: Multi window.
DisplayContent displayContent = getDefaultDisplayContentLocked();
- AppTokenList appTokens = displayContent.mAppTokens;
- for (int pos = appTokens.size() - 1; pos >= 0; pos--) {
- AppWindowToken atoken = appTokens.get(pos);
+ AppTokenIterator iterator = displayContent.new AppTokenIterator(true);
+ while (iterator.hasNext()) {
+ AppWindowToken atoken = iterator.next();
if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + atoken);
@@ -4294,11 +4290,12 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Removing app " + wtoken + " delayed=" + delayed
+ " animation=" + wtoken.mAppAnimator.animation
+ " animating=" + wtoken.mAppAnimator.animating);
+ DisplayContent displayContent = mTaskIdToDisplayContents.get(wtoken.groupId);
if (delayed) {
// set the token aside because it has an active animation to be finished
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"removeAppToken make exiting: " + wtoken);
- mTaskIdToDisplayContents.get(wtoken.groupId).mExitingAppTokens.add(wtoken);
+ displayContent.mExitingAppTokens.add(wtoken);
} else {
// Make sure there is no animation running on this token,
// so any windows associated with it will be removed as
@@ -4308,7 +4305,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"removeAppToken: " + wtoken);
- mTaskIdToDisplayContents.get(wtoken.groupId).removeAppToken(wtoken);
+ displayContent.removeAppToken(wtoken);
wtoken.removed = true;
if (wtoken.startingData != null) {
startingToken = wtoken;
@@ -4364,9 +4361,10 @@ public class WindowManagerService extends IWindowManager.Stub
while (iterator.hasNext()) {
DisplayContent displayContent = iterator.next();
Slog.v(TAG, " Display " + displayContent.getDisplayId());
- AppTokenList appTokens = displayContent.mAppTokens;
- for (int i=appTokens.size()-1; i>=0; i--) {
- Slog.v(TAG, " #" + i + ": " + appTokens.get(i).token);
+ AppTokenIterator appIterator = displayContent.new AppTokenIterator(true);
+ int i = appIterator.size();
+ while (appIterator.hasNext()) {
+ Slog.v(TAG, " #" + --i + ": " + appIterator.next().token);
}
}
}
@@ -4420,28 +4418,24 @@ public class WindowManagerService extends IWindowManager.Stub
tokenPos--;
continue;
}
- int i = wtoken.windows.size();
- while (i > 0) {
- i--;
+ for (int i = wtoken.windows.size() - 1; i >= 0; --i) {
WindowState win = wtoken.windows.get(i);
- int j = win.mChildWindows.size();
- while (j > 0) {
- j--;
+ for (int j = win.mChildWindows.size() - 1; j >= 0; --j) {
WindowState cwin = win.mChildWindows.get(j);
if (cwin.mSubLayer >= 0) {
- for (int pos=NW-1; pos>=0; pos--) {
+ for (int pos = NW - 1; pos >= 0; pos--) {
if (windows.get(pos) == cwin) {
if (DEBUG_REORDER) Slog.v(TAG,
- "Found child win @" + (pos+1));
- return pos+1;
+ "Found child win @" + (pos + 1));
+ return pos + 1;
}
}
}
}
- for (int pos=NW-1; pos>=0; pos--) {
+ for (int pos = NW - 1; pos >= 0; pos--) {
if (windows.get(pos) == win) {
- if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos+1));
- return pos+1;
+ if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos + 1));
+ return pos + 1;
}
}
}
@@ -4451,6 +4445,58 @@ public class WindowManagerService extends IWindowManager.Stub
return 0;
}
+ private int findAppWindowInsertionPointLocked(AppWindowToken target) {
+ final int taskId = target.groupId;
+ DisplayContent displayContent = mTaskIdToDisplayContents.get(taskId);
+ if (displayContent == null) {
+ Slog.w(TAG, "findTopAppWindowLocked: no DisplayContent for " + target);
+ return 0;
+ }
+ final WindowList windows = displayContent.getWindowList();
+ final int NW = windows.size();
+
+ AppTokenIterator iterator = displayContent.new AppTokenIterator(true);
+ while (iterator.hasNext()) {
+ if (iterator.next() == target) {
+ break;
+ }
+ }
+
+ while (iterator.hasNext()) {
+ // Find the first app token below the new position that has
+ // a window displayed.
+ final AppWindowToken wtoken = iterator.next();
+ if (DEBUG_REORDER) Slog.v(TAG, "Looking for lower windows in " + wtoken.token);
+ if (wtoken.sendingToBottom) {
+ if (DEBUG_REORDER) Slog.v(TAG, "Skipping token -- currently sending to bottom");
+ continue;
+ }
+ for (int i = wtoken.windows.size() - 1; i >= 0; --i) {
+ WindowState win = wtoken.windows.get(i);
+ for (int j = win.mChildWindows.size() - 1; j >= 0; --j) {
+ WindowState cwin = win.mChildWindows.get(j);
+ if (cwin.mSubLayer >= 0) {
+ for (int pos = NW - 1; pos >= 0; pos--) {
+ if (windows.get(pos) == cwin) {
+ if (DEBUG_REORDER) Slog.v(TAG,
+ "Found child win @" + (pos + 1));
+ return pos + 1;
+ }
+ }
+ }
+ }
+ for (int pos = NW - 1; pos >= 0; pos--) {
+ if (windows.get(pos) == win) {
+ if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos + 1));
+ return pos + 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+ }
+
private final int reAddWindowLocked(int index, WindowState win) {
final WindowList windows = win.getWindowList();
final int NCW = win.mChildWindows.size();
@@ -4507,7 +4553,6 @@ public class WindowManagerService extends IWindowManager.Stub
final AppWindowToken wtoken = findAppWindowToken(token);
DisplayContent displayContent = mTaskIdToDisplayContents.get(wtoken.groupId);
final AppTokenList appTokens = displayContent.mAppTokens;
- final AppTokenList animatingAppTokens = displayContent.mAnimatingAppTokens;
final int oldIndex = appTokens.indexOf(wtoken);
if (DEBUG_TOKEN_MOVEMENT || DEBUG_REORDER) Slog.v(TAG,
"Start moving token " + wtoken + " initially at "
@@ -4569,7 +4614,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (wtoken != null) {
final DisplayContent displayContent = mTaskIdToDisplayContents.get(wtoken.groupId);
if (DEBUG_REORDER || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "Temporarily removing "
- + wtoken + " from " + displayContent.mAppTokens.indexOf(wtoken));
+ + wtoken);
if (!displayContent.mAppTokens.remove(wtoken)) {
Slog.w(TAG, "Attempting to reorder token that doesn't exist: "
+ token + " (" + wtoken + ")");
@@ -4580,8 +4625,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ WindowList mSavedWindows;
private void moveAppWindowsLocked(List<IBinder> tokens, DisplayContent displayContent,
int tokenPos) {
+ if (DEBUG_TASK_MOVEMENT) {
+ mSavedWindows = new WindowList(displayContent.getWindowList());
+ }
// First remove all of the windows from the list.
final int N = tokens.size();
int i;
@@ -4700,6 +4749,132 @@ public class WindowManagerService extends IWindowManager.Stub
Binder.restoreCallingIdentity(origId);
}
+ private void moveTaskWindowsLocked(int taskId) {
+ DisplayContent displayContent = mTaskIdToDisplayContents.get(taskId);
+ if (displayContent == null) {
+ Slog.w(TAG, "moveTaskWindowsLocked: can't find DisplayContent for taskId=" + taskId);
+ return;
+ }
+
+ WindowList windows;
+ WindowList windowsAtStart;
+ if (DEBUG_TASK_MOVEMENT) {
+ windows = displayContent.getWindowList();
+ windowsAtStart = new WindowList(windows);
+ windows.clear();
+ windows.addAll(mSavedWindows);
+ }
+
+ TaskList taskList = displayContent.mTaskIdToTaskList.get(taskId);
+ if (taskList == null) {
+ Slog.w(TAG, "moveTaskWindowsLocked: can't find TaskList for taskId=" + taskId);
+ return;
+ }
+
+ // First remove all of the windows from the list.
+ for (AppWindowToken wtoken : taskList.mAppTokens) {
+ tmpRemoveAppWindowsLocked(wtoken);
+ }
+
+ // And now add them back at the correct place.
+ // Where to start adding?
+ int pos = findAppWindowInsertionPointLocked(taskList.mAppTokens.get(0));
+ for (AppWindowToken wtoken : taskList.mAppTokens) {
+ if (wtoken != null) {
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
+ if (newPos != pos) {
+ displayContent.layoutNeeded = true;
+ }
+ pos = newPos;
+ }
+ }
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/)) {
+ assignLayersLocked(displayContent.getWindowList());
+ }
+
+ if (DEBUG_TASK_MOVEMENT) {
+ // Compare windowsAtStart with current windows.
+ if (windowsAtStart.size() != windows.size()) {
+ Slog.e(TAG, "moveTaskWindowsLocked: Mismatch in size!");
+ }
+ for (int i = 0; i < windowsAtStart.size(); i++) {
+ if (windowsAtStart.get(i) != windows.get(i)) {
+ Slog.e(TAG, "moveTaskWindowsLocked: Mismatch at " + i
+ + " app=" + windowsAtStart.get(i) + " task=" + windows.get(i));
+ }
+ }
+ }
+
+ updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/);
+ mInputMonitor.setUpdateInputWindowsNeededLw();
+ performLayoutAndPlaceSurfacesLocked();
+ mInputMonitor.updateInputWindowsLw(false /*force*/);
+
+ //dump();
+ }
+
+ public void moveTaskToTop(int taskId) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized(mWindowMap) {
+ DisplayContent displayContent = mTaskIdToDisplayContents.get(taskId);
+ if (displayContent == null) {
+ Slog.e(TAG, "moveTaskToTop: taskId=" + taskId
+ + " not found in mTaskIdToDisplayContents");
+ return;
+ }
+ TaskList taskList = displayContent.mTaskIdToTaskList.get(taskId);
+ if (taskList == null) {
+ Slog.e(TAG, "moveTaskToTop: taskId=" + taskId
+ + " not found in mTaskIdToTaskLists");
+ return;
+ }
+ if (!displayContent.mTaskLists.remove(taskList)) {
+ Slog.e(TAG, "moveTaskToTop: taskId=" + taskId + " not found in mTaskLists");
+ }
+ displayContent.mTaskLists.add(taskList);
+ displayContent.verifyAppTokens();
+
+ displayContent.refillAnimatingAppTokens();
+ moveTaskWindowsLocked(taskId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ public void moveTaskToBottom(int taskId) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized(mWindowMap) {
+ DisplayContent displayContent = mTaskIdToDisplayContents.get(taskId);
+ if (displayContent == null) {
+ Slog.e(TAG, "moveTaskToBottom: taskId=" + taskId
+ + " not found in mTaskIdToDisplayContents");
+ return;
+ }
+ TaskList taskList = displayContent.mTaskIdToTaskList.get(taskId);
+ if (taskList == null) {
+ Slog.e(TAG, "moveTaskToTopBottom: taskId=" + taskId
+ + " not found in mTaskIdToTaskLists");
+ return;
+ }
+ if (!displayContent.mTaskLists.remove(taskList)) {
+ Slog.e(TAG, "moveTaskToBottom: taskId=" + taskId + " not found in mTaskLists");
+ }
+ displayContent.mTaskLists.add(0, taskList);
+ displayContent.verifyAppTokens();
+
+ displayContent.refillAnimatingAppTokens();
+ moveTaskWindowsLocked(taskId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
// -------------------------------------------------------------
// Misc IWindowSession methods
// -------------------------------------------------------------
@@ -6988,11 +7163,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
Slog.w(TAG, "App freeze timeout expired.");
DisplayContent displayContent = getDefaultDisplayContentLocked();
- AppTokenList appTokens = displayContent.mAppTokens;
- int i = appTokens.size();
- while (i > 0) {
- i--;
- AppWindowToken tok = appTokens.get(i);
+ AppTokenIterator iterator = displayContent.new AppTokenIterator(true);
+ while (iterator.hasNext()) {
+ AppWindowToken tok = iterator.next();
if (tok.mAppAnimator.freezingScreen) {
Slog.w(TAG, "Force clearing freeze: " + tok);
unsetAppFreezingScreenLocked(tok, true, true);
@@ -8779,8 +8952,7 @@ public class WindowManagerService extends IWindowManager.Stub
token.mAppAnimator.animating = false;
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"performLayout: App token exiting now removed" + token);
- displayContent.mAppTokens.remove(token);
- displayContent.mAnimatingAppTokens.remove(token);
+ displayContent.removeAppToken(token);
exitingAppTokens.remove(i);
}
}
@@ -9217,9 +9389,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
private WindowState findFocusedWindowLocked(DisplayContent displayContent) {
- final AppTokenList appTokens = displayContent.mAppTokens;
- int nextAppIndex = appTokens.size()-1;
- WindowToken nextApp = nextAppIndex >= 0 ? appTokens.get(nextAppIndex) : null;
+ AppTokenIterator iterator = displayContent.new AppTokenIterator(true);
+ WindowToken nextApp = iterator.hasNext() ? iterator.next() : null;
final WindowList windows = displayContent.getWindowList();
for (int i = windows.size() - 1; i >= 0; i--) {
@@ -9245,8 +9416,8 @@ public class WindowManagerService extends IWindowManager.Stub
// through the app tokens until we find its app.
if (thisApp != null && nextApp != null && thisApp != nextApp
&& win.mAttrs.type != TYPE_APPLICATION_STARTING) {
- int origAppIndex = nextAppIndex;
- while (nextAppIndex > 0) {
+ final WindowToken origAppToken = nextApp;
+ while (iterator.hasNext()) {
if (nextApp == mFocusedApp) {
// Whoops, we are below the focused app... no focus
// for you!
@@ -9254,8 +9425,7 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Reached focused app: " + mFocusedApp);
return null;
}
- nextAppIndex--;
- nextApp = appTokens.get(nextAppIndex);
+ nextApp = iterator.next();
if (nextApp == thisApp) {
break;
}
@@ -9264,8 +9434,14 @@ public class WindowManagerService extends IWindowManager.Stub
// Uh oh, the app token doesn't exist! This shouldn't
// happen, but if it does we can get totally hosed...
// so restart at the original app.
- nextAppIndex = origAppIndex;
- nextApp = appTokens.get(nextAppIndex);
+ nextApp = origAppToken;
+ iterator = displayContent.new AppTokenIterator(true);
+ while (iterator.hasNext()) {
+ // return iterator to same place.
+ if (iterator.next() == origAppToken) {
+ break;
+ }
+ }
}
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index bb0851923e80..f0045b176759 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -52,6 +52,12 @@ import java.io.PrintWriter;
import java.util.ArrayList;
class WindowList extends ArrayList<WindowState> {
+ WindowList() {
+ super();
+ }
+ WindowList(WindowList windows) {
+ super(windows);
+ }
}
/**
diff --git a/tests/BiDiTests/res/layout/canvas.xml b/tests/BiDiTests/res/layout/canvas.xml
deleted file mode 100644
index 0319a83b0382..000000000000
--- a/tests/BiDiTests/res/layout/canvas.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/canvas"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <SeekBar android:id="@+id/seekbar"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- />
-
- <view class="com.android.bidi.BiDiTestView"
- android:id="@+id/testview"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#FF0000"
- />
-
- </LinearLayout>
-
-</FrameLayout> \ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index 209597e27de8..b88a885efa43 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -101,7 +101,6 @@ public class BiDiTestActivity extends Activity {
addItem(result, "Basic", BiDiTestBasic.class, R.id.basic);
- addItem(result, "Canvas", BiDiTestCanvas.class, R.id.canvas);
addItem(result, "Canvas2", BiDiTestCanvas2.class, R.id.canvas2);
addItem(result, "TextView LTR", BiDiTestTextViewLtr.class, R.id.textview_ltr);
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvas.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvas.java
deleted file mode 100644
index 33ed731749e2..000000000000
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvas.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bidi;
-
-import android.app.Fragment;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.SeekBar;
-
-import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE;
-import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE;
-
-public class BiDiTestCanvas extends Fragment {
-
- static final int INIT_TEXT_SIZE = (FONT_MAX_SIZE - FONT_MIN_SIZE) / 2;
-
- private BiDiTestView testView;
- private SeekBar textSizeSeekBar;
- private View currentView;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- currentView = inflater.inflate(R.layout.canvas, container, false);
- return currentView;
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- testView = (BiDiTestView) currentView.findViewById(R.id.testview);
- testView.setCurrentTextSize(INIT_TEXT_SIZE);
-
- textSizeSeekBar = (SeekBar) currentView.findViewById(R.id.seekbar);
- textSizeSeekBar.setProgress(INIT_TEXT_SIZE);
- textSizeSeekBar.setMax(FONT_MAX_SIZE - FONT_MIN_SIZE);
-
- textSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- testView.setCurrentTextSize(FONT_MIN_SIZE + progress);
- }
-
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- }
-}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
deleted file mode 100644
index 0b1974a5b11d..000000000000
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bidi;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.text.TextPaint;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-public class BiDiTestView extends View {
-
- private static final String TAG = "BiDiTestView";
-
- private static final int BORDER_PADDING = 4;
- private static final int TEXT_PADDING = 16;
- private static final int TEXT_SIZE = 16;
- private static final int ORIGIN = 80;
-
- private static final float DEFAULT_ITALIC_SKEW_X = -0.25f;
-
- private Rect rect = new Rect();
-
- private String NORMAL_TEXT;
- private String NORMAL_LONG_TEXT;
- private String NORMAL_LONG_TEXT_2;
- private String NORMAL_LONG_TEXT_3;
- private String ITALIC_TEXT;
- private String BOLD_TEXT;
- private String BOLD_ITALIC_TEXT;
- private String ARABIC_TEXT;
- private String CHINESE_TEXT;
- private String MIXED_TEXT_1;
- private String HEBREW_TEXT;
- private String RTL_TEXT;
- private String THAI_TEXT;
-
- private int currentTextSize;
-
- public BiDiTestView(Context context) {
- super(context);
- init(context);
- }
-
- public BiDiTestView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
-
- public BiDiTestView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(context);
- }
-
- private void init(Context context) {
- NORMAL_TEXT = context.getString(R.string.normal_text);
- NORMAL_LONG_TEXT = context.getString(R.string.normal_long_text);
- NORMAL_LONG_TEXT_2 = context.getString(R.string.normal_long_text_2);
- NORMAL_LONG_TEXT_3 = context.getString(R.string.normal_long_text_3);
- ITALIC_TEXT = context.getString(R.string.italic_text);
- BOLD_TEXT = context.getString(R.string.bold_text);
- BOLD_ITALIC_TEXT = context.getString(R.string.bold_italic_text);
- ARABIC_TEXT = context.getString(R.string.arabic_text);
- CHINESE_TEXT = context.getString(R.string.chinese_text);
- MIXED_TEXT_1 = context.getString(R.string.mixed_text_1);
- HEBREW_TEXT = context.getString(R.string.hebrew_text);
- RTL_TEXT = context.getString(R.string.rtl);
- THAI_TEXT = context.getString(R.string.pointer_location);
- }
-
- public void setCurrentTextSize(int size) {
- currentTextSize = size;
- invalidate();
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- drawInsideRect(canvas, new Paint(), Color.BLACK);
-
- int deltaX = 0;
-
- deltaX = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN,
- false, false, Paint.DIRECTION_LTR, currentTextSize);
-
- deltaX += testString(canvas, ITALIC_TEXT, ORIGIN + deltaX, ORIGIN,
- true, false, Paint.DIRECTION_LTR, currentTextSize);
-
- deltaX += testString(canvas, BOLD_TEXT, ORIGIN + deltaX, ORIGIN,
- false, true, Paint.DIRECTION_LTR, currentTextSize);
-
- deltaX += testString(canvas, BOLD_ITALIC_TEXT, ORIGIN + deltaX, ORIGIN,
- true, true, Paint.DIRECTION_LTR, currentTextSize);
-
- // Test with a long string
- deltaX = testString(canvas, NORMAL_LONG_TEXT, ORIGIN, ORIGIN + 2 * currentTextSize,
- false, false, Paint.DIRECTION_LTR, currentTextSize);
-
- // Test with a long string
- deltaX = testString(canvas, NORMAL_LONG_TEXT_2, ORIGIN, ORIGIN + 4 * currentTextSize,
- false, false, Paint.DIRECTION_LTR, currentTextSize);
-
- // Test with a long string
- deltaX = testString(canvas, NORMAL_LONG_TEXT_3, ORIGIN, ORIGIN + 6 * currentTextSize,
- false, false, Paint.DIRECTION_LTR, currentTextSize);
-
- // Test Arabic ligature
- deltaX = testString(canvas, ARABIC_TEXT, ORIGIN, ORIGIN + 8 * currentTextSize,
- false, false, Paint.DIRECTION_RTL, currentTextSize);
-
- // Test Chinese
- deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 10 * currentTextSize,
- false, false, Paint.DIRECTION_LTR, currentTextSize);
-
- // Test Mixed (English and Arabic)
- deltaX = testString(canvas, MIXED_TEXT_1, ORIGIN, ORIGIN + 12 * currentTextSize,
- false, false, Paint.DIRECTION_LTR, currentTextSize);
-
- // Test Hebrew
- deltaX = testString(canvas, RTL_TEXT, ORIGIN, ORIGIN + 14 * currentTextSize,
- false, false, Paint.DIRECTION_RTL, currentTextSize);
-
- // Test Thai
- deltaX = testString(canvas, THAI_TEXT, ORIGIN, ORIGIN + 16 * currentTextSize,
- false, false, Paint.DIRECTION_LTR, currentTextSize);
- }
-
- private int testString(Canvas canvas, String text, int x, int y,
- boolean isItalic, boolean isBold, int dir, int textSize) {
-
- TextPaint paint = new TextPaint();
- paint.setAntiAlias(true);
-
- // Set paint properties
- boolean oldFakeBold = paint.isFakeBoldText();
- paint.setFakeBoldText(isBold);
-
- float oldTextSkewX = paint.getTextSkewX();
- if (isItalic) {
- paint.setTextSkewX(DEFAULT_ITALIC_SKEW_X);
- }
-
- paint.setTextSize(textSize);
- paint.setColor(Color.WHITE);
- canvas.drawText(text, x, y, paint);
-
- int length = text.length();
- float[] advances = new float[length];
- float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0);
- setPaintDir(paint, dir);
- float textWidthICU = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0,
- 1 /* use ICU */);
-
- logAdvances(text, textWidthHB, textWidthICU, advances);
- drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN);
-
- // Restore old paint properties
- paint.setFakeBoldText(oldFakeBold);
- paint.setTextSkewX(oldTextSkewX);
-
- return (int) Math.ceil(textWidthHB) + TEXT_PADDING;
- }
-
- private void setPaintDir(Paint paint, int dir) {
- Log.v(TAG, "Setting Paint dir=" + dir);
- paint.setBidiFlags(dir);
- }
-
- private void drawInsideRect(Canvas canvas, Paint paint, int color) {
- paint.setColor(color);
- int width = getWidth();
- int height = getHeight();
- rect.set(BORDER_PADDING, BORDER_PADDING, width - BORDER_PADDING, height - BORDER_PADDING);
- canvas.drawRect(rect, paint);
- }
-
- private void drawMetricsAroundText(Canvas canvas, int x, int y, float textWidthHB,
- float textWidthICU, int textSize, int color, int colorICU) {
- Paint paint = new Paint();
- paint.setColor(color);
- canvas.drawLine(x, y - textSize, x, y + 8, paint);
- canvas.drawLine(x, y + 8, x + textWidthHB, y + 8, paint);
- canvas.drawLine(x + textWidthHB, y - textSize, x + textWidthHB, y + 8, paint);
- paint.setColor(colorICU);
- canvas.drawLine(x + textWidthICU, y - textSize, x + textWidthICU, y + 8, paint);
- }
-
- private void logAdvances(String text, float textWidth, float textWidthICU, float[] advances) {
- Log.v(TAG, "Advances for text: " + text + " total= " + textWidth + " - totalICU= " + textWidthICU);
-// int length = advances.length;
-// for(int n=0; n<length; n++){
-// Log.v(TAG, "adv[" + n + "]=" + advances[n]);
-// }
- }
-}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 32b2771f56d1..0a789085bc09 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -32,6 +32,12 @@ import android.widget.Spinner;
import android.widget.TextView;
import android.view.View;
import android.util.Log;
+import android.renderscript.ScriptC;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Script;
import android.os.Environment;
import java.io.BufferedWriter;
@@ -44,6 +50,11 @@ public class ImageProcessingActivity extends Activity
private final String TAG = "Img";
public final String RESULT_FILE = "image_processing_result.csv";
+ RenderScript mRS;
+ Allocation mInPixelsAllocation;
+ Allocation mInPixelsAllocation2;
+ Allocation mOutPixelsAllocation;
+
/**
* Define enum type for test names
*/
@@ -408,6 +419,13 @@ public class ImageProcessingActivity extends Activity
mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
mBenchmarkResult.setText("Result: not run");
+
+ mRS = RenderScript.create(this);
+ mInPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapIn);
+ mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, mBitmapIn2);
+ mOutPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapOut);
+
+
setupTests();
changeTest(TestName.LEVELS_VEC3_RELAXED);
}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java
index faef83aafb0b..a353d9c4446d 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java
@@ -45,7 +45,6 @@ public class TestBase {
protected Allocation mInPixelsAllocation;
protected Allocation mInPixelsAllocation2;
protected Allocation mOutPixelsAllocation;
-
protected ImageProcessingActivity act;
private class MessageProcessor extends RenderScript.RSMessageHandler {
@@ -107,12 +106,12 @@ public class TestBase {
public final void createBaseTest(ImageProcessingActivity ipact, Bitmap b, Bitmap b2, Bitmap outb) {
act = ipact;
- mRS = RenderScript.create(act);
+ mRS = ipact.mRS;
mRS.setMessageHandler(new MessageProcessor(act));
- mInPixelsAllocation = Allocation.createFromBitmap(mRS, b);
- mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, b2);
- mOutPixelsAllocation = Allocation.createFromBitmap(mRS, outb);
+ mInPixelsAllocation = ipact.mInPixelsAllocation;
+ mInPixelsAllocation2 = ipact.mInPixelsAllocation2;
+ mOutPixelsAllocation = ipact.mOutPixelsAllocation;
createTest(act.getResources());
}
@@ -135,8 +134,7 @@ public class TestBase {
}
public void destroy() {
- mRS.destroy();
- mRS = null;
+ mRS.setMessageHandler(null);
}
public void updateBitmap(Bitmap b) {
diff --git a/tests/RenderScriptTests/LivePreview/res/layout/cf_main.xml b/tests/RenderScriptTests/LivePreview/res/layout/cf_main.xml
index c7dcca502fb8..ecb736baaf39 100644
--- a/tests/RenderScriptTests/LivePreview/res/layout/cf_main.xml
+++ b/tests/RenderScriptTests/LivePreview/res/layout/cf_main.xml
@@ -53,7 +53,7 @@
android:layout_height="fill_parent"
android:layout_weight="3" >
- <ImageView
+ <TextureView
android:id="@+id/format_view"
android:layout_height="0dp"
android:layout_width="fill_parent"
diff --git a/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/CameraPreviewActivity.java b/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/CameraPreviewActivity.java
index 89eec2c4c61b..62dcaa8eab04 100644
--- a/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/CameraPreviewActivity.java
+++ b/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/CameraPreviewActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,7 +65,7 @@ public class CameraPreviewActivity extends Activity
private int mPreviewTexWidth;
private int mPreviewTexHeight;
- private ImageView mFormatView;
+ //private TextureView mFormatView;
private Spinner mCameraSpinner;
private Spinner mResolutionSpinner;
@@ -77,7 +77,8 @@ public class CameraPreviewActivity extends Activity
private Camera.Size mNextPreviewSize;
private Camera.Size mPreviewSize;
- private Bitmap mCallbackBitmap;
+ private TextureView mOutputView;
+ //private Bitmap mCallbackBitmap;
private static final int STATE_OFF = 0;
private static final int STATE_PREVIEW = 1;
@@ -97,7 +98,7 @@ public class CameraPreviewActivity extends Activity
setContentView(R.layout.cf_main);
mPreviewView = (TextureView) findViewById(R.id.preview_view);
- mFormatView = (ImageView) findViewById(R.id.format_view);
+ mOutputView = (TextureView) findViewById(R.id.format_view);
mPreviewView.setSurfaceTextureListener(this);
@@ -115,8 +116,9 @@ public class CameraPreviewActivity extends Activity
mResolutionSpinner = (Spinner) findViewById(R.id.resolution_selection);
mResolutionSpinner.setOnItemSelectedListener(mResolutionSelectedListener);
-
mRS = RenderScript.create(this);
+ mFilterYuv = new RsYuv(mRS);
+ mOutputView.setSurfaceTextureListener(mFilterYuv);
}
@Override
@@ -227,8 +229,8 @@ public class CameraPreviewActivity extends Activity
// Set initial values
- mNextPreviewSize = mPreviewSizes.get(0);
- mResolutionSpinner.setSelection(0);
+ mNextPreviewSize = mPreviewSizes.get(15);
+ mResolutionSpinner.setSelection(15);
if (mPreviewTexture != null) {
startPreview();
@@ -271,6 +273,7 @@ public class CameraPreviewActivity extends Activity
mPreviewTexHeight * (1 - heightRatio/widthRatio)/2);
mPreviewView.setTransform(transform);
+ mOutputView.setTransform(transform);
mPreviewSize = mNextPreviewSize;
@@ -305,7 +308,7 @@ public class CameraPreviewActivity extends Activity
long t1 = java.lang.System.currentTimeMillis();
- mFilterYuv.execute(data, mCallbackBitmap);
+ mFilterYuv.execute(data);
long t2 = java.lang.System.currentTimeMillis();
mTiming[mTimingSlot++] = t2 - t1;
@@ -325,7 +328,7 @@ public class CameraPreviewActivity extends Activity
}
protected void onPostExecute(Boolean result) {
- mFormatView.invalidate();
+ mOutputView.invalidate();
}
}
@@ -355,21 +358,13 @@ public class CameraPreviewActivity extends Activity
mProcessInProgress = true;
- if (mCallbackBitmap == null ||
- mPreviewSize.width != mCallbackBitmap.getWidth() ||
- mPreviewSize.height != mCallbackBitmap.getHeight() ) {
- mCallbackBitmap =
- Bitmap.createBitmap(
- mPreviewSize.width, mPreviewSize.height,
- Bitmap.Config.ARGB_8888);
- mFilterYuv = new RsYuv(mRS, getResources(), mPreviewSize.width, mPreviewSize.height);
- mFormatView.setImageBitmap(mCallbackBitmap);
- }
-
+ if ((mFilterYuv == null) ||
+ (mPreviewSize.width != mFilterYuv.getWidth()) ||
+ (mPreviewSize.height != mFilterYuv.getHeight()) ) {
- mFormatView.invalidate();
+ mFilterYuv.reset(mPreviewSize.width, mPreviewSize.height);
+ }
- mCamera.addCallbackBuffer(data);
mProcessInProgress = true;
new ProcessPreviewDataTask().execute(data);
}
diff --git a/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/RsYuv.java b/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/RsYuv.java
index 978ae12d2d80..4d1627de5f0f 100644
--- a/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/RsYuv.java
+++ b/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/RsYuv.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import android.renderscript.*;
import android.graphics.Bitmap;
-public class RsYuv
+public class RsYuv implements TextureView.SurfaceTextureListener
{
private int mHeight;
private int mWidth;
@@ -43,36 +43,104 @@ public class RsYuv
private Allocation mAllocationIn;
private ScriptC_yuv mScript;
private ScriptIntrinsicYuvToRGB mYuv;
+ private boolean mHaveSurface;
+ private SurfaceTexture mSurface;
+ private ScriptGroup mGroup;
- RsYuv(RenderScript rs, Resources res, int width, int height) {
+ RsYuv(RenderScript rs) {
+ mRS = rs;
+ mScript = new ScriptC_yuv(mRS);
+ mYuv = ScriptIntrinsicYuvToRGB.create(rs, Element.RGBA_8888(mRS));
+ }
+
+ void setupSurface() {
+ if (mAllocationOut != null) {
+ mAllocationOut.setSurfaceTexture(mSurface);
+ }
+ if (mSurface != null) {
+ mHaveSurface = true;
+ } else {
+ mHaveSurface = false;
+ }
+ }
+
+ void reset(int width, int height) {
+ if (mAllocationOut != null) {
+ mAllocationOut.destroy();
+ }
+
+ android.util.Log.v("cpa", "reset " + width + ", " + height);
mHeight = height;
mWidth = width;
- mRS = rs;
- mScript = new ScriptC_yuv(mRS, res, R.raw.yuv);
mScript.invoke_setSize(mWidth, mHeight);
- mYuv = ScriptIntrinsicYuvToRGB.create(rs, Element.RGBA_8888(mRS));
-
Type.Builder tb = new Type.Builder(mRS, Element.RGBA_8888(mRS));
tb.setX(mWidth);
tb.setY(mHeight);
-
- mAllocationOut = Allocation.createTyped(rs, tb.create());
- mAllocationIn = Allocation.createSized(rs, Element.U8(mRS), (mHeight * mWidth) +
+ Type t = tb.create();
+ mAllocationOut = Allocation.createTyped(mRS, t, Allocation.USAGE_SCRIPT |
+ Allocation.USAGE_IO_OUTPUT);
+ mAllocationIn = Allocation.createSized(mRS, Element.U8(mRS), (mHeight * mWidth) +
((mHeight / 2) * (mWidth / 2) * 2));
-
mYuv.setInput(mAllocationIn);
+ setupSurface();
+
+
+ ScriptGroup.Builder b = new ScriptGroup.Builder(mRS);
+ b.addKernel(mScript.getKernelID_root());
+ b.addKernel(mYuv.getKernelID());
+ b.addConnection(t, mYuv.getKernelID(), mScript.getKernelID_root());
+ mGroup = b.create();
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+ public int getHeight() {
+ return mHeight;
}
private long mTiming[] = new long[50];
private int mTimingSlot = 0;
- void execute(byte[] yuv, Bitmap b) {
+ void execute(byte[] yuv) {
mAllocationIn.copyFrom(yuv);
- mYuv.forEach(mAllocationOut);
- mScript.forEach_root(mAllocationOut, mAllocationOut);
- mAllocationOut.copyTo(b);
+ if (mHaveSurface) {
+ mGroup.setOutput(mScript.getKernelID_root(), mAllocationOut);
+ mGroup.execute();
+
+ //mYuv.forEach(mAllocationOut);
+ //mScript.forEach_root(mAllocationOut, mAllocationOut);
+ mAllocationOut.ioSendOutput();
+ }
}
+
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ android.util.Log.v("cpa", "onSurfaceTextureAvailable " + surface);
+ mSurface = surface;
+ setupSurface();
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ android.util.Log.v("cpa", "onSurfaceTextureSizeChanged " + surface);
+ mSurface = surface;
+ setupSurface();
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ android.util.Log.v("cpa", "onSurfaceTextureDestroyed " + surface);
+ mSurface = surface;
+ setupSurface();
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ }
}
diff --git a/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/yuv.rs b/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/yuv.rs
index 884812da2580..c4f698f0af6a 100644
--- a/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/yuv.rs
+++ b/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/yuv.rs
@@ -1,7 +1,7 @@
#pragma version(1)
#pragma rs java_package_name(com.android.rs.livepreview)
-#pragma rs_fp_relaxed
+//#pragma rs_fp_relaxed
static int gWidth;
static int gHeight;
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_foreach_bounds.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_foreach_bounds.java
index 782f7888b2dc..97f3a326da7e 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_foreach_bounds.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_foreach_bounds.java
@@ -33,6 +33,10 @@ public class UT_foreach_bounds extends UnitTest {
Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
int X = 5;
int Y = 7;
+ final int xStart = 2;
+ final int xEnd = 5;
+ final int yStart = 3;
+ final int yEnd = 6;
s.set_dimX(X);
s.set_dimY(Y);
typeBuilder.setX(X).setY(Y);
@@ -41,12 +45,16 @@ public class UT_foreach_bounds extends UnitTest {
s.set_s(s);
s.set_ain(A);
s.set_aout(A);
- s.set_xStart(2);
- s.set_xEnd(5);
- s.set_yStart(3);
- s.set_yEnd(6);
+ s.set_xStart(xStart);
+ s.set_xEnd(xEnd);
+ s.set_yStart(yStart);
+ s.set_yEnd(yEnd);
s.forEach_zero(A);
+ Script.LaunchOptions sc = new Script.LaunchOptions();
+ sc.setX(xStart, xEnd).setY(yStart, yEnd);
+ s.forEach_root(A, sc);
+
return;
}
@@ -55,6 +63,7 @@ public class UT_foreach_bounds extends UnitTest {
ScriptC_foreach_bounds s = new ScriptC_foreach_bounds(pRS);
pRS.setMessageHandler(mRsMessage);
initializeGlobals(pRS, s);
+ s.invoke_verify_root();
s.invoke_foreach_bounds_test();
pRS.finish();
waitForMessage();
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/foreach_bounds.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/foreach_bounds.rs
index 89df090da1bf..fa76390027d3 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/foreach_bounds.rs
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/foreach_bounds.rs
@@ -7,6 +7,8 @@ int xEnd = 0;
int yStart = 0;
int yEnd = 0;
+static bool failed = false;
+
rs_script s;
rs_allocation aRaw;
rs_allocation ain;
@@ -27,9 +29,6 @@ static bool test_root_output() {
for (j = 0; j < dimY; j++) {
for (i = 0; i < dimX; i++) {
int v = rsGetElementAt_int(aRaw, i, j);
- rsDebug("i: ", i);
- rsDebug("j: ", j);
- rsDebug("a[j][i]: ", v);
if (i < xStart || i >= xEnd || j < yStart || j >= yEnd) {
_RS_ASSERT(v == 0);
} else {
@@ -48,20 +47,11 @@ static bool test_root_output() {
return failed;
}
-void foreach_bounds_test() {
- static bool failed = false;
-
- rs_script_call_t rssc = {0};
- rssc.strategy = RS_FOR_EACH_STRATEGY_DONT_CARE;
- rssc.xStart = xStart;
- rssc.xEnd = xEnd;
- rssc.yStart = yStart;
- rssc.yEnd = yEnd;
-
- rsForEach(s, ain, aout, NULL, 0, &rssc);
-
+void verify_root() {
failed |= test_root_output();
+}
+void foreach_bounds_test() {
if (failed) {
rsSendToClientBlocking(RS_MSG_TEST_FAILED);
}
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 5b71adc81cbe..2149190b10be 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1185,6 +1185,11 @@ bool AaptGroupEntry::getDensityName(const char* name,
return true;
}
+ if (strcmp(name, "xxxhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
+ return true;
+ }
+
char* c = (char*)name;
while (*c >= '0' && *c <= '9') {
c++;
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 8701cc8068a6..2414d705be8a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -188,6 +188,11 @@ public final class Typeface_Delegate {
return delegate.mStyle;
}
+ @LayoutlibDelegate
+ /*package*/ static void setGammaForText(float blackGamma, float whiteGamma) {
+ // This is for device testing only: pass
+ }
+
// ---- Private delegate/helper methods ----
private Typeface_Delegate(String family, int style) {
diff --git a/tools/layoutlib/bridge/src/android/text/format/DateFormat_Delegate.java b/tools/layoutlib/bridge/src/android/text/format/DateFormat_Delegate.java
new file mode 100644
index 000000000000..8cd1a695eabc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/format/DateFormat_Delegate.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.Context;
+
+
+/**
+ * Delegate used to provide new implementation for the native methods of {@link DateFormat}
+ *
+ * Through the layoutlib_create tool, the original methods of DateFormat have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class DateFormat_Delegate {
+
+ @LayoutlibDelegate
+ /*package*/ static boolean is24HourFormat(Context context) {
+ return false;
+ }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 80a1a60cb297..d9550404d48a 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -110,6 +110,7 @@ public final class CreateInfo implements ICreateInfo {
"android.os.Handler#sendMessageAtTime",
"android.os.HandlerThread#run",
"android.os.Build#getString",
+ "android.text.format.DateFormat#is24HourFormat",
"android.view.Choreographer#getRefreshRate",
"android.view.Display#updateDisplayInfoLocked",
"android.view.LayoutInflater#rInflate",
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index bf82792779bb..b971fc338735 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -277,7 +277,6 @@ public class WifiConfiguration implements Parcelable {
/**
* The enterprise configuration details specifying the EAP method,
* certificates and other settings associated with the EAP.
- * @hide
*/
public WifiEnterpriseConfig enterpriseConfig;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 7313e7ee511b..95ffb1c8b188 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -42,7 +42,10 @@ import java.security.spec.PKCS8EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
-/** Enterprise configuration details for Wi-Fi @hide */
+/**
+ * Enterprise configuration details for Wi-Fi. Stores details about the EAP method
+ * and any associated credentials.
+ */
public class WifiEnterpriseConfig implements Parcelable {
private static final String TAG = "WifiEnterpriseConfig";
/**
@@ -211,22 +214,32 @@ public class WifiEnterpriseConfig implements Parcelable {
}
};
+ /** The Extensible Authentication Protocol method used */
public static final class Eap {
- /* NONE represents an empty enterprise config */
+ /** No EAP method used. Represents an empty config */
public static final int NONE = -1;
+ /** Protected EAP */
public static final int PEAP = 0;
+ /** EAP-Transport Layer Security */
public static final int TLS = 1;
+ /** EAP-Tunneled Transport Layer Security */
public static final int TTLS = 2;
+ /** EAP-Password */
public static final int PWD = 3;
/** @hide */
public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD" };
}
+ /** The inner authentication method used */
public static final class Phase2 {
public static final int NONE = 0;
+ /** Password Authentication Protocol */
public static final int PAP = 1;
+ /** Microsoft Challenge Handshake Authentication Protocol */
public static final int MSCHAP = 2;
+ /** Microsoft Challenge Handshake Authentication Protocol v2 */
public static final int MSCHAPV2 = 3;
+ /** Generic Token Card */
public static final int GTC = 4;
private static final String PREFIX = "auth=";
/** @hide */
@@ -249,6 +262,7 @@ public class WifiEnterpriseConfig implements Parcelable {
* Set the EAP authentication method.
* @param eapMethod is one {@link Eap#PEAP}, {@link Eap#TLS}, {@link Eap#TTLS} or
* {@link Eap#PWD}
+ * @throws IllegalArgumentException on an invalid eap method
*/
public void setEapMethod(int eapMethod) {
switch (eapMethod) {
@@ -279,6 +293,7 @@ public class WifiEnterpriseConfig implements Parcelable {
* @param phase2Method is the inner authentication method and can be one of {@link Phase2#NONE},
* {@link Phase2#PAP}, {@link Phase2#MSCHAP}, {@link Phase2#MSCHAPV2},
* {@link Phase2#GTC}
+ * @throws IllegalArgumentException on an invalid phase2 method
*
*/
public void setPhase2Method(int phase2Method) {
@@ -378,7 +393,10 @@ public class WifiEnterpriseConfig implements Parcelable {
* Specify a X.509 certificate that identifies the server.
*
* <p>A default name is automatically assigned to the certificate and used
- * with this configuration.
+ * with this configuration. The framework takes care of installing the
+ * certificate when the config is saved and removing the certificate when
+ * the config is removed.
+ *
* @param cert X.509 CA certificate
* @throws IllegalArgumentException if not a CA certificate
*/
@@ -425,9 +443,13 @@ public class WifiEnterpriseConfig implements Parcelable {
* Specify a private key and client certificate for client authorization.
*
* <p>A default name is automatically assigned to the key entry and used
- * with this configuration.
+ * with this configuration. The framework takes care of installing the
+ * key entry when the config is saved and removing the key entry when
+ * the config is removed.
+
* @param privateKey
* @param clientCertificate
+ * @throws IllegalArgumentException for an invalid key or certificate.
*/
public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) {
if (clientCertificate != null) {