Merge "Add flag for APV Software Codec" into main
diff --git a/media/audioaidlconversion/AidlConversionCppNdk.cpp b/media/audioaidlconversion/AidlConversionCppNdk.cpp
index 90996a3..40d5f5f 100644
--- a/media/audioaidlconversion/AidlConversionCppNdk.cpp
+++ b/media/audioaidlconversion/AidlConversionCppNdk.cpp
@@ -2315,6 +2315,15 @@
audio_port_config_device_ext legacy{};
RETURN_IF_ERROR(aidl2legacy_AudioDevice_audio_device(
aidl.device, &legacy.type, legacy.address));
+ const bool isInput = false; // speaker_layout_channel_mask only represents output.
+ if (aidl.speakerLayout.has_value()) {
+ legacy.speaker_layout_channel_mask =
+ VALUE_OR_RETURN(aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+ aidl.speakerLayout.value(), isInput));
+ } else {
+ // Default to none when the field is null in the AIDL.
+ legacy.speaker_layout_channel_mask = AUDIO_CHANNEL_NONE;
+ }
return legacy;
}
@@ -2323,6 +2332,14 @@
AudioPortDeviceExt aidl;
aidl.device = VALUE_OR_RETURN(
legacy2aidl_audio_device_AudioDevice(legacy.type, legacy.address));
+ const bool isInput = false; // speaker_layout_channel_mask only represents output.
+ // The AIDL speakerLayout is nullable and if set, can only be a layoutMask.
+ if (audio_channel_mask_is_valid(legacy.speaker_layout_channel_mask) &&
+ audio_channel_mask_get_representation(legacy.speaker_layout_channel_mask) ==
+ AUDIO_CHANNEL_REPRESENTATION_POSITION) {
+ aidl.speakerLayout = VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+ legacy.speaker_layout_channel_mask, isInput));
+ }
return aidl;
}
diff --git a/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
index 7f55e48..aa6cb0d 100644
--- a/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
+++ b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
@@ -670,6 +670,25 @@
AudioEncapsulationMetadataType::FRAMEWORK_TUNER,
AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR));
+TEST(AudioPortDeviceExt_speakerLayoutRoundTripTest, Aidl2Legacy2Aidl_layoutMask) {
+ AudioPortDeviceExt initial{};
+ initial.speakerLayout = make_ACL_Stereo();
+ auto conv = aidl2legacy_AudioPortDeviceExt_audio_port_config_device_ext(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_port_config_device_ext_AudioPortDeviceExt(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+
+TEST(AudioPortDeviceExt_speakerLayoutRoundTripTest, Aidl2Legacy2Aidl_null) {
+ const AudioPortDeviceExt initial{}; // speakerLayout is null
+ auto conv = aidl2legacy_AudioPortDeviceExt_audio_port_config_device_ext(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_port_config_device_ext_AudioPortDeviceExt(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+
class AudioGainModeRoundTripTest : public testing::TestWithParam<AudioGainMode> {};
TEST_P(AudioGainModeRoundTripTest, Aidl2Legacy2Aidl) {
const auto initial = GetParam();
diff --git a/media/libeffects/preprocessing/Android.bp b/media/libeffects/preprocessing/Android.bp
index 44b7d97..d791fab 100644
--- a/media/libeffects/preprocessing/Android.bp
+++ b/media/libeffects/preprocessing/Android.bp
@@ -55,8 +55,8 @@
defaults: ["libaudiopreprocessing-defaults"],
relative_install_path: "soundfx",
srcs: ["PreProcessing.cpp"],
- header_libs: [
- "libwebrtc_absl_headers",
+ static_libs: [
+ "libabsl",
],
}
@@ -77,6 +77,7 @@
"libutils",
],
static_libs: [
+ "libabsl",
"webrtc_audio_processing",
],
header_libs: [
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 3aa0107..cb3c185 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -54,6 +54,7 @@
#include "include/HevcUtils.h"
#include <com_android_media_editing_flags.h>
+namespace editing_flags = com::android::media::editing::flags;
#ifndef __predict_false
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
@@ -160,6 +161,7 @@
bool isAvc() const { return mIsAvc; }
bool isHevc() const { return mIsHevc; }
bool isAv1() const { return mIsAv1; }
+ bool isApv() const { return mIsApv; }
bool isHeic() const { return mIsHeic; }
bool isAvif() const { return mIsAvif; }
bool isHeif() const { return mIsHeif; }
@@ -328,6 +330,7 @@
bool mIsAvc;
bool mIsHevc;
bool mIsAv1;
+ bool mIsApv;
bool mIsDovi;
bool mIsAudio;
bool mIsVideo;
@@ -479,6 +482,7 @@
void writeAvccBox();
void writeHvccBox();
void writeAv1cBox();
+ void writeApvcBox();
void writeDoviConfigBox();
void writeUrlBox();
void writeDrefBox();
@@ -680,6 +684,9 @@
return "hvc1";
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime)) {
return "av01";
+ } else if (editing_flags::muxer_mp4_enable_apv() &&
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_APV, mime)) {
+ return "apv1";
}
} else if (!strncasecmp(mime, "application/", 12)) {
return "mett";
@@ -2266,6 +2273,7 @@
mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
mIsHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
mIsAv1 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1);
+ mIsApv = editing_flags::muxer_mp4_enable_apv() && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_APV);
mIsDovi = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
mIsAudio = !strncasecmp(mime, "audio/", 6);
mIsVideo = !strncasecmp(mime, "video/", 6);
@@ -2708,6 +2716,9 @@
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1) ||
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) {
mMeta->findData(kKeyAV1C, &type, &data, &size);
+ } else if (editing_flags::muxer_mp4_enable_apv() &&
+ !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_APV)) {
+ mMeta->findData(kKeyAPVC, &type, &data, &size);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
getDolbyVisionProfile();
if (!mMeta->findData(kKeyAVCC, &type, &data, &size) &&
@@ -3609,7 +3620,7 @@
(const uint8_t *)buffer->data()
+ buffer->range_offset(),
buffer->range_length());
- } else if (mIsMPEG4 || mIsAv1) {
+ } else if (mIsMPEG4 || mIsAv1 || mIsApv) {
err = copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(),
buffer->range_length());
}
@@ -4338,6 +4349,7 @@
!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime) ||
+ (editing_flags::muxer_mp4_enable_apv() && !strcasecmp(MEDIA_MIMETYPE_VIDEO_APV, mime)) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, mime) ||
!strcasecmp(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, mime) ||
!strcasecmp(MEDIA_MIMETYPE_IMAGE_AVIF, mime)) {
@@ -4512,6 +4524,9 @@
writeHvccBox();
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime)) {
writeAv1cBox();
+ } else if (editing_flags::muxer_mp4_enable_apv() &&
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_APV, mime)) {
+ writeApvcBox();
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, mime)) {
if (mDoviProfile <= DolbyVisionProfileDvheSt) {
writeHvccBox();
@@ -5103,6 +5118,15 @@
mOwner->endBox(); // av1C
}
+void MPEG4Writer::Track::writeApvcBox() {
+ CHECK(mCodecSpecificData);
+ CHECK_GE(mCodecSpecificDataSize, 4u);
+
+ mOwner->beginBox("apvC");
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+ mOwner->endBox(); // apvC
+}
+
void MPEG4Writer::Track::writeDoviConfigBox() {
CHECK_NE(mDoviProfile, 0u);
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index a7d2eb9..9dce55b 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -63,6 +63,7 @@
kKeyDVVC = 'dvvc', // raw data
kKeyDVWC = 'dvwc', // raw data
kKeyAV1C = 'av1c', // raw data
+ kKeyAPVC = 'apvc', // raw data
kKeyThumbnailHVCC = 'thvc', // raw data
kKeyThumbnailAV1C = 'tav1', // raw data
kKeyD263 = 'd263', // raw data
diff --git a/media/module/codecs/amrnb/common/Android.bp b/media/module/codecs/amrnb/common/Android.bp
index 0bc6ed2..35937cb 100644
--- a/media/module/codecs/amrnb/common/Android.bp
+++ b/media/module/codecs/amrnb/common/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_codec_framework",
default_applicable_licenses: [
"frameworks_av_media_codecs_amrnb_common_license",
],
@@ -42,8 +43,8 @@
"src/gains_tbl.cpp",
"src/gc_pred.cpp",
"src/gmed_n.cpp",
- "src/grid_tbl.cpp",
"src/gray_tbl.cpp",
+ "src/grid_tbl.cpp",
"src/int_lpc.cpp",
"src/inv_sqrt.cpp",
"src/inv_sqrt_tbl.cpp",
@@ -91,9 +92,9 @@
export_include_dirs: ["include"],
cflags: [
- "-DOSCL_UNUSED_ARG(x)=(void)(x)",
- "-DOSCL_IMPORT_REF=",
"-DOSCL_EXPORT_REF=",
+ "-DOSCL_IMPORT_REF=",
+ "-DOSCL_UNUSED_ARG(x)=(void)(x)",
"-Werror",
],
diff --git a/media/module/codecs/amrnb/dec/Android.bp b/media/module/codecs/amrnb/dec/Android.bp
index 70741d2..a28500a 100644
--- a/media/module/codecs/amrnb/dec/Android.bp
+++ b/media/module/codecs/amrnb/dec/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_codec_framework",
default_applicable_licenses: [
"frameworks_av_media_codecs_amrnb_dec_license",
],
@@ -47,12 +48,12 @@
"src/b_cn_cod.cpp",
"src/bgnscd.cpp",
"src/c_g_aver.cpp",
- "src/d1035pf.cpp",
- "src/d2_11pf.cpp",
"src/d2_9pf.cpp",
+ "src/d2_11pf.cpp",
"src/d3_14pf.cpp",
"src/d4_17pf.cpp",
"src/d8_31pf.cpp",
+ "src/d1035pf.cpp",
"src/d_gain_c.cpp",
"src/d_gain_p.cpp",
"src/d_plsf.cpp",
@@ -81,8 +82,8 @@
export_include_dirs: ["src"],
cflags: [
- "-DOSCL_UNUSED_ARG(x)=(void)(x)",
"-DOSCL_IMPORT_REF=",
+ "-DOSCL_UNUSED_ARG(x)=(void)(x)",
"-Werror",
],
@@ -94,8 +95,8 @@
//},
shared_libs: [
- "libstagefright_amrnb_common",
"liblog",
+ "libstagefright_amrnb_common",
],
target: {
@@ -113,19 +114,22 @@
srcs: ["test/amrnbdec_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
local_include_dirs: ["src"],
static_libs: [
- "libstagefright_amrnbdec",
"libsndfile",
+ "libstagefright_amrnbdec",
],
shared_libs: [
- "libstagefright_amrnb_common",
"libaudioutils",
"liblog",
+ "libstagefright_amrnb_common",
],
target: {
diff --git a/media/module/codecs/amrnb/enc/Android.bp b/media/module/codecs/amrnb/enc/Android.bp
index 3c6566e..13bb29c 100644
--- a/media/module/codecs/amrnb/enc/Android.bp
+++ b/media/module/codecs/amrnb/enc/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_codec_framework",
default_applicable_licenses: [
"frameworks_av_media_codecs_amrnb_enc_license",
],
@@ -42,12 +43,12 @@
srcs: [
"src/amrencode.cpp",
"src/autocorr.cpp",
- "src/c1035pf.cpp",
- "src/c2_11pf.cpp",
"src/c2_9pf.cpp",
+ "src/c2_11pf.cpp",
"src/c3_14pf.cpp",
"src/c4_17pf.cpp",
"src/c8_31pf.cpp",
+ "src/c1035pf.cpp",
"src/calc_cor.cpp",
"src/calc_en.cpp",
"src/cbsearch.cpp",
@@ -132,7 +133,10 @@
srcs: ["test/amrnb_enc_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
local_include_dirs: ["src"],
diff --git a/media/module/codecs/amrnb/enc/fuzzer/Android.bp b/media/module/codecs/amrnb/enc/fuzzer/Android.bp
index bcbcee2..1b2ec87 100644
--- a/media/module/codecs/amrnb/enc/fuzzer/Android.bp
+++ b/media/module/codecs/amrnb/enc/fuzzer/Android.bp
@@ -19,6 +19,7 @@
*/
package {
+ default_team: "trendy_team_media_codec_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_media_codecs_amrnb_enc_license"
@@ -39,8 +40,8 @@
static_libs: [
"liblog",
- "libstagefright_amrnbenc",
"libstagefright_amrnb_common",
+ "libstagefright_amrnbenc",
],
fuzz_config: {
diff --git a/media/module/codecs/amrnb/fuzzer/Android.bp b/media/module/codecs/amrnb/fuzzer/Android.bp
index 3f29267..c5cbbe2 100644
--- a/media/module/codecs/amrnb/fuzzer/Android.bp
+++ b/media/module/codecs/amrnb/fuzzer/Android.bp
@@ -19,6 +19,7 @@
*/
package {
+ default_team: "trendy_team_media_codec_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -34,9 +35,9 @@
"amrnb_dec_fuzzer.cpp",
],
static_libs: [
- "libstagefright_amrnbdec",
- "libstagefright_amrnb_common",
"liblog",
+ "libstagefright_amrnb_common",
+ "libstagefright_amrnbdec",
],
target: {
darwin: {
diff --git a/media/module/extractors/Android.bp b/media/module/extractors/Android.bp
index e29d3e6..cbaabe3 100644
--- a/media/module/extractors/Android.bp
+++ b/media/module/extractors/Android.bp
@@ -81,6 +81,12 @@
srcs: ["extractor.aconfig"],
}
+java_aconfig_library {
+ name: "android.media.extractor.flags-aconfig-java",
+ aconfig_declarations: "android.media.extractor.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
cc_aconfig_library {
name: "android.media.extractor.flags-aconfig-cc",
aconfig_declarations: "android.media.extractor.flags-aconfig",
diff --git a/media/module/extractors/extractor.aconfig b/media/module/extractors/extractor.aconfig
index c9bf694..a7d3397 100644
--- a/media/module/extractors/extractor.aconfig
+++ b/media/module/extractors/extractor.aconfig
@@ -8,7 +8,16 @@
name: "extractor_sniff_midi_optimizations"
is_exported: true
is_fixed_read_only: true
- namespace: "media_extractor"
+ namespace: "media_solutions"
description: "Enable SniffMidi optimizations."
bug: "359920208"
}
+
+flag {
+ name: "extractor_mp4_enable_apv"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "media_solutions"
+ description: "Enable APV support in mp4 extractor."
+ bug: "370061501"
+}
diff --git a/media/module/extractors/fuzzers/Android.bp b/media/module/extractors/fuzzers/Android.bp
index 3da1589..f3da389 100644
--- a/media/module/extractors/fuzzers/Android.bp
+++ b/media/module/extractors/fuzzers/Android.bp
@@ -129,12 +129,18 @@
],
static_libs: [
+ "android.media.extractor.flags-aconfig-cc",
+ "libaconfig_storage_read_api_cc",
"libstagefright_id3",
"libstagefright_esds",
"libmp4extractor",
"libstagefright_metadatautils",
],
+ shared_libs: [
+ "server_configurable_flags",
+ ],
+
dictionary: "mp4_extractor_fuzzer.dict",
corpus: ["corpus_mp4/*"],
diff --git a/media/module/extractors/mp4/Android.bp b/media/module/extractors/mp4/Android.bp
index 8072002..effd24a 100644
--- a/media/module/extractors/mp4/Android.bp
+++ b/media/module/extractors/mp4/Android.bp
@@ -42,12 +42,18 @@
],
static_libs: [
+ "android.media.extractor.flags-aconfig-cc",
+ "libaconfig_storage_read_api_cc",
"libstagefright_esds",
"libstagefright_foundation",
"libstagefright_id3",
"libutils",
],
+ shared_libs: [
+ "server_configurable_flags",
+ ],
+
host_supported: true,
target: {
diff --git a/media/module/extractors/mp4/MPEG4Extractor.cpp b/media/module/extractors/mp4/MPEG4Extractor.cpp
index 12c0aaf..f062491 100644
--- a/media/module/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/module/extractors/mp4/MPEG4Extractor.cpp
@@ -33,6 +33,7 @@
#include "SampleTable.h"
#include "ItemTable.h"
+#include <com_android_media_extractor_flags.h>
#include <media/esds/ESDS.h>
#include <ID3.h>
#include <media/stagefright/DataSourceBase.h>
@@ -147,6 +148,7 @@
bool mIsAVC;
bool mIsHEVC;
+ bool mIsAPV;
bool mIsDolbyVision;
bool mIsAC4;
bool mIsMpegH = false;
@@ -366,6 +368,13 @@
case FOURCC("hev1"):
return MEDIA_MIMETYPE_VIDEO_HEVC;
+ case FOURCC("apv1"):
+ if (!com::android::media::extractor::flags::extractor_mp4_enable_apv()) {
+ ALOGV("APV support not enabled");
+ return "application/octet-stream";
+ }
+ return MEDIA_MIMETYPE_VIDEO_APV;
+
case FOURCC("dvav"):
case FOURCC("dva1"):
case FOURCC("dvhe"):
@@ -2106,6 +2115,7 @@
case FOURCC("dav1"):
case FOURCC("av01"):
case FOURCC("vp09"):
+ case FOURCC("apv1"):
{
uint8_t buffer[78];
if (chunk_data_size < (ssize_t)sizeof(buffer)) {
@@ -2623,8 +2633,16 @@
break;
}
+ case FOURCC("apvC"):
case FOURCC("av1C"):
{
+ if (!com::android::media::extractor::flags::extractor_mp4_enable_apv() &&
+ chunk_type == FOURCC("apvC")) {
+ ALOGV("APV support not enabled");
+ *offset += chunk_size;
+ break;
+ }
+
auto buffer = heapbuffer<uint8_t>(chunk_data_size);
if (buffer.get() == NULL) {
@@ -5145,6 +5163,7 @@
mCurrentSampleInfoOffsets(NULL),
mIsAVC(false),
mIsHEVC(false),
+ mIsAPV(false),
mIsDolbyVision(false),
mIsAC4(false),
mIsPcm(false),
@@ -5187,6 +5206,8 @@
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+ mIsAPV = com::android::media::extractor::flags::extractor_mp4_enable_apv() &&
+ !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_APV);
mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
mIsHeif = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) && mItemTable != NULL;
diff --git a/media/module/extractors/tests/Android.bp b/media/module/extractors/tests/Android.bp
index d6e79c7..5f0f4fa 100644
--- a/media/module/extractors/tests/Android.bp
+++ b/media/module/extractors/tests/Android.bp
@@ -21,6 +21,7 @@
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_av_license"],
+ default_team: "trendy_team_android_media_solutions_playback",
}
cc_test {
@@ -31,6 +32,8 @@
srcs: ["ExtractorUnitTest.cpp"],
static_libs: [
+ "android.media.extractor.flags-aconfig-cc",
+ "libaconfig_storage_read_api_cc",
"libaacextractor",
"libamrextractor",
"libmp3extractor",
@@ -77,6 +80,7 @@
"libhidlmemory",
"libhidlbase",
"libbase",
+ "server_configurable_flags",
],
compile_multilib: "first",
diff --git a/media/mtp/OWNERS b/media/mtp/OWNERS
index 6b5336e..bdb6cdb 100644
--- a/media/mtp/OWNERS
+++ b/media/mtp/OWNERS
@@ -1,10 +1,9 @@
set noparent
-aprasath@google.com
anothermark@google.com
-kumarashishg@google.com
-sarup@google.com
+febinthattil@google.com
+aprasath@google.com
jsharkey@android.com
jameswei@google.com
rmojumder@google.com
-
+kumarashishg@google.com
\ No newline at end of file