diff options
18 files changed, 269 insertions, 30 deletions
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index 22d6fcdc7983..92b630f3063a 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -63,6 +63,7 @@ import java.lang.reflect.Modifier; * your new thread. The given Runnable or Message will then be scheduled * in the Handler's message queue and processed when appropriate. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class Handler { /* * Set this flag to true to detect anonymous, local or member classes diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java index 4dd797a70fcb..fcd57313a28d 100644 --- a/core/java/android/os/HandlerThread.java +++ b/core/java/android/os/HandlerThread.java @@ -25,6 +25,7 @@ import android.annotation.Nullable; * <p> * Note that just like with a regular {@link Thread}, {@link #start()} must still be called. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class HandlerThread extends Thread { int mPriority; int mTid = -1; diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index 712d328e9dc9..ddf2b61324ad 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -24,6 +24,8 @@ import android.util.Printer; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import java.util.Objects; + /** * Class used to run a message loop for a thread. Threads by default do * not have a message loop associated with them; to create one, call @@ -54,6 +56,7 @@ import android.util.proto.ProtoOutputStream; * } * }</pre> */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class Looper { /* * API Implementation Note: @@ -144,6 +147,30 @@ public final class Looper { } /** + * Force the application's main looper to the given value. The main looper is typically + * configured automatically by the OS, so this capability is only intended to enable testing. + * + * @hide + */ + public static void setMainLooperForTest(@NonNull Looper looper) { + synchronized (Looper.class) { + sMainLooper = Objects.requireNonNull(looper); + } + } + + /** + * Clear the application's main looper to be undefined. The main looper is typically + * configured automatically by the OS, so this capability is only intended to enable testing. + * + * @hide + */ + public static void clearMainLooperForTest() { + synchronized (Looper.class) { + sMainLooper = null; + } + } + + /** * Set the transaction observer for all Loopers in this process. * * @hide @@ -282,11 +309,7 @@ public final class Looper { // Allow overriding a threshold with a system prop. e.g. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start' - final int thresholdOverride = - SystemProperties.getInt("log.looper." - + Process.myUid() + "." - + Thread.currentThread().getName() - + ".slow", -1); + final int thresholdOverride = getThresholdOverride(); me.mSlowDeliveryDetected = false; @@ -297,6 +320,18 @@ public final class Looper { } } + @android.ravenwood.annotation.RavenwoodReplace + private static int getThresholdOverride() { + return SystemProperties.getInt("log.looper." + + Process.myUid() + "." + + Thread.currentThread().getName() + + ".slow", -1); + } + + private static int getThresholdOverride$ravenwood() { + return -1; + } + private static boolean showSlowLog(long threshold, long measureStart, long measureEnd, String what, Message msg) { final long actualTime = measureEnd - measureStart; diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index 72fb4ae03a63..da647e2b78cb 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -33,6 +33,7 @@ import com.android.internal.annotations.VisibleForTesting; * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull * them from a pool of recycled objects.</p> */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class Message implements Parcelable { /** * User-defined message code so that the recipient can identify diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index 9d8a71bf4abd..c60f949da408 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -40,6 +40,9 @@ import java.util.ArrayList; * <p>You can retrieve the MessageQueue for the current thread with * {@link Looper#myQueue() Looper.myQueue()}. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass +@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( + "com.android.hoststubgen.nativesubstitution.MessageQueue_host") public final class MessageQueue { private static final String TAG = "MessageQueue"; private static final boolean DEBUG = false; @@ -194,6 +197,7 @@ public final class MessageQueue { * @see OnFileDescriptorEventListener * @see #removeOnFileDescriptorEventListener */ + @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd, @OnFileDescriptorEventListener.Events int events, @NonNull OnFileDescriptorEventListener listener) { @@ -221,6 +225,7 @@ public final class MessageQueue { * @see OnFileDescriptorEventListener * @see #addOnFileDescriptorEventListener */ + @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); @@ -231,6 +236,7 @@ public final class MessageQueue { } } + @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, OnFileDescriptorEventListener listener) { final int fdNum = fd.getInt$(); diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 13572fb1bbbb..11660f9930c3 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -842,15 +842,19 @@ public class Process { return "amd64".equals(System.getProperty("os.arch")); } - private static SomeArgs sIdentity$ravenwood; + private static ThreadLocal<SomeArgs> sIdentity$ravenwood; /** @hide */ @android.ravenwood.annotation.RavenwoodKeep - public static void init$ravenwood(int uid, int pid) { - final SomeArgs args = SomeArgs.obtain(); - args.argi1 = uid; - args.argi2 = pid; - sIdentity$ravenwood = args; + public static void init$ravenwood(final int uid, final int pid) { + sIdentity$ravenwood = ThreadLocal.withInitial(() -> { + final SomeArgs args = SomeArgs.obtain(); + args.argi1 = uid; + args.argi2 = pid; + args.argi3 = Long.hashCode(Thread.currentThread().getId()); + args.argi4 = THREAD_PRIORITY_DEFAULT; + return args; + }); } /** @hide */ @@ -870,7 +874,7 @@ public class Process { /** @hide */ public static final int myPid$ravenwood() { - return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi2; + return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi2; } /** @@ -886,10 +890,16 @@ public class Process { * Returns the identifier of the calling thread, which be used with * {@link #setThreadPriority(int, int)}. */ + @android.ravenwood.annotation.RavenwoodReplace public static final int myTid() { return Os.gettid(); } + /** @hide */ + public static final int myTid$ravenwood() { + return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi3; + } + /** * Returns the identifier of this process's uid. This is the kernel uid * that the process is running under, which is the identity of its @@ -903,7 +913,7 @@ public class Process { /** @hide */ public static final int myUid$ravenwood() { - return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi1; + return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi1; } /** @@ -1086,9 +1096,22 @@ public class Process { * not have permission to modify the given thread, or to use the given * priority. */ + @android.ravenwood.annotation.RavenwoodReplace public static final native void setThreadPriority(int tid, int priority) throws IllegalArgumentException, SecurityException; + /** @hide */ + public static final void setThreadPriority$ravenwood(int tid, int priority) { + final SomeArgs args = + Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get(); + if (args.argi3 == tid) { + args.argi4 = priority; + } else { + throw new UnsupportedOperationException( + "Cross-thread priority management not yet available in Ravenwood"); + } + } + /** * Call with 'false' to cause future calls to {@link #setThreadPriority(int)} to * throw an exception if passed a background-level thread priority. This is only @@ -1226,9 +1249,15 @@ public class Process { * * @see #setThreadPriority(int, int) */ + @android.ravenwood.annotation.RavenwoodReplace public static final native void setThreadPriority(int priority) throws IllegalArgumentException, SecurityException; + /** @hide */ + public static final void setThreadPriority$ravenwood(int priority) { + setThreadPriority(myTid(), priority); + } + /** * Return the current priority of a thread, based on Linux priorities. * @@ -1242,9 +1271,22 @@ public class Process { * @throws IllegalArgumentException Throws IllegalArgumentException if * <var>tid</var> does not exist. */ + @android.ravenwood.annotation.RavenwoodReplace public static final native int getThreadPriority(int tid) throws IllegalArgumentException; + /** @hide */ + public static final int getThreadPriority$ravenwood(int tid) { + final SomeArgs args = + Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get(); + if (args.argi3 == tid) { + return args.argi4; + } else { + throw new UnsupportedOperationException( + "Cross-thread priority management not yet available in Ravenwood"); + } + } + /** * Return the current scheduling policy of a thread, based on Linux. * diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java index 49a0bd3289aa..2e6cccbd435f 100644 --- a/core/java/android/os/SystemClock.java +++ b/core/java/android/os/SystemClock.java @@ -110,6 +110,11 @@ public final class SystemClock { private static volatile IAlarmManager sIAlarmManager; /** + * Since {@code nanoTime()} is arbitrary, anchor our Ravenwood clocks against it. + */ + private static final long sAnchorNanoTime$ravenwood = System.nanoTime(); + + /** * This class is uninstantiable. */ @UnsupportedAppUsage @@ -193,9 +198,7 @@ public final class SystemClock { /** @hide */ public static long uptimeMillis$ravenwood() { - // Ravenwood booted in Jan 2023, and has been in deep sleep for one week - return System.currentTimeMillis() - (1672556400L * 1_000) - - (DateUtils.WEEK_IN_MILLIS * 1_000); + return uptimeNanos() / 1_000_000; } /** @@ -210,9 +213,7 @@ public final class SystemClock { /** @hide */ public static long uptimeNanos$ravenwood() { - // Ravenwood booted in Jan 2023, and has been in deep sleep for one week - return System.nanoTime() - (1672556400L * 1_000_000_000) - - (DateUtils.WEEK_IN_MILLIS * 1_000_000_000); + return System.nanoTime() - sAnchorNanoTime$ravenwood; } /** @@ -241,8 +242,7 @@ public final class SystemClock { /** @hide */ public static long elapsedRealtime$ravenwood() { - // Ravenwood booted in Jan 2023, and has been in deep sleep for one week - return System.currentTimeMillis() - (1672556400L * 1_000); + return elapsedRealtimeNanos() / 1_000_000; } /** @@ -271,8 +271,8 @@ public final class SystemClock { /** @hide */ public static long elapsedRealtimeNanos$ravenwood() { - // Ravenwood booted in Jan 2023, and has been in deep sleep for one week - return System.nanoTime() - (1672556400L * 1_000_000_000); + // Elapsed realtime is uptime plus an hour that we've been "asleep" + return uptimeNanos() + (DateUtils.HOUR_IN_MILLIS * 1_000_000); } /** diff --git a/core/java/android/os/ThreadLocalWorkSource.java b/core/java/android/os/ThreadLocalWorkSource.java index e9adb209ddc6..7c4a2be09bf8 100644 --- a/core/java/android/os/ThreadLocalWorkSource.java +++ b/core/java/android/os/ThreadLocalWorkSource.java @@ -37,6 +37,7 @@ package android.os; * * @hide Only for use within system server. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class ThreadLocalWorkSource { public static final int UID_NONE = Message.UID_NONE; private static final ThreadLocal<int []> sWorkSourceUid = diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java index a234a9b6fc7c..0bb1f39cd453 100644 --- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java +++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java @@ -34,4 +34,13 @@ import java.lang.annotation.Target; @Target({METHOD, CONSTRUCTOR}) @Retention(RetentionPolicy.CLASS) public @interface RavenwoodThrow { + /** + * One or more classes that aren't yet supported by Ravenwood, which is why this method throws. + */ + Class<?>[] blockedBy() default {}; + + /** + * General free-form description of why this method throws. + */ + String reason() default ""; } diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt index 48e93280f73f..b92663c83889 100644 --- a/ravenwood/framework-minus-apex-ravenwood-policies.txt +++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt @@ -109,6 +109,7 @@ class android.os.Bundle stubclass class android.os.PersistableBundle stubclass # Misc +class android.os.HandlerExecutor stubclass class android.os.PatternMatcher stubclass class android.os.ParcelUuid stubclass diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java index 1caef26d50eb..be0c09ee4a25 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java @@ -16,13 +16,35 @@ package android.platform.test.ravenwood; +import android.os.HandlerThread; +import android.os.Looper; + +import java.util.Objects; + public class RavenwoodRuleImpl { + private static final String MAIN_THREAD_NAME = "RavenwoodMain"; + + public static boolean isUnderRavenwood() { + return true; + } + public static void init(RavenwoodRule rule) { android.os.Process.init$ravenwood(rule.mUid, rule.mPid); android.os.Binder.init$ravenwood(); + + if (rule.mProvideMainThread) { + final HandlerThread main = new HandlerThread(MAIN_THREAD_NAME); + main.start(); + Looper.setMainLooperForTest(main.getLooper()); + } } public static void reset(RavenwoodRule rule) { + if (rule.mProvideMainThread) { + Looper.getMainLooper().quit(); + Looper.clearMainLooperForTest(); + } + android.os.Process.reset$ravenwood(); android.os.Binder.reset$ravenwood(); } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 79f9e58aaa1a..9db5b9895749 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -34,6 +34,8 @@ import java.util.concurrent.atomic.AtomicInteger; public class RavenwoodRule implements TestRule { private static AtomicInteger sNextPid = new AtomicInteger(100); + private static final boolean IS_UNDER_RAVENWOOD = RavenwoodRuleImpl.isUnderRavenwood(); + private static final int SYSTEM_UID = 1000; private static final int NOBODY_UID = 9999; private static final int FIRST_APPLICATION_UID = 10000; @@ -45,6 +47,8 @@ public class RavenwoodRule implements TestRule { int mUid = NOBODY_UID; int mPid = sNextPid.getAndIncrement(); + boolean mProvideMainThread = false; + public RavenwoodRule() { } @@ -72,6 +76,15 @@ public class RavenwoodRule implements TestRule { return this; } + /** + * Configure a "main" thread to be available for the duration of the test, as defined + * by {@code Looper.getMainLooper()}. Has no effect under non-Ravenwood environments. + */ + public Builder setProvideMainThread(boolean provideMainThread) { + mRule.mProvideMainThread = provideMainThread; + return this; + } + public RavenwoodRule build() { return mRule; } @@ -81,8 +94,7 @@ public class RavenwoodRule implements TestRule { * Return if the current process is running under a Ravenwood test environment. */ public boolean isUnderRavenwood() { - // TODO: give ourselves a better environment signal - return System.getProperty("java.class.path").contains("ravenwood"); + return IS_UNDER_RAVENWOOD; } @Override @@ -90,17 +102,16 @@ public class RavenwoodRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { - final boolean isUnderRavenwood = isUnderRavenwood(); if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { - Assume.assumeFalse(isUnderRavenwood); + Assume.assumeFalse(IS_UNDER_RAVENWOOD); } - if (isUnderRavenwood) { + if (IS_UNDER_RAVENWOOD) { RavenwoodRuleImpl.init(RavenwoodRule.this); } try { base.evaluate(); } finally { - if (isUnderRavenwood) { + if (IS_UNDER_RAVENWOOD) { RavenwoodRuleImpl.reset(RavenwoodRule.this); } } diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java index ecaff8084888..fb71e9d1ac6f 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java +++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java @@ -17,6 +17,10 @@ package android.platform.test.ravenwood; public class RavenwoodRuleImpl { + public static boolean isUnderRavenwood() { + return false; + } + public static void init(RavenwoodRule rule) { // Must be provided by impl to reference runtime internals throw new UnsupportedOperationException(); diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index a791f682fecf..e943786a5369 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -12,9 +12,16 @@ android.util.Xml android.os.Binder android.os.Binder$IdentitySupplier +android.os.Handler +android.os.HandlerExecutor +android.os.HandlerThread android.os.IBinder +android.os.Looper +android.os.Message +android.os.MessageQueue android.os.Process android.os.SystemClock +android.os.ThreadLocalWorkSource android.os.UserHandle android.content.ClipData diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt index f842f33bc95b..8ad21fa53a51 100644 --- a/ravenwood/ravenwood-standard-options.txt +++ b/ravenwood/ravenwood-standard-options.txt @@ -7,6 +7,7 @@ # Uncomment below lines to enable each feature. # --enable-non-stub-method-check +--no-non-stub-method-check #--default-method-call-hook # com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/tools/hoststubgen/hoststubgen/framework-policy-override.txt index 493ad56a5cbb..af3789e270a4 100644 --- a/tools/hoststubgen/hoststubgen/framework-policy-override.txt +++ b/tools/hoststubgen/hoststubgen/framework-policy-override.txt @@ -100,3 +100,6 @@ class android.os.BadTypeParcelableException stubclass class android.os.BaseBundle stubclass class android.os.Bundle stubclass class android.os.PersistableBundle stubclass + +class android.os.MessageQueue stubclass +class android.os.MessageQueue !com.android.hoststubgen.nativesubstitution.MessageQueue_host diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java new file mode 100644 index 000000000000..2e47d48f4fa0 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 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.hoststubgen.nativesubstitution; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +public class MessageQueue_host { + private static final AtomicLong sNextId = new AtomicLong(1); + private static final Map<Long, MessageQueue_host> sInstances = new ConcurrentHashMap<>(); + + private boolean mDeleted = false; + + private final Object mPoller = new Object(); + private volatile boolean mPolling; + + private void validate() { + if (mDeleted) { + // TODO: Put more info + throw new RuntimeException("MessageQueue already destroyed"); + } + } + + private static MessageQueue_host getInstance(long id) { + MessageQueue_host q = sInstances.get(id); + if (q == null) { + throw new RuntimeException("MessageQueue doesn't exist with id=" + id); + } + q.validate(); + return q; + } + + public static long nativeInit() { + final long id = sNextId.getAndIncrement(); + final MessageQueue_host q = new MessageQueue_host(); + sInstances.put(id, q); + return id; + } + + public static void nativeDestroy(long ptr) { + getInstance(ptr).mDeleted = true; + sInstances.remove(ptr); + } + + public static void nativePollOnce(android.os.MessageQueue queue, long ptr, int timeoutMillis) { + var q = getInstance(ptr); + synchronized (q.mPoller) { + q.mPolling = true; + try { + if (timeoutMillis == 0) { + // Calling epoll_wait() with 0 returns immediately + } else if (timeoutMillis == -1) { + q.mPoller.wait(); + } else { + q.mPoller.wait(timeoutMillis); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + q.mPolling = false; + } + } + + public static void nativeWake(long ptr) { + var q = getInstance(ptr); + synchronized (q.mPoller) { + q.mPoller.notifyAll(); + } + } + + public static boolean nativeIsPolling(long ptr) { + var q = getInstance(ptr); + return q.mPolling; + } + + public static void nativeSetFileDescriptorEvents(long ptr, int fd, int events) { + throw new UnsupportedOperationException(); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java index 4a3a79803b65..98d5778f047e 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java @@ -36,7 +36,7 @@ public class Parcel_host { private Parcel_host() { } - private static final AtomicLong sNextId = new AtomicLong(0); + private static final AtomicLong sNextId = new AtomicLong(1); private static final Map<Long, Parcel_host> sInstances = new ConcurrentHashMap<>(); |