describeContents() returns CONTENTS_FILE_DESCRIPTOR
Parcelable.describeContents() returns CONTENTS_FILE_DESCRIPTOR when the
contents hold a file descriptor.
Bug: 170677046
Test: aidl_unittests / aidl_integration_test
Change-Id: Idb06123def25c78f7e4ed5317e61ddee8ca39d5d
diff --git a/Android.bp b/Android.bp
index e86d167..90fe3ff 100644
--- a/Android.bp
+++ b/Android.bp
@@ -267,6 +267,7 @@
"tests/android/aidl/tests/ICppJavaTests.aidl",
"tests/android/aidl/tests/SimpleParcelable.aidl",
"tests/android/aidl/tests/Union.aidl",
+ "tests/android/aidl/tests/UnionWithFd.aidl",
"tests/android/aidl/tests/extension/*.aidl",
],
path: "tests",
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 3bf8c47..63a7d4f 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -405,14 +405,16 @@
comments_(comments),
split_name_(Split(unresolved_name, ".")) {}
-AidlTypeSpecifier AidlTypeSpecifier::ArrayBase() const {
+const AidlTypeSpecifier& AidlTypeSpecifier::ArrayBase() const {
AIDL_FATAL_IF(!is_array_, this);
// Declaring array of generic type cannot happen, it is grammar error.
AIDL_FATAL_IF(IsGeneric(), this);
- AidlTypeSpecifier array_base = *this;
- array_base.is_array_ = false;
- return array_base;
+ if (!array_base_) {
+ array_base_.reset(new AidlTypeSpecifier(*this));
+ array_base_->is_array_ = false;
+ }
+ return *array_base_;
}
bool AidlTypeSpecifier::IsHidden() const {
diff --git a/aidl_language.h b/aidl_language.h
index e8bf546..4b56a5f 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -267,7 +267,7 @@
virtual ~AidlTypeSpecifier() = default;
// Copy of this type which is not an array.
- AidlTypeSpecifier ArrayBase() const;
+ const AidlTypeSpecifier& ArrayBase() const;
// Returns the full-qualified name of the base type.
// int -> int
@@ -319,6 +319,7 @@
bool is_array_;
string comments_;
vector<string> split_name_;
+ mutable shared_ptr<AidlTypeSpecifier> array_base_;
};
// Returns the universal value unaltered.
diff --git a/aidl_typenames.cpp b/aidl_typenames.cpp
index 5e5cdbd..16afbf6 100644
--- a/aidl_typenames.cpp
+++ b/aidl_typenames.cpp
@@ -299,6 +299,15 @@
return nullptr;
}
+const AidlParcelable* AidlTypenames::GetParcelable(const AidlTypeSpecifier& type) const {
+ if (auto defined_type = TryGetDefinedType(type.GetName()); defined_type != nullptr) {
+ if (auto parcelable = defined_type->AsParcelable(); parcelable != nullptr) {
+ return parcelable;
+ }
+ }
+ return nullptr;
+}
+
void AidlTypenames::IterateTypes(const std::function<void(const AidlDefinedType&)>& body) const {
for (const auto& kv : defined_types_) {
body(*kv.second);
diff --git a/aidl_typenames.h b/aidl_typenames.h
index 469de4b..7ae3690 100644
--- a/aidl_typenames.h
+++ b/aidl_typenames.h
@@ -33,6 +33,7 @@
class AidlDefinedType;
class AidlEnumDeclaration;
class AidlInterface;
+class AidlParcelable;
class AidlTypeSpecifier;
class AidlDocument;
@@ -79,6 +80,9 @@
// Returns the AidlInterface of the given type, or nullptr if the type
// is not an AidlInterface;
const AidlInterface* GetInterface(const AidlTypeSpecifier& type) const;
+ // Returns the AidlParcelable of the given type, or nullptr if the type
+ // is not an AidlParcelable;
+ const AidlParcelable* GetParcelable(const AidlTypeSpecifier& type) const;
// Iterates over all defined and then preprocessed types
void IterateTypes(const std::function<void(const AidlDefinedType&)>& body) const;
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 9dc2af0..16356ae 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -76,7 +76,7 @@
p/Foo.aidl :
)";
-const char kExpectedJavaParcelableOutputContests[] =
+const char kExpectedJavaParcelableOutputContents[] =
R"(/*
* This file is auto-generated. DO NOT MODIFY.
*/
@@ -93,6 +93,8 @@
public int y = 0;
public android.os.ParcelFileDescriptor fd;
+
+ public java.util.List<android.os.ParcelFileDescriptor> fds;
public static final android.os.Parcelable.Creator<Rect> CREATOR = new android.os.Parcelable.Creator<Rect>() {
@Override
public Rect createFromParcel(android.os.Parcel _aidl_source) {
@@ -140,6 +142,7 @@
fd = null;
}
if (_aidl_parcel.dataPosition() - _aidl_start_pos >= _aidl_parcelable_size) return;
+ if (_aidl_parcel.dataPosition() - _aidl_start_pos >= _aidl_parcelable_size) return;
} finally {
if (_aidl_start_pos > (Integer.MAX_VALUE - _aidl_parcelable_size)) {
throw new android.os.BadParcelableException("Overflow in the size of parcelable");
@@ -147,9 +150,12 @@
_aidl_parcel.setDataPosition(_aidl_start_pos + _aidl_parcelable_size);
}
}
- @Override public int describeContents()
- {
- return 0;
+ @Override
+ public int describeContents() {
+ int _mask = 0;
+ if (fd != null) _mask |= fd.describeContents();
+ if (fds != null) for (ParcelFileDescriptor _v0: fds) if (_v0 != null) _mask |= _v0.describeContents();
+ return _mask;
}
}
)";
@@ -765,6 +771,7 @@
"+ \"y\")\n"
" int y;\n"
" ParcelFileDescriptor fd;\n"
+ " List<ParcelFileDescriptor> fds;\n"
"}");
vector<string> args{"aidl", "Rect.aidl"};
@@ -773,7 +780,7 @@
string output;
EXPECT_TRUE(io_delegate_.GetWrittenContents("Rect.java", &output));
- EXPECT_EQ(kExpectedJavaParcelableOutputContests, output);
+ EXPECT_EQ(kExpectedJavaParcelableOutputContents, output);
}
TEST_F(AidlTest, CppHeaderIncludes) {
@@ -2699,6 +2706,7 @@
#include <a/ByteEnum.h>
#include <binder/Parcel.h>
+#include <binder/ParcelFileDescriptor.h>
#include <binder/Status.h>
#include <cstdint>
#include <type_traits>
@@ -2732,6 +2740,7 @@
enum Tag : int32_t {
ns = 0, // int[] ns;
e, // a.ByteEnum e;
+ pfd, // ParcelFileDescriptor pfd;
};
template<typename _Tp>
@@ -2789,7 +2798,7 @@
return DESCIPTOR;
}
private:
- std::variant<::std::vector<int32_t>, ::a::ByteEnum> _value;
+ std::variant<::std::vector<int32_t>, ::a::ByteEnum, ::android::os::ParcelFileDescriptor> _value;
}; // class Foo
} // namespace a
@@ -2814,6 +2823,11 @@
if ((_aidl_ret_status = _aidl_parcel->readByte(reinterpret_cast<int8_t *>(&_aidl_value))) != ::android::OK) return _aidl_ret_status;
set<e>(std::move(_aidl_value));
return ::android::OK; }
+ case pfd: {
+ ::android::os::ParcelFileDescriptor _aidl_value;
+ if ((_aidl_ret_status = _aidl_parcel->readParcelable(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
+ set<pfd>(std::move(_aidl_value));
+ return ::android::OK; }
}
return ::android::BAD_VALUE;
}
@@ -2824,6 +2838,7 @@
switch (getTag()) {
case ns: return _aidl_parcel->writeInt32Vector(get<ns>());
case e: return _aidl_parcel->writeByte(static_cast<int8_t>(get<e>()));
+ case pfd: return _aidl_parcel->writeParcelable(get<pfd>());
}
abort();
}
@@ -2857,6 +2872,7 @@
enum Tag : int32_t {
ns = 0, // int[] ns;
e, // a.ByteEnum e;
+ pfd, // ParcelFileDescriptor pfd;
};
template<typename _Tp>
@@ -2911,7 +2927,7 @@
binder_status_t writeToParcel(AParcel* _parcel) const;
static const ::ndk::parcelable_stability_t _aidl_stability = ::ndk::STABILITY_LOCAL;
private:
- std::variant<std::vector<int32_t>, ::aidl::a::ByteEnum> _value;
+ std::variant<std::vector<int32_t>, ::aidl::a::ByteEnum, ::ndk::ScopedFileDescriptor> _value;
};
} // namespace a
} // namespace aidl
@@ -2940,6 +2956,11 @@
if ((_aidl_ret_status = AParcel_readByte(_parcel, reinterpret_cast<int8_t*>(&_aidl_value))) != STATUS_OK) return _aidl_ret_status;
set<e>(std::move(_aidl_value));
return STATUS_OK; }
+ case pfd: {
+ ::ndk::ScopedFileDescriptor _aidl_value;
+ if ((_aidl_ret_status = ::ndk::AParcel_readRequiredParcelFileDescriptor(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
+ set<pfd>(std::move(_aidl_value));
+ return STATUS_OK; }
}
return STATUS_BAD_VALUE;
}
@@ -2949,6 +2970,7 @@
switch (getTag()) {
case ns: return ::ndk::AParcel_writeVector(_parcel, get<ns>());
case e: return AParcel_writeByte(_parcel, static_cast<int8_t>(get<e>()));
+ case pfd: return ::ndk::AParcel_writeRequiredParcelFileDescriptor(_parcel, get<pfd>());
}
abort();
}
@@ -2967,6 +2989,7 @@
// tags for union fields
public final static int ns = 0; // int[] ns;
public final static int e = 1; // a.ByteEnum e;
+ public final static int pfd = 2; // ParcelFileDescriptor pfd;
private int _tag;
private Object _value;
@@ -3018,6 +3041,21 @@
_set(e, _value);
}
+ // ParcelFileDescriptor pfd;
+
+ public static Foo pfd(android.os.ParcelFileDescriptor _value) {
+ return new Foo(pfd, _value);
+ }
+
+ public android.os.ParcelFileDescriptor getPfd() {
+ _assertTag(pfd);
+ return (android.os.ParcelFileDescriptor) _value;
+ }
+
+ public void setPfd(android.os.ParcelFileDescriptor _value) {
+ _set(pfd, _value);
+ }
+
public static final android.os.Parcelable.Creator<Foo> CREATOR = new android.os.Parcelable.Creator<Foo>() {
@Override
public Foo createFromParcel(android.os.Parcel _aidl_source) {
@@ -3039,6 +3077,15 @@
case e:
_aidl_parcel.writeByte(getE());
break;
+ case pfd:
+ if ((getPfd()!=null)) {
+ _aidl_parcel.writeInt(1);
+ getPfd().writeToParcel(_aidl_parcel, 0);
+ }
+ else {
+ _aidl_parcel.writeInt(0);
+ }
+ break;
}
}
@@ -3056,13 +3103,29 @@
_aidl_value = _aidl_parcel.readByte();
_set(_aidl_tag, _aidl_value);
return; }
+ case pfd: {
+ android.os.ParcelFileDescriptor _aidl_value;
+ if ((0!=_aidl_parcel.readInt())) {
+ _aidl_value = android.os.ParcelFileDescriptor.CREATOR.createFromParcel(_aidl_parcel);
+ }
+ else {
+ _aidl_value = null;
+ }
+ _set(_aidl_tag, _aidl_value);
+ return; }
}
throw new RuntimeException("union: out of range: " + _aidl_tag);
}
@Override
public int describeContents() {
- return 0;
+ int _mask = 0;
+ switch (getTag()) {
+ case pfd:
+ if (getPfd() != null) _mask |= getPfd().describeContents();
+ break;
+ }
+ return _mask;
}
private void _assertTag(int tag) {
@@ -3075,6 +3138,7 @@
switch (_tag) {
case ns: return "ns";
case e: return "e";
+ case pfd: return "pfd";
}
throw new IllegalStateException("unknown field: " + _tag);
}
@@ -3094,6 +3158,7 @@
union Foo {
int[] ns = {42};
ByteEnum e;
+ ParcelFileDescriptor pfd;
}
)");
io_delegate_.SetFileContents("a/ByteEnum.aidl", R"(
diff --git a/code_writer.h b/code_writer.h
index 469cd5f..4feb63b 100644
--- a/code_writer.h
+++ b/code_writer.h
@@ -16,11 +16,13 @@
#pragma once
+#include <stdio.h>
+
+#include <functional>
#include <memory>
#include <ostream>
#include <string>
-
-#include <stdio.h>
+#include <utility>
namespace android {
namespace aidl {
@@ -37,6 +39,14 @@
// The buffer gets updated only after Close() is called or the CodeWriter
// is deleted -- much like a real file.
static CodeWriterPtr ForString(std::string* buf);
+ // Run a Code Generater (which accepts CodeWriter& as a first parameter)
+ // and return a result as a string.
+ template <typename... Args>
+ static std::string RunWith(void (*gen)(CodeWriter&, Args...), Args&&... args) {
+ std::string code;
+ (*gen)(*ForString(&code), std::forward<Args>(args)...);
+ return code;
+ }
// Write a formatted string to this writer in the usual printf sense.
// Returns false on error.
virtual bool Write(const char* format, ...) __attribute__((format(printf, 2, 3)));
diff --git a/generate_java.cpp b/generate_java.cpp
index aeeea81..ed57049 100644
--- a/generate_java.cpp
+++ b/generate_java.cpp
@@ -23,8 +23,10 @@
#include <algorithm>
#include <map>
#include <memory>
+#include <optional>
#include <sstream>
+#include <android-base/format.h>
#include <android-base/stringprintf.h>
#include "aidl_to_java.h"
@@ -36,6 +38,7 @@
using ::android::base::StartsWith;
using std::string;
using std::unique_ptr;
+using std::vector;
namespace {
// join two non-empty strings according to `camelCase` naming.
@@ -51,6 +54,87 @@
inline string setter_name(const AidlVariableDeclaration& variable) {
return camelcase_join("set", variable.GetName(), variable);
}
+
+// Some types contribute to Parcelable.describeContents().
+// e.g. FileDescriptor, Parcelables, List<Parcelables> ...
+std::optional<string> DescribeContents(const AidlTypenames& types, const AidlTypeSpecifier& type,
+ const string& value, int nest_level = 0) {
+ if (type.IsArray() || type.GetName() == "List") {
+ const auto& base_type = type.IsArray() ? type.ArrayBase() : *type.GetTypeParameters()[0];
+ auto base_var = "_v" + std::to_string(nest_level);
+ auto base_describer = DescribeContents(types, base_type, base_var, nest_level + 1);
+ if (base_describer) {
+ return fmt::format(
+ "if ({value} != null) for ({base_type} {base_var}: {value}) {base_describer}",
+ fmt::arg("value", value), fmt::arg("base_type", base_type.GetName()),
+ fmt::arg("base_var", base_var), fmt::arg("base_describer", *base_describer));
+ }
+ return std::nullopt;
+ }
+
+ if (type.GetName() == "Map") {
+ const auto& base_type = type.GetTypeParameters()[1];
+ auto base_var = "_v" + std::to_string(nest_level);
+ auto base_describer = DescribeContents(types, *base_type, base_var, nest_level + 1);
+ if (base_describer) {
+ return fmt::format(
+ "if ({value} != null) for ({base_type} {base_var}: {value}.values()) {base_describer}",
+ fmt::arg("value", value), fmt::arg("base_type", base_type->GetName()),
+ fmt::arg("base_var", base_var), fmt::arg("base_describer", *base_describer));
+ }
+ return std::nullopt;
+ }
+
+ if (type.GetName() == "FileDescriptor") {
+ return fmt::format(
+ "_mask |= ({} != null) ? android.os.Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;\n", value);
+ }
+
+ if (type.GetName() == "ParcelFileDescriptor" || type.GetName() == "ParcelableHolder" ||
+ types.GetParcelable(type) != nullptr) {
+ return fmt::format("if ({} != null) _mask |= {}.describeContents();\n", value, value);
+ }
+
+ return std::nullopt;
+}
+
+void GenerateParcelableDescribeContents(CodeWriter& out, const AidlStructuredParcelable& decl,
+ const AidlTypenames& types) {
+ out << "@Override\n";
+ out << "public int describeContents() {\n";
+ out.Indent();
+ out << "int _mask = 0;\n";
+ for (const auto& f : decl.GetFields()) {
+ if (auto describer = DescribeContents(types, f->GetType(), f->GetName()); describer) {
+ out << *describer;
+ }
+ }
+ out << "return _mask;\n";
+ out.Dedent();
+ out << "}\n";
+}
+
+void GenerateParcelableDescribeContents(CodeWriter& out, const AidlUnionDecl& decl,
+ const AidlTypenames& types) {
+ out << "@Override\n";
+ out << "public int describeContents() {\n";
+ out.Indent();
+ out << "int _mask = 0;\n";
+ out << "switch (getTag()) {\n";
+ for (const auto& f : decl.GetFields()) {
+ if (auto describer = DescribeContents(types, f->GetType(), getter_name(*f) + "()"); describer) {
+ out << fmt::format("case {}:\n", f->GetName());
+ out.Indent();
+ out << *describer;
+ out << "break;\n";
+ out.Dedent();
+ }
+ }
+ out << "}\n";
+ out << "return _mask;\n";
+ out.Dedent();
+ out << "}\n";
+}
} // namespace
namespace android {
@@ -410,13 +494,9 @@
parcel_class->elements.push_back(std::make_shared<LiteralClassElement>(out.str()));
}
- auto describe_contents_method = std::make_shared<Method>();
- describe_contents_method->modifiers = PUBLIC | OVERRIDE;
- describe_contents_method->returnType = "int";
- describe_contents_method->name = "describeContents";
- describe_contents_method->statements = std::make_shared<StatementBlock>();
- describe_contents_method->statements->Add(std::make_shared<LiteralStatement>("return 0;\n"));
- parcel_class->elements.push_back(describe_contents_method);
+ auto describe_contents_method =
+ CodeWriter::RunWith(GenerateParcelableDescribeContents, *parcel, typenames);
+ parcel_class->elements.push_back(std::make_shared<LiteralClassElement>(describe_contents_method));
return parcel_class;
}
@@ -574,15 +654,18 @@
out << "@Override\n";
out << "public final void writeToParcel(android.os.Parcel _aidl_parcel, int _aidl_flag) {\n";
- out << " " + write_to_parcel(tag_type_specifier, tag_name, "_aidl_parcel");
- out << " switch (" + tag_name + ") {\n";
+ out.Indent();
+ out << write_to_parcel(tag_type_specifier, tag_name, "_aidl_parcel");
+ out << "switch (" + tag_name + ") {\n";
for (const auto& variable : decl->GetFields()) {
- out << " case " + variable->GetName() + ":\n";
- out << " " +
- write_to_parcel(variable->GetType(), getter_name(*variable) + "()", "_aidl_parcel");
- out << " break;\n";
+ out << "case " + variable->GetName() + ":\n";
+ out.Indent();
+ out << write_to_parcel(variable->GetType(), getter_name(*variable) + "()", "_aidl_parcel");
+ out << "break;\n";
+ out.Dedent();
}
- out << " }\n";
+ out << "}\n";
+ out.Dedent();
out << "}\n\n";
// keep this across different fields in order to create the classloader
@@ -606,26 +689,28 @@
// Not override, but as a user-defined parcelable, this method should be public
out << "public void readFromParcel(android.os.Parcel _aidl_parcel) {\n";
- out << " " + tag_type + " _aidl_tag;\n";
- out << " " + read_from_parcel(tag_type_specifier, "_aidl_tag", "_aidl_parcel");
- out << " switch (_aidl_tag) {\n";
+ out.Indent();
+ out << tag_type + " _aidl_tag;\n";
+ out << read_from_parcel(tag_type_specifier, "_aidl_tag", "_aidl_parcel");
+ out << "switch (_aidl_tag) {\n";
for (const auto& variable : decl->GetFields()) {
auto var_name = variable->GetName();
auto var_type = JavaSignatureOf(variable->GetType(), typenames);
- out << " case " + var_name + ": {\n";
- out << " " + var_type + " _aidl_value;\n";
- out << " " + read_from_parcel(variable->GetType(), "_aidl_value", "_aidl_parcel");
- out << " _set(_aidl_tag, _aidl_value);\n";
- out << " return; }\n";
+ out << "case " + var_name + ": {\n";
+ out.Indent();
+ out << var_type + " _aidl_value;\n";
+ out << read_from_parcel(variable->GetType(), "_aidl_value", "_aidl_parcel");
+ out << "_set(_aidl_tag, _aidl_value);\n";
+ out << "return; }\n";
+ out.Dedent();
}
- out << " }\n";
- out << " throw new RuntimeException(\"union: out of range: \" + _aidl_tag);\n";
+ out << "}\n";
+ out << "throw new RuntimeException(\"union: out of range: \" + _aidl_tag);\n";
+ out.Dedent();
out << "}\n\n";
- out << "@Override\n";
- out << "public int describeContents() {\n";
- out << " return 0;\n";
- out << "}\n\n";
+ GenerateParcelableDescribeContents(out, *decl, typenames);
+ out << "\n";
// helper: _assertTag
out << "private void _assertTag(" + tag_type + " tag) {\n";
diff --git a/tests/android/aidl/tests/UnionWithFd.aidl b/tests/android/aidl/tests/UnionWithFd.aidl
new file mode 100644
index 0000000..5f59695
--- /dev/null
+++ b/tests/android/aidl/tests/UnionWithFd.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aidl.tests;
+
+union UnionWithFd {
+ int num;
+ ParcelFileDescriptor pfd;
+}
diff --git a/tests/java/src/android/aidl/tests/UnionTests.java b/tests/java/src/android/aidl/tests/UnionTests.java
index 45f9686..c95032b 100644
--- a/tests/java/src/android/aidl/tests/UnionTests.java
+++ b/tests/java/src/android/aidl/tests/UnionTests.java
@@ -19,9 +19,16 @@
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import android.aidl.tests.Union;
+import android.aidl.tests.UnionWithFd;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
@@ -67,4 +74,27 @@
parcel.recycle();
}
+
+ @Test
+ public void unionDescribeContents() {
+ UnionWithFd u = UnionWithFd.num(0);
+ assertTrue((u.describeContents() & Parcelable.CONTENTS_FILE_DESCRIPTOR) == 0);
+
+ final Parcel parcel = Parcel.obtain();
+ try {
+ u.setPfd(ParcelFileDescriptor.open(new File("/system"), ParcelFileDescriptor.MODE_READ_ONLY));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException("can't open /system", e);
+ }
+ assertTrue((u.describeContents() & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0);
+
+ u.writeToParcel(parcel, 0);
+
+ UnionWithFd v = UnionWithFd.num(0);
+ parcel.setDataPosition(0);
+ v.readFromParcel(parcel);
+ assertTrue((v.describeContents() & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0);
+
+ parcel.recycle();
+ }
}