From 9f20683a6c9f40c252472e1f83ee1efa5dbe53b8 Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 7 Jun 2018 02:41:21 -0700 Subject: [PATCH] Factor out Handler creation in construtor to prevent warning suppression. Using new Handler(this) in a constructor potentially leaks an uninitialized object. This is mostly not a problem because we don't use the Handler within the constructor. Added a Util method to keep the warning suppression local. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=199605377 --- .../exoplayer2/metadata/MetadataRenderer.java | 25 ++++++++------ .../android/exoplayer2/text/TextRenderer.java | 33 ++++++++++-------- .../google/android/exoplayer2/util/Util.java | 34 ++++++++++++++++++- .../video/VideoFrameReleaseTimeHelper.java | 2 +- .../source/dash/PlayerEmsgHandler.java | 4 +-- 5 files changed, 68 insertions(+), 30 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java index 7d36d87a9e..a66ac9a2b5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java @@ -19,12 +19,14 @@ import android.os.Handler; import android.os.Handler.Callback; import android.os.Looper; import android.os.Message; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.Util; import java.util.Arrays; /** @@ -46,7 +48,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { private final MetadataDecoderFactory decoderFactory; private final MetadataOutput output; - private final Handler outputHandler; + private final @Nullable Handler outputHandler; private final FormatHolder formatHolder; private final MetadataInputBuffer buffer; private final Metadata[] pendingMetadata; @@ -61,11 +63,11 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { * @param output The output. * @param outputLooper The looper associated with the thread on which the output should be called. * If the output makes use of standard Android UI components, then this should normally be the - * looper associated with the application's main thread, which can be obtained using - * {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be - * called directly on the player's internal rendering thread. + * looper associated with the application's main thread, which can be obtained using {@link + * android.app.Activity#getMainLooper()}. Null may be passed if the output should be called + * directly on the player's internal rendering thread. */ - public MetadataRenderer(MetadataOutput output, Looper outputLooper) { + public MetadataRenderer(MetadataOutput output, @Nullable Looper outputLooper) { this(output, outputLooper, MetadataDecoderFactory.DEFAULT); } @@ -73,16 +75,17 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { * @param output The output. * @param outputLooper The looper associated with the thread on which the output should be called. * If the output makes use of standard Android UI components, then this should normally be the - * looper associated with the application's main thread, which can be obtained using - * {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be - * called directly on the player's internal rendering thread. + * looper associated with the application's main thread, which can be obtained using {@link + * android.app.Activity#getMainLooper()}. Null may be passed if the output should be called + * directly on the player's internal rendering thread. * @param decoderFactory A factory from which to obtain {@link MetadataDecoder} instances. */ - public MetadataRenderer(MetadataOutput output, Looper outputLooper, - MetadataDecoderFactory decoderFactory) { + public MetadataRenderer( + MetadataOutput output, @Nullable Looper outputLooper, MetadataDecoderFactory decoderFactory) { super(C.TRACK_TYPE_METADATA); this.output = Assertions.checkNotNull(output); - this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this); + this.outputHandler = + outputLooper == null ? null : Util.createHandler(outputLooper, /* callback= */ this); this.decoderFactory = Assertions.checkNotNull(decoderFactory); formatHolder = new FormatHolder(); buffer = new MetadataInputBuffer(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java index c6d7f6f163..876beb6a1c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java @@ -20,6 +20,7 @@ import android.os.Handler.Callback; import android.os.Looper; import android.os.Message; import android.support.annotation.IntDef; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; @@ -27,6 +28,7 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; +import com.google.android.exoplayer2.util.Util; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; @@ -70,7 +72,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { private static final int MSG_UPDATE_OUTPUT = 0; - private final Handler outputHandler; + private final @Nullable Handler outputHandler; private final TextOutput output; private final SubtitleDecoderFactory decoderFactory; private final FormatHolder formatHolder; @@ -87,30 +89,31 @@ public final class TextRenderer extends BaseRenderer implements Callback { /** * @param output The output. - * @param outputLooper The looper associated with the thread on which the output should be - * called. If the output makes use of standard Android UI components, then this should - * normally be the looper associated with the application's main thread, which can be obtained - * using {@link android.app.Activity#getMainLooper()}. Null may be passed if the output - * should be called directly on the player's internal rendering thread. + * @param outputLooper The looper associated with the thread on which the output should be called. + * If the output makes use of standard Android UI components, then this should normally be the + * looper associated with the application's main thread, which can be obtained using {@link + * android.app.Activity#getMainLooper()}. Null may be passed if the output should be called + * directly on the player's internal rendering thread. */ - public TextRenderer(TextOutput output, Looper outputLooper) { + public TextRenderer(TextOutput output, @Nullable Looper outputLooper) { this(output, outputLooper, SubtitleDecoderFactory.DEFAULT); } /** * @param output The output. - * @param outputLooper The looper associated with the thread on which the output should be - * called. If the output makes use of standard Android UI components, then this should - * normally be the looper associated with the application's main thread, which can be obtained - * using {@link android.app.Activity#getMainLooper()}. Null may be passed if the output - * should be called directly on the player's internal rendering thread. + * @param outputLooper The looper associated with the thread on which the output should be called. + * If the output makes use of standard Android UI components, then this should normally be the + * looper associated with the application's main thread, which can be obtained using {@link + * android.app.Activity#getMainLooper()}. Null may be passed if the output should be called + * directly on the player's internal rendering thread. * @param decoderFactory A factory from which to obtain {@link SubtitleDecoder} instances. */ - public TextRenderer(TextOutput output, Looper outputLooper, - SubtitleDecoderFactory decoderFactory) { + public TextRenderer( + TextOutput output, @Nullable Looper outputLooper, SubtitleDecoderFactory decoderFactory) { super(C.TRACK_TYPE_TEXT); this.output = Assertions.checkNotNull(output); - this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this); + this.outputHandler = + outputLooper == null ? null : Util.createHandler(outputLooper, /* callback= */ this); this.decoderFactory = decoderFactory; formatHolder = new FormatHolder(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java index b7a403978c..774e0eb874 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -29,6 +29,8 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Build; +import android.os.Handler; +import android.os.Looper; import android.os.Parcel; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -66,6 +68,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; import org.checkerframework.checker.nullness.qual.PolyNull; /** @@ -237,12 +240,41 @@ public final class Util { * @param length The output array length. Must be less or equal to the length of the input array. * @return The copied array. */ - @SuppressWarnings("nullness:assignment.type.incompatible") + @SuppressWarnings({"nullness:argument.type.incompatible", "nullness:return.type.incompatible"}) public static T[] nullSafeArrayCopy(T[] input, int length) { Assertions.checkArgument(length <= input.length); return Arrays.copyOf(input, length); } + /** + * Creates a {@link Handler} with the specified {@link Handler.Callback} on the current {@link + * Looper} thread. The method accepts partially initialized objects as callback under the + * assumption that the Handler won't be used to send messages until the callback is fully + * initialized. + * + * @param callback A {@link Handler.Callback}. May be a partially initialized class. + * @return A {@link Handler} with the specified callback on the current {@link Looper} thread. + */ + public static Handler createHandler(Handler.@UnknownInitialization Callback callback) { + return createHandler(Looper.myLooper(), callback); + } + + /** + * Creates a {@link Handler} with the specified {@link Handler.Callback} on the specified {@link + * Looper} thread. The method accepts partially initialized objects as callback under the + * assumption that the Handler won't be used to send messages until the callback is fully + * initialized. + * + * @param looper A {@link Looper} to run the callback on. + * @param callback A {@link Handler.Callback}. May be a partially initialized class. + * @return A {@link Handler} with the specified callback on the current {@link Looper} thread. + */ + @SuppressWarnings({"nullness:argument.type.incompatible", "nullness:return.type.incompatible"}) + public static Handler createHandler( + Looper looper, Handler.@UnknownInitialization Callback callback) { + return new Handler(looper, callback); + } + /** * Instantiates a new single threaded executor whose thread has the specified name. * diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/VideoFrameReleaseTimeHelper.java b/library/core/src/main/java/com/google/android/exoplayer2/video/VideoFrameReleaseTimeHelper.java index b4835186ff..3c0fb92191 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/VideoFrameReleaseTimeHelper.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/VideoFrameReleaseTimeHelper.java @@ -291,7 +291,7 @@ public final class VideoFrameReleaseTimeHelper { sampledVsyncTimeNs = C.TIME_UNSET; choreographerOwnerThread = new HandlerThread("ChoreographerOwner:Handler"); choreographerOwnerThread.start(); - handler = new Handler(choreographerOwnerThread.getLooper(), this); + handler = Util.createHandler(choreographerOwnerThread.getLooper(), /* callback= */ this); handler.sendEmptyMessage(CREATE_CHOREOGRAPHER); } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java index 1bb08c4398..0ef6b5ff1a 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java @@ -35,6 +35,7 @@ import com.google.android.exoplayer2.source.chunk.Chunk; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.util.ParsableByteArray; +import com.google.android.exoplayer2.util.Util; import java.io.IOException; import java.util.Iterator; import java.util.Map; @@ -100,7 +101,6 @@ public final class PlayerEmsgHandler implements Handler.Callback { * messages that generate DASH media source events. * @param allocator An {@link Allocator} from which allocations can be obtained. */ - @SuppressWarnings("nullness") public PlayerEmsgHandler( DashManifest manifest, PlayerEmsgCallback playerEmsgCallback, Allocator allocator) { this.manifest = manifest; @@ -108,7 +108,7 @@ public final class PlayerEmsgHandler implements Handler.Callback { this.allocator = allocator; manifestPublishTimeToExpiryTimeUs = new TreeMap<>(); - handler = new Handler(this); + handler = Util.createHandler(/* callback= */ this); decoder = new EventMessageDecoder(); lastLoadedChunkEndTimeUs = C.TIME_UNSET; lastLoadedChunkEndTimeBeforeRefreshUs = C.TIME_UNSET;