summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt4
-rw-r--r--api/system-current.txt4
-rw-r--r--api/test-current.txt35
-rw-r--r--core/java/android/app/Activity.java12
-rw-r--r--core/java/android/app/ActivityManager.java69
-rw-r--r--core/java/android/app/INotificationManager.aidl6
-rw-r--r--core/java/android/app/NotificationManager.java12
-rw-r--r--core/java/android/content/pm/PackageInstaller.java14
-rw-r--r--core/java/android/content/pm/PackageParser.java10
-rw-r--r--core/java/android/content/res/ApkAssets.java19
-rw-r--r--core/java/android/content/res/StringBlock.java48
-rw-r--r--core/java/android/os/GraphicsEnvironment.java15
-rw-r--r--core/java/android/os/ZygoteProcess.java7
-rw-r--r--core/java/android/provider/Settings.java14
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl2
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java14
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java4
-rw-r--r--core/java/android/util/proto/ProtoInputStream.java3
-rw-r--r--core/java/android/util/proto/TEST_MAPPING7
-rw-r--r--core/java/android/view/ViewTreeObserver.java6
-rw-r--r--core/java/android/view/Window.java64
-rw-r--r--core/java/com/android/internal/policy/DecorView.java27
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java37
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/drawable/ic_bluetooth_share_icon.xml4
-rw-r--r--core/res/res/values/attrs.xml38
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--media/OWNERS1
-rw-r--r--media/apex/java/android/media/MediaPlayer2.java2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java13
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java104
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java35
-rw-r--r--packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml2
-rw-r--r--packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_settings_bluetooth.xml1
-rw-r--r--packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml2
-rw-r--r--packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_settings_bluetooth.xml1
-rw-r--r--packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml2
-rw-r--r--packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_settings_bluetooth.xml1
-rw-r--r--packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml4
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java111
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java19
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java9
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java44
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java25
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java15
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java1
-rw-r--r--tests/ProtoInputStreamTests/Android.mk34
-rw-r--r--tests/ProtoInputStreamTests/AndroidManifest.xml27
-rw-r--r--tests/ProtoInputStreamTests/AndroidTest.xml28
-rw-r--r--tests/ProtoInputStreamTests/TEST_MAPPING7
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java500
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java423
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java728
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java570
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java547
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java649
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java679
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java565
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java645
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java507
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java547
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java648
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java547
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java622
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java404
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java564
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java641
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java45
-rw-r--r--tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto115
73 files changed, 10728 insertions, 252 deletions
diff --git a/api/current.txt b/api/current.txt
index 661b9c6ed827..4bbae3b14f7d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -51676,7 +51676,7 @@ package android.view {
method public void addOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void addOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
method public void addOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener);
- method public void addOnSystemGestureExclusionRectsChangedListener(java.util.function.Consumer<java.util.List<android.graphics.Rect>>);
+ method public void addOnSystemGestureExclusionRectsChangedListener(@NonNull java.util.function.Consumer<java.util.List<android.graphics.Rect>>);
method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
method public void addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
method public void addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
@@ -51691,7 +51691,7 @@ package android.view {
method public void removeOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void removeOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
method public void removeOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener);
- method public void removeOnSystemGestureExclusionRectsChangedListener(java.util.function.Consumer<java.util.List<android.graphics.Rect>>);
+ method public void removeOnSystemGestureExclusionRectsChangedListener(@NonNull java.util.function.Consumer<java.util.List<android.graphics.Rect>>);
method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
method public void removeOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
method public void removeOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
diff --git a/api/system-current.txt b/api/system-current.txt
index 76b8f6610590..f2423437c3fc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -553,7 +553,7 @@ package android.app {
}
public class NotificationManager {
- method @NonNull public java.util.List<java.lang.String> getAllowedAssistantCapabilities();
+ method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
@@ -6631,8 +6631,8 @@ package android.service.notification {
method public final void adjustNotification(@NonNull android.service.notification.Adjustment);
method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>);
method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
+ method public void onAllowedAdjustmentsChanged();
method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method public void onCapabilitiesChanged();
method public void onNotificationDirectReplied(@NonNull String);
method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification);
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
diff --git a/api/test-current.txt b/api/test-current.txt
index 7121a549176a..c3215a6867ae 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,7 @@ package android {
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
@@ -328,9 +329,9 @@ package android.app {
}
public class NotificationManager {
- method public void allowAssistantCapability(String);
- method public void disallowAssistantCapability(String);
- method @NonNull public java.util.List<java.lang.String> getAllowedAssistantCapabilities();
+ method public void allowAssistantAdjustment(String);
+ method public void disallowAssistantAdjustment(String);
+ method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
method public android.content.ComponentName getEffectsSuppressor();
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
@@ -2488,8 +2489,8 @@ package android.service.notification {
method public final void adjustNotification(@NonNull android.service.notification.Adjustment);
method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>);
method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
+ method public void onAllowedAdjustmentsChanged();
method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method public void onCapabilitiesChanged();
method public void onNotificationDirectReplied(@NonNull String);
method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification);
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
@@ -2698,6 +2699,7 @@ package android.telephony {
public class TelephonyManager {
method public int checkCarrierPrivilegesForPackage(String);
method public int getCarrierIdListVersion();
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
@@ -2880,31 +2882,6 @@ package android.util.proto {
method public void writeRawZigZag64(long);
}
- public final class ProtoInputStream extends android.util.proto.ProtoStream {
- ctor public ProtoInputStream(java.io.InputStream, int);
- ctor public ProtoInputStream(java.io.InputStream);
- ctor public ProtoInputStream(byte[]);
- method public int decodeZigZag32(int);
- method public long decodeZigZag64(long);
- method public String dumpDebugData();
- method public void end(long);
- method public int getFieldNumber();
- method public int getOffset();
- method public int getWireType();
- method public boolean isNextField(long) throws java.io.IOException;
- method public int nextField() throws java.io.IOException;
- method public boolean readBoolean(long) throws java.io.IOException;
- method public byte[] readBytes(long) throws java.io.IOException;
- method public double readDouble(long) throws java.io.IOException;
- method public float readFloat(long) throws java.io.IOException;
- method public int readInt(long) throws java.io.IOException;
- method public long readLong(long) throws java.io.IOException;
- method public String readString(long) throws java.io.IOException;
- method public void skip() throws java.io.IOException;
- method public long start(long) throws java.io.IOException;
- field public static final int NO_MORE_FIELDS = -1; // 0xffffffff
- }
-
public final class ProtoOutputStream extends android.util.proto.ProtoStream {
ctor public ProtoOutputStream();
ctor public ProtoOutputStream(int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 54fe65db499c..9079ace4b8a3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -132,6 +132,7 @@ import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
@@ -4904,6 +4905,17 @@ public class Activity extends ContextThemeWrapper
mTaskDescription.setNavigationBarColor(navigationBarColor);
}
+ final int targetSdk = getApplicationInfo().targetSdkVersion;
+ final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
+ if (!targetPreQ) {
+ mTaskDescription.setEnsureStatusBarContrastWhenTransparent(a.getBoolean(
+ R.styleable.ActivityTaskDescription_ensureStatusBarContrastWhenTransparent,
+ false));
+ mTaskDescription.setEnsureNavigationBarContrastWhenTransparent(a.getBoolean(
+ R.styleable.ActivityTaskDescription_ensureNavigationBarContrastWhenTransparent,
+ true));
+ }
+
a.recycle();
setTaskDescription(mTaskDescription);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 395c867de9d7..b80f7813640c 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -986,6 +986,8 @@ public class ActivityManager {
private int mColorBackground;
private int mStatusBarColor;
private int mNavigationBarColor;
+ private boolean mEnsureStatusBarContrastWhenTransparent;
+ private boolean mEnsureNavigationBarContrastWhenTransparent;
/**
* Creates the TaskDescription to the specified values.
@@ -998,7 +1000,7 @@ public class ActivityManager {
*/
@Deprecated
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
- this(label, icon, 0, null, colorPrimary, 0, 0, 0);
+ this(label, icon, 0, null, colorPrimary, 0, 0, 0, false, false);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1014,7 +1016,7 @@ public class ActivityManager {
* opaque.
*/
public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) {
- this(label, null, iconRes, null, colorPrimary, 0, 0, 0);
+ this(label, null, iconRes, null, colorPrimary, 0, 0, 0, false, false);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1029,7 +1031,7 @@ public class ActivityManager {
*/
@Deprecated
public TaskDescription(String label, Bitmap icon) {
- this(label, icon, 0, null, 0, 0, 0, 0);
+ this(label, icon, 0, null, 0, 0, 0, 0, false, false);
}
/**
@@ -1040,7 +1042,7 @@ public class ActivityManager {
* activity.
*/
public TaskDescription(String label, @DrawableRes int iconRes) {
- this(label, null, iconRes, null, 0, 0, 0, 0);
+ this(label, null, iconRes, null, 0, 0, 0, 0, false, false);
}
/**
@@ -1049,19 +1051,21 @@ public class ActivityManager {
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
- this(label, null, 0, null, 0, 0, 0, 0);
+ this(label, null, 0, null, 0, 0, 0, 0, false, false);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
- this(null, null, 0, null, 0, 0, 0, 0);
+ this(null, null, 0, null, 0, 0, 0, 0, false, false);
}
/** @hide */
public TaskDescription(String label, Bitmap bitmap, int iconRes, String iconFilename,
- int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor) {
+ int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor,
+ boolean ensureStatusBarContrastWhenTransparent,
+ boolean ensureNavigationBarContrastWhenTransparent) {
mLabel = label;
mIcon = bitmap;
mIconRes = iconRes;
@@ -1070,6 +1074,9 @@ public class ActivityManager {
mColorBackground = colorBackground;
mStatusBarColor = statusBarColor;
mNavigationBarColor = navigationBarColor;
+ mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent;
+ mEnsureNavigationBarContrastWhenTransparent =
+ ensureNavigationBarContrastWhenTransparent;
}
/**
@@ -1092,6 +1099,9 @@ public class ActivityManager {
mColorBackground = other.mColorBackground;
mStatusBarColor = other.mStatusBarColor;
mNavigationBarColor = other.mNavigationBarColor;
+ mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent;
+ mEnsureNavigationBarContrastWhenTransparent =
+ other.mEnsureNavigationBarContrastWhenTransparent;
}
/**
@@ -1114,6 +1124,9 @@ public class ActivityManager {
if (other.mNavigationBarColor != 0) {
mNavigationBarColor = other.mNavigationBarColor;
}
+ mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent;
+ mEnsureNavigationBarContrastWhenTransparent =
+ other.mEnsureNavigationBarContrastWhenTransparent;
}
private TaskDescription(Parcel source) {
@@ -1272,6 +1285,37 @@ public class ActivityManager {
return mNavigationBarColor;
}
+ /**
+ * @hide
+ */
+ public boolean getEnsureStatusBarContrastWhenTransparent() {
+ return mEnsureStatusBarContrastWhenTransparent;
+ }
+
+ /**
+ * @hide
+ */
+ public void setEnsureStatusBarContrastWhenTransparent(
+ boolean ensureStatusBarContrastWhenTransparent) {
+ mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean getEnsureNavigationBarContrastWhenTransparent() {
+ return mEnsureNavigationBarContrastWhenTransparent;
+ }
+
+ /**
+ * @hide
+ */
+ public void setEnsureNavigationBarContrastWhenTransparent(
+ boolean ensureNavigationBarContrastWhenTransparent) {
+ mEnsureNavigationBarContrastWhenTransparent =
+ ensureNavigationBarContrastWhenTransparent;
+ }
+
/** @hide */
public void saveToXml(XmlSerializer out) throws IOException {
if (mLabel != null) {
@@ -1332,6 +1376,8 @@ public class ActivityManager {
dest.writeInt(mColorBackground);
dest.writeInt(mStatusBarColor);
dest.writeInt(mNavigationBarColor);
+ dest.writeBoolean(mEnsureStatusBarContrastWhenTransparent);
+ dest.writeBoolean(mEnsureNavigationBarContrastWhenTransparent);
if (mIconFilename == null) {
dest.writeInt(0);
} else {
@@ -1348,6 +1394,8 @@ public class ActivityManager {
mColorBackground = source.readInt();
mStatusBarColor = source.readInt();
mNavigationBarColor = source.readInt();
+ mEnsureStatusBarContrastWhenTransparent = source.readBoolean();
+ mEnsureNavigationBarContrastWhenTransparent = source.readBoolean();
mIconFilename = source.readInt() > 0 ? source.readString() : null;
}
@@ -1366,8 +1414,11 @@ public class ActivityManager {
return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
" IconRes: " + mIconRes + " IconFilename: " + mIconFilename +
" colorPrimary: " + mColorPrimary + " colorBackground: " + mColorBackground +
- " statusBarColor: " + mColorBackground +
- " navigationBarColor: " + mNavigationBarColor;
+ " statusBarColor: " + mStatusBarColor + (
+ mEnsureStatusBarContrastWhenTransparent ? " (contrast when transparent)"
+ : "") + " navigationBarColor: " + mNavigationBarColor + (
+ mEnsureNavigationBarContrastWhenTransparent
+ ? " (contrast when transparent)" : "");
}
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7884872a7ef3..b3c2429004b8 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -70,9 +70,9 @@ interface INotificationManager
boolean areNotificationsEnabled(String pkg);
int getPackageImportance(String pkg);
- List<String> getAllowedAssistantCapabilities(String pkg);
- void allowAssistantCapability(String adjustmentType);
- void disallowAssistantCapability(String adjustmentType);
+ List<String> getAllowedAssistantAdjustments(String pkg);
+ void allowAssistantAdjustment(String adjustmentType);
+ void disallowAssistantAdjustment(String adjustmentType);
boolean shouldHideSilentStatusIcons(String callingPkg);
void setHideSilentStatusIcons(boolean hide);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index d54aca89c50d..dd39376f80ca 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1206,10 +1206,10 @@ public class NotificationManager {
*/
@SystemApi
@TestApi
- public @NonNull @Adjustment.Keys List<String> getAllowedAssistantCapabilities() {
+ public @NonNull @Adjustment.Keys List<String> getAllowedAssistantAdjustments() {
INotificationManager service = getService();
try {
- return service.getAllowedAssistantCapabilities(mContext.getOpPackageName());
+ return service.getAllowedAssistantAdjustments(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1219,10 +1219,10 @@ public class NotificationManager {
* @hide
*/
@TestApi
- public void allowAssistantCapability(String capability) {
+ public void allowAssistantAdjustment(String capability) {
INotificationManager service = getService();
try {
- service.allowAssistantCapability(capability);
+ service.allowAssistantAdjustment(capability);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1232,10 +1232,10 @@ public class NotificationManager {
* @hide
*/
@TestApi
- public void disallowAssistantCapability(String capability) {
+ public void disallowAssistantAdjustment(String capability) {
INotificationManager service = getService();
try {
- service.disallowAssistantCapability(capability);
+ service.disallowAssistantAdjustment(capability);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 7fe840c11dfa..a71f7d2c6455 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -30,7 +30,6 @@ import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.PackageManager.DeleteFlags;
import android.content.pm.PackageManager.InstallReason;
@@ -504,12 +503,14 @@ public class PackageInstaller {
*
* <p>Staged session is active iff:
* <ul>
- * <li>It is committed.
- * <li>It is not applied.
- * <li>It is not failed.
+ * <li>It is committed, i.e. {@link SessionInfo#isCommitted()} is {@code true}, and
+ * <li>it is not applied, i.e. {@link SessionInfo#isStagedSessionApplied()} is {@code
+ * false}, and
+ * <li>it is not failed, i.e. {@link SessionInfo#isStagedSessionFailed()} is {@code false}.
* </ul>
*
- * <p>In case of a multi-apk session, parent session will be returned.
+ * <p>In case of a multi-apk session, reasoning above is applied to the parent session, since
+ * that is the one that should been {@link Session#commit committed}.
*/
public @Nullable SessionInfo getActiveStagedSession() {
final List<SessionInfo> stagedSessions = getStagedSessions();
@@ -2307,7 +2308,8 @@ public class PackageInstaller {
}
/**
- * Whenever this session was committed.
+ * Returns {@code true} if {@link Session#commit(IntentSender)}} was called for this
+ * session.
*/
public boolean isCommitted() {
return isCommitted;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 270aea851791..b2b4e0e2edfb 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1580,8 +1580,8 @@ public class PackageParser {
final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
XmlResourceParser parser = null;
+ ApkAssets apkAssets = null;
try {
- final ApkAssets apkAssets;
try {
apkAssets = fd != null
? ApkAssets.loadFromFd(fd, debugPathName, false, false)
@@ -1618,7 +1618,13 @@ public class PackageParser {
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
- // TODO(b/72056911): Implement and call close() on ApkAssets.
+ if (apkAssets != null) {
+ try {
+ apkAssets.close();
+ } catch (Throwable ignored) {
+ }
+ }
+ // TODO(b/72056911): Implement AutoCloseable on ApkAssets.
}
}
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index dc1d052da64f..69462ab99483 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -36,7 +36,9 @@ import java.io.IOException;
*/
public final class ApkAssets {
@GuardedBy("this") private final long mNativePtr;
- @GuardedBy("this") private StringBlock mStringBlock;
+ @GuardedBy("this") private final StringBlock mStringBlock;
+
+ @GuardedBy("this") private boolean mOpen = true;
/**
* Creates a new ApkAssets instance from the given path on disk.
@@ -180,7 +182,20 @@ public final class ApkAssets {
@Override
protected void finalize() throws Throwable {
- nativeDestroy(mNativePtr);
+ close();
+ }
+
+ /**
+ * Closes this class and the contained {@link #mStringBlock}.
+ */
+ public void close() throws Throwable {
+ synchronized (this) {
+ if (mOpen) {
+ mOpen = false;
+ mStringBlock.close();
+ nativeDestroy(mNativePtr);
+ }
+ }
}
private static native long nativeLoad(
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index b5ec0f9f3712..b7bc8229fa45 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -18,13 +18,34 @@ package android.content.res;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Color;
-import android.text.*;
-import android.text.style.*;
-import android.util.Log;
-import android.util.SparseArray;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.text.Annotation;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannedString;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.BulletSpan;
+import android.text.style.CharacterStyle;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.LineHeightSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TextAppearanceSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
import java.util.Arrays;
@@ -40,8 +61,12 @@ final class StringBlock {
private final long mNative;
private final boolean mUseSparse;
private final boolean mOwnsNative;
+
private CharSequence[] mStrings;
private SparseArray<CharSequence> mSparseStrings;
+
+ @GuardedBy("this") private boolean mOpen = true;
+
StyleIDs mStyleIDs = null;
public StringBlock(byte[] data, boolean useSparse) {
@@ -141,12 +166,23 @@ final class StringBlock {
}
}
+ @Override
protected void finalize() throws Throwable {
try {
super.finalize();
} finally {
- if (mOwnsNative) {
- nativeDestroy(mNative);
+ close();
+ }
+ }
+
+ public void close() throws Throwable {
+ synchronized (this) {
+ if (mOpen) {
+ mOpen = false;
+
+ if (mOwnsNative) {
+ nativeDestroy(mNative);
+ }
}
}
}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 53503f47ca74..e56b6e0a33b8 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -188,11 +188,16 @@ public class GraphicsEnvironment {
if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) {
Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp);
- final String paths = getDebugLayerAppPaths(pm, gpuDebugLayerApp);
- if (paths != null) {
- // Append the path so files placed in the app's base directory will
- // override the external path
- layerPaths += paths + ":";
+ // If a colon is present, treat this as multiple apps, so Vulkan and GLES
+ // layer apps can be provided at the same time.
+ String[] layerApps = gpuDebugLayerApp.split(":");
+ for (int i = 0; i < layerApps.length; i++) {
+ String paths = getDebugLayerAppPaths(pm, layerApps[i]);
+ if (paths != null) {
+ // Append the path so files placed in the app's base directory will
+ // override the external path
+ layerPaths += paths + ":";
+ }
}
}
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index bd70f23c8b5d..ab19fd6e8ca1 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -646,9 +646,14 @@ public class ZygoteProcess {
ZygoteConfig.USAP_POOL_ENABLED, USAP_POOL_ENABLED_DEFAULT);
if (!propertyString.isEmpty()) {
- mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean(
+ if (SystemProperties.get("dalvik.vm.boot-image", "").endsWith("apex.art")) {
+ // TODO(b/119800099): Tweak usap configuration in jitzygote mode.
+ mUsapPoolEnabled = false;
+ } else {
+ mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean(
ZygoteConfig.USAP_POOL_ENABLED,
Boolean.parseBoolean(USAP_POOL_ENABLED_DEFAULT));
+ }
}
boolean valueChanged = origVal != mUsapPoolEnabled;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6e897975963f..e3b2d898f9b6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -955,6 +955,20 @@ public final class Settings {
"android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
/**
+ * Activity Action: Open the advanced power usage details page of an associated app.
+ * <p>
+ * Input: Intent's data URI set with an application name, using the
+ * "package" schema (like "package:com.my.app")
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VIEW_ADVANCED_POWER_USAGE_DETAIL =
+ "android.settings.VIEW_ADVANCED_POWER_USAGE_DETAIL";
+
+ /**
* Activity Action: Show screen for controlling background data
* restrictions for a particular application.
* <p>
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 8bb5f97ca878..5977bafd7cf1 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -53,5 +53,5 @@ oneway interface INotificationListener
void onNotificationDirectReply(String key);
void onSuggestedReplySent(String key, in CharSequence reply, int source);
void onActionClicked(String key, in Notification.Action action, int source);
- void onCapabilitiesChanged();
+ void onAllowedAdjustmentsChanged();
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index b4fd39701233..cafeb87691bd 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -220,10 +220,10 @@ public abstract class NotificationAssistantService extends NotificationListenerS
/**
* Implement this to know when a user has changed which features of
* their notifications the assistant can modify.
- * <p> Query {@link NotificationManager#getAllowedAssistantCapabilities()} to see what
+ * <p> Query {@link NotificationManager#getAllowedAssistantAdjustments()} to see what
* {@link Adjustment adjustments} you are currently allowed to make.</p>
*/
- public void onCapabilitiesChanged() {
+ public void onAllowedAdjustmentsChanged() {
}
/**
@@ -361,8 +361,8 @@ public abstract class NotificationAssistantService extends NotificationListenerS
}
@Override
- public void onCapabilitiesChanged() {
- mHandler.obtainMessage(MyHandler.MSG_ON_CAPABILITIES_CHANGED).sendToTarget();
+ public void onAllowedAdjustmentsChanged() {
+ mHandler.obtainMessage(MyHandler.MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED).sendToTarget();
}
}
@@ -374,7 +374,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
public static final int MSG_ON_ACTION_INVOKED = 7;
- public static final int MSG_ON_CAPABILITIES_CHANGED = 8;
+ public static final int MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED = 8;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -456,8 +456,8 @@ public abstract class NotificationAssistantService extends NotificationListenerS
onActionInvoked(key, action, source);
break;
}
- case MSG_ON_CAPABILITIES_CHANGED: {
- onCapabilitiesChanged();
+ case MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED: {
+ onAllowedAdjustmentsChanged();
break;
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 016f4aacef2a..3ec21e39e514 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -16,6 +16,7 @@
package android.service.notification;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
@@ -1399,7 +1400,7 @@ public abstract class NotificationListenerService extends Service {
}
@Override
- public void onCapabilitiesChanged() {
+ public void onAllowedAdjustmentsChanged() {
// no-op in the listener
}
@@ -1680,6 +1681,7 @@ public abstract class NotificationListenerService extends Service {
*
* @return the time of the last alerting behavior, in milliseconds.
*/
+ @CurrentTimeMillisLong
public long getLastAudiblyAlertedMillis() {
return mLastAudiblyAlertedMs;
}
diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java
index cd2b6ce3dc67..c290dffc42c9 100644
--- a/core/java/android/util/proto/ProtoInputStream.java
+++ b/core/java/android/util/proto/ProtoInputStream.java
@@ -16,8 +16,6 @@
package android.util.proto;
-import android.annotation.TestApi;
-
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@@ -64,7 +62,6 @@ import java.util.ArrayList;
*
* @hide
*/
-@TestApi
public final class ProtoInputStream extends ProtoStream {
public static final int NO_MORE_FIELDS = -1;
diff --git a/core/java/android/util/proto/TEST_MAPPING b/core/java/android/util/proto/TEST_MAPPING
new file mode 100644
index 000000000000..cf9f0772ac2d
--- /dev/null
+++ b/core/java/android/util/proto/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "ProtoInputStreamTests"
+ }
+ ]
+} \ No newline at end of file
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 2896bd049e7c..c50a3aa8ac7c 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -932,7 +932,8 @@ public final class ViewTreeObserver {
* @param listener listener to add
* @see View#setSystemGestureExclusionRects(List)
*/
- public void addOnSystemGestureExclusionRectsChangedListener(Consumer<List<Rect>> listener) {
+ public void addOnSystemGestureExclusionRectsChangedListener(
+ @NonNull Consumer<List<Rect>> listener) {
checkIsAlive();
if (mGestureExclusionListeners == null) {
mGestureExclusionListeners = new CopyOnWriteArray<>();
@@ -945,7 +946,8 @@ public final class ViewTreeObserver {
* @see #addOnSystemGestureExclusionRectsChangedListener(Consumer)
* @see View#setSystemGestureExclusionRects(List)
*/
- public void removeOnSystemGestureExclusionRectsChangedListener(Consumer<List<Rect>> listener) {
+ public void removeOnSystemGestureExclusionRectsChangedListener(
+ @NonNull Consumer<List<Rect>> listener) {
checkIsAlive();
if (mGestureExclusionListeners == null) {
return;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 3544a8733c68..a9463e998bbb 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2329,6 +2329,70 @@ public abstract class Window {
return 0;
}
+ /**
+ * Sets whether the system should ensure that the status bar has enough
+ * contrast when a fully transparent background is requested.
+ *
+ * <p>If set to this value, the system will determine whether a scrim is necessary
+ * to ensure that the status bar has enough contrast with the contents of
+ * this app, and set an appropriate effective bar background color accordingly.
+ *
+ * <p>When the status bar color has a non-zero alpha value, the value of this
+ * property has no effect.
+ *
+ * @see android.R.attr#ensureStatusBarContrastWhenTransparent
+ * @hide pending API
+ */
+ public void setEnsureStatusBarContrastWhenTransparent(boolean ensureContrast) {
+ }
+
+ /**
+ * Returns whether the system is ensuring that the status bar has enough contrast when a
+ * fully transparent background is requested.
+ *
+ * <p>When the status bar color has a non-zero alpha value, the value of this
+ * property has no effect.
+ *
+ * @see android.R.attr#ensureStatusBarContrastWhenTransparent
+ * @return true, if the system is ensuring contrast, false otherwise.
+ * @hide pending API
+ */
+ public boolean isEnsureStatusBarContrastWhenTransparent() {
+ return false;
+ }
+
+ /**
+ * Sets whether the system should ensure that the navigation bar has enough
+ * contrast when a fully transparent background is requested.
+ *
+ * <p>If set to this value, the system will determine whether a scrim is necessary
+ * to ensure that the navigation bar has enough contrast with the contents of
+ * this app, and set an appropriate effective bar background color accordingly.
+ *
+ * <p>When the navigation bar color has a non-zero alpha value, the value of this
+ * property has no effect.
+ *
+ * @see android.R.attr#ensureNavigationBarContrastWhenTransparent
+ * @hide pending API
+ */
+ public void setEnsureNavigationBarContrastWhenTransparent(boolean ensureContrast) {
+ }
+
+ /**
+ * Returns whether the system is ensuring that the navigation bar has enough contrast when a
+ * fully transparent background is requested.
+ *
+ * <p>When the navigation bar color has a non-zero alpha value, the value of this
+ * property has no effect.
+ *
+ * @return true, if the system is ensuring contrast, false otherwise.
+ * @see android.R.attr#ensureNavigationBarContrastWhenTransparent
+ * @hide pending API
+ */
+ public boolean isEnsureNavigationBarContrastWhenTransparent() {
+ return false;
+ }
+
/** @hide */
public void setTheme(int resId) {
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 1c0030d20c63..d945e139dd0f 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -38,6 +38,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+
import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
import android.animation.Animator;
@@ -125,6 +126,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
// The height of a window which has not in DIP.
private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
+ private static final int SCRIM_LIGHT = 0x99ffffff; // 60% white
+
public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
@@ -1237,19 +1240,31 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private int calculateStatusBarColor() {
return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_STATUS,
- mSemiTransparentBarColor, mWindow.mStatusBarColor);
+ mSemiTransparentBarColor, mWindow.mStatusBarColor,
+ getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+ mWindow.mEnsureStatusBarContrastWhenTransparent);
}
private int calculateNavigationBarColor() {
return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_NAVIGATION,
- mSemiTransparentBarColor, mWindow.mNavigationBarColor);
+ mSemiTransparentBarColor, mWindow.mNavigationBarColor,
+ getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ mWindow.mEnsureNavigationBarContrastWhenTransparent
+ && getContext().getResources().getBoolean(R.bool.config_navBarNeedsScrim));
}
public static int calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor,
- int barColor) {
- return (flags & translucentFlag) != 0 ? semiTransparentBarColor
- : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? barColor
- : Color.BLACK;
+ int barColor, int sysuiVis, int lightSysuiFlag, boolean scrimTransparent) {
+ if ((flags & translucentFlag) != 0) {
+ return semiTransparentBarColor;
+ } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
+ return Color.BLACK;
+ } else if (scrimTransparent && barColor == Color.TRANSPARENT) {
+ boolean light = (sysuiVis & lightSysuiFlag) != 0;
+ return light ? SCRIM_LIGHT : semiTransparentBarColor;
+ } else {
+ return barColor;
+ }
}
private int getCurrentColor(ColorViewState state) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 04559e4e55c6..16d6c52d82ea 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -50,6 +50,7 @@ import android.media.AudioManager;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
@@ -247,6 +248,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private boolean mForcedStatusBarColor = false;
private boolean mForcedNavigationBarColor = false;
+ boolean mEnsureStatusBarContrastWhenTransparent;
+ boolean mEnsureNavigationBarContrastWhenTransparent;
+
@UnsupportedAppUsage
private CharSequence mTitle = null;
@@ -2439,6 +2443,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
+ final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
final boolean targetHcNeedsOptions = context.getResources().getBoolean(
R.bool.target_honeycomb_needs_options_menu);
final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
@@ -2457,6 +2462,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
0x00000000);
}
+ if (!targetPreQ) {
+ mEnsureStatusBarContrastWhenTransparent = a.getBoolean(
+ R.styleable.Window_ensureStatusBarContrastWhenTransparent, false);
+ mEnsureNavigationBarContrastWhenTransparent = a.getBoolean(
+ R.styleable.Window_ensureNavigationBarContrastWhenTransparent, true);
+ }
WindowManager.LayoutParams params = getAttributes();
@@ -3845,6 +3856,32 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
return mNavigationBarDividerColor;
}
+ @Override
+ public void setEnsureStatusBarContrastWhenTransparent(boolean ensureContrast) {
+ mEnsureStatusBarContrastWhenTransparent = ensureContrast;
+ if (mDecor != null) {
+ mDecor.updateColorViews(null, false /* animate */);
+ }
+ }
+
+ @Override
+ public boolean isEnsureStatusBarContrastWhenTransparent() {
+ return mEnsureStatusBarContrastWhenTransparent;
+ }
+
+ @Override
+ public void setEnsureNavigationBarContrastWhenTransparent(boolean ensureContrast) {
+ mEnsureNavigationBarContrastWhenTransparent = ensureContrast;
+ if (mDecor != null) {
+ mDecor.updateColorViews(null, false /* animate */);
+ }
+ }
+
+ @Override
+ public boolean isEnsureNavigationBarContrastWhenTransparent() {
+ return mEnsureNavigationBarContrastWhenTransparent;
+ }
+
public void setIsStartingWindow(boolean isStartingWindow) {
mIsStartingWindow = isStartingWindow;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b94eb1626e37..cc3b3a4c7ccb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1637,8 +1637,8 @@
android:label="@string/permlab_bluetooth"
android:protectionLevel="normal" />
- <!-- @SystemApi Allows an application to suspend other apps, which will prevent the user
- from using them until they are unsuspended.
+ <!-- @SystemApi @TestApi Allows an application to suspend other apps, which will prevent the
+ user from using them until they are unsuspended.
@hide
-->
<permission android:name="android.permission.SUSPEND_APPS"
diff --git a/core/res/res/drawable/ic_bluetooth_share_icon.xml b/core/res/res/drawable/ic_bluetooth_share_icon.xml
index 2446402be93f..2152af55d5b6 100644
--- a/core/res/res/drawable/ic_bluetooth_share_icon.xml
+++ b/core/res/res/drawable/ic_bluetooth_share_icon.xml
@@ -19,9 +19,9 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="@android:color/accent_device_default">
+ android:tint="@android:color/accent_device_default_light">
<path
android:fillColor="@android:color/white"
android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" />
-</vector> \ No newline at end of file
+</vector>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9d48fe384f3f..a5104242c71c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2091,6 +2091,40 @@
Corresponds to {@link android.view.Window#setNavigationBarDividerColor(int)}. -->
<attr name="navigationBarDividerColor" format="color" />
+ <!-- Sets whether the system should ensure that the status bar has enough
+ contrast when a fully transparent background is requested.
+
+ <p>If set to this value, the system will determine whether a scrim is necessary
+ to ensure that the status bar has enough contrast with the contents of
+ this app, and set an appropriate effective bar background color accordingly.
+
+ <p>When the status bar color has a non-zero alpha value, the value of this
+ attribute has no effect.
+
+ <p>If the app does not target at least {@link android.os.Build.VERSION_CODES#Q Q},
+ this attribute is ignored.
+
+ @see android.view.Window#setEnsureStatusBarContrastWhenTransparent
+ @hide pendingAPI -->
+ <attr name="ensureStatusBarContrastWhenTransparent" format="boolean" />
+
+ <!-- Sets whether the system should ensure that the navigation bar has enough
+ contrast when a fully transparent background is requested.
+
+ <p>If set to this value, the system will determine whether a scrim is necessary
+ to ensure that the navigation bar has enough contrast with the contents of
+ this app, and set an appropriate effective bar background color accordingly.
+
+ <p>When the navigation bar color has a non-zero alpha value, the value of this
+ attribute has no effect.
+
+ <p>If the app does not target at least {@link android.os.Build.VERSION_CODES#Q Q},
+ this attribute is ignored.
+
+ @see android.view.Window#setEnsureNavigationBarContrastWhenTransparent
+ @hide pendingApi -->
+ <attr name="ensureNavigationBarContrastWhenTransparent" format="boolean" />
+
<!-- The duration, in milliseconds, of the window background fade duration
when transitioning into or away from an Activity when called with an
Activity Transition. Corresponds to
@@ -8980,6 +9014,10 @@
<!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar
color. -->
<attr name="navigationBarColor"/>
+ <!-- @hide From Window.ensureStatusBarContrastWhenTransparent -->
+ <attr name="ensureStatusBarContrastWhenTransparent"/>
+ <!-- @hide From Window.ensureNavigationBarContrastWhenTransparent -->
+ <attr name="ensureNavigationBarContrastWhenTransparent"/>
</declare-styleable>
<declare-styleable name="Shortcut">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fe4fd133dbef..8fcdc7b1d748 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3254,6 +3254,10 @@
<!-- Controls the size of the back gesture inset. -->
<dimen name="config_backGestureInset">0dp</dimen>
+ <!-- Controls whether the navbar needs a scrim with
+ {@link Window#setEnsureNavigationBarContrastWhenTransparent}. -->
+ <bool name="config_navBarNeedsScrim">true</bool>
+
<!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
These values are in DPs and will be converted to pixel sizes internally. -->
<string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dc8c62c9489e..8cdce6bd6533 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2848,6 +2848,7 @@
<java-symbol type="integer" name="config_navBarInteractionMode" />
<java-symbol type="bool" name="config_navBarCanMove" />
<java-symbol type="bool" name="config_navBarTapThrough" />
+ <java-symbol type="bool" name="config_navBarNeedsScrim" />
<java-symbol type="dimen" name="config_backGestureInset" />
<java-symbol type="color" name="system_bar_background_semi_transparent" />
diff --git a/media/OWNERS b/media/OWNERS
index 72c89529974b..a33a990f5ba4 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,3 +1,4 @@
+andrewlewis@google.com
chz@google.com
dwkang@google.com
elaurent@google.com
diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java
index db33e8239175..72c18f6148f0 100644
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ b/media/apex/java/android/media/MediaPlayer2.java
@@ -546,7 +546,7 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
@Override
void process() {
if (getState() == PLAYER_STATE_PLAYING) {
- pause();
+ native_pause();
}
playNextDataSource();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index 0a20eaa0b888..a371a1d8db01 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.content.res.TypedArray;
import android.os.UserHandle;
import android.util.AttributeSet;
+import android.view.Display;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -203,4 +204,16 @@ public class CarFacetButton extends LinearLayout {
mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
}
}
+
+ /**
+ * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
+ * a display.
+ */
+ public int getDisplayId() {
+ Display display = getDisplay();
+ if (display == null) {
+ return Display.INVALID_DISPLAY;
+ }
+ return display.getDisplayId();
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 7811a1caeb88..d20038db9151 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -22,10 +22,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.view.Display;
+import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -40,15 +43,16 @@ import javax.inject.Singleton;
@Singleton
public class CarFacetButtonController {
- protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
- protected HashMap<String, CarFacetButton> mButtonsByPackage = new HashMap<>();
- protected HashMap<String, CarFacetButton> mButtonsByComponentName = new HashMap<>();
- protected CarFacetButton mSelectedFacetButton;
+ protected ButtonMap mButtonsByCategory = new ButtonMap();
+ protected ButtonMap mButtonsByPackage = new ButtonMap();
+ protected ButtonMap mButtonsByComponentName = new ButtonMap();
+ protected HashSet<CarFacetButton> mSelectedFacetButtons;
protected Context mContext;
@Inject
public CarFacetButtonController(Context context) {
mContext = context;
+ mSelectedFacetButtons = new HashSet<>();
}
/**
@@ -59,27 +63,40 @@ public class CarFacetButtonController {
public void addFacetButton(CarFacetButton facetButton) {
String[] categories = facetButton.getCategories();
for (int i = 0; i < categories.length; i++) {
- mButtonsByCategory.put(categories[i], facetButton);
+ mButtonsByCategory.add(categories[i], facetButton);
}
String[] facetPackages = facetButton.getFacetPackages();
for (int i = 0; i < facetPackages.length; i++) {
- mButtonsByPackage.put(facetPackages[i], facetButton);
+ mButtonsByPackage.add(facetPackages[i], facetButton);
}
String[] componentNames = facetButton.getComponentName();
for (int i = 0; i < componentNames.length; i++) {
- mButtonsByComponentName.put(componentNames[i], facetButton);
+ mButtonsByComponentName.add(componentNames[i], facetButton);
}
- // Using the following as a default button for display id info it's not
- // attached to a screen at this point so it can't be extracted here.
- mSelectedFacetButton = facetButton;
}
public void removeAll() {
mButtonsByCategory.clear();
mButtonsByPackage.clear();
mButtonsByComponentName.clear();
- mSelectedFacetButton = null;
+ mSelectedFacetButtons.clear();
+ }
+
+ /**
+ * Iterate through a view looking for CarFacetButtons and adding them to the controller if found
+ *
+ * @param v the View that may contain CarFacetButtons
+ */
+ public void addAllFacetButtons(View v) {
+ if (v instanceof CarFacetButton) {
+ addFacetButton((CarFacetButton) v);
+ } else if (v instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) v;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ addAllFacetButtons(viewGroup.getChildAt(i));
+ }
+ }
}
/**
@@ -94,12 +111,10 @@ public class CarFacetButtonController {
* @param stackInfoList of the currently running application
*/
public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
- int displayId = getDisplayId();
ActivityManager.StackInfo validStackInfo = null;
- for (ActivityManager.StackInfo stackInfo : stackInfoList) {
- // If the display id is unknown or it matches the stack, it's valid for use
- if ((displayId == -1 || displayId == stackInfo.displayId)
- && stackInfo.topActivity != null) {
+ for (ActivityManager.StackInfo stackInfo :stackInfoList) {
+ // Find the first stack info with a topActivity
+ if (stackInfo.topActivity != null) {
validStackInfo = stackInfo;
break;
}
@@ -110,12 +125,20 @@ public class CarFacetButtonController {
return;
}
- if (mSelectedFacetButton != null) {
- mSelectedFacetButton.setSelected(false);
+ if (mSelectedFacetButtons != null) {
+ Iterator<CarFacetButton> iterator = mSelectedFacetButtons.iterator();
+ while(iterator.hasNext()) {
+ CarFacetButton carFacetButton = iterator.next();
+ if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
+ carFacetButton.setSelected(false);
+ iterator.remove();
+ }
+ }
}
String packageName = validStackInfo.topActivity.getPackageName();
- CarFacetButton facetButton = findFacetButtongByComponentName(validStackInfo.topActivity);
+ HashSet<CarFacetButton> facetButton =
+ findFacetButtonByComponentName(validStackInfo.topActivity);
if (facetButton == null) {
facetButton = mButtonsByPackage.get(packageName);
}
@@ -127,26 +150,21 @@ public class CarFacetButtonController {
}
}
- if (facetButton != null && facetButton.getVisibility() == View.VISIBLE) {
- facetButton.setSelected(true);
- mSelectedFacetButton = facetButton;
- }
-
- }
-
- private int getDisplayId() {
- if (mSelectedFacetButton != null) {
- Display display = mSelectedFacetButton.getDisplay();
- if (display != null) {
- return display.getDisplayId();
+ if (facetButton != null) {
+ for (CarFacetButton carFacetButton : facetButton) {
+ if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
+ carFacetButton.setSelected(true);
+ mSelectedFacetButtons.add(carFacetButton);
+ }
}
}
- return -1;
+
}
- private CarFacetButton findFacetButtongByComponentName(ComponentName componentName) {
- CarFacetButton button = mButtonsByComponentName.get(componentName.flattenToShortString());
- return (button != null) ? button :
+ private HashSet<CarFacetButton> findFacetButtonByComponentName(ComponentName componentName) {
+ HashSet<CarFacetButton> buttons =
+ mButtonsByComponentName.get(componentName.flattenToShortString());
+ return (buttons != null) ? buttons :
mButtonsByComponentName.get(componentName.flattenToString());
}
@@ -168,4 +186,18 @@ public class CarFacetButtonController {
}
return null;
}
+
+ // simple multi-map
+ private static class ButtonMap extends HashMap<String, HashSet<CarFacetButton>> {
+
+ public boolean add(String key, CarFacetButton value) {
+ if (containsKey(key)) {
+ return get(key).add(value);
+ }
+ HashSet<CarFacetButton> set = new HashSet<>();
+ set.add(value);
+ put(key, set);
+ return true;
+ }
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 9bcc1ab90e47..44e8874f8386 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -187,11 +187,13 @@ public class CarStatusBar extends StatusBar implements
if (mIsKeyguard) {
updateNavBarForKeyguardContent();
}
+ // CarFacetButtonController was reset therefore we need to re-add the status bar elements
+ // to the controller.
+ mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow);
}
private void addTemperatureViewToController(View v) {
if (v instanceof TemperatureView) {
- Log.d(TAG, "addTemperatureViewToController: found ");
mHvacController.addHvacTextView((TemperatureView) v);
} else if (v instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) v;
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index 530c73a2448b..fb5c16b92930 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -19,6 +19,7 @@ package com.android.settingslib.fuelgauge;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
@@ -33,7 +34,25 @@ import android.util.Slog;
public class BatterySaverUtils {
private static final String TAG = "BatterySaverUtils";
- public static final String EXTRA_CONFIRM_ONLY = "extra_confirm_only";
+ /**
+ * When set to "true" the notification will be a generic confirm message instead of asking the
+ * user if they want to turn on battery saver. If set to false the dialog will specifically
+ * talk about turning on battery saver and provide a button for taking the action.
+ */
+ public static final String EXTRA_CONFIRM_TEXT_ONLY = "extra_confirm_only";
+ /**
+ * Ignored if {@link #EXTRA_CONFIRM_TEXT_ONLY} is "false". Can be set to any of the values in
+ * {@link PowerManager.AutoPowerSaveModeTriggers}. If set the dialog will set the power
+ * save mode trigger to the specified value after the user acknowledges the trigger.
+ */
+ public static final String EXTRA_POWER_SAVE_MODE_TRIGGER = "extra_power_save_mode_trigger";
+ /**
+ * Ignored if {@link #EXTRA_CONFIRM_TEXT_ONLY} is "false". can be set to any value between
+ * 0-100 that will be used if {@link #EXTRA_POWER_SAVE_MODE_TRIGGER} is
+ * {@link PowerManager#POWER_SAVE_MODE_TRIGGER_PERCENTAGE}.
+ */
+ public static final String EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL =
+ "extra_power_save_mode_trigger_level";
private BatterySaverUtils() {
}
@@ -98,7 +117,10 @@ public class BatterySaverUtils {
}
final ContentResolver cr = context.getContentResolver();
- if (enable && needFirstTimeWarning && maybeShowBatterySaverConfirmation(context, false)) {
+ final Bundle confirmationExtras = new Bundle(1);
+ confirmationExtras.putBoolean(EXTRA_CONFIRM_TEXT_ONLY, false);
+ if (enable && needFirstTimeWarning
+ && maybeShowBatterySaverConfirmation(context, confirmationExtras)) {
return false;
}
if (enable && !needFirstTimeWarning) {
@@ -118,7 +140,7 @@ public class BatterySaverUtils {
&& Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0
&& Secure.getInt(cr,
Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) {
- showAutoBatterySaverSuggestion(context, false);
+ showAutoBatterySaverSuggestion(context, confirmationExtras);
}
}
@@ -129,34 +151,36 @@ public class BatterySaverUtils {
/**
* Shows the battery saver confirmation warning if it hasn't been acknowledged by the user in
- * the past before. When confirmOnly is true, the dialog will have generic info about battery
- * saver but will only update that the user has been shown the notification and take no
- * further action. if confirmOnly is false it will show a more specific version of the dialog
- * that toggles battery saver when acknowledged
+ * the past before. Various extras can be provided that will change the behavior of this
+ * notification as well as the ui for it.
* @param context A valid context
- * @param confirmOnly Whether to show the actionless generic dialog (true) or the specific one
- * that toggles battery saver (false)
+ * @param extras Any extras to include in the intent to trigger this confirmation that will
+ * help the system disambiguate what to show/do
+ *
* @return True if it showed the notification because it has not been previously acknowledged.
+ * @see #EXTRA_CONFIRM_TEXT_ONLY
+ * @see #EXTRA_POWER_SAVE_MODE_TRIGGER
+ * @see #EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL
*/
- public static boolean maybeShowBatterySaverConfirmation(Context context, boolean confirmOnly) {
+ public static boolean maybeShowBatterySaverConfirmation(Context context, Bundle extras) {
if (Secure.getInt(context.getContentResolver(),
Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) != 0) {
return false; // Already shown.
}
context.sendBroadcast(
- getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION, confirmOnly));
+ getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION, extras));
return true;
}
- private static void showAutoBatterySaverSuggestion(Context context, boolean confirmOnly) {
- context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, confirmOnly));
+ private static void showAutoBatterySaverSuggestion(Context context, Bundle extras) {
+ context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, extras));
}
- private static Intent getSystemUiBroadcast(String action, boolean confirmOnly) {
+ private static Intent getSystemUiBroadcast(String action, Bundle extras) {
final Intent i = new Intent(action);
i.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
i.setPackage(SYSUI_PACKAGE);
- i.putExtra(EXTRA_CONFIRM_ONLY, confirmOnly);
+ i.putExtras(extras);
return i;
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 10f727bc7189..e92aa519b9b7 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -22,15 +22,19 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioAttributes;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.text.Annotation;
import android.text.Layout;
@@ -547,9 +551,15 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
updateNotification();
}
- private void showStartSaverConfirmation(boolean confirmOnly) {
+ private void showStartSaverConfirmation(Bundle extras) {
if (mSaverConfirmation != null) return;
final SystemUIDialog d = new SystemUIDialog(mContext);
+ final boolean confirmOnly = extras.getBoolean(BatterySaverUtils.EXTRA_CONFIRM_TEXT_ONLY);
+ final int batterySaverTriggerMode =
+ extras.getInt(BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
+ final int batterySaverTriggerLevel =
+ extras.getInt(BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL, 0);
d.setMessage(getBatterySaverDescription());
// Sad hack for http://b/78261259 and http://b/78298335. Otherwise "Battery" may be split
@@ -563,14 +573,25 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
if (confirmOnly) {
d.setTitle(R.string.battery_saver_confirmation_title_generic);
d.setPositiveButton(com.android.internal.R.string.confirm_battery_saver,
- (dialog, which) -> Secure.putInt(
- mContext.getContentResolver(),
- Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
- 1));
+ (dialog, which) -> {
+ final ContentResolver resolver = mContext.getContentResolver();
+ Secure.putInt(
+ resolver,
+ Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
+ 1);
+ Settings.Global.putInt(
+ resolver,
+ Global.AUTOMATIC_POWER_SAVE_MODE,
+ batterySaverTriggerMode);
+ Settings.Global.putInt(
+ resolver,
+ Global.LOW_POWER_MODE_TRIGGER_LEVEL,
+ batterySaverTriggerLevel);
+ });
} else {
d.setTitle(R.string.battery_saver_confirmation_title);
d.setPositiveButton(R.string.battery_saver_confirmation_ok,
- (dialog, which) -> setSaverMode(true, false));
+ (dialog, which) -> setSaverMode(true, false));
d.setNegativeButton(android.R.string.cancel, null);
}
d.setShowForAllUsers(true);
@@ -731,7 +752,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
dismissLowBatteryNotification();
} else if (action.equals(ACTION_SHOW_START_SAVER_CONFIRMATION)) {
dismissLowBatteryNotification();
- showStartSaverConfirmation(intent.getBooleanExtra(EXTRA_CONFIRM_ONLY, false));
+ showStartSaverConfirmation(intent.getExtras());
} else if (action.equals(ACTION_DISMISSED_WARNING)) {
dismissLowBatteryWarning();
} else if (ACTION_CLICKED_TEMP_WARNING.equals(action)) {
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml
index 380ff346c6f2..4d844a1c4e0f 100644
--- a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml
@@ -16,7 +16,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
- android:tint="@*android:color/accent_device_default"
+ android:tint="@*android:color/accent_device_default_light"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp" >
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_settings_bluetooth.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_settings_bluetooth.xml
index 452a032c49e9..19731249bfc7 100644
--- a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_settings_bluetooth.xml
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_settings_bluetooth.xml
@@ -16,6 +16,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp" >
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml
index 8719f155f94a..df79827ce624 100644
--- a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml
@@ -16,7 +16,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
- android:tint="@*android:color/accent_device_default"
+ android:tint="@*android:color/accent_device_default_light"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp" >
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_settings_bluetooth.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_settings_bluetooth.xml
index 09643e606350..58800c8d29be 100644
--- a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_settings_bluetooth.xml
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_settings_bluetooth.xml
@@ -16,6 +16,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp" >
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml
index d0f9d9b567f3..feed70c49138 100644
--- a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml
@@ -16,7 +16,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
- android:tint="@*android:color/accent_device_default"
+ android:tint="@*android:color/accent_device_default_light"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp" >
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_settings_bluetooth.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_settings_bluetooth.xml
index 3d270b3fce42..5e1a5f20c6d6 100644
--- a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_settings_bluetooth.xml
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_settings_bluetooth.xml
@@ -16,6 +16,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp" >
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
index 704ff2ee2aa3..86fd47bfc63f 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
@@ -33,4 +33,8 @@
<!-- Controls the size of the back gesture inset. -->
<dimen name="config_backGestureInset">20dp</dimen>
+ <!-- Controls whether the navbar needs a scrim with
+ {@link Window#setEnsureNavigationBarContrastWhenTransparent}. -->
+ <bool name="config_navBarNeedsScrim">false</bool>
+
</resources>
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0402b8fb9285..fdc01e0c1093 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -704,6 +704,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mClient.asBinder().linkToDeath(mClientVulture, 0);
} catch (RemoteException e) {
Slog.w(TAG, "could not set binder death listener on autofill client: " + e);
+ mClientVulture = null;
}
}
@@ -714,6 +715,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (!unlinked) {
Slog.w(TAG, "unlinking vulture from death failed for " + mActivityToken);
}
+ mClientVulture = null;
}
}
@@ -1243,18 +1245,55 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* when necessary.
*/
public void logContextCommitted() {
- mHandler.sendMessage(obtainMessage(
- Session::doLogContextCommitted, this));
+ mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this));
+ }
+
+ private void handleLogContextCommitted() {
+ final FillResponse lastResponse;
+ synchronized (mLock) {
+ lastResponse = getLastResponseLocked("logContextCommited()");
+ }
+
+ if (lastResponse == null) {
+ Slog.w(TAG, "handleLogContextCommitted(): last response is null");
+ return;
+ }
+
+ // Merge UserData if necessary.
+ // Fields in packageUserData will override corresponding fields in genericUserData.
+ final UserData genericUserData = mService.getUserData();
+ final UserData packageUserData = lastResponse.getUserData();
+ final FieldClassificationUserData userData;
+ if (packageUserData == null && genericUserData == null) {
+ userData = null;
+ } else if (packageUserData != null && genericUserData != null) {
+ userData = new CompositeUserData(genericUserData, packageUserData);
+ } else if (packageUserData != null) {
+ userData = packageUserData;
+ } else {
+ userData = mService.getUserData();
+ }
+
+ final FieldClassificationStrategy fcStrategy = mService.getFieldClassificationStrategy();
+
+ // Sets field classification scores
+ if (userData != null && fcStrategy != null) {
+ logFieldClassificationScore(fcStrategy, userData);
+ } else {
+ logContextCommitted(null, null);
+ }
}
- private void doLogContextCommitted() {
+ private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds,
+ @Nullable ArrayList<FieldClassification> detectedFieldClassifications) {
synchronized (mLock) {
- logContextCommittedLocked();
+ logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications);
}
}
@GuardedBy("mLock")
- private void logContextCommittedLocked() {
+ private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds,
+ @Nullable ArrayList<FieldClassification> detectedFieldClassifications) {
final FillResponse lastResponse = getLastResponseLocked("logContextCommited()");
if (lastResponse == null) return;
@@ -1308,21 +1347,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
- // Merge UserData if necessary.
- // Fields in packageUserData will override corresponding fields in genericUserData.
- final UserData genericUserData = mService.getUserData();
- final UserData packageUserData = lastResponse.getUserData();
- final FieldClassificationUserData userData;
- if (packageUserData == null && genericUserData == null) {
- userData = null;
- } else if (packageUserData != null && genericUserData != null) {
- userData = new CompositeUserData(genericUserData, packageUserData);
- } else if (packageUserData != null) {
- userData = packageUserData;
- } else {
- userData = mService.getUserData();
- }
-
for (int i = 0; i < mViewStates.size(); i++) {
final ViewState viewState = mViewStates.valueAt(i);
final int state = viewState.getState();
@@ -1447,33 +1471,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- // Sets field classification scores
- final FieldClassificationStrategy fcStrategy = mService.getFieldClassificationStrategy();
- if (userData != null && fcStrategy != null) {
- logFieldClassificationScoreLocked(fcStrategy, ignoredDatasets, changedFieldIds,
- changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds,
- userData, mViewStates.values());
- } else {
- mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
- ignoredDatasets, changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds,
- mComponentName, mCompatMode);
- }
+ mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
+ ignoredDatasets, changedFieldIds, changedDatasetIds,
+ manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds,
+ detectedFieldClassifications, mComponentName, mCompatMode);
}
/**
* Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for
* {@code fieldId} based on its {@code currentValue} and {@code userData}.
*/
- private void logFieldClassificationScoreLocked(
- @NonNull FieldClassificationStrategy fcStrategy,
- @NonNull ArraySet<String> ignoredDatasets,
- @NonNull ArrayList<AutofillId> changedFieldIds,
- @NonNull ArrayList<String> changedDatasetIds,
- @NonNull ArrayList<AutofillId> manuallyFilledFieldIds,
- @NonNull ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
- @NonNull FieldClassificationUserData userData,
- @NonNull Collection<ViewState> viewStates) {
+ private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy,
+ @NonNull FieldClassificationUserData userData) {
final String[] userValues = userData.getValues();
final String[] categoryIds = userData.getCategoryIds();
@@ -1499,6 +1508,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>(
maxFieldsSize);
+ final Collection<ViewState> viewStates;
+ synchronized (mLock) {
+ viewStates = mViewStates.values();
+ }
+
final int viewsSize = viewStates.size();
// First, we get all scores.
@@ -1514,10 +1528,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final RemoteCallback callback = new RemoteCallback((result) -> {
if (result == null) {
if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
- mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
- ignoredDatasets, changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds,
- mComponentName, mCompatMode);
+ logContextCommitted(null, null);
return;
}
final Scores scores = result.getParcelable(EXTRA_SCORES);
@@ -1544,7 +1555,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final Float currentScore = scoresByField.get(categoryId);
if (currentScore != null && currentScore > score) {
if (sVerbose) {
- Slog.v(TAG, "skipping score " + score
+ Slog.v(TAG, "skipping score " + score
+ " because it's less than " + currentScore);
}
continue;
@@ -1554,8 +1565,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ autofillId);
}
scoresByField.put(categoryId, score);
- }
- else if (sVerbose) {
+ } else if (sVerbose) {
Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId);
}
}
@@ -1579,10 +1589,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
- mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
- ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
- manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
- mComponentName, mCompatMode);
+ logContextCommitted(detectedFieldIds, detectedFieldClassifications);
});
fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index dec47a1082fc..7f1b25ca3ca3 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1516,6 +1516,11 @@ public class NotificationManagerService extends SystemService {
}
@VisibleForTesting
+ void setZenHelper(ZenModeHelper zenHelper) {
+ mZenModeHelper = zenHelper;
+ }
+
+ @VisibleForTesting
void setIsAutomotive(boolean isAutomotive) {
mIsAutomotive = isAutomotive;
}
@@ -2856,7 +2861,7 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public List<String> getAllowedAssistantCapabilities(String pkg) {
+ public List<String> getAllowedAssistantAdjustments(String pkg) {
checkCallerIsSystemOrSameApp(pkg);
if (!isCallerSystemOrPhone()
@@ -2864,11 +2869,11 @@ public class NotificationManagerService extends SystemService {
throw new SecurityException("Not currently an assistant");
}
- return mAssistants.getAllowedAssistantCapabilities();
+ return mAssistants.getAllowedAssistantAdjustments();
}
@Override
- public void allowAssistantCapability(String adjustmentType) {
+ public void allowAssistantAdjustment(String adjustmentType) {
checkCallerIsSystemOrSystemUiOrShell();
mAssistants.allowAdjustmentType(adjustmentType);
@@ -2876,7 +2881,7 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void disallowAssistantCapability(String adjustmentType) {
+ public void disallowAssistantAdjustment(String adjustmentType) {
checkCallerIsSystemOrSystemUiOrShell();
mAssistants.disallowAdjustmentType(adjustmentType);
@@ -3563,7 +3568,7 @@ public class NotificationManagerService extends SystemService {
return;
}
boolean accessAllowed = false;
- String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
+ String[] packages = mPackageManagerClient.getPackagesForUid(uid);
final int packageCount = packages.length;
for (int i = 0; i < packageCount; i++) {
if (mConditionProviders.isPackageOrComponentAllowed(
@@ -7410,7 +7415,7 @@ public class NotificationManagerService extends SystemService {
}
}
- protected List<String> getAllowedAssistantCapabilities() {
+ protected List<String> getAllowedAssistantAdjustments() {
synchronized (mLock) {
List<String> types = new ArrayList<>();
types.addAll(mAllowedAdjustments);
@@ -7470,7 +7475,7 @@ public class NotificationManagerService extends SystemService {
private void notifyCapabilitiesChanged(final ManagedServiceInfo info) {
final INotificationListener assistant = (INotificationListener) info.service;
try {
- assistant.onCapabilitiesChanged();
+ assistant.onAllowedAdjustmentsChanged();
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify assistant (capabilities): " + assistant, ex);
}
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 714c2274af8e..9713befc91f6 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1688,6 +1688,8 @@ class TaskRecord extends ConfigurationContainer {
int colorBackground = 0;
int statusBarColor = 0;
int navigationBarColor = 0;
+ boolean statusBarContrastWhenTransparent = false;
+ boolean navigationBarContrastWhenTransparent = false;
boolean topActivity = true;
for (--activityNdx; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
@@ -1711,12 +1713,17 @@ class TaskRecord extends ConfigurationContainer {
colorBackground = r.taskDescription.getBackgroundColor();
statusBarColor = r.taskDescription.getStatusBarColor();
navigationBarColor = r.taskDescription.getNavigationBarColor();
+ statusBarContrastWhenTransparent =
+ r.taskDescription.getEnsureStatusBarContrastWhenTransparent();
+ navigationBarContrastWhenTransparent =
+ r.taskDescription.getEnsureNavigationBarContrastWhenTransparent();
}
}
topActivity = false;
}
lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename,
- colorPrimary, colorBackground, statusBarColor, navigationBarColor);
+ colorPrimary, colorBackground, statusBarColor, navigationBarColor,
+ statusBarContrastWhenTransparent, navigationBarContrastWhenTransparent);
if (mTask != null) {
mTask.setTaskDescription(lastTaskDescription);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 6fe8b43db139..f31416cdc620 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -362,11 +362,9 @@ class TaskSnapshotController {
}
final int color = ColorUtils.setAlphaComponent(
task.getTaskDescription().getBackgroundColor(), 255);
- final int statusBarColor = task.getTaskDescription().getStatusBarColor();
- final int navigationBarColor = task.getTaskDescription().getNavigationBarColor();
final LayoutParams attrs = mainWindow.getAttrs();
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
- attrs.privateFlags, attrs.systemUiVisibility, statusBarColor, navigationBarColor);
+ attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription());
final int width = mainWindow.getFrameLw().width();
final int height = mainWindow.getFrameLw().height();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 39b56625c011..5d99db5a00de 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -18,6 +18,8 @@ package com.android.server.wm;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
@@ -148,9 +150,8 @@ class TaskSnapshotSurface implements StartingSurface {
final Rect tmpStableInsets = new Rect();
final InsetsState mTmpInsetsState = new InsetsState();
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
- int backgroundColor = WHITE;
- int statusBarColor = 0;
- int navigationBarColor = 0;
+ final TaskDescription taskDescription = new TaskDescription();
+ taskDescription.setBackgroundColor(WHITE);
final int sysUiVis;
final int windowFlags;
final int windowPrivateFlags;
@@ -194,11 +195,9 @@ class TaskSnapshotSurface implements StartingSurface {
layoutParams.systemUiVisibility = sysUiVis;
layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
- final TaskDescription taskDescription = task.getTaskDescription();
- if (taskDescription != null) {
- backgroundColor = taskDescription.getBackgroundColor();
- statusBarColor = taskDescription.getStatusBarColor();
- navigationBarColor = taskDescription.getNavigationBarColor();
+ final TaskDescription td = task.getTaskDescription();
+ if (td != null) {
+ taskDescription.copyFrom(td);
}
taskBounds = new Rect();
task.getBounds(taskBounds);
@@ -216,8 +215,8 @@ class TaskSnapshotSurface implements StartingSurface {
// Local call.
}
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
- surfaceControl, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
- navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds,
+ surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
+ windowFlags, windowPrivateFlags, taskBounds,
currentOrientation);
window.setOuter(snapshotSurface);
try {
@@ -234,9 +233,9 @@ class TaskSnapshotSurface implements StartingSurface {
@VisibleForTesting
TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
- TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
- int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
- Rect taskBounds, int currentOrientation) {
+ TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
+ int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
+ int currentOrientation) {
mService = service;
mSurface = new Surface();
mHandler = new Handler(mService.mH.getLooper());
@@ -245,11 +244,12 @@ class TaskSnapshotSurface implements StartingSurface {
mSurfaceControl = surfaceControl;
mSnapshot = snapshot;
mTitle = title;
+ int backgroundColor = taskDescription.getBackgroundColor();
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
mTaskBounds = taskBounds;
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
- windowPrivateFlags, sysUiVis, statusBarColor, navigationBarColor);
- mStatusBarColor = statusBarColor;
+ windowPrivateFlags, sysUiVis, taskDescription);
+ mStatusBarColor = taskDescription.getStatusBarColor();
mOrientationOnCreation = currentOrientation;
}
@@ -490,7 +490,7 @@ class TaskSnapshotSurface implements StartingSurface {
private final int mSysUiVis;
SystemBarBackgroundPainter( int windowFlags, int windowPrivateFlags, int sysUiVis,
- int statusBarColor, int navigationBarColor) {
+ TaskDescription taskDescription) {
mWindowFlags = windowFlags;
mWindowPrivateFlags = windowPrivateFlags;
mSysUiVis = sysUiVis;
@@ -498,11 +498,17 @@ class TaskSnapshotSurface implements StartingSurface {
final int semiTransparent = context.getColor(
R.color.system_bar_background_semi_transparent);
mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
- semiTransparent, statusBarColor);
+ semiTransparent, taskDescription.getStatusBarColor(), sysUiVis,
+ SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+ taskDescription.getEnsureStatusBarContrastWhenTransparent());
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
- FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, navigationBarColor);
+ FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
+ taskDescription.getNavigationBarColor(), sysUiVis,
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ taskDescription.getEnsureNavigationBarContrastWhenTransparent()
+ && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
mStatusBarPaint.setColor(mStatusBarColor);
- mNavigationBarPaint.setColor(navigationBarColor);
+ mNavigationBarPaint.setColor(mNavigationBarColor);
}
void setInsets(Rect contentInsets, Rect stableInsets) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index bafcd5f0f8bd..355ff63d18f7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -348,6 +348,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
+ when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG});
// write to a test file; the system file isn't readable from tests
mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -4948,22 +4949,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals(1, mService.getNotificationRecordCount());
}
- public void testGetAllowedAssistantCapabilities() throws Exception {
- List<String> capabilities = mBinderService.getAllowedAssistantCapabilities(null);
+ @Test
+ public void testGetAllowedAssistantAdjustments() throws Exception {
+ List<String> capabilities = mBinderService.getAllowedAssistantAdjustments(null);
assertNotNull(capabilities);
for (int i = capabilities.size() - 1; i >= 0; i--) {
String capability = capabilities.get(i);
- mBinderService.disallowAssistantCapability(capability);
- assertEquals(i + 1, mBinderService.getAllowedAssistantCapabilities(null).size());
- List<String> currentCapabilities = mBinderService.getAllowedAssistantCapabilities(null);
+ mBinderService.disallowAssistantAdjustment(capability);
+ assertEquals(i + 1, mBinderService.getAllowedAssistantAdjustments(null).size());
+ List<String> currentCapabilities = mBinderService.getAllowedAssistantAdjustments(null);
assertNotNull(currentCapabilities);
assertFalse(currentCapabilities.contains(capability));
}
}
+ @Test
public void testAdjustRestrictedKey() throws Exception {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
when(mAssistants.isAdjustmentAllowed(KEY_IMPORTANCE)).thenReturn(true);
when(mAssistants.isAdjustmentAllowed(KEY_USER_SENTIMENT)).thenReturn(false);
@@ -4981,8 +4986,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals(USER_SENTIMENT_NEUTRAL, r.getUserSentiment());
}
+ @Test
public void testAutomaticZenRuleValidation_policyFilterAgreement() throws Exception {
- ComponentName owner = mock(ComponentName.class);
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ mService.setZenHelper(mock(ZenModeHelper.class));
+ ComponentName owner = new ComponentName(mContext, this.getClass());
ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
boolean isEnabled = true;
AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
@@ -4990,7 +4999,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
try {
mBinderService.addAutomaticZenRule(rule);
- fail("Zen policy only aplies to priority only mode");
+ fail("Zen policy only applies to priority only mode");
} catch (IllegalArgumentException e) {
// yay
}
@@ -5004,6 +5013,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.addAutomaticZenRule(rule);
}
+ @Test
public void testAreNotificationsEnabledForPackage_crossUser() throws Exception {
try {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
@@ -5020,6 +5030,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mUid + UserHandle.PER_USER_RANGE);
}
+ @Test
public void testAreBubblesAllowedForPackage_crossUser() throws Exception {
try {
mBinderService.areBubblesAllowedForPackage(mContext.getPackageName(),
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index ca815eca9a1f..cb6dc6d42bd5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -30,6 +30,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
+import android.app.ActivityManager.TaskDescription;
import android.app.ActivityManager.TaskSnapshot;
import android.content.ComponentName;
import android.graphics.Canvas;
@@ -38,7 +39,6 @@ import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.view.Surface;
import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
@@ -67,8 +67,17 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
- Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds,
- ORIENTATION_PORTRAIT);
+ createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
+ taskBounds, ORIENTATION_PORTRAIT);
+ }
+
+ private static TaskDescription createTaskDescription(int background, int statusBar,
+ int navigationBar) {
+ final TaskDescription td = new TaskDescription();
+ td.setBackgroundColor(background);
+ td.setStatusBarColor(statusBar);
+ td.setNavigationBarColor(navigationBar);
+ return td;
}
private void setupSurface(int width, int height) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9c63a82b5673..0d3bc1db831f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3790,6 +3790,7 @@ public class TelephonyManager {
* @hide
* nobody seems to call this.
*/
+ @TestApi
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getLine1AlphaTag() {
return getLine1AlphaTag(getSubId());
diff --git a/tests/ProtoInputStreamTests/Android.mk b/tests/ProtoInputStreamTests/Android.mk
new file mode 100644
index 000000000000..eb747cc2cdcc
--- /dev/null
+++ b/tests/ProtoInputStreamTests/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := ProtoInputStreamTests
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-proto-files-under, src)
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx.test.rules \
+ frameworks-base-testutils \
+ mockito-target-minus-junit4
+
+include $(BUILD_PACKAGE) \ No newline at end of file
diff --git a/tests/ProtoInputStreamTests/AndroidManifest.xml b/tests/ProtoInputStreamTests/AndroidManifest.xml
new file mode 100644
index 000000000000..c11aa7393431
--- /dev/null
+++ b/tests/ProtoInputStreamTests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.protoinputstream">
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.protoinputstream"
+ android:label="ProtoInputStream Tests">
+ </instrumentation>
+</manifest> \ No newline at end of file
diff --git a/tests/ProtoInputStreamTests/AndroidTest.xml b/tests/ProtoInputStreamTests/AndroidTest.xml
new file mode 100644
index 000000000000..51ab88e4d4d0
--- /dev/null
+++ b/tests/ProtoInputStreamTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Configuration for ProtoInputStream Tests">
+ <option name="test-suite-tag" value="ProtoInputStreamTests" />
+ <option name="config-descriptor:metadata" key="component" value="metrics" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="ProtoInputStreamTests.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.test.protoinputstream" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/ProtoInputStreamTests/TEST_MAPPING b/tests/ProtoInputStreamTests/TEST_MAPPING
new file mode 100644
index 000000000000..cf9f0772ac2d
--- /dev/null
+++ b/tests/ProtoInputStreamTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "ProtoInputStreamTests"
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java
new file mode 100644
index 000000000000..c21c4033a0ff
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamBoolTest extends TestCase {
+
+ /**
+ * Test reading single bool field
+ */
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ /**
+ * Implementation of testRead with a given chunkSize.
+ */
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 3 -> 1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ boolean[] results = new boolean[2];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readBoolean(fieldId2);
+ break;
+ case (int) fieldId3:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(false, results[0]);
+ assertEquals(true, results[1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(false);
+ testReadCompat(true);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(boolean val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+ final long fieldId = fieldFlags | ((long) 130 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.boolField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ boolean result = false; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readBoolean(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.boolField, result);
+ }
+
+
+ /**
+ * Test reading repeated bool field
+ */
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ /**
+ * Implementation of testRepeated with a given chunkSize.
+ */
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+
+ // 4 -> 0
+ (byte) 0x20,
+ (byte) 0x00,
+ // 4 -> 1
+ (byte) 0x20,
+ (byte) 0x01,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+
+ // 3 -> 0
+ (byte) 0x18,
+ (byte) 0x00,
+ // 3 -> 1
+ (byte) 0x18,
+ (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ boolean[][] results = new boolean[3][2];
+ int[] indices = new int[3];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readBoolean(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readBoolean(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readBoolean(fieldId3);
+ break;
+ case (int) fieldId4:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(false, results[0][0]);
+ assertEquals(false, results[0][1]);
+ assertEquals(true, results[1][0]);
+ assertEquals(true, results[1][1]);
+ assertEquals(false, results[2][0]);
+ assertEquals(true, results[2][1]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new boolean[0]);
+ testRepeatedCompat(new boolean[]{false, true});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(boolean[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BOOL;
+ final long fieldId = fieldFlags | ((long) 131 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.boolFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ boolean[] result = new boolean[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readBoolean(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.boolFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.boolFieldRepeated[i], result[i]);
+ }
+ }
+
+ /**
+ * Test reading packed bool field
+ */
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ /**
+ * Implementation of testPacked with a given chunkSize.
+ */
+ public void testPacked(int chunkSize) throws IOException {
+
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 4 -> 0,1
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x01,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 3 -> 0,1
+ (byte) 0x1a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x01,
+ };
+
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ boolean[][] results = new boolean[3][2];
+ int[] indices = new int[3];
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readBoolean(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readBoolean(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readBoolean(fieldId3);
+ break;
+ case (int) fieldId4:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(false, results[0][0]);
+ assertEquals(false, results[0][1]);
+ assertEquals(true, results[1][0]);
+ assertEquals(true, results[1][1]);
+ assertEquals(false, results[2][0]);
+ assertEquals(true, results[2][1]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new boolean[0]);
+ testPackedCompat(new boolean[]{false, true});
+ }
+
+ /**
+ * Implementation of testPackedCompat with a given value.
+ */
+ private void testPackedCompat(boolean[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_BOOL;
+ final long fieldId = fieldFlags | ((long) 132 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.boolFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ boolean[] result = new boolean[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readBoolean(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.boolFieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.boolFieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readBoolean(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readBoolean(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readBoolean(fieldId3);
+ // don't fail, length delimited is ok (represents packed booleans)
+ break;
+ case (int) fieldId6:
+ pi.readBoolean(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java
new file mode 100644
index 000000000000..09fe40edda6c
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+public class ProtoInputStreamBytesTest extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> { } - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 5 -> { 0, 1, 2, 3, 4 }
+ (byte) 0x2a,
+ (byte) 0x05,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+ // 3 -> { 0, 1, 2, 3, 4, 5 }
+ (byte) 0x1a,
+ (byte) 0x06,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc }
+ (byte) 0x22,
+ (byte) 0x04,
+ (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ byte[][] results = new byte[4][];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0] = pi.readBytes(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readBytes(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readBytes(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readBytes(fieldId4);
+ break;
+ case (int) fieldId5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertTrue("Expected: [] Actual: " + Arrays.toString(results[0]),
+ Arrays.equals(new byte[]{}, results[0]));
+ assertTrue("Expected: [] Actual: " + Arrays.toString(results[1]),
+ Arrays.equals(new byte[]{}, results[1]));
+ assertTrue("Expected: [0, 1, 2, 3, 4, 5] Actual: " + Arrays.toString(results[2]),
+ Arrays.equals(new byte[]{0, 1, 2, 3, 4, 5}, results[2]));
+ assertTrue("Expected: [-1, -2, -3, -4] Actual: " + Arrays.toString(results[3]),
+ Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc},
+ results[3]));
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(new byte[0]);
+ testReadCompat(new byte[]{1, 2, 3, 4});
+ testReadCompat(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc});
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(byte[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+ final long fieldId = fieldFlags | ((long) 150 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.bytesField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ byte[] result = new byte[val.length];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readBytes(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.bytesField.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.bytesField[i], result[i]);
+ }
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> { } - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> { 0, 1, 2, 3, 4, 5 }
+ (byte) 0x1a,
+ (byte) 0x06,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc }
+ (byte) 0x22,
+ (byte) 0x04,
+ (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc,
+
+ // 5 -> { 0, 1, 2, 3, 4}
+ (byte) 0x2a,
+ (byte) 0x05,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> { } - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> { 0, 1, 2, 3, 4, 5 }
+ (byte) 0x1a,
+ (byte) 0x06,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc }
+ (byte) 0x22,
+ (byte) 0x04,
+ (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ byte[][][] results = new byte[4][2][];
+ int[] indices = new int[4];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readBytes(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readBytes(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readBytes(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readBytes(fieldId4);
+ break;
+ case (int) fieldId5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assert (Arrays.equals(new byte[]{}, results[0][0]));
+ assert (Arrays.equals(new byte[]{}, results[0][1]));
+ assert (Arrays.equals(new byte[]{}, results[1][0]));
+ assert (Arrays.equals(new byte[]{}, results[1][1]));
+ assert (Arrays.equals(new byte[]{1, 2, 3, 4}, results[2][0]));
+ assert (Arrays.equals(new byte[]{1, 2, 3, 4}, results[2][1]));
+ assert (Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc},
+ results[3][0]));
+ assert (Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc},
+ results[3][1]));
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new byte[0][]);
+ testRepeatedCompat(new byte[][]{
+ new byte[0],
+ new byte[]{1, 2, 3, 4},
+ new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc}
+ });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(byte[][] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BYTES;
+ final long fieldId = fieldFlags | ((long) 151 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.bytesFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ byte[][] result = new byte[val.length][]; // start off with default value
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readBytes(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.bytesFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.bytesFieldRepeated[i].length, result[i].length);
+ for (int j = 0; j < result[i].length; j++) {
+ assertEquals(readback.bytesFieldRepeated[i][j], result[i][j]);
+ }
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> {1}
+ (byte) 0x0a,
+ (byte) 0x01,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readBytes(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readBytes(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readBytes(fieldId3);
+ // don't fail, length delimited is ok
+ break;
+ case (int) fieldId6:
+ pi.readBytes(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java
new file mode 100644
index 000000000000..118fe3431e01
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamDoubleTest extends TestCase {
+
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x51,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x19,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x21,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x29,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x39,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x41,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x49,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ double[] results = new double[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readDouble(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readDouble(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readDouble(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readDouble(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readDouble(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readDouble(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7] = pi.readDouble(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8] = pi.readDouble(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0, results[0]);
+ assertEquals(1.0, results[1]);
+ assertEquals(-1234.432, results[2]);
+ assertEquals(42.42, results[3]);
+ assertEquals(Double.MIN_NORMAL, results[4]);
+ assertEquals(Double.MIN_VALUE, results[5]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6]);
+ assertEquals(Double.NaN, results[7]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1234.432);
+ testReadCompat(42.42);
+ testReadCompat(Double.MIN_NORMAL);
+ testReadCompat(Double.MIN_VALUE);
+ testReadCompat(Double.NEGATIVE_INFINITY);
+ testReadCompat(Double.NaN);
+ testReadCompat(Double.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(double val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+ final long fieldId = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.doubleField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ double result = 0.0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readDouble(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.doubleField, result);
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x19,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x21,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x29,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x39,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x41,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x49,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ // 10 -> 1
+ (byte) 0x51,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x19,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x21,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x29,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x39,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x41,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x49,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ double[][] results = new double[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readDouble(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readDouble(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readDouble(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readDouble(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readDouble(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readDouble(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readDouble(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7][indices[7]++] = pi.readDouble(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8][indices[8]++] = pi.readDouble(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0, results[0][0]);
+ assertEquals(0.0, results[0][1]);
+ assertEquals(1.0, results[1][0]);
+ assertEquals(1.0, results[1][1]);
+ assertEquals(-1234.432, results[2][0]);
+ assertEquals(-1234.432, results[2][1]);
+ assertEquals(42.42, results[3][0]);
+ assertEquals(42.42, results[3][1]);
+ assertEquals(Double.MIN_NORMAL, results[4][0]);
+ assertEquals(Double.MIN_NORMAL, results[4][1]);
+ assertEquals(Double.MIN_VALUE, results[5][0]);
+ assertEquals(Double.MIN_VALUE, results[5][1]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Double.NaN, results[7][0]);
+ assertEquals(Double.NaN, results[7][1]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new double[0]);
+ testRepeatedCompat(new double[]{0, 1, -1234.432, 42.42,
+ Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN,
+ Double.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(double[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE;
+ final long fieldId = fieldFlags | ((long) 11 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.doubleFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ double[] result = new double[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readDouble(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.doubleFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.doubleFieldRepeated[i], result[i]);
+ }
+ }
+
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x52,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1a,
+ (byte) 0x10,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x22,
+ (byte) 0x10,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x2a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x3a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x42,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x4a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ double[][] results = new double[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readDouble(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readDouble(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readDouble(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readDouble(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readDouble(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readDouble(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readDouble(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7][indices[7]++] = pi.readDouble(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8][indices[8]++] = pi.readDouble(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0, results[0][0]);
+ assertEquals(0.0, results[0][1]);
+ assertEquals(1.0, results[1][0]);
+ assertEquals(1.0, results[1][1]);
+ assertEquals(-1234.432, results[2][0]);
+ assertEquals(-1234.432, results[2][1]);
+ assertEquals(42.42, results[3][0]);
+ assertEquals(42.42, results[3][1]);
+ assertEquals(Double.MIN_NORMAL, results[4][0]);
+ assertEquals(Double.MIN_NORMAL, results[4][1]);
+ assertEquals(Double.MIN_VALUE, results[5][0]);
+ assertEquals(Double.MIN_VALUE, results[5][1]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Double.NaN, results[7][0]);
+ assertEquals(Double.NaN, results[7][1]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new double[0]);
+ testPackedCompat(new double[]{0, 1, -1234.432, 42.42,
+ Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN,
+ Double.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testPackedCompat with a given value.
+ */
+ private void testPackedCompat(double[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE;
+ final long fieldId = fieldFlags | ((long) 12 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.doubleFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ double[] result = new double[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readDouble(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.doubleFieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.doubleFieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x09,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readDouble(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readDouble(fieldId2);
+ // don't fail, fixed64 is ok
+ break;
+ case (int) fieldId3:
+ pi.readDouble(fieldId3);
+ // don't fail, length delimited is ok (represents packed doubles)
+ break;
+ case (int) fieldId6:
+ pi.readDouble(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java
new file mode 100644
index 000000000000..f55d95129588
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamEnumTest extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+ final long fieldId = fieldFlags | ((long) 160 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.outsideField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ // Nano proto drops values that are outside the range, so compare against val
+ assertEquals(val, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[]{});
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_ENUM;
+ final long fieldId = fieldFlags | ((long) 161 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.outsideFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ // Nano proto drops values that are outside the range, so compare against val
+ assertEquals(val.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(val[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[]{});
+ testPackedCompat(new int[]{0, 1});
+
+ // Nano proto has a bug. It gets the size with computeInt32SizeNoTag (correctly)
+ // but incorrectly uses writeRawVarint32 to write the value for negative numbers.
+ //testPackedCompat(new int[] { 0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_ENUM;
+ final long fieldId = fieldFlags | ((long) 162 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.outsideFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ // Nano proto drops values that are outside the range, so compare against val
+ assertEquals(val.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(val[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed enums)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java
new file mode 100644
index 000000000000..df68476f0c36
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamFixed32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+ final long fieldId = fieldFlags | ((long) 90 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32;
+ final long fieldId = fieldFlags | ((long) 91 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x32,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32;
+ final long fieldId = fieldFlags | ((long) 92 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x0d,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x04,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed fixed32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ // don't fail, fixed32 is ok
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java
new file mode 100644
index 000000000000..af4130b28cd8
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamFixed64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+ final long fieldId = fieldFlags | ((long) 100 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64;
+ final long fieldId = fieldFlags | ((long) 101 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 8 -> 1
+ (byte) 0x42,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64;
+ final long fieldId = fieldFlags | ((long) 102 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x09,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ // don't fail, fixed64 is ok
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed fixed64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java
new file mode 100644
index 000000000000..9bc07dc513e1
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamFloatTest extends TestCase {
+
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x55,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1d,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x25,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x45,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ float[] results = new float[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readFloat(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readFloat(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readFloat(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readFloat(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readFloat(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readFloat(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7] = pi.readFloat(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8] = pi.readFloat(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0f, results[0]);
+ assertEquals(1.0f, results[1]);
+ assertEquals(-1234.432f, results[2]);
+ assertEquals(42.42f, results[3]);
+ assertEquals(Float.MIN_NORMAL, results[4]);
+ assertEquals(Float.MIN_VALUE, results[5]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6]);
+ assertEquals(Float.NaN, results[7]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1234.432f);
+ testReadCompat(42.42f);
+ testReadCompat(Float.MIN_NORMAL);
+ testReadCompat(Float.MIN_VALUE);
+ testReadCompat(Float.NEGATIVE_INFINITY);
+ testReadCompat(Float.NaN);
+ testReadCompat(Float.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(float val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+ final long fieldId = fieldFlags | ((long) 20 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.floatField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ float result = 0.0f; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readFloat(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.floatField, result);
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1d,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x25,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x45,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+
+ // 10 -> 1
+ (byte) 0x55,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1d,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x25,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x45,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ float[][] results = new float[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readFloat(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readFloat(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readFloat(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readFloat(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readFloat(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readFloat(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readFloat(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7][indices[7]++] = pi.readFloat(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8][indices[8]++] = pi.readFloat(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0f, results[0][0]);
+ assertEquals(0.0f, results[0][1]);
+ assertEquals(1.0f, results[1][0]);
+ assertEquals(1.0f, results[1][1]);
+ assertEquals(-1234.432f, results[2][0]);
+ assertEquals(-1234.432f, results[2][1]);
+ assertEquals(42.42f, results[3][0]);
+ assertEquals(42.42f, results[3][1]);
+ assertEquals(Float.MIN_NORMAL, results[4][0]);
+ assertEquals(Float.MIN_NORMAL, results[4][1]);
+ assertEquals(Float.MIN_VALUE, results[5][0]);
+ assertEquals(Float.MIN_VALUE, results[5][1]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Float.NaN, results[7][0]);
+ assertEquals(Float.NaN, results[7][1]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new float[0]);
+ testRepeatedCompat(new float[]{0, 1, -1234.432f, 42.42f,
+ Float.MIN_NORMAL, Float.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.NaN,
+ Float.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(float[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FLOAT;
+ final long fieldId = fieldFlags | ((long) 21 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.floatFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ float[] result = new float[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readFloat(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.floatFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.floatFieldRepeated[i], result[i]);
+ }
+ }
+
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x52,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x22,
+ (byte) 0x08,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x42,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ float[][] results = new float[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readFloat(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readFloat(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readFloat(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readFloat(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readFloat(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readFloat(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readFloat(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7][indices[7]++] = pi.readFloat(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8][indices[8]++] = pi.readFloat(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0f, results[0][0]);
+ assertEquals(0.0f, results[0][1]);
+ assertEquals(1.0f, results[1][0]);
+ assertEquals(1.0f, results[1][1]);
+ assertEquals(-1234.432f, results[2][0]);
+ assertEquals(-1234.432f, results[2][1]);
+ assertEquals(42.42f, results[3][0]);
+ assertEquals(42.42f, results[3][1]);
+ assertEquals(Float.MIN_NORMAL, results[4][0]);
+ assertEquals(Float.MIN_NORMAL, results[4][1]);
+ assertEquals(Float.MIN_VALUE, results[5][0]);
+ assertEquals(Float.MIN_VALUE, results[5][1]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Float.NaN, results[7][0]);
+ assertEquals(Float.NaN, results[7][1]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new float[0]);
+ testPackedCompat(new float[]{0, 1, -1234.432f, 42.42f,
+ Float.MIN_NORMAL, Float.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.NaN,
+ Float.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testPackedCompat with a given value.
+ */
+ private void testPackedCompat(float[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FLOAT;
+ final long fieldId = fieldFlags | ((long) 22 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.floatFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ float[] result = new float[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readFloat(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.floatFieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.floatFieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x0d,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x04,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readFloat(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readFloat(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readFloat(fieldId3);
+ // don't fail, length delimited is ok (represents packed floats)
+ break;
+ case (int) fieldId6:
+ pi.readFloat(fieldId6);
+ // don't fail, fixed32 is ok
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java
new file mode 100644
index 000000000000..0065870486f2
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamInt32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+ final long fieldId = fieldFlags | ((long) 30 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32;
+ final long fieldId = fieldFlags | ((long) 31 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32;
+ final long fieldId = fieldFlags | ((long) 32 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed int32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java
new file mode 100644
index 000000000000..4d6d105e60b0
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamInt64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 8 -> Long.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+ final long fieldId = fieldFlags | ((long) 40 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> Long.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64;
+ final long fieldId = fieldFlags | ((long) 41 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 8 -> Long.MAX_VALUE
+ (byte) 0x42,
+ (byte) 0x12,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x12,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64;
+ final long fieldId = fieldFlags | ((long) 42 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed int64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java
new file mode 100644
index 000000000000..5e49eeafb8af
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamObjectTest extends TestCase {
+
+
+ class SimpleObject {
+ public char mChar;
+ public char mLargeChar;
+ public String mString;
+ public SimpleObject mNested;
+
+ void parseProto(ProtoInputStream pi) throws IOException {
+ final long uintFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long stringFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+ final long charId = uintFieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long largeCharId = uintFieldFlags | ((long) 5000 & 0x0ffffffffL);
+ final long stringId = stringFieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long nestedId = messageFieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) charId:
+ mChar = (char) pi.readInt(charId);
+ break;
+ case (int) largeCharId:
+ mLargeChar = (char) pi.readInt(largeCharId);
+ break;
+ case (int) stringId:
+ mString = pi.readString(stringId);
+ break;
+ case (int) nestedId:
+ long token = pi.start(nestedId);
+ mNested = new SimpleObject();
+ mNested.parseProto(pi);
+ pi.end(token);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Test reading an object with one char in it.
+ */
+ public void testObjectOneChar() throws IOException {
+ testObjectOneChar(0);
+ testObjectOneChar(1);
+ testObjectOneChar(5);
+ }
+
+ /**
+ * Implementation of testObjectOneChar for a given chunkSize.
+ */
+ private void testObjectOneChar(int chunkSize) throws IOException {
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long messageId1 = messageFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // Message 2 : { char 2 : 'c' }
+ (byte) 0x12, (byte) 0x02, (byte) 0x10, (byte) 0x63,
+ // Message 1 : { char 2 : 'b' }
+ (byte) 0x0a, (byte) 0x02, (byte) 0x10, (byte) 0x62,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject result = null;
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) messageId1:
+ final long token = pi.start(messageId1);
+ result = new SimpleObject();
+ result.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) messageId2:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNotNull(result);
+ assertEquals('b', result.mChar);
+ }
+
+ /**
+ * Test reading an object with one multibyte unicode char in it.
+ */
+ public void testObjectOneLargeChar() throws IOException {
+ testObjectOneLargeChar(0);
+ testObjectOneLargeChar(1);
+ testObjectOneLargeChar(5);
+ }
+
+ /**
+ * Implementation of testObjectOneLargeChar for a given chunkSize.
+ */
+ private void testObjectOneLargeChar(int chunkSize) throws IOException {
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long messageId1 = messageFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // Message 2 : { char 5000 : '\u3110' }
+ (byte) 0x12, (byte) 0x05, (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x02, (byte) 0x90, (byte) 0x62,
+ // Message 1 : { char 5000 : '\u3110' }
+ (byte) 0x0a, (byte) 0x05, (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x02, (byte) 0x90, (byte) 0x62,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject result = null;
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) messageId1:
+ final long token = pi.start(messageId1);
+ result = new SimpleObject();
+ result.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) messageId2:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNotNull(result);
+ assertEquals('\u3110', result.mLargeChar);
+ }
+
+ /**
+ * Test reading a char, then an object, then a char.
+ */
+ public void testObjectAndTwoChars() throws IOException {
+ testObjectAndTwoChars(0);
+ testObjectAndTwoChars(1);
+ testObjectAndTwoChars(5);
+ }
+
+ /**
+ * Implementation of testObjectAndTwoChars for a given chunkSize.
+ */
+ private void testObjectAndTwoChars(int chunkSize) throws IOException {
+ final long uintFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long charId1 = uintFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long charId4 = uintFieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 'a'
+ (byte) 0x08, (byte) 0x61,
+ // Message 1 : { char 2 : 'b' }
+ (byte) 0x12, (byte) 0x02, (byte) 0x10, (byte) 0x62,
+ // 4 -> 'c'
+ (byte) 0x20, (byte) 0x63,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject obj = null;
+ char char1 = '\0';
+ char char4 = '\0';
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) charId1:
+ char1 = (char) pi.readInt(charId1);
+ break;
+ case (int) messageId2:
+ final long token = pi.start(messageId2);
+ obj = new SimpleObject();
+ obj.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) charId4:
+ char4 = (char) pi.readInt(charId4);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals('a', char1);
+ assertNotNull(obj);
+ assertEquals('b', obj.mChar);
+ assertEquals('c', char4);
+ }
+
+ /**
+ * Test reading a char, then an object with an int and a string in it, then a char.
+ */
+ public void testComplexObject() throws IOException {
+ testComplexObject(0);
+ testComplexObject(1);
+ testComplexObject(5);
+ }
+
+ /**
+ * Implementation of testComplexObject for a given chunkSize.
+ */
+ private void testComplexObject(int chunkSize) throws IOException {
+ final long uintFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long charId1 = uintFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long charId4 = uintFieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 'x'
+ (byte) 0x08, (byte) 0x78,
+ // begin object 2
+ (byte) 0x12, (byte) 0x10,
+ // 2 -> 'y'
+ (byte) 0x10, (byte) 0x79,
+ // 4 -> "abcdefghijkl"
+ (byte) 0x22, (byte) 0x0c,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66,
+ (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6a, (byte) 0x6b, (byte) 0x6c,
+ // 4 -> 'z'
+ (byte) 0x20, (byte) 0x7a,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject obj = null;
+ char char1 = '\0';
+ char char4 = '\0';
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) charId1:
+ char1 = (char) pi.readInt(charId1);
+ break;
+ case (int) messageId2:
+ final long token = pi.start(messageId2);
+ obj = new SimpleObject();
+ obj.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) charId4:
+ char4 = (char) pi.readInt(charId4);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals('x', char1);
+ assertNotNull(obj);
+ assertEquals('y', obj.mChar);
+ assertEquals("abcdefghijkl", obj.mString);
+ assertEquals('z', char4);
+ }
+
+ /**
+ * Test reading 3 levels deep of objects.
+ */
+ public void testDeepObjects() throws IOException {
+ testDeepObjects(0);
+ testDeepObjects(1);
+ testDeepObjects(5);
+ }
+
+ /**
+ * Implementation of testDeepObjects for a given chunkSize.
+ */
+ private void testDeepObjects(int chunkSize) throws IOException {
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // begin object id 2
+ (byte) 0x12, (byte) 0x1a,
+ // 2 -> 'a'
+ (byte) 0x10, (byte) 0x61,
+ // begin nested object id 5
+ (byte) 0x2a, (byte) 0x15,
+ // 5000 -> '\u3110'
+ (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x02, (byte) 0x90, (byte) 0x62,
+ // begin nested object id 5
+ (byte) 0x2a, (byte) 0x0e,
+ // 4 -> "abcdefghijkl"
+ (byte) 0x22, (byte) 0x0c,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66,
+ (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6a, (byte) 0x6b, (byte) 0x6c,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject obj = null;
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) messageId2:
+ final long token = pi.start(messageId2);
+ obj = new SimpleObject();
+ obj.parseProto(pi);
+ pi.end(token);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNotNull(obj);
+ assertEquals('a', obj.mChar);
+ assertNotNull(obj.mNested);
+ assertEquals('\u3110', obj.mNested.mLargeChar);
+ assertNotNull(obj.mNested.mNested);
+ assertEquals("abcdefghijkl", obj.mNested.mNested.mString);
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> {1}
+ (byte) 0x0a,
+ (byte) 0x01,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readBytes(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readBytes(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readBytes(fieldId3);
+ // don't fail, length delimited is ok
+ break;
+ case (int) fieldId6:
+ pi.readBytes(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java
new file mode 100644
index 000000000000..75c88a44614b
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSFixed32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+ final long fieldId = fieldFlags | ((long) 110 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32;
+ final long fieldId = fieldFlags | ((long) 111 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x32,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32;
+ final long fieldId = fieldFlags | ((long) 112 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x04,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed sfixed32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ // don't fail, fixed32 is ok
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java
new file mode 100644
index 000000000000..4c65cf49318d
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSFixed64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 8 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+ final long fieldId = fieldFlags | ((long) 120 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64;
+ final long fieldId = fieldFlags | ((long) 121 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 8 -> 1
+ (byte) 0x42,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64;
+ final long fieldId = fieldFlags | ((long) 122 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ // don't fail, fixed32 is ok
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed sfixed64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java
new file mode 100644
index 000000000000..6854cd8aad28
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSInt32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+ final long fieldId = fieldFlags | ((long) 70 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32;
+ final long fieldId = fieldFlags | ((long) 71 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x02,
+ (byte) 0x02,
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32;
+ final long fieldId = fieldFlags | ((long) 72 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed sint32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java
new file mode 100644
index 000000000000..c53e9d72562a
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSInt64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+ final long fieldId = fieldFlags | ((long) 80 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64;
+ final long fieldId = fieldFlags | ((long) 81 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x02,
+ (byte) 0x02,
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x42,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x14,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64;
+ final long fieldId = fieldFlags | ((long) 82 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed sint64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java
new file mode 100644
index 000000000000..816d5f900a3d
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamStringTest extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, not written
+ // 2 -> "" - default value, not written
+ // 3 -> "abcd\u3110!"
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
+ (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
+ // 5 -> "Hi"
+ (byte) 0x2a,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+ // 4 -> "Hi"
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ String[] results = new String[4];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0] = pi.readString(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readString(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readString(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readString(fieldId4);
+ break;
+ case (int) fieldId5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNull(results[0]);
+ assertNull(results[1]);
+ assertEquals("abcd\u3110!", results[2]);
+ assertEquals("Hi", results[3]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat("");
+ testReadCompat("abcd\u3110!");
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(String val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+ final long fieldId = fieldFlags | ((long) 140 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.stringField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ String result = "";
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readString(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.stringField, result);
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> "" - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> "abcd\u3110!"
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
+ (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
+ // 4 -> "Hi"
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+
+ // 5 -> "Hi"
+ (byte) 0x2a,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> "" - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> "abcd\u3110!"
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
+ (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
+ // 4 -> "Hi"
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ String[][] results = new String[4][2];
+ int[] indices = new int[4];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readString(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readString(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readString(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readString(fieldId4);
+ break;
+ case (int) fieldId5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals("", results[0][0]);
+ assertEquals("", results[0][1]);
+ assertEquals("", results[1][0]);
+ assertEquals("", results[1][1]);
+ assertEquals("abcd\u3110!", results[2][0]);
+ assertEquals("abcd\u3110!", results[2][1]);
+ assertEquals("Hi", results[3][0]);
+ assertEquals("Hi", results[3][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new String[0]);
+ testRepeatedCompat(new String[]{"", "abcd\u3110!", "Hi"});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(String[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING;
+ final long fieldId = fieldFlags | ((long) 141 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.stringFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ String[] result = new String[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readString(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.stringFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.stringFieldRepeated[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> {1}
+ (byte) 0x0a,
+ (byte) 0x01,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readString(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readString(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readString(fieldId3);
+ // don't fail, length delimited is ok (represents packed booleans)
+ break;
+ case (int) fieldId6:
+ pi.readString(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java
new file mode 100644
index 000000000000..50fc537767a4
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamUInt32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long fieldId = fieldFlags | ((long) 50 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
+ final long fieldId = fieldFlags | ((long) 51 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
+ final long fieldId = fieldFlags | ((long) 52 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed uint32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java
new file mode 100644
index 000000000000..20969e9056a9
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamUInt64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+ final long fieldId = fieldFlags | ((long) 60 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64;
+ final long fieldId = fieldFlags | ((long) 61 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x42,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x12,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64;
+ final long fieldId = fieldFlags | ((long) 62 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed uint64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java
new file mode 100644
index 000000000000..cdf6ae20f370
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.protoinputstream;
+
+import junit.framework.TestSuite;
+
+public class ProtoTests {
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite(ProtoTests.class.getName());
+
+ suite.addTestSuite(ProtoInputStreamDoubleTest.class);
+ suite.addTestSuite(ProtoInputStreamFloatTest.class);
+ suite.addTestSuite(ProtoInputStreamInt32Test.class);
+ suite.addTestSuite(ProtoInputStreamInt64Test.class);
+ suite.addTestSuite(ProtoInputStreamUInt32Test.class);
+ suite.addTestSuite(ProtoInputStreamUInt64Test.class);
+ suite.addTestSuite(ProtoInputStreamSInt32Test.class);
+ suite.addTestSuite(ProtoInputStreamSInt64Test.class);
+ suite.addTestSuite(ProtoInputStreamFixed32Test.class);
+ suite.addTestSuite(ProtoInputStreamFixed64Test.class);
+ suite.addTestSuite(ProtoInputStreamSFixed32Test.class);
+ suite.addTestSuite(ProtoInputStreamSFixed64Test.class);
+ suite.addTestSuite(ProtoInputStreamBoolTest.class);
+ suite.addTestSuite(ProtoInputStreamStringTest.class);
+ suite.addTestSuite(ProtoInputStreamBytesTest.class);
+ suite.addTestSuite(ProtoInputStreamEnumTest.class);
+ suite.addTestSuite(ProtoInputStreamObjectTest.class);
+
+ return suite;
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto
new file mode 100644
index 000000000000..9ff1d7e2d19a
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.test.protoinputstream;
+
+/**
+ * Enum that outside the scope of any classes.
+ */
+enum Outside {
+ OUTSIDE_0 = 0;
+ OUTSIDE_1 = 1;
+};
+
+/**
+ * Message that is recursive.
+ */
+message Nested {
+ optional int32 data = 10001;
+ optional Nested nested = 10002;
+};
+
+/**
+ * Message with all of the field types.
+ */
+message All {
+ /**
+ * Enum that is inside the scope of a class.
+ */
+ enum Inside {
+ option allow_alias = true;
+ INSIDE_0 = 0;
+ INSIDE_1 = 1;
+ INSIDE_1A = 1;
+ };
+
+ optional double double_field = 10;
+ repeated double double_field_repeated = 11;
+ repeated double double_field_packed = 12 [packed=true];
+
+ optional float float_field = 20;
+ repeated float float_field_repeated = 21;
+ repeated float float_field_packed = 22 [packed=true];
+
+ optional int32 int32_field = 30;
+ repeated int32 int32_field_repeated = 31;
+ repeated int32 int32_field_packed = 32 [packed=true];
+
+ optional int64 int64_field = 40;
+ repeated int64 int64_field_repeated = 41;
+ repeated int64 int64_field_packed = 42 [packed=true];
+
+ optional uint32 uint32_field = 50;
+ repeated uint32 uint32_field_repeated = 51;
+ repeated uint32 uint32_field_packed = 52 [packed=true];
+
+ optional uint64 uint64_field = 60;
+ repeated uint64 uint64_field_repeated = 61;
+ repeated uint64 uint64_field_packed = 62 [packed=true];
+
+ optional sint32 sint32_field = 70;
+ repeated sint32 sint32_field_repeated = 71;
+ repeated sint32 sint32_field_packed = 72 [packed=true];
+
+ optional sint64 sint64_field = 80;
+ repeated sint64 sint64_field_repeated = 81;
+ repeated sint64 sint64_field_packed = 82 [packed=true];
+
+ optional fixed32 fixed32_field = 90;
+ repeated fixed32 fixed32_field_repeated = 91;
+ repeated fixed32 fixed32_field_packed = 92 [packed=true];
+
+ optional fixed64 fixed64_field = 100;
+ repeated fixed64 fixed64_field_repeated = 101;
+ repeated fixed64 fixed64_field_packed = 102 [packed=true];
+
+ optional sfixed32 sfixed32_field = 110;
+ repeated sfixed32 sfixed32_field_repeated = 111;
+ repeated sfixed32 sfixed32_field_packed = 112 [packed=true];
+
+ optional sfixed64 sfixed64_field = 120;
+ repeated sfixed64 sfixed64_field_repeated = 121;
+ repeated sfixed64 sfixed64_field_packed = 122 [packed=true];
+
+ optional bool bool_field = 130;
+ repeated bool bool_field_repeated = 131;
+ repeated bool bool_field_packed = 132 [packed=true];
+
+ optional string string_field = 140;
+ repeated string string_field_repeated = 141;
+
+ optional bytes bytes_field = 150;
+ repeated bytes bytes_field_repeated = 151;
+
+ optional Outside outside_field = 160;
+ repeated Outside outside_field_repeated = 161;
+ repeated Outside outside_field_packed = 162 [packed=true];
+
+ optional Nested nested_field = 170;
+ repeated Nested nested_field_repeated = 171;
+};